pumuki-ast-hooks 5.3.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 (567) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1105 -0
  3. package/bin/__tests__/auto-fix-violations.spec.js +132 -0
  4. package/bin/__tests__/auto-restart-guards.spec.js +11 -0
  5. package/bin/__tests__/check-doc-drift.spec.js +11 -0
  6. package/bin/__tests__/check-version.spec.js +240 -0
  7. package/bin/__tests__/cli.spec.js +11 -0
  8. package/bin/__tests__/guard-auto-manager.spec.js +11 -0
  9. package/bin/__tests__/guard-supervisor.spec.js +11 -0
  10. package/bin/__tests__/hook-status.spec.js +11 -0
  11. package/bin/__tests__/install.spec.js +11 -0
  12. package/bin/__tests__/nightly-metrics-report.spec.js +94 -0
  13. package/bin/__tests__/plan-review.spec.js +11 -0
  14. package/bin/__tests__/predictive-hooks.spec.js +11 -0
  15. package/bin/__tests__/run-ast-adapter.spec.js +11 -0
  16. package/bin/__tests__/run-orchestrator.spec.js +11 -0
  17. package/bin/__tests__/run-playbook.spec.js +11 -0
  18. package/bin/__tests__/setup-eslint.spec.js +11 -0
  19. package/bin/__tests__/violations-api.spec.js +11 -0
  20. package/bin/__tests__/watch-hooks.spec.js +11 -0
  21. package/bin/ai-commit.sh +5 -0
  22. package/bin/audit +5 -0
  23. package/bin/audit-library.js +6 -0
  24. package/bin/auto-fix-violations.js +19 -0
  25. package/bin/auto-restart-guards.js +6 -0
  26. package/bin/check-doc-drift.js +6 -0
  27. package/bin/check-version.js +19 -0
  28. package/bin/cleanup-branches.sh +5 -0
  29. package/bin/cli.js +6 -0
  30. package/bin/demo-recording.sh +5 -0
  31. package/bin/demo-violations +5 -0
  32. package/bin/fix-enforcer +5 -0
  33. package/bin/fix-gitflow-enforcement.sh +5 -0
  34. package/bin/generate-progress-report.sh +5 -0
  35. package/bin/git-analyze-pairs.sh +5 -0
  36. package/bin/git-leave-branch-check.sh +5 -0
  37. package/bin/gitflow +5 -0
  38. package/bin/gitflow-shell-integration.sh +5 -0
  39. package/bin/guard-auto-manager.js +6 -0
  40. package/bin/guard-autostart.sh +5 -0
  41. package/bin/guard-env.sh +5 -0
  42. package/bin/guard-supervisor.js +6 -0
  43. package/bin/hook-status.js +6 -0
  44. package/bin/install-git-wrapper.sh +5 -0
  45. package/bin/install.js +6 -0
  46. package/bin/kill-mcp-zombies.sh +5 -0
  47. package/bin/nightly-metrics-report.js +8 -0
  48. package/bin/plan-review.js +6 -0
  49. package/bin/predictive-hooks.js +6 -0
  50. package/bin/pumuki-audit.js +6 -0
  51. package/bin/pumuki-init.js +19 -0
  52. package/bin/pumuki-mcp-server.js +13 -0
  53. package/bin/pumuki-mcp.js +6 -0
  54. package/bin/pumuki-rules.js +6 -0
  55. package/bin/request-no-verify-approval.sh +5 -0
  56. package/bin/run-ast-adapter.js +6 -0
  57. package/bin/run-intelligent-audit.sh +5 -0
  58. package/bin/run-orchestrator.js +6 -0
  59. package/bin/run-playbook.js +6 -0
  60. package/bin/session-loader.sh +5 -0
  61. package/bin/setup-eslint.js +6 -0
  62. package/bin/start-guards.sh +5 -0
  63. package/bin/sync-autonomous-orchestrator.sh +5 -0
  64. package/bin/sync-to-library.sh +5 -0
  65. package/bin/update-evidence.sh +5 -0
  66. package/bin/update-session-context.sh +5 -0
  67. package/bin/verify-no-verify.sh +5 -0
  68. package/bin/violations +5 -0
  69. package/bin/violations-api.js +6 -0
  70. package/bin/watch-hooks.js +6 -0
  71. package/docs/API_REFERENCE.md +161 -0
  72. package/docs/ARCHITECTURE.md +236 -0
  73. package/docs/ARCHITECTURE_DETAILED.md +499 -0
  74. package/docs/BRANCH_PROTECTION_GUIDE.md +236 -0
  75. package/docs/CODE_STANDARDS.md +440 -0
  76. package/docs/CONTRIBUTING.md +246 -0
  77. package/docs/DEPENDENCIES.md +541 -0
  78. package/docs/HOW_IT_WORKS.md +716 -0
  79. package/docs/INSTALLATION.md +784 -0
  80. package/docs/MCP_SERVERS.md +786 -0
  81. package/docs/TESTING.md +423 -0
  82. package/docs/USAGE.md +856 -0
  83. package/docs/images/ast_intelligence_01.png +0 -0
  84. package/docs/images/ast_intelligence_02.png +0 -0
  85. package/docs/images/ast_intelligence_03.png +0 -0
  86. package/docs/images/ast_intelligence_04.png +0 -0
  87. package/docs/images/ast_intelligence_05.png +0 -0
  88. package/hooks/getSkillRulesPath.ts +52 -0
  89. package/hooks/git-status-monitor.ts +160 -0
  90. package/hooks/index.js +5 -0
  91. package/hooks/notify-macos.ts +42 -0
  92. package/hooks/package.json +16 -0
  93. package/hooks/post-tool-use-tracker.sh +89 -0
  94. package/hooks/pre-tool-use-evidence-validator.ts +252 -0
  95. package/hooks/pre-tool-use-guard.ts +151 -0
  96. package/hooks/skill-activation-prompt.sh +8 -0
  97. package/hooks/skill-activation-prompt.ts +307 -0
  98. package/index.js +49 -0
  99. package/package.json +117 -0
  100. package/presentation/cli/audit.sh +24 -0
  101. package/presentation/cli/autonomous-status.sh +92 -0
  102. package/presentation/cli/categorize-violations.sh +179 -0
  103. package/presentation/cli/direct-audit-option2.sh +23 -0
  104. package/presentation/cli/direct-audit.sh +33 -0
  105. package/scripts/hooks-system/.AI_TOKEN_STATUS.txt +16 -0
  106. package/scripts/hooks-system/.audit-reports/auto-recovery.log +1 -0
  107. package/scripts/hooks-system/.audit-reports/install-wizard.log +4 -0
  108. package/scripts/hooks-system/.audit-reports/notifications.log +425 -0
  109. package/scripts/hooks-system/.audit-reports/token-monitor.log +1275 -0
  110. package/scripts/hooks-system/.audit_tmp/intelligent-report.json +44953 -0
  111. package/scripts/hooks-system/.audit_tmp/intelligent-report.txt +1338 -0
  112. package/scripts/hooks-system/.audit_tmp/severity-history.jsonl +1 -0
  113. package/scripts/hooks-system/.audit_tmp/token-usage.jsonl +1 -0
  114. package/scripts/hooks-system/.hook-system/config.json +8 -0
  115. package/scripts/hooks-system/application/CompositionRoot.js +325 -0
  116. package/scripts/hooks-system/application/__tests__/CompositionRoot.spec.js +84 -0
  117. package/scripts/hooks-system/application/commands/index.js +64 -0
  118. package/scripts/hooks-system/application/queries/index.js +60 -0
  119. package/scripts/hooks-system/application/services/AutonomousOrchestrator.js +130 -0
  120. package/scripts/hooks-system/application/services/ContextDetectionEngine.js +181 -0
  121. package/scripts/hooks-system/application/services/DynamicRulesLoader.js +182 -0
  122. package/scripts/hooks-system/application/services/GitFlowService.js +156 -0
  123. package/scripts/hooks-system/application/services/GitTreeState.js +140 -0
  124. package/scripts/hooks-system/application/services/HookSystemScheduler.js +77 -0
  125. package/scripts/hooks-system/application/services/IntelligentCommitAnalyzer.js +151 -0
  126. package/scripts/hooks-system/application/services/IntelligentGitTreeMonitor.js +118 -0
  127. package/scripts/hooks-system/application/services/PlatformAnalysisService.js +173 -0
  128. package/scripts/hooks-system/application/services/PlatformDetectionService.js +168 -0
  129. package/scripts/hooks-system/application/services/PlaybookRunner.js +39 -0
  130. package/scripts/hooks-system/application/services/PredictiveHookAdvisor.js +56 -0
  131. package/scripts/hooks-system/application/services/RealtimeGuardPlugin.js +62 -0
  132. package/scripts/hooks-system/application/services/RealtimeGuardService.js +374 -0
  133. package/scripts/hooks-system/application/services/SmartDirtyTreeAnalyzer.js +63 -0
  134. package/scripts/hooks-system/application/services/__tests__/AutonomousOrchestrator.spec.js +36 -0
  135. package/scripts/hooks-system/application/services/__tests__/ContextDetectionEngine.spec.js +33 -0
  136. package/scripts/hooks-system/application/services/__tests__/DynamicRulesLoader.spec.js +43 -0
  137. package/scripts/hooks-system/application/services/__tests__/GitTreeState.spec.js +163 -0
  138. package/scripts/hooks-system/application/services/__tests__/HookSystemScheduler.spec.js +207 -0
  139. package/scripts/hooks-system/application/services/__tests__/IntelligentCommitAnalyzer.spec.js +365 -0
  140. package/scripts/hooks-system/application/services/__tests__/IntelligentGitTreeMonitor.spec.js +188 -0
  141. package/scripts/hooks-system/application/services/__tests__/PlatformDetectionService.spec.js +28 -0
  142. package/scripts/hooks-system/application/services/__tests__/PlaybookRunner.spec.js +143 -0
  143. package/scripts/hooks-system/application/services/__tests__/PredictiveHookAdvisor.spec.js +181 -0
  144. package/scripts/hooks-system/application/services/__tests__/RealtimeGuardPlugin.spec.js +45 -0
  145. package/scripts/hooks-system/application/services/__tests__/RealtimeGuardService.critical.spec.js +401 -0
  146. package/scripts/hooks-system/application/services/commit/CommitMessageGenerator.js +34 -0
  147. package/scripts/hooks-system/application/services/commit/FeatureDetector.js +101 -0
  148. package/scripts/hooks-system/application/services/evidence/EvidenceContextManager.js +163 -0
  149. package/scripts/hooks-system/application/services/evidence/__tests__/EvidenceContextManager.spec.js +98 -0
  150. package/scripts/hooks-system/application/services/guard/GuardAutoManagerService.js +169 -0
  151. package/scripts/hooks-system/application/services/guard/GuardConfig.js +15 -0
  152. package/scripts/hooks-system/application/services/guard/GuardEventLogger.js +70 -0
  153. package/scripts/hooks-system/application/services/guard/GuardHealthReminder.js +54 -0
  154. package/scripts/hooks-system/application/services/guard/GuardHeartbeatMonitor.js +94 -0
  155. package/scripts/hooks-system/application/services/guard/GuardLockManager.js +72 -0
  156. package/scripts/hooks-system/application/services/guard/GuardMonitorLoop.js +29 -0
  157. package/scripts/hooks-system/application/services/guard/GuardNotificationHandler.js +36 -0
  158. package/scripts/hooks-system/application/services/guard/GuardProcessManager.js +113 -0
  159. package/scripts/hooks-system/application/services/guard/GuardRecoveryService.js +90 -0
  160. package/scripts/hooks-system/application/services/guard/__tests__/GuardAutoManagerService.spec.js +77 -0
  161. package/scripts/hooks-system/application/services/installation/ConfigurationGeneratorService.js +123 -0
  162. package/scripts/hooks-system/application/services/installation/FileSystemInstallerService.js +112 -0
  163. package/scripts/hooks-system/application/services/installation/GitEnvironmentService.js +166 -0
  164. package/scripts/hooks-system/application/services/installation/HookInstaller.js +197 -0
  165. package/scripts/hooks-system/application/services/installation/IdeIntegrationService.js +37 -0
  166. package/scripts/hooks-system/application/services/installation/InstallService.js +130 -0
  167. package/scripts/hooks-system/application/services/installation/McpConfigurator.js +172 -0
  168. package/scripts/hooks-system/application/services/installation/PlatformDetectorService.js +36 -0
  169. package/scripts/hooks-system/application/services/installation/VSCodeTaskConfigurator.js +97 -0
  170. package/scripts/hooks-system/application/services/logging/UnifiedLogger.js +142 -0
  171. package/scripts/hooks-system/application/services/logging/__tests__/UnifiedLogger.spec.js +66 -0
  172. package/scripts/hooks-system/application/services/monitoring/ActivityMonitor.js +80 -0
  173. package/scripts/hooks-system/application/services/monitoring/AstMonitor.js +140 -0
  174. package/scripts/hooks-system/application/services/monitoring/DevDocsMonitor.js +85 -0
  175. package/scripts/hooks-system/application/services/monitoring/EvidenceMonitor.js +103 -0
  176. package/scripts/hooks-system/application/services/monitoring/EvidenceMonitorService.js +162 -0
  177. package/scripts/hooks-system/application/services/monitoring/GitTreeMonitor.js +123 -0
  178. package/scripts/hooks-system/application/services/monitoring/GitTreeMonitorService.js +114 -0
  179. package/scripts/hooks-system/application/services/monitoring/HealthCheckProviders.js +153 -0
  180. package/scripts/hooks-system/application/services/monitoring/HealthCheckService.js +118 -0
  181. package/scripts/hooks-system/application/services/monitoring/HeartbeatMonitorService.js +61 -0
  182. package/scripts/hooks-system/application/services/monitoring/TokenMonitor.js +60 -0
  183. package/scripts/hooks-system/application/services/monitoring/__tests__/EvidenceMonitorService.spec.js +107 -0
  184. package/scripts/hooks-system/application/services/monitoring/__tests__/GitTreeMonitorService.spec.js +27 -0
  185. package/scripts/hooks-system/application/services/monitoring/__tests__/HealthCheckProviders.spec.js +68 -0
  186. package/scripts/hooks-system/application/services/monitoring/__tests__/HealthCheckService.spec.js +69 -0
  187. package/scripts/hooks-system/application/services/monitoring/__tests__/HeartbeatMonitorService.spec.js +35 -0
  188. package/scripts/hooks-system/application/services/notification/MacNotificationSender.js +106 -0
  189. package/scripts/hooks-system/application/services/notification/NotificationCenterService.js +221 -0
  190. package/scripts/hooks-system/application/services/notification/NotificationDispatcher.js +42 -0
  191. package/scripts/hooks-system/application/services/notification/__tests__/NotificationCenterService.spec.js +40 -0
  192. package/scripts/hooks-system/application/services/notification/components/NotificationCooldownManager.js +62 -0
  193. package/scripts/hooks-system/application/services/notification/components/NotificationDeduplicator.js +67 -0
  194. package/scripts/hooks-system/application/services/notification/components/NotificationQueue.js +36 -0
  195. package/scripts/hooks-system/application/services/notification/components/NotificationRetryExecutor.js +58 -0
  196. package/scripts/hooks-system/application/services/platform/PlatformHeuristics.js +144 -0
  197. package/scripts/hooks-system/application/services/recovery/AutoRecoveryManager.js +137 -0
  198. package/scripts/hooks-system/application/services/recovery/__tests__/AutoRecoveryManager.spec.js +62 -0
  199. package/scripts/hooks-system/application/services/smart-commit/CommitMessageSuggester.js +97 -0
  200. package/scripts/hooks-system/application/services/smart-commit/FileContextGrouper.js +114 -0
  201. package/scripts/hooks-system/application/services/smart-commit/SmartCommitSummaryBuilder.js +53 -0
  202. package/scripts/hooks-system/application/services/token/CursorTokenService.js +44 -0
  203. package/scripts/hooks-system/application/services/token/TokenMetricsService.js +109 -0
  204. package/scripts/hooks-system/application/services/token/TokenMonitorService.js +160 -0
  205. package/scripts/hooks-system/application/services/token/TokenStatusReporter.js +56 -0
  206. package/scripts/hooks-system/application/services/token/__tests__/CursorTokenService.spec.js +69 -0
  207. package/scripts/hooks-system/application/services/token/__tests__/TokenMonitorService.spec.js +185 -0
  208. package/scripts/hooks-system/application/state/HookSystemStateMachine.js +59 -0
  209. package/scripts/hooks-system/application/state/__tests__/HookSystemStateMachine.spec.js +115 -0
  210. package/scripts/hooks-system/application/use-cases/AnalyzeCodebaseUseCase.js +54 -0
  211. package/scripts/hooks-system/application/use-cases/AnalyzeStagedFilesUseCase.js +61 -0
  212. package/scripts/hooks-system/application/use-cases/AutoExecuteAIStartUseCase.js +123 -0
  213. package/scripts/hooks-system/application/use-cases/BlockCommitUseCase.js +90 -0
  214. package/scripts/hooks-system/application/use-cases/GenerateAuditReportUseCase.js +184 -0
  215. package/scripts/hooks-system/application/use-cases/__tests__/AnalyzeCodebaseUseCase.spec.js +156 -0
  216. package/scripts/hooks-system/application/use-cases/__tests__/AnalyzeStagedFilesUseCase.spec.js +146 -0
  217. package/scripts/hooks-system/application/use-cases/__tests__/AutoExecuteAIStartUseCase.spec.js +89 -0
  218. package/scripts/hooks-system/application/use-cases/__tests__/BlockCommitUseCase.spec.js +171 -0
  219. package/scripts/hooks-system/application/use-cases/__tests__/GenerateAuditReportUseCase.spec.js +207 -0
  220. package/scripts/hooks-system/bin/__tests__/auto-fix-violations.spec.js +132 -0
  221. package/scripts/hooks-system/bin/__tests__/auto-restart-guards.spec.js +11 -0
  222. package/scripts/hooks-system/bin/__tests__/check-doc-drift.spec.js +11 -0
  223. package/scripts/hooks-system/bin/__tests__/check-version.spec.js +240 -0
  224. package/scripts/hooks-system/bin/__tests__/cli.spec.js +11 -0
  225. package/scripts/hooks-system/bin/__tests__/guard-auto-manager.spec.js +11 -0
  226. package/scripts/hooks-system/bin/__tests__/guard-supervisor.spec.js +11 -0
  227. package/scripts/hooks-system/bin/__tests__/hook-status.spec.js +11 -0
  228. package/scripts/hooks-system/bin/__tests__/install.spec.js +11 -0
  229. package/scripts/hooks-system/bin/__tests__/nightly-metrics-report.spec.js +94 -0
  230. package/scripts/hooks-system/bin/__tests__/plan-review.spec.js +11 -0
  231. package/scripts/hooks-system/bin/__tests__/predictive-hooks.spec.js +11 -0
  232. package/scripts/hooks-system/bin/__tests__/run-ast-adapter.spec.js +11 -0
  233. package/scripts/hooks-system/bin/__tests__/run-orchestrator.spec.js +11 -0
  234. package/scripts/hooks-system/bin/__tests__/run-playbook.spec.js +11 -0
  235. package/scripts/hooks-system/bin/__tests__/setup-eslint.spec.js +11 -0
  236. package/scripts/hooks-system/bin/__tests__/violations-api.spec.js +11 -0
  237. package/scripts/hooks-system/bin/__tests__/watch-hooks.spec.js +11 -0
  238. package/scripts/hooks-system/bin/ai-commit.sh +63 -0
  239. package/scripts/hooks-system/bin/audit +463 -0
  240. package/scripts/hooks-system/bin/audit-library.js +54 -0
  241. package/scripts/hooks-system/bin/auto-fix-violations.js +130 -0
  242. package/scripts/hooks-system/bin/auto-restart-guards.js +93 -0
  243. package/scripts/hooks-system/bin/check-doc-drift.js +35 -0
  244. package/scripts/hooks-system/bin/check-version.js +201 -0
  245. package/scripts/hooks-system/bin/cleanup-branches.sh +106 -0
  246. package/scripts/hooks-system/bin/cli.js +208 -0
  247. package/scripts/hooks-system/bin/demo-recording.sh +57 -0
  248. package/scripts/hooks-system/bin/demo-violations +44 -0
  249. package/scripts/hooks-system/bin/fix-enforcer +27 -0
  250. package/scripts/hooks-system/bin/fix-gitflow-enforcement.sh +68 -0
  251. package/scripts/hooks-system/bin/generate-progress-report.sh +129 -0
  252. package/scripts/hooks-system/bin/git-analyze-pairs.sh +0 -0
  253. package/scripts/hooks-system/bin/git-leave-branch-check.sh +73 -0
  254. package/scripts/hooks-system/bin/gitflow +17 -0
  255. package/scripts/hooks-system/bin/gitflow-shell-integration.sh +64 -0
  256. package/scripts/hooks-system/bin/guard-auto-manager.js +44 -0
  257. package/scripts/hooks-system/bin/guard-autostart.sh +158 -0
  258. package/scripts/hooks-system/bin/guard-env.sh +40 -0
  259. package/scripts/hooks-system/bin/guard-supervisor.js +516 -0
  260. package/scripts/hooks-system/bin/hook-status.js +41 -0
  261. package/scripts/hooks-system/bin/install-git-wrapper.sh +53 -0
  262. package/scripts/hooks-system/bin/install.js +10 -0
  263. package/scripts/hooks-system/bin/kill-mcp-zombies.sh +48 -0
  264. package/scripts/hooks-system/bin/nightly-metrics-report.js +138 -0
  265. package/scripts/hooks-system/bin/plan-review.js +31 -0
  266. package/scripts/hooks-system/bin/predictive-hooks.js +18 -0
  267. package/scripts/hooks-system/bin/pumuki-audit.js +113 -0
  268. package/scripts/hooks-system/bin/pumuki-init.js +104 -0
  269. package/scripts/hooks-system/bin/pumuki-mcp.js +74 -0
  270. package/scripts/hooks-system/bin/pumuki-rules.js +74 -0
  271. package/scripts/hooks-system/bin/request-no-verify-approval.sh +116 -0
  272. package/scripts/hooks-system/bin/run-ast-adapter.js +86 -0
  273. package/scripts/hooks-system/bin/run-intelligent-audit.sh +67 -0
  274. package/scripts/hooks-system/bin/run-orchestrator.js +27 -0
  275. package/scripts/hooks-system/bin/run-playbook.js +23 -0
  276. package/scripts/hooks-system/bin/session-loader.sh +264 -0
  277. package/scripts/hooks-system/bin/setup-eslint.js +110 -0
  278. package/scripts/hooks-system/bin/start-guards.sh +190 -0
  279. package/scripts/hooks-system/bin/sync-autonomous-orchestrator.sh +32 -0
  280. package/scripts/hooks-system/bin/sync-to-library.sh +46 -0
  281. package/scripts/hooks-system/bin/update-evidence.sh +1167 -0
  282. package/scripts/hooks-system/bin/update-session-context.sh +261 -0
  283. package/scripts/hooks-system/bin/verify-no-verify.sh +68 -0
  284. package/scripts/hooks-system/bin/violations +20 -0
  285. package/scripts/hooks-system/bin/violations-api.js +345 -0
  286. package/scripts/hooks-system/bin/watch-hooks.js +20 -0
  287. package/scripts/hooks-system/config/project.config.json +36 -0
  288. package/scripts/hooks-system/config/state-map.json +12 -0
  289. package/scripts/hooks-system/domain/entities/AuditResult.js +139 -0
  290. package/scripts/hooks-system/domain/entities/Finding.js +116 -0
  291. package/scripts/hooks-system/domain/entities/SeverityConfig.js +73 -0
  292. package/scripts/hooks-system/domain/entities/SeverityConfig.ts +90 -0
  293. package/scripts/hooks-system/domain/entities/__tests__/AuditResult.spec.js +450 -0
  294. package/scripts/hooks-system/domain/entities/__tests__/Finding.spec.js +335 -0
  295. package/scripts/hooks-system/domain/entities/__tests__/SeverityConfig.spec.js +240 -0
  296. package/scripts/hooks-system/domain/entities/__tests__/entities.spec.js +29 -0
  297. package/scripts/hooks-system/domain/errors/__tests__/DomainErrors.spec.js +59 -0
  298. package/scripts/hooks-system/domain/errors/index.js +169 -0
  299. package/scripts/hooks-system/domain/events/__tests__/DomainEvents.spec.js +60 -0
  300. package/scripts/hooks-system/domain/events/index.js +121 -0
  301. package/scripts/hooks-system/domain/ports/IAstPort.js +67 -0
  302. package/scripts/hooks-system/domain/ports/IEvidencePort.js +86 -0
  303. package/scripts/hooks-system/domain/ports/IGitCommandPort.js +110 -0
  304. package/scripts/hooks-system/domain/ports/IGitPort.js +114 -0
  305. package/scripts/hooks-system/domain/ports/IGitQueryPort.js +93 -0
  306. package/scripts/hooks-system/domain/ports/INotificationPort.js +35 -0
  307. package/scripts/hooks-system/domain/ports/__tests__/ports.spec.js +36 -0
  308. package/scripts/hooks-system/domain/ports/index.js +14 -0
  309. package/scripts/hooks-system/domain/repositories/ICursorTokenRepository.js +13 -0
  310. package/scripts/hooks-system/domain/repositories/IFindingsRepository.js +30 -0
  311. package/scripts/hooks-system/domain/repositories/__tests__/IFindingsRepository.spec.js +18 -0
  312. package/scripts/hooks-system/domain/rules/CommitBlockingRules.js +142 -0
  313. package/scripts/hooks-system/domain/rules/__tests__/CommitBlockingRules.spec.js +18 -0
  314. package/scripts/hooks-system/domain/services/AuditAnalyzer.js +103 -0
  315. package/scripts/hooks-system/domain/services/AuditFilter.js +26 -0
  316. package/scripts/hooks-system/domain/services/AuditResultSerializer.js +35 -0
  317. package/scripts/hooks-system/domain/services/AuditScorer.js +38 -0
  318. package/scripts/hooks-system/domain/values/Severity.js +93 -0
  319. package/scripts/hooks-system/index.js +49 -0
  320. package/scripts/hooks-system/infrastructure/adapters/AstAnalyzerAdapter.js +150 -0
  321. package/scripts/hooks-system/infrastructure/adapters/FileEvidenceAdapter.js +140 -0
  322. package/scripts/hooks-system/infrastructure/adapters/GitCliAdapter.js +16 -0
  323. package/scripts/hooks-system/infrastructure/adapters/GitCommandAdapter.js +68 -0
  324. package/scripts/hooks-system/infrastructure/adapters/GitHubCliAdapter.js +85 -0
  325. package/scripts/hooks-system/infrastructure/adapters/GitQueryAdapter.js +58 -0
  326. package/scripts/hooks-system/infrastructure/adapters/LegacyAnalyzerAdapter.js +61 -0
  327. package/scripts/hooks-system/infrastructure/adapters/MacOSNotificationAdapter.js +99 -0
  328. package/scripts/hooks-system/infrastructure/adapters/__tests__/AstAnalyzerAdapter.spec.js +32 -0
  329. package/scripts/hooks-system/infrastructure/adapters/__tests__/FileEvidenceAdapter.spec.js +31 -0
  330. package/scripts/hooks-system/infrastructure/adapters/__tests__/GitCliAdapter.spec.js +39 -0
  331. package/scripts/hooks-system/infrastructure/adapters/__tests__/MacOSNotificationAdapter.spec.js +33 -0
  332. package/scripts/hooks-system/infrastructure/adapters/git/GitCommandRunner.js +78 -0
  333. package/scripts/hooks-system/infrastructure/adapters/git/GitCommandService.js +67 -0
  334. package/scripts/hooks-system/infrastructure/adapters/git/GitQueryService.js +50 -0
  335. package/scripts/hooks-system/infrastructure/adapters/index.js +14 -0
  336. package/scripts/hooks-system/infrastructure/ast/README.md +198 -0
  337. package/scripts/hooks-system/infrastructure/ast/__tests__/ast-core.spec.js +160 -0
  338. package/scripts/hooks-system/infrastructure/ast/__tests__/ast-intelligence.spec.js +20 -0
  339. package/scripts/hooks-system/infrastructure/ast/android/__tests__/ast-android.spec.js +33 -0
  340. package/scripts/hooks-system/infrastructure/ast/android/__tests__/clean-architecture-analyzer.spec.js +96 -0
  341. package/scripts/hooks-system/infrastructure/ast/android/__tests__/ddd-analyzer.spec.js +113 -0
  342. package/scripts/hooks-system/infrastructure/ast/android/__tests__/detekt-runner.spec.js +36 -0
  343. package/scripts/hooks-system/infrastructure/ast/android/__tests__/feature-first-analyzer.spec.js +80 -0
  344. package/scripts/hooks-system/infrastructure/ast/android/__tests__/native-bridge.spec.js +31 -0
  345. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidASTIntelligentAnalyzer.js +15 -0
  346. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidASTParser.js +157 -0
  347. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidAnalysisOrchestrator.js +164 -0
  348. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidArchitectureDetector.js +334 -0
  349. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidClassAnalyzer.js +162 -0
  350. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidForbiddenLiteralsAnalyzer.js +261 -0
  351. package/scripts/hooks-system/infrastructure/ast/android/analyzers/AndroidSOLIDAnalyzer.js +287 -0
  352. package/scripts/hooks-system/infrastructure/ast/android/analyzers/__tests__/AndroidForbiddenLiteralsAnalyzer.spec.js +58 -0
  353. package/scripts/hooks-system/infrastructure/ast/android/analyzers/__tests__/AndroidSOLIDAnalyzer.spec.js +84 -0
  354. package/scripts/hooks-system/infrastructure/ast/android/ast-android.js +1785 -0
  355. package/scripts/hooks-system/infrastructure/ast/android/clean-architecture-analyzer.js +115 -0
  356. package/scripts/hooks-system/infrastructure/ast/android/ddd-analyzer.js +70 -0
  357. package/scripts/hooks-system/infrastructure/ast/android/detekt-runner.js +81 -0
  358. package/scripts/hooks-system/infrastructure/ast/android/feature-first-analyzer.js +53 -0
  359. package/scripts/hooks-system/infrastructure/ast/android/native-bridge.js +119 -0
  360. package/scripts/hooks-system/infrastructure/ast/archive/README.md +18 -0
  361. package/scripts/hooks-system/infrastructure/ast/archive/ast-intelligence.ts +276 -0
  362. package/scripts/hooks-system/infrastructure/ast/archive/ios-rules.js +329 -0
  363. package/scripts/hooks-system/infrastructure/ast/archive/kotlin-analyzer.js +332 -0
  364. package/scripts/hooks-system/infrastructure/ast/archive/kotlin-parser.js +303 -0
  365. package/scripts/hooks-system/infrastructure/ast/archive/swift-analyzer.js +390 -0
  366. package/scripts/hooks-system/infrastructure/ast/ast-core.js +594 -0
  367. package/scripts/hooks-system/infrastructure/ast/ast-intelligence.js +617 -0
  368. package/scripts/hooks-system/infrastructure/ast/backend/__tests__/ast-backend.spec.js +20 -0
  369. package/scripts/hooks-system/infrastructure/ast/backend/__tests__/clean-architecture-analyzer.spec.js +151 -0
  370. package/scripts/hooks-system/infrastructure/ast/backend/__tests__/ddd-analyzer.spec.js +124 -0
  371. package/scripts/hooks-system/infrastructure/ast/backend/__tests__/feature-first-analyzer.spec.js +128 -0
  372. package/scripts/hooks-system/infrastructure/ast/backend/__tests__/forbidden-literals-analyzer.spec.js +95 -0
  373. package/scripts/hooks-system/infrastructure/ast/backend/__tests__/nestjs-patterns-analyzer.spec.js +59 -0
  374. package/scripts/hooks-system/infrastructure/ast/backend/__tests__/solid-analyzer.spec.js +114 -0
  375. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/BackendArchitectureDetector.js +141 -0
  376. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/BackendPatternDetector.js +23 -0
  377. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/__tests__/BackendArchitectureDetector.spec.js +239 -0
  378. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/__tests__/BackendPatternDetector.spec.js +58 -0
  379. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/detectors/CQRSDetector.js +41 -0
  380. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/detectors/CleanArchitectureDetector.js +52 -0
  381. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/detectors/FeatureFirstCleanDetector.js +74 -0
  382. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/detectors/LayeredArchitectureDetector.js +25 -0
  383. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/detectors/MVCDetector.js +32 -0
  384. package/scripts/hooks-system/infrastructure/ast/backend/analyzers/detectors/OnionArchitectureDetector.js +32 -0
  385. package/scripts/hooks-system/infrastructure/ast/backend/ast-backend-clean.js +44 -0
  386. package/scripts/hooks-system/infrastructure/ast/backend/ast-backend.js +2048 -0
  387. package/scripts/hooks-system/infrastructure/ast/backend/clean-architecture-analyzer.js +142 -0
  388. package/scripts/hooks-system/infrastructure/ast/backend/ddd-analyzer.js +256 -0
  389. package/scripts/hooks-system/infrastructure/ast/backend/feature-first-analyzer.js +70 -0
  390. package/scripts/hooks-system/infrastructure/ast/backend/forbidden-literals-analyzer.js +236 -0
  391. package/scripts/hooks-system/infrastructure/ast/backend/nestjs-patterns-analyzer.js +11 -0
  392. package/scripts/hooks-system/infrastructure/ast/backend/solid-analyzer.js +392 -0
  393. package/scripts/hooks-system/infrastructure/ast/common/BDDTDDWorkflowRules.js +52 -0
  394. package/scripts/hooks-system/infrastructure/ast/common/__tests__/BDDTDDWorkflowRules.spec.js +133 -0
  395. package/scripts/hooks-system/infrastructure/ast/common/__tests__/ast-common.spec.js +20 -0
  396. package/scripts/hooks-system/infrastructure/ast/common/__tests__/documentation-analyzer.spec.js +120 -0
  397. package/scripts/hooks-system/infrastructure/ast/common/__tests__/images-backend-analyzer.spec.js +123 -0
  398. package/scripts/hooks-system/infrastructure/ast/common/__tests__/monorepo-health-analyzer.spec.js +118 -0
  399. package/scripts/hooks-system/infrastructure/ast/common/__tests__/network-resilience-analyzer.spec.js +180 -0
  400. package/scripts/hooks-system/infrastructure/ast/common/__tests__/offline-backend-analyzer.spec.js +111 -0
  401. package/scripts/hooks-system/infrastructure/ast/common/__tests__/push-backend-analyzer.spec.js +124 -0
  402. package/scripts/hooks-system/infrastructure/ast/common/ast-common.js +345 -0
  403. package/scripts/hooks-system/infrastructure/ast/common/documentation-analyzer.js +217 -0
  404. package/scripts/hooks-system/infrastructure/ast/common/images-backend-analyzer.js +36 -0
  405. package/scripts/hooks-system/infrastructure/ast/common/monorepo-health-analyzer.js +452 -0
  406. package/scripts/hooks-system/infrastructure/ast/common/network-resilience-analyzer.js +178 -0
  407. package/scripts/hooks-system/infrastructure/ast/common/offline-backend-analyzer.js +53 -0
  408. package/scripts/hooks-system/infrastructure/ast/common/push-backend-analyzer.js +42 -0
  409. package/scripts/hooks-system/infrastructure/ast/common/rules/BDDRules.js +87 -0
  410. package/scripts/hooks-system/infrastructure/ast/common/rules/ImplementationRules.js +83 -0
  411. package/scripts/hooks-system/infrastructure/ast/common/rules/TDDRules.js +109 -0
  412. package/scripts/hooks-system/infrastructure/ast/common/rules/WorkflowRules.js +137 -0
  413. package/scripts/hooks-system/infrastructure/ast/frontend/__tests__/ast-frontend.spec.js +20 -0
  414. package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendArchitectureDetector.js +289 -0
  415. package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendForbiddenLiteralsAnalyzer.js +257 -0
  416. package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/FrontendSOLIDAnalyzer.js +274 -0
  417. package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/__tests__/FrontendArchitectureDetector.spec.js +151 -0
  418. package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/__tests__/FrontendForbiddenLiteralsAnalyzer.spec.js +20 -0
  419. package/scripts/hooks-system/infrastructure/ast/frontend/analyzers/__tests__/FrontendSOLIDAnalyzer.spec.js +108 -0
  420. package/scripts/hooks-system/infrastructure/ast/frontend/ast-frontend-clean.js +42 -0
  421. package/scripts/hooks-system/infrastructure/ast/frontend/ast-frontend.js +2094 -0
  422. package/scripts/hooks-system/infrastructure/ast/frontend/clean-architecture-analyzer.js +88 -0
  423. package/scripts/hooks-system/infrastructure/ast/frontend/ddd-analyzer.js +94 -0
  424. package/scripts/hooks-system/infrastructure/ast/frontend/feature-first-analyzer.js +51 -0
  425. package/scripts/hooks-system/infrastructure/ast/ios/__tests__/ast-ios.spec.js +40 -0
  426. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSArchitectureDetector.spec.js +20 -0
  427. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSArchitectureRules.spec.js +61 -0
  428. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSCICDRules.spec.js +10 -0
  429. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSEnterpriseAnalyzer.spec.js +36 -0
  430. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSForbiddenLiteralsAnalyzer.spec.js +64 -0
  431. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSNetworkingAdvancedRules.spec.js +10 -0
  432. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSPerformanceRules.spec.js +34 -0
  433. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSSPMRules.spec.js +10 -0
  434. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/__tests__/iOSSwiftUIAdvancedRules.spec.js +10 -0
  435. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSASTIntelligentAnalyzer.js +894 -0
  436. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureDetector.js +445 -0
  437. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSArchitectureRules.js +700 -0
  438. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSCICDRules.js +431 -0
  439. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSEnterpriseAnalyzer.js +580 -0
  440. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSForbiddenLiteralsAnalyzer.js +261 -0
  441. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSNetworkingAdvancedRules.js +177 -0
  442. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSPerformanceRules.js +11 -0
  443. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSSPMRules.js +496 -0
  444. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSSwiftUIAdvancedRules.js +333 -0
  445. package/scripts/hooks-system/infrastructure/ast/ios/analyzers/iOSTestingAdvancedRules.js +225 -0
  446. package/scripts/hooks-system/infrastructure/ast/ios/ast-ios.js +2176 -0
  447. package/scripts/hooks-system/infrastructure/ast/ios/native-bridge.js +92 -0
  448. package/scripts/hooks-system/infrastructure/ast/ios/parsers/SourceKittenParser.js +471 -0
  449. package/scripts/hooks-system/infrastructure/ast/ios/parsers/__tests__/SourceKittenParser.spec.js +41 -0
  450. package/scripts/hooks-system/infrastructure/ast/text/__tests__/text-scanner.spec.js +20 -0
  451. package/scripts/hooks-system/infrastructure/ast/text/text-scanner.js +1120 -0
  452. package/scripts/hooks-system/infrastructure/cache/CacheService.js +160 -0
  453. package/scripts/hooks-system/infrastructure/cli/__tests__/install-wizard.spec.js +16 -0
  454. package/scripts/hooks-system/infrastructure/cli/install-wizard.js +74 -0
  455. package/scripts/hooks-system/infrastructure/core/GitOperations.js +50 -0
  456. package/scripts/hooks-system/infrastructure/core/GitOperations.ts +112 -0
  457. package/scripts/hooks-system/infrastructure/core/__tests__/GitOperations.spec.js +146 -0
  458. package/scripts/hooks-system/infrastructure/eslint/eslint-integration.sh +75 -0
  459. package/scripts/hooks-system/infrastructure/events/EventListeners.js +143 -0
  460. package/scripts/hooks-system/infrastructure/events/__tests__/events.spec.js +14 -0
  461. package/scripts/hooks-system/infrastructure/external-tools/GitOperations.js +54 -0
  462. package/scripts/hooks-system/infrastructure/external-tools/eslint/backend.config.template.mjs +58 -0
  463. package/scripts/hooks-system/infrastructure/git-hooks/pre-push +35 -0
  464. package/scripts/hooks-system/infrastructure/git-server/pre-receive-hook +253 -0
  465. package/scripts/hooks-system/infrastructure/guards/git-wrapper.sh +32 -0
  466. package/scripts/hooks-system/infrastructure/guards/master-validator.sh +247 -0
  467. package/scripts/hooks-system/infrastructure/guards/prevent-no-verify.sh +34 -0
  468. package/scripts/hooks-system/infrastructure/hooks/__tests__/skill-activation-prompt.spec.js +11 -0
  469. package/scripts/hooks-system/infrastructure/hooks/pre-tool-use-intelligent-enforcer.sh +489 -0
  470. package/scripts/hooks-system/infrastructure/hooks/skill-activation-prompt.js +244 -0
  471. package/scripts/hooks-system/infrastructure/logging/UnifiedLoggerFactory.js +40 -0
  472. package/scripts/hooks-system/infrastructure/logging/__tests__/logging.spec.js +9 -0
  473. package/scripts/hooks-system/infrastructure/mcp/README.md +116 -0
  474. package/scripts/hooks-system/infrastructure/mcp/__tests__/ast-intelligence-automation.spec.js +38 -0
  475. package/scripts/hooks-system/infrastructure/mcp/__tests__/evidence-watcher.spec.js +38 -0
  476. package/scripts/hooks-system/infrastructure/mcp/ast-intelligence-automation.js +1097 -0
  477. package/scripts/hooks-system/infrastructure/mcp/evidence-watcher.js +128 -0
  478. package/scripts/hooks-system/infrastructure/mcp/package.json +17 -0
  479. package/scripts/hooks-system/infrastructure/mcp/services/EvidenceService.js +87 -0
  480. package/scripts/hooks-system/infrastructure/mcp/services/McpProtocolHandler.js +166 -0
  481. package/scripts/hooks-system/infrastructure/orchestration/__tests__/intelligent-audit.spec.js +11 -0
  482. package/scripts/hooks-system/infrastructure/orchestration/intelligent-audit.js +353 -0
  483. package/scripts/hooks-system/infrastructure/patterns/pattern-checks.sh +98 -0
  484. package/scripts/hooks-system/infrastructure/reporting/ReportImpactAnalyzer.js +109 -0
  485. package/scripts/hooks-system/infrastructure/reporting/ReportMetricsCalculator.js +114 -0
  486. package/scripts/hooks-system/infrastructure/reporting/ReportPresenter.js +86 -0
  487. package/scripts/hooks-system/infrastructure/reporting/__tests__/reporting.spec.js +15 -0
  488. package/scripts/hooks-system/infrastructure/reporting/report-generator.js +130 -0
  489. package/scripts/hooks-system/infrastructure/reporting/severity-tracker.js +105 -0
  490. package/scripts/hooks-system/infrastructure/repositories/CursorTokenRepository.js +76 -0
  491. package/scripts/hooks-system/infrastructure/repositories/FileFindingsRepository.js +88 -0
  492. package/scripts/hooks-system/infrastructure/repositories/__tests__/repositories.spec.js +20 -0
  493. package/scripts/hooks-system/infrastructure/repositories/datasources/CursorApiDataSource.js +73 -0
  494. package/scripts/hooks-system/infrastructure/repositories/datasources/CursorFileDataSource.js +55 -0
  495. package/scripts/hooks-system/infrastructure/severity/__tests__/severity-evaluator.spec.js +18 -0
  496. package/scripts/hooks-system/infrastructure/severity/analyzers/__tests__/maintainability-analyzer.spec.js +170 -0
  497. package/scripts/hooks-system/infrastructure/severity/analyzers/__tests__/performance-analyzer.spec.js +186 -0
  498. package/scripts/hooks-system/infrastructure/severity/analyzers/__tests__/security-analyzer.spec.js +151 -0
  499. package/scripts/hooks-system/infrastructure/severity/analyzers/__tests__/stability-analyzer.spec.js +143 -0
  500. package/scripts/hooks-system/infrastructure/severity/analyzers/maintainability-analyzer.js +100 -0
  501. package/scripts/hooks-system/infrastructure/severity/analyzers/performance-analyzer.js +109 -0
  502. package/scripts/hooks-system/infrastructure/severity/analyzers/security-analyzer.js +104 -0
  503. package/scripts/hooks-system/infrastructure/severity/analyzers/stability-analyzer.js +85 -0
  504. package/scripts/hooks-system/infrastructure/severity/context/analyzers/CodeClassificationAnalyzer.js +71 -0
  505. package/scripts/hooks-system/infrastructure/severity/context/analyzers/DataAnalyzer.js +64 -0
  506. package/scripts/hooks-system/infrastructure/severity/context/analyzers/ImpactAnalyzer.js +68 -0
  507. package/scripts/hooks-system/infrastructure/severity/context/analyzers/SafetyAnalyzer.js +82 -0
  508. package/scripts/hooks-system/infrastructure/severity/context/context-builder.js +88 -0
  509. package/scripts/hooks-system/infrastructure/severity/generators/RecommendationGenerator.js +153 -0
  510. package/scripts/hooks-system/infrastructure/severity/mappers/SeverityMapper.js +10 -0
  511. package/scripts/hooks-system/infrastructure/severity/policies/gate-policies.js +136 -0
  512. package/scripts/hooks-system/infrastructure/severity/policies/severity-policies.json +206 -0
  513. package/scripts/hooks-system/infrastructure/severity/scorers/ContextMultiplier.js +49 -0
  514. package/scripts/hooks-system/infrastructure/severity/severity-evaluator.js +117 -0
  515. package/scripts/hooks-system/infrastructure/shell/core/constants.sh +26 -0
  516. package/scripts/hooks-system/infrastructure/shell/core/utils.sh +45 -0
  517. package/scripts/hooks-system/infrastructure/shell/gitflow/git-wrapper.sh +646 -0
  518. package/scripts/hooks-system/infrastructure/shell/gitflow/gitflow-enforcer.sh +620 -0
  519. package/scripts/hooks-system/infrastructure/shell/gitflow/gitflow-state-manager.sh +235 -0
  520. package/scripts/hooks-system/infrastructure/shell/gitflow-state-manager.sh +225 -0
  521. package/scripts/hooks-system/infrastructure/shell/orchestrators/audit-orchestrator.sh +1106 -0
  522. package/scripts/hooks-system/infrastructure/shell/security/detect-secrets.sh +26 -0
  523. package/scripts/hooks-system/infrastructure/shell/security/detect_secrets.py +182 -0
  524. package/scripts/hooks-system/infrastructure/shell/validate-clean-architecture.sh +254 -0
  525. package/scripts/hooks-system/infrastructure/shell/validators/check-doc-structure.sh +62 -0
  526. package/scripts/hooks-system/infrastructure/shell/validators/ensure-critical-docs.sh +26 -0
  527. package/scripts/hooks-system/infrastructure/shell/validators/validate-ai-protocol.sh +474 -0
  528. package/scripts/hooks-system/infrastructure/shell/validators/validate-clean-architecture.sh +303 -0
  529. package/scripts/hooks-system/infrastructure/shell/validators/validate-conventional-commit.sh +42 -0
  530. package/scripts/hooks-system/infrastructure/storage/file-operations.sh +31 -0
  531. package/scripts/hooks-system/infrastructure/telemetry/TelemetryService.js +165 -0
  532. package/scripts/hooks-system/infrastructure/telemetry/__tests__/telemetry.spec.js +15 -0
  533. package/scripts/hooks-system/infrastructure/telemetry/metrics-logger.js +66 -0
  534. package/scripts/hooks-system/infrastructure/telemetry/metrics-server.js +61 -0
  535. package/scripts/hooks-system/infrastructure/utils/__tests__/utils.spec.js +8 -0
  536. package/scripts/hooks-system/infrastructure/utils/error-utils.js +28 -0
  537. package/scripts/hooks-system/infrastructure/utils/timestamp-helper.sh +106 -0
  538. package/scripts/hooks-system/infrastructure/utils/token-manager.js +121 -0
  539. package/scripts/hooks-system/infrastructure/validators/__tests__/detect-commit-language.spec.js +16 -0
  540. package/scripts/hooks-system/infrastructure/validators/__tests__/enforce-english-literals.spec.js +67 -0
  541. package/scripts/hooks-system/infrastructure/validators/detect-commit-language.js +145 -0
  542. package/scripts/hooks-system/infrastructure/validators/enforce-english-literals.js +202 -0
  543. package/scripts/hooks-system/infrastructure/watchdog/__tests__/.audit-reports/token-monitor.log +18 -0
  544. package/scripts/hooks-system/infrastructure/watchdog/__tests__/auto-recovery.spec.js +14 -0
  545. package/scripts/hooks-system/infrastructure/watchdog/__tests__/token-monitor.spec.js +67 -0
  546. package/scripts/hooks-system/infrastructure/watchdog/__tests__/watchdog.spec.js +22 -0
  547. package/scripts/hooks-system/infrastructure/watchdog/ai-watchdog.sh +278 -0
  548. package/scripts/hooks-system/infrastructure/watchdog/auto-recovery.js +32 -0
  549. package/scripts/hooks-system/infrastructure/watchdog/health-check.js +58 -0
  550. package/scripts/hooks-system/infrastructure/watchdog/token-monitor-loop.sh +20 -0
  551. package/scripts/hooks-system/infrastructure/watchdog/token-monitor.js +69 -0
  552. package/scripts/hooks-system/infrastructure/watchdog/token-tracker.sh +208 -0
  553. package/scripts/hooks-system/presentation/cli/audit.sh +32 -0
  554. package/scripts/hooks-system/presentation/cli/autonomous-status.sh +92 -0
  555. package/scripts/hooks-system/presentation/cli/categorize-violations.sh +179 -0
  556. package/scripts/hooks-system/presentation/cli/direct-audit-option2.sh +23 -0
  557. package/scripts/hooks-system/presentation/cli/direct-audit.sh +33 -0
  558. package/skills/android-guidelines/SKILL.md +475 -0
  559. package/skills/android-guidelines/resources/advanced-topics.md +44 -0
  560. package/skills/android-guidelines/resources/architecture-overview.md +44 -0
  561. package/skills/backend-guidelines/SKILL.md +335 -0
  562. package/skills/backend-guidelines/resources/architecture-overview.md +48 -0
  563. package/skills/frontend-guidelines/SKILL.md +367 -0
  564. package/skills/frontend-guidelines/resources/architecture-overview.md +44 -0
  565. package/skills/ios-guidelines/SKILL.md +406 -0
  566. package/skills/ios-guidelines/resources/architecture-overview.md +47 -0
  567. package/skills/skill-rules.json +334 -0
@@ -0,0 +1,1785 @@
1
+
2
+ const path = require('path');
3
+ const glob = require('glob');
4
+ const { pushFinding, mapToLevel, SyntaxKind, platformOf } = require(path.join(__dirname, '../ast-core'));
5
+ const { analyzeAndroidFiles: runDetektAnalysis } = require(path.join(__dirname, './detekt-runner'));
6
+ const { AndroidSOLIDAnalyzer } = require(path.join(__dirname, 'analyzers/AndroidSOLIDAnalyzer'));
7
+ const { AndroidForbiddenLiteralsAnalyzer } = require(path.join(__dirname, 'analyzers/AndroidForbiddenLiteralsAnalyzer'));
8
+ const { AndroidASTIntelligentAnalyzer } = require(path.join(__dirname, 'analyzers/AndroidASTIntelligentAnalyzer'));
9
+ const { AndroidArchitectureDetector } = require(path.join(__dirname, 'analyzers/AndroidArchitectureDetector'));
10
+
11
+ /**
12
+ * Run Android-specific AST intelligence analysis
13
+ * @param {Project} project - TypeScript morph project
14
+ * @param {Array} findings - Findings array to populate
15
+ * @param {string} platform - Platform identifier
16
+ */
17
+ function runAndroidIntelligence(project, findings, platform) {
18
+ console.error(`[Android AST Intelligence] Running Kotlin AST analysis...`);
19
+ const astAnalyzer = new AndroidASTIntelligentAnalyzer(findings);
20
+ const { getRepoRoot } = require(path.join(__dirname, '../ast-core'));
21
+ const root = getRepoRoot();
22
+ const kotlinFiles = glob.sync('**/*.kt', {
23
+ cwd: root,
24
+ ignore: ['**/node_modules/**', '**/build/**', '**/.gradle/**'],
25
+ absolute: true,
26
+ });
27
+
28
+ for (const kotlinFile of kotlinFiles) {
29
+ astAnalyzer.analyzeFile(kotlinFile);
30
+ }
31
+ console.error(`[Android AST Intelligence] Analyzed ${kotlinFiles.length} Kotlin files with AST`);
32
+
33
+ if (kotlinFiles.length > 0) {
34
+ try {
35
+ const architectureDetector = new AndroidArchitectureDetector(root);
36
+ const detectedPattern = architectureDetector.detect();
37
+ const detectionSummary = architectureDetector.getDetectionSummary();
38
+
39
+ console.error(`[Android Architecture] Pattern detected: ${detectedPattern} (confidence: ${detectionSummary.confidence}%)`);
40
+
41
+ if (detectionSummary.warnings.length > 0) {
42
+ detectionSummary.warnings.forEach(warning => {
43
+ pushFinding('android.architecture.detection_warning', warning.severity.toLowerCase(), null, null, warning.message + '\n\n' + warning.recommendation, findings);
44
+ });
45
+ }
46
+ } catch (error) {
47
+ console.error('[Android Architecture] Error during architecture detection:', error.message);
48
+ }
49
+ }
50
+
51
+ project.getSourceFiles().forEach((sf) => {
52
+ if (!sf || typeof sf.getFilePath !== 'function') return;
53
+ const filePath = sf.getFilePath();
54
+
55
+ if (platformOf(filePath) !== "android") return;
56
+
57
+ if (/\/ast-[^/]+\.js$/.test(filePath)) return;
58
+ if (/infrastructure\/ast\/|analyzers\/|detectors\//.test(filePath)) return;
59
+
60
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
61
+ const text = call.getExpression().getText();
62
+
63
+ if (text.includes("password") || text.includes("token") || text.includes("secret") || text.includes("key") || text.includes("api_key")) {
64
+ if (!sf.getFullText().includes("BuildConfig") && !sf.getFullText().includes("EncryptedSharedPreferences") && !sf.getFullText().includes("Keystore")) {
65
+ pushFinding("android.hardcoded_secrets", "critical", sf, call, "Hardcoded sensitive data - use EncryptedSharedPreferences, Keystore, or BuildConfig", findings);
66
+ }
67
+ }
68
+
69
+ if (text.includes("!!")) {
70
+ pushFinding("android.force_unwrapping", "critical", sf, call, "Force unwrapping (!) detected - use safe calls or null checks", findings);
71
+ }
72
+
73
+ if (filePath.endsWith(".java") && !filePath.includes("legacy") && !filePath.includes("old")) {
74
+ pushFinding("android.java_code", "critical", sf, sf, "Java file detected - use Kotlin for new development", findings);
75
+ }
76
+
77
+ if (filePath.includes("layout") && filePath.endsWith(".xml")) {
78
+ pushFinding("android.xml_layouts", "critical", sf, sf, "XML layout detected - use Jetpack Compose for new UIs", findings);
79
+ }
80
+
81
+ if (text.includes("Singleton") || text.includes("INSTANCE") || text.includes("getInstance()")) {
82
+ pushFinding("android.singletons", "critical", sf, call, "Singleton pattern detected - use dependency injection instead", findings);
83
+ }
84
+
85
+ if (text.includes("this@") || text.includes("context") && (text.includes("static") || text.includes("companion"))) {
86
+ pushFinding("android.context_leaks", "critical", sf, call, "Potential context leak - avoid storing context in static fields", findings);
87
+ }
88
+
89
+ if (filePath.endsWith(".kt")) {
90
+ // Null safety
91
+ if (text.includes("Any?") || text.includes("Any!") || text.includes("as?")) {
92
+ pushFinding("android.missing_null_safety", "high", sf, call, "Unsafe null handling - leverage Kotlin's null safety", findings);
93
+ }
94
+
95
+ if (text.includes("@Composable") && !sf.getFullText().includes("@Preview")) {
96
+ const hasPreview = sf.getDescendantsOfKind(SyntaxKind.Decorator).some(d =>
97
+ d.expression?.expression?.escapedText === "Preview"
98
+ );
99
+ if (!hasPreview) {
100
+ pushFinding("android.missing_composable", "high", sf, call, "Composable without @Preview - add previews for better development", findings);
101
+ }
102
+ }
103
+
104
+ if (text.includes("@Composable") && (text.includes("LaunchedEffect") || text.includes("DisposableEffect"))) {
105
+ if (!text.includes("key") || text.includes("Unit")) {
106
+ pushFinding("android.side_effects_composable", "medium", sf, call, "Composable with side effects - specify proper keys for LaunchedEffect/DisposableEffect", findings);
107
+ }
108
+ }
109
+
110
+ // Missing entity
111
+ if (text.includes("@Entity") && !sf.getFullText().includes("data class")) {
112
+ pushFinding("android.missing_entity", "medium", sf, call, "Entity not using data class - Room entities should be data classes", findings);
113
+ }
114
+
115
+ if (text.includes("ViewModel") && !sf.getFullText().includes("androidx.lifecycle.ViewModel")) {
116
+ pushFinding("android.missing_viewmodel", "high", sf, call, "Custom ViewModel without extending androidx.lifecycle.ViewModel", findings);
117
+ }
118
+
119
+ // Missing JUnit5
120
+ if (filePath.includes("test") && !sf.getFullText().includes("org.junit.jupiter")) {
121
+ pushFinding("android.missing_junit5", "high", sf, sf, "Test file not using JUnit5 - prefer JUnit5 over JUnit4", findings);
122
+ }
123
+ }
124
+
125
+ if (text.includes("findViewById")) {
126
+ pushFinding("android.findviewbyid", "high", sf, call, "findViewById usage - use View Binding or Compose instead", findings);
127
+ }
128
+
129
+ if (text.includes("AsyncTask")) {
130
+ pushFinding("android.asynctask", "high", sf, call, "AsyncTask usage - deprecated, use Coroutines instead", findings);
131
+ }
132
+
133
+ if (text.includes("startActivity") && !text.includes("Intent")) {
134
+ pushFinding("android.startactivity", "medium", sf, call, "Direct activity navigation - consider Navigation Component", findings);
135
+ }
136
+
137
+ if (text.includes("SharedPreferences") && !text.includes("Encrypted")) {
138
+ pushFinding("android.sharedpreferences", "medium", sf, call, "Plain SharedPreferences - use EncryptedSharedPreferences for sensitive data", findings);
139
+ }
140
+
141
+ if (text.includes("Handler") && text.includes("postDelayed")) {
142
+ pushFinding("android.handler_leak", "medium", sf, call, "Handler potential leak - use weak references or ViewModel scope", findings);
143
+ }
144
+
145
+ if (text.includes("Thread") || text.includes("Runnable")) {
146
+ pushFinding("android.raw_threads", "medium", sf, call, "Raw threads - prefer Coroutines for concurrency", findings);
147
+ }
148
+
149
+ if (text.includes("LiveData") && !text.includes("observe")) {
150
+ pushFinding("android.livedata_observe", "low", sf, call, "LiveData not observed - ensure LiveData is observed in UI", findings);
151
+ }
152
+
153
+ if (text.includes("Flow") && !text.includes("collect")) {
154
+ pushFinding("android.flow_collect", "low", sf, call, "Flow not collected - Flows need to be collected to emit values", findings);
155
+ }
156
+
157
+ if (text.includes("suspend") && !text.includes("viewModelScope") && !text.includes("lifecycleScope")) {
158
+ pushFinding("android.suspend_scope", "medium", sf, call, "Suspend function without proper scope - use viewModelScope or lifecycleScope", findings);
159
+ }
160
+
161
+ if (text.includes("MutableLiveData") && text.includes("public")) {
162
+ pushFinding("android.mutable_exposure", "medium", sf, call, "Mutable LiveData exposed publicly - expose only immutable LiveData", findings);
163
+ }
164
+
165
+ if (text.includes("data class") && text.includes("var")) {
166
+ pushFinding("android.data_class_var", "low", sf, call, "Data class with var properties - prefer val for immutable data", findings);
167
+ }
168
+
169
+ if (text.includes("sealed class") && !text.includes("when")) {
170
+ pushFinding("android.sealed_when", "low", sf, call, "Sealed class without exhaustive when - use when for sealed classes", findings);
171
+ }
172
+
173
+ if (text.includes("enum class") && !text.includes("companion object")) {
174
+ pushFinding("android.enum_companion", "low", sf, call, "Enum without companion object - consider adding utility functions", findings);
175
+ }
176
+
177
+ if (text.includes("inline") && text.includes("reified")) {
178
+ pushFinding("android.inline_reified", "low", sf, call, "Inline reified function - good for type-safe generics", findings);
179
+ }
180
+
181
+ if (text.includes("@SuppressLint")) {
182
+ pushFinding("android.suppress_lint", "medium", sf, call, "@SuppressLint usage - avoid suppressing lint warnings, fix the issues instead", findings);
183
+ }
184
+
185
+ if (text.includes("lateinit") && !text.includes("private")) {
186
+ pushFinding("android.lateinit_visibility", "medium", sf, call, "Public lateinit property - prefer private lateinit with public getter", findings);
187
+ }
188
+
189
+ if (text.includes("by lazy") && text.includes("public")) {
190
+ pushFinding("android.lazy_public", "low", sf, call, "Public lazy property - ensure thread safety if accessed from multiple threads", findings);
191
+ }
192
+
193
+ if (text.includes("companion object") && text.includes("const val")) {
194
+ pushFinding("android.companion_const", "low", sf, call, "Companion object constants - good practice for static constants", findings);
195
+ }
196
+
197
+ if (text.includes("operator fun")) {
198
+ pushFinding("android.operator_overload", "low", sf, call, "Operator overloading - use judiciously for clarity", findings);
199
+ }
200
+
201
+ if (text.includes("init") && text.includes("block")) {
202
+ pushFinding("android.init_block", "low", sf, call, "Init block usage - prefer primary constructor for simple initialization", findings);
203
+ }
204
+
205
+ if (text.includes("typealias")) {
206
+ pushFinding("android.typealias", "low", sf, call, "Typealias usage - good for simplifying complex types", findings);
207
+ }
208
+
209
+ if (text.includes("@Parcelize")) {
210
+ pushFinding("android.parcelize", "low", sf, call, "Parcelize usage - good for passing data between components", findings);
211
+ }
212
+
213
+ if (text.includes("remember") && !text.includes("key")) {
214
+ pushFinding("android.remember_key", "medium", sf, call, "Remember without key - provide keys for proper recomposition", findings);
215
+ }
216
+
217
+ if (text.includes("derivedStateOf") && !text.includes("remember")) {
218
+ pushFinding("android.derived_state", "low", sf, call, "Derived state - good for computed values from state", findings);
219
+ }
220
+
221
+ if (text.includes("LaunchedEffect") && text.includes("Unit")) {
222
+ pushFinding("android.launched_effect", "medium", sf, call, "LaunchedEffect with Unit key - may restart unnecessarily", findings);
223
+ }
224
+
225
+ if (text.includes("DisposableEffect") && !text.includes("onDispose")) {
226
+ pushFinding("android.disposable_effect", "medium", sf, call, "DisposableEffect without onDispose - add cleanup logic", findings);
227
+ }
228
+
229
+ if (text.includes("Modifier") && text.includes("then")) {
230
+ pushFinding("android.modifier_chain", "low", sf, call, "Modifier chaining - good practice for composable styling", findings);
231
+ }
232
+
233
+ if (text.includes("@Preview") && !text.includes("name")) {
234
+ pushFinding("android.preview_name", "low", sf, call, "Preview without name - add descriptive names for better organization", findings);
235
+ }
236
+
237
+ if (text.includes("Scaffold") && !text.includes("content")) {
238
+ pushFinding("android.scaffold_usage", "low", sf, call, "Scaffold usage - good for Material Design structure", findings);
239
+ }
240
+
241
+ if (text.includes("Column") || text.includes("Row")) {
242
+ pushFinding("android.layout_composables", "low", sf, call, "Layout composables - good for structuring UI", findings);
243
+ }
244
+
245
+ if (text.includes("LazyColumn") || text.includes("LazyRow")) {
246
+ pushFinding("android.lazy_lists", "low", sf, call, "Lazy lists - good for performance with large datasets", findings);
247
+ }
248
+
249
+ if (text.includes("ViewModel") && text.includes("factory")) {
250
+ pushFinding("android.viewmodel_factory", "low", sf, call, "ViewModel factory - good for dependency injection in ViewModels", findings);
251
+ }
252
+
253
+ if (text.includes("HiltViewModel")) {
254
+ pushFinding("android.hilt_viewmodel", "low", sf, call, "Hilt ViewModel - good for DI in ViewModels", findings);
255
+ }
256
+
257
+ if (text.includes("@Inject")) {
258
+ pushFinding("android.hilt_injection", "low", sf, call, "Hilt injection - good dependency injection practice", findings);
259
+ }
260
+
261
+ if (text.includes("@Module")) {
262
+ pushFinding("android.hilt_module", "low", sf, call, "Hilt module - good for providing dependencies", findings);
263
+ }
264
+
265
+ if (text.includes("@Provides")) {
266
+ pushFinding("android.provides", "low", sf, call, "@Provides - good for providing third-party or interface implementations", findings);
267
+ }
268
+
269
+ if (text.includes("@Binds")) {
270
+ pushFinding("android.binds", "low", sf, call, "@Binds - good for binding interfaces to implementations", findings);
271
+ }
272
+
273
+ if (text.includes("@Singleton")) {
274
+ pushFinding("android.singleton_scope", "low", sf, call, "Singleton scope - appropriate for application-wide dependencies", findings);
275
+ }
276
+
277
+ if (text.includes("@ViewModelScoped")) {
278
+ pushFinding("android.viewmodel_scoped", "low", sf, call, "ViewModel scoped - good for ViewModel-specific dependencies", findings);
279
+ }
280
+
281
+ if (text.includes("Retrofit")) {
282
+ pushFinding("android.retrofit_usage", "low", sf, call, "Retrofit usage - good REST client", findings);
283
+ }
284
+
285
+ if (text.includes("suspend") && text.includes("Retrofit")) {
286
+ pushFinding("android.suspend_functions", "low", sf, call, "Suspend functions with Retrofit - good for async networking", findings);
287
+ }
288
+
289
+ if (text.includes("OkHttp")) {
290
+ pushFinding("android.okhttp", "low", sf, call, "OkHttp usage - good HTTP client with interceptors", findings);
291
+ }
292
+
293
+ if (text.includes("Moshi") || text.includes("Gson")) {
294
+ pushFinding("android.json_serialization", "low", sf, call, "JSON serialization - good for parsing API responses", findings);
295
+ }
296
+
297
+ if (text.includes("Room")) {
298
+ pushFinding("android.room_usage", "low", sf, call, "Room usage - good type-safe SQLite wrapper", findings);
299
+ }
300
+
301
+ if (text.includes("@Dao")) {
302
+ pushFinding("android.dao_pattern", "low", sf, call, "DAO pattern - good for data access abstraction", findings);
303
+ }
304
+
305
+ if (text.includes("@Query") || text.includes("@Insert") || text.includes("@Update") || text.includes("@Delete")) {
306
+ pushFinding("android.room_annotations", "low", sf, call, "Room annotations - good for defining database operations", findings);
307
+ }
308
+
309
+ if (text.includes("Flow") && text.includes("Room")) {
310
+ pushFinding("android.room_flow", "low", sf, call, "Room with Flow - good for reactive database queries", findings);
311
+ }
312
+
313
+ if (text.includes("WorkManager")) {
314
+ pushFinding("android.work_manager", "low", sf, call, "WorkManager usage - good for background tasks", findings);
315
+ }
316
+
317
+ if (text.includes("StateFlow") || text.includes("SharedFlow")) {
318
+ pushFinding("android.state_flow", "low", sf, call, "State/SharedFlow usage - good for reactive state management", findings);
319
+ }
320
+
321
+ if (text.includes("collectAsState")) {
322
+ pushFinding("android.collect_state", "low", sf, call, "collectAsState - good for observing flows in Compose", findings);
323
+ }
324
+
325
+ if (text.includes("stateIn")) {
326
+ pushFinding("android.state_in", "low", sf, call, "stateIn operator - good for converting cold flows to hot StateFlows", findings);
327
+ }
328
+
329
+ if (text.includes("combine")) {
330
+ pushFinding("android.combine", "low", sf, call, "Combine operator - good for combining multiple flows", findings);
331
+ }
332
+
333
+ if (text.includes("flatMapLatest")) {
334
+ pushFinding("android.flatmap_latest", "low", sf, call, "flatMapLatest - good for switching between flows", findings);
335
+ }
336
+
337
+ if (text.includes("JUnit5") || text.includes("jupiter")) {
338
+ pushFinding("android.junit5", "low", sf, call, "JUnit5 usage - good modern testing framework", findings);
339
+ }
340
+
341
+ if (text.includes("MockK")) {
342
+ pushFinding("android.mockk", "low", sf, call, "MockK usage - good mocking library for Kotlin", findings);
343
+ }
344
+
345
+ if (text.includes("Turbine")) {
346
+ pushFinding("android.turbine", "low", sf, call, "Turbine - good for testing flows", findings);
347
+ }
348
+
349
+ if (text.includes("ComposeTestRule")) {
350
+ pushFinding("android.compose_testing", "low", sf, call, "Compose testing - good for UI testing", findings);
351
+ }
352
+
353
+ if (text.includes("Robolectric")) {
354
+ pushFinding("android.robolectric", "low", sf, call, "Robolectric - good for testing Android framework code", findings);
355
+ }
356
+
357
+ if (text.includes("Truth")) {
358
+ pushFinding("android.truth_assertions", "low", sf, call, "Truth assertions - good for readable test assertions", findings);
359
+ }
360
+
361
+ if (text.includes("Coil")) {
362
+ pushFinding("android.coil", "low", sf, call, "Coil usage - good async image loading for Compose", findings);
363
+ }
364
+
365
+ if (text.includes("Paging")) {
366
+ pushFinding("android.paging", "low", sf, call, "Paging library - good for handling large datasets", findings);
367
+ }
368
+
369
+ if (text.includes("Navigation")) {
370
+ pushFinding("android.navigation", "low", sf, call, "Navigation component - good for app navigation", findings);
371
+ }
372
+
373
+ if (text.includes("DataStore")) {
374
+ pushFinding("android.datastore", "low", sf, call, "DataStore - good modern replacement for SharedPreferences", findings);
375
+ }
376
+
377
+ if (text.includes("BiometricPrompt")) {
378
+ pushFinding("android.biometric", "low", sf, call, "Biometric authentication - good for secure user authentication", findings);
379
+ }
380
+
381
+ if (text.includes("SafetyNet")) {
382
+ pushFinding("android.safetynet", "low", sf, call, "SafetyNet - good for device integrity checks", findings);
383
+ }
384
+
385
+ if (text.includes("LeakCanary")) {
386
+ pushFinding("android.leakcanary", "low", sf, call, "LeakCanary - good for detecting memory leaks", findings);
387
+ }
388
+
389
+ if (text.includes("BaselineProfile")) {
390
+ pushFinding("android.baseline_profile", "low", sf, call, "Baseline profiles - good for app startup optimization", findings);
391
+ }
392
+
393
+ if (text.includes("Kotlin DSL")) {
394
+ pushFinding("android.kotlin_dsl", "low", sf, call, "Kotlin DSL for Gradle - good for type-safe build scripts", findings);
395
+ }
396
+
397
+ if (text.includes("version catalogs")) {
398
+ pushFinding("android.version_catalogs", "low", sf, call, "Version catalogs - good for centralized dependency management", findings);
399
+ }
400
+
401
+ if (text.includes("buildSrc")) {
402
+ pushFinding("android.buildsrc", "low", sf, call, "buildSrc - good for shared build logic", findings);
403
+ }
404
+
405
+ if (text.includes("product flavors")) {
406
+ pushFinding("android.product_flavors", "low", sf, call, "Product flavors - good for different app variants", findings);
407
+ }
408
+
409
+ if (text.includes("build variants")) {
410
+ pushFinding("android.build_variants", "low", sf, call, "Build variants - good for different build configurations", findings);
411
+ }
412
+
413
+ if (text.includes("Detekt")) {
414
+ pushFinding("android.detekt", "low", sf, call, "Detekt - good static analysis for Kotlin", findings);
415
+ }
416
+
417
+ if (text.includes("Firebase App Distribution")) {
418
+ pushFinding("android.firebase_distribution", "low", sf, call, "Firebase App Distribution - good for beta testing", findings);
419
+ }
420
+
421
+ if (text.includes("Play Console")) {
422
+ pushFinding("android.play_console", "low", sf, call, "Play Console - good for production deployments", findings);
423
+ }
424
+
425
+ if (text.includes("Timber")) {
426
+ pushFinding("android.timber", "low", sf, call, "Timber - good logging library", findings);
427
+ }
428
+
429
+ if (text.includes("Crashlytics")) {
430
+ pushFinding("android.crashlytics", "low", sf, call, "Crashlytics - good for crash reporting", findings);
431
+ }
432
+
433
+ if (text.includes("Analytics")) {
434
+ pushFinding("android.analytics", "low", sf, call, "Analytics - good for user behavior tracking", findings);
435
+ }
436
+
437
+ if (text.includes("strings.xml")) {
438
+ pushFinding("android.strings_xml", "low", sf, call, "strings.xml usage - good for localization", findings);
439
+ }
440
+
441
+ if (text.includes("plurals")) {
442
+ pushFinding("android.plurals", "low", sf, call, "Plurals support - good for proper pluralization", findings);
443
+ }
444
+
445
+ if (text.includes("DateFormat")) {
446
+ pushFinding("android.date_format", "low", sf, call, "DateFormat - good for localized date formatting", findings);
447
+ }
448
+
449
+ if (text.includes("NumberFormat")) {
450
+ pushFinding("android.number_format", "low", sf, call, "NumberFormat - good for localized number formatting", findings);
451
+ }
452
+
453
+ if (text.includes("TalkBack")) {
454
+ pushFinding("android.talkback", "low", sf, call, "TalkBack support - good for accessibility", findings);
455
+ }
456
+
457
+ if (text.includes("contentDescription")) {
458
+ pushFinding("android.content_description", "low", sf, call, "Content description - good for screen reader support", findings);
459
+ }
460
+
461
+ if (text.includes("semantics")) {
462
+ pushFinding("android.semantics", "low", sf, call, "Semantics modifier - good for accessibility in Compose", findings);
463
+ }
464
+
465
+ if (text.includes("DTO")) {
466
+ pushFinding("android.dto_sharing", "low", sf, call, "DTO classes - consider sharing with backend via code generation", findings);
467
+ }
468
+
469
+ if (text.includes("Repository")) {
470
+ pushFinding("android.repository_pattern", "low", sf, call, "Repository pattern - good for data abstraction", findings);
471
+ }
472
+
473
+ if (text.includes("UseCase")) {
474
+ pushFinding("android.use_case", "low", sf, call, "Use case pattern - good for business logic encapsulation", findings);
475
+ }
476
+
477
+ if (text.includes("ViewModel")) {
478
+ pushFinding("android.viewmodel_pattern", "low", sf, call, "ViewModel pattern - good for UI state management", findings);
479
+ }
480
+ });
481
+ });
482
+
483
+ project.getSourceFiles().forEach((sf) => {
484
+ if (!sf || typeof sf.getFilePath !== 'function') return;
485
+ const filePath = sf.getFilePath();
486
+ if (platformOf(filePath) !== "android" || !filePath.endsWith('.kt')) return;
487
+
488
+ const content = sf.getFullText();
489
+
490
+ const emptyCatchPattern = /catch\s*\([^)]*\)\s*\{\s*\}/g;
491
+ let match;
492
+ while ((match = emptyCatchPattern.exec(content)) !== null) {
493
+ const lineNumber = content.substring(0, match.index).split('\n').length;
494
+ pushFinding(
495
+ "android.error_handling.empty_catch",
496
+ "high",
497
+ sf,
498
+ sf,
499
+ `Line ${lineNumber}: Empty catch block - handle exception or use specific exception types`,
500
+ findings
501
+ );
502
+ }
503
+
504
+ const genericExceptionPattern = /catch\s*\(\s*\w+\s*:\s*Exception\s*\)/g;
505
+ while ((match = genericExceptionPattern.exec(content)) !== null) {
506
+ const lineNumber = content.substring(0, match.index).split('\n').length;
507
+ const hasSpecific = content.includes('IOException') || content.includes('NetworkException') ||
508
+ content.includes('HttpException') || content.includes('SQLException');
509
+ if (!hasSpecific) {
510
+ pushFinding(
511
+ "android.error_handling.generic_exception",
512
+ "medium",
513
+ sf,
514
+ sf,
515
+ `Line ${lineNumber}: Catch generic Exception - prefer specific exceptions (IOException, HttpException, etc.)`,
516
+ findings
517
+ );
518
+ }
519
+ }
520
+
521
+ const forceUnwrapPattern = /!!/g;
522
+ while ((match = forceUnwrapPattern.exec(content)) !== null) {
523
+ const lineNumber = content.substring(0, match.index).split('\n').length;
524
+ pushFinding(
525
+ "android.error_handling.force_unwrap",
526
+ "high",
527
+ sf,
528
+ sf,
529
+ `Line ${lineNumber}: NEVER use '!!' force unwrap - use safe calls (?.) or requireNotNull() with message`,
530
+ findings
531
+ );
532
+ }
533
+
534
+ const anyTypePattern = /:\s*Any\b/g;
535
+ while ((match = anyTypePattern.exec(content)) !== null) {
536
+ const lineNumber = content.substring(0, match.index).split('\n').length;
537
+ const lineText = content.split('\n')[lineNumber - 1];
538
+ const varName = lineText.match(/\b(\w+)\s*:\s*Any\b/)?.[1];
539
+ if (varName) {
540
+ const subsequentLines = content.split('\n').slice(lineNumber, lineNumber + 10).join('\n');
541
+ const hasTypeCheck = new RegExp(`${varName}\\s+is\\s+|${varName}\\s+as\\?|when\\s*\\(\\s*${varName}\\s*\\)`).test(subsequentLines);
542
+ if (!hasTypeCheck) {
543
+ pushFinding(
544
+ "android.typescript.any_without_guard",
545
+ "high",
546
+ sf,
547
+ sf,
548
+ `Line ${lineNumber}: Variable '${varName}: Any' used without type checking - use 'is', 'as?', or 'when' guards`,
549
+ findings
550
+ );
551
+ }
552
+ }
553
+ }
554
+
555
+ // ==========================================
556
+ // ==========================================
557
+
558
+ // 1. JETPACK COMPOSE
559
+
560
+ if (content.includes('findViewById')) {
561
+ const lineNumber = content.indexOf('findViewById') ? content.substring(0, content.indexOf('findViewById')).split('\n').length : 1;
562
+ pushFinding(
563
+ "android.compose.findviewbyid",
564
+ "high",
565
+ sf,
566
+ sf,
567
+ `Line ${lineNumber}: findViewById detected - use Jetpack Compose for new UI`,
568
+ findings
569
+ );
570
+ }
571
+
572
+ const composableFunctionPattern = /fun\s+([A-Z]\w+)\s*\([^)]*\)\s*\{[^}]*\b(Text|Button|Column|Row|Box|Card|LazyColumn)\b/g;
573
+ while ((match = composableFunctionPattern.exec(content)) !== null) {
574
+ const lineNumber = content.substring(0, match.index).split('\n').length;
575
+ const functionBlock = content.substring(match.index - 50, match.index);
576
+ if (!functionBlock.includes('@Composable')) {
577
+ pushFinding(
578
+ "android.compose.missing_annotation",
579
+ "high",
580
+ sf,
581
+ sf,
582
+ `Line ${lineNumber}: Function '${match[1]}' renders Compose UI - add @Composable annotation`,
583
+ findings
584
+ );
585
+ }
586
+ }
587
+
588
+ const sideEffectPattern = /@Composable[^{]*\{[^}]*\b(viewModel\.|repository\.|api\.)/g;
589
+ while ((match = sideEffectPattern.exec(content)) !== null) {
590
+ const lineNumber = content.substring(0, match.index).split('\n').length;
591
+ const block = content.substring(match.index, match.index + 300);
592
+ if (!block.includes('LaunchedEffect') && !block.includes('DisposableEffect')) {
593
+ pushFinding(
594
+ "android.compose.side_effect_without_effect",
595
+ "high",
596
+ sf,
597
+ sf,
598
+ `Line ${lineNumber}: Side effect in Composable without LaunchedEffect - wrap in LaunchedEffect`,
599
+ findings
600
+ );
601
+ }
602
+ }
603
+
604
+
605
+ const factoryPattern = /object\s+\w+Factory|class\s+\w+Factory/g;
606
+ while ((match = factoryPattern.exec(content)) !== null) {
607
+ const lineNumber = content.substring(0, match.index).split('\n').length;
608
+ pushFinding(
609
+ "android.di.manual_factory",
610
+ "medium",
611
+ sf,
612
+ sf,
613
+ `Line ${lineNumber}: Manual factory detected - use Hilt @Inject constructor instead`,
614
+ findings
615
+ );
616
+ }
617
+
618
+ const viewModelPattern = /class\s+(\w+ViewModel)\s*\([^)]*\)/g;
619
+ while ((match = viewModelPattern.exec(content)) !== null) {
620
+ const lineNumber = content.substring(0, match.index).split('\n').length;
621
+ const constructorBlock = content.substring(match.index - 50, match.index);
622
+ if (!constructorBlock.includes('@Inject')) {
623
+ pushFinding(
624
+ "android.di.missing_inject",
625
+ "medium",
626
+ sf,
627
+ sf,
628
+ `Line ${lineNumber}: ViewModel '${match[1]}' without @Inject - use Hilt constructor injection`,
629
+ findings
630
+ );
631
+ }
632
+ }
633
+
634
+ // 3. COROUTINES
635
+
636
+ if (content.includes('GlobalScope')) {
637
+ const lineNumber = content.indexOf('GlobalScope') ? content.substring(0, content.indexOf('GlobalScope')).split('\n').length : 1;
638
+ pushFinding(
639
+ "android.coroutines.global_scope",
640
+ "high",
641
+ sf,
642
+ sf,
643
+ `Line ${lineNumber}: GlobalScope detected - use viewModelScope or lifecycleScope for automatic cancellation`,
644
+ findings
645
+ );
646
+ }
647
+
648
+ const blockingCallsPattern = /Dispatchers\.Main[^}]*\b(Thread\.sleep|\.get\(\)|\.await\(\))/g;
649
+ while ((match = blockingCallsPattern.exec(content)) !== null) {
650
+ const lineNumber = content.substring(0, match.index).split('\n').length;
651
+ pushFinding(
652
+ "android.coroutines.blocking_on_main",
653
+ "critical",
654
+ sf,
655
+ sf,
656
+ `Line ${lineNumber}: Blocking call on Main dispatcher - move to Dispatchers.IO`,
657
+ findings
658
+ );
659
+ }
660
+
661
+ const suspendFunctionPattern = /suspend\s+fun\s+\w+[^{]*\{[^}]*\b(database|api|file|network)\b/g;
662
+ while ((match = suspendFunctionPattern.exec(content)) !== null) {
663
+ const lineNumber = content.substring(0, match.index).split('\n').length;
664
+ const functionBlock = content.substring(match.index, match.index + 300);
665
+ if (!functionBlock.includes('withContext')) {
666
+ pushFinding(
667
+ "android.coroutines.missing_withcontext",
668
+ "medium",
669
+ sf,
670
+ sf,
671
+ `Line ${lineNumber}: Suspend function with I/O operations - use withContext(Dispatchers.IO)`,
672
+ findings
673
+ );
674
+ }
675
+ }
676
+
677
+ // 4. FLOW
678
+
679
+ if (content.includes('LiveData') && !filePath.includes('legacy') && !filePath.includes('migration')) {
680
+ const lineNumber = content.indexOf('LiveData') ? content.substring(0, content.indexOf('LiveData')).split('\n').length : 1;
681
+ pushFinding(
682
+ "android.flow.livedata_in_new_code",
683
+ "medium",
684
+ sf,
685
+ sf,
686
+ `Line ${lineNumber}: LiveData in new code - prefer Flow/StateFlow for reactive streams`,
687
+ findings
688
+ );
689
+ }
690
+
691
+ const flowPattern = /:\s*Flow</g;
692
+ while ((match = flowPattern.exec(content)) !== null) {
693
+ const lineNumber = content.substring(0, match.index).split('\n').length;
694
+ const varName = content.split('\n')[lineNumber - 1].match(/val\s+(\w+)/)?.[1];
695
+ if (varName) {
696
+ const usagePattern = new RegExp(`${varName}\\.collect`);
697
+ if (!usagePattern.test(content)) {
698
+ pushFinding(
699
+ "android.flow.uncollected_flow",
700
+ "medium",
701
+ sf,
702
+ sf,
703
+ `Line ${lineNumber}: Flow '${varName}' defined but never collected`,
704
+ findings
705
+ );
706
+ }
707
+ }
708
+ }
709
+
710
+ // 5. ROOM DATABASE
711
+
712
+ const rawSqlPattern = /database\.execSQL|rawQuery\(/g;
713
+ while ((match = rawSqlPattern.exec(content)) !== null) {
714
+ const lineNumber = content.substring(0, match.index).split('\n').length;
715
+ pushFinding(
716
+ "android.room.raw_sql",
717
+ "high",
718
+ sf,
719
+ sf,
720
+ `Line ${lineNumber}: Raw SQL query - use Room @Query annotation for type safety`,
721
+ findings
722
+ );
723
+ }
724
+
725
+ if (content.includes('@Dao')) {
726
+ const daoBlock = content.substring(content.indexOf('@Dao'));
727
+ const hasSuspend = daoBlock.includes('suspend fun');
728
+ if (!hasSuspend && daoBlock.includes('@Insert')) {
729
+ const lineNumber = content.indexOf('@Dao') ? content.substring(0, content.indexOf('@Dao')).split('\n').length : 1;
730
+ pushFinding(
731
+ "android.room.dao_not_suspend",
732
+ "medium",
733
+ sf,
734
+ sf,
735
+ `Line ${lineNumber}: DAO operations should be suspend functions for async execution`,
736
+ findings
737
+ );
738
+ }
739
+ }
740
+
741
+
742
+ const mutableStatePattern = /var\s+\w+\s*=\s*mutableListOf|var\s+\w+\s*=\s*mutableMapOf/g;
743
+ while ((match = mutableStatePattern.exec(content)) !== null) {
744
+ const lineNumber = content.substring(0, match.index).split('\n').length;
745
+ const classContext = content.substring(0, match.index);
746
+ if (classContext.includes('ViewModel')) {
747
+ pushFinding(
748
+ "android.state.mutable_without_stateflow",
749
+ "medium",
750
+ sf,
751
+ sf,
752
+ `Line ${lineNumber}: Mutable state in ViewModel - use StateFlow for observable state`,
753
+ findings
754
+ );
755
+ }
756
+ }
757
+
758
+ const directMutationPattern = /\w+\.\w+\s*=\s*/g;
759
+ let mutationCount = 0;
760
+ while ((match = directMutationPattern.exec(content)) !== null && mutationCount < 3) {
761
+ const lineNumber = content.substring(0, match.index).split('\n').length;
762
+ const lineText = content.split('\n')[lineNumber - 1];
763
+ if (lineText.includes('state.') && !lineText.includes('copy(') && !lineText.includes('_state')) {
764
+ pushFinding(
765
+ "android.state.direct_mutation",
766
+ "medium",
767
+ sf,
768
+ sf,
769
+ `Line ${lineNumber}: Direct state mutation - use data class .copy() for immutability`,
770
+ findings
771
+ );
772
+ mutationCount++;
773
+ }
774
+ }
775
+
776
+
777
+ const retrofitCallPattern = /@GET|@POST|@PUT|@DELETE|@PATCH/g;
778
+ while ((match = retrofitCallPattern.exec(content)) !== null) {
779
+ const lineNumber = content.substring(0, match.index).split('\n').length;
780
+ const nextLines = content.split('\n').slice(lineNumber, lineNumber + 5).join('\n');
781
+ if (!nextLines.includes('suspend') && !nextLines.includes('Call<')) {
782
+ pushFinding(
783
+ "android.networking.sync_call",
784
+ "high",
785
+ sf,
786
+ sf,
787
+ `Line ${lineNumber}: Retrofit endpoint should be suspend function for async execution`,
788
+ findings
789
+ );
790
+ }
791
+ }
792
+
793
+ if (content.includes('Retrofit.Builder()') && !content.includes('addInterceptor')) {
794
+ const lineNumber = content.indexOf('Retrofit.Builder') ? content.substring(0, content.indexOf('Retrofit.Builder')).split('\n').length : 1;
795
+ pushFinding(
796
+ "android.networking.missing_interceptor",
797
+ "medium",
798
+ sf,
799
+ sf,
800
+ `Line ${lineNumber}: Retrofit without interceptors - add for auth tokens, logging`,
801
+ findings
802
+ );
803
+ }
804
+
805
+ // 8. SECURITY
806
+
807
+ if (content.includes('SharedPreferences') && (content.includes('password') || content.includes('token') || content.includes('secret'))) {
808
+ const lineNumber = content.indexOf('SharedPreferences') ? content.substring(0, content.indexOf('SharedPreferences')).split('\n').length : 1;
809
+ if (!content.includes('EncryptedSharedPreferences')) {
810
+ pushFinding(
811
+ "android.security.shared_prefs_sensitive",
812
+ "critical",
813
+ sf,
814
+ sf,
815
+ `Line ${lineNumber}: Sensitive data in SharedPreferences - use EncryptedSharedPreferences`,
816
+ findings
817
+ );
818
+ }
819
+ }
820
+
821
+ const apiKeyPattern = /api[_-]?key\s*=\s*"[^"]+"|BuildConfig\.\w*API\w*KEY\w*\s*=\s*"[^"]+"/gi;
822
+ while ((match = apiKeyPattern.exec(content)) !== null) {
823
+ const lineNumber = content.substring(0, match.index).split('\n').length;
824
+ if (!content.includes('local.properties') && !content.includes('secrets-gradle-plugin')) {
825
+ pushFinding(
826
+ "android.security.hardcoded_api_key",
827
+ "critical",
828
+ sf,
829
+ sf,
830
+ `Line ${lineNumber}: Hardcoded API key - use secrets-gradle-plugin or BuildConfig`,
831
+ findings
832
+ );
833
+ }
834
+ }
835
+
836
+
837
+ if (content.includes('RecyclerView') && !filePath.includes('legacy')) {
838
+ const lineNumber = content.indexOf('RecyclerView') ? content.substring(0, content.indexOf('RecyclerView')).split('\n').length : 1;
839
+ pushFinding(
840
+ "android.compose.recyclerview",
841
+ "medium",
842
+ sf,
843
+ sf,
844
+ `Line ${lineNumber}: RecyclerView in new code - use LazyColumn/LazyRow in Compose`,
845
+ findings
846
+ );
847
+ }
848
+
849
+ const composablePattern = /@Composable[^{]*\{[^}]*\b(filter|map|sortedBy|groupBy)\b/g;
850
+ while ((match = composablePattern.exec(content)) !== null) {
851
+ const lineNumber = content.substring(0, match.index).split('\n').length;
852
+ const block = content.substring(match.index, match.index + 200);
853
+ if (!block.includes('remember') && !block.includes('derivedStateOf')) {
854
+ pushFinding(
855
+ "android.compose.missing_remember",
856
+ "medium",
857
+ sf,
858
+ sf,
859
+ `Line ${lineNumber}: Expensive calculation in Composable - wrap in remember or derivedStateOf`,
860
+ findings
861
+ );
862
+ }
863
+ }
864
+
865
+ // 10. ARCHITECTURE
866
+
867
+ const activityPattern = /class\s+(\w+Activity)\s*:/g;
868
+ while ((match = activityPattern.exec(content)) !== null) {
869
+ const className = match[1];
870
+ const classStart = match.index;
871
+ const classEnd = content.indexOf('\n}', classStart);
872
+ if (classEnd > -1) {
873
+ const lines = content.substring(classStart, classEnd).split('\n').length;
874
+ if (lines > 500) {
875
+ pushFinding(
876
+ "android.architecture.god_activity",
877
+ "high",
878
+ sf,
879
+ sf,
880
+ `${className} has ${lines} lines - use Single Activity + Composables pattern`,
881
+ findings
882
+ );
883
+ }
884
+ }
885
+ }
886
+
887
+ if (content.includes('Activity') || content.includes('@Composable')) {
888
+ const businessLogicPatterns = ['repository.', 'database.', 'api.', 'Retrofit'];
889
+ businessLogicPatterns.forEach(pattern => {
890
+ if (content.includes(pattern)) {
891
+ const lineNumber = content.indexOf(pattern) ? content.substring(0, content.indexOf(pattern)).split('\n').length : 1;
892
+ const contextBlock = content.substring(0, match ? match.index : 0);
893
+ if (!contextBlock.includes('ViewModel') && !contextBlock.includes('UseCase')) {
894
+ pushFinding(
895
+ "android.architecture.business_logic_in_ui",
896
+ "high",
897
+ sf,
898
+ sf,
899
+ `Line ${lineNumber}: Business logic in UI layer - move to ViewModel or UseCase`,
900
+ findings
901
+ );
902
+ }
903
+ }
904
+ });
905
+ }
906
+
907
+ // 11. TESTING
908
+
909
+ if (content.includes('ViewModel') && !filePath.includes('Test') && !filePath.includes('Fake')) {
910
+ const className = content.match(/class\s+(\w+ViewModel)/)?.[1];
911
+ if (className) {
912
+ pushFinding(
913
+ "android.testing.missing_viewmodel_tests",
914
+ "medium",
915
+ sf,
916
+ sf,
917
+ `ViewModel '${className}' - ensure test file exists with JUnit5`,
918
+ findings
919
+ );
920
+ }
921
+ }
922
+
923
+ // 12. LOCALIZATION
924
+
925
+ const hardcodedStringPattern = /Text\s*\(\s*text\s*=\s*"[^"]{5,}"\s*\)|text\s*=\s*"[^"]+"/g;
926
+ let hardcodedCount = 0;
927
+ while ((match = hardcodedStringPattern.exec(content)) !== null && hardcodedCount < 5) {
928
+ const lineNumber = content.substring(0, match.index).split('\n').length;
929
+ const stringContent = match[0];
930
+ if (!stringContent.includes('stringResource') && !stringContent.match(/"(OK|Cancel|Error)"/)) {
931
+ pushFinding(
932
+ "android.i18n.hardcoded_string",
933
+ "medium",
934
+ sf,
935
+ sf,
936
+ `Line ${lineNumber}: Hardcoded string - use stringResource(R.string.xxx)`,
937
+ findings
938
+ );
939
+ hardcodedCount++;
940
+ }
941
+ }
942
+
943
+ // left/right instead of start/end
944
+ if (content.includes('Modifier.padding') && (content.includes('paddingLeft') || content.includes('paddingRight'))) {
945
+ const lineNumber = content.indexOf('paddingLeft') || content.indexOf('paddingRight') ?
946
+ content.substring(0, content.indexOf('paddingLeft') || content.indexOf('paddingRight')).split('\n').length : 1;
947
+ pushFinding(
948
+ "android.i18n.left_right_padding",
949
+ "medium",
950
+ sf,
951
+ sf,
952
+ `Line ${lineNumber}: Using left/right padding - use start/end for RTL support`,
953
+ findings
954
+ );
955
+ }
956
+
957
+ // 13. ACCESSIBILITY
958
+
959
+ if (content.includes('Image(') && !content.includes('contentDescription')) {
960
+ const imagePattern = /Image\s*\(/g;
961
+ let imageCount = 0;
962
+ while ((match = imagePattern.exec(content)) !== null && imageCount < 3) {
963
+ const lineNumber = content.substring(0, match.index).split('\n').length;
964
+ const nextLines = content.split('\n').slice(lineNumber - 1, lineNumber + 3).join('\n');
965
+ if (!nextLines.includes('contentDescription')) {
966
+ pushFinding(
967
+ "android.accessibility.missing_content_description",
968
+ "medium",
969
+ sf,
970
+ sf,
971
+ `Line ${lineNumber}: Image without contentDescription - add for TalkBack support`,
972
+ findings
973
+ );
974
+ imageCount++;
975
+ }
976
+ }
977
+ }
978
+
979
+ const modifierPattern = /Modifier\.size\s*\(\s*(\d+)\.dp\s*\)/g;
980
+ while ((match = modifierPattern.exec(content)) !== null) {
981
+ const size = parseInt(match[1]);
982
+ if (size < 48) {
983
+ const lineNumber = content.substring(0, match.index).split('\n').length;
984
+ pushFinding(
985
+ "android.accessibility.touch_target_small",
986
+ "medium",
987
+ sf,
988
+ sf,
989
+ `Line ${lineNumber}: Touch target ${size}dp - minimum 48dp for accessibility`,
990
+ findings
991
+ );
992
+ }
993
+ }
994
+
995
+
996
+ const lineCount = content.split('\n').length;
997
+ if (lineCount > 500 && !filePath.includes('Generated')) {
998
+ pushFinding(
999
+ "android.organization.file_too_large",
1000
+ "medium",
1001
+ sf,
1002
+ sf,
1003
+ `File has ${lineCount} lines - split into smaller files (max 500)`,
1004
+ findings
1005
+ );
1006
+ }
1007
+
1008
+ // 15. ANTI-PATTERNS
1009
+
1010
+ if (content.includes('AsyncTask')) {
1011
+ const lineNumber = content.indexOf('AsyncTask') ? content.substring(0, content.indexOf('AsyncTask')).split('\n').length : 1;
1012
+ pushFinding(
1013
+ "android.antipattern.async_task",
1014
+ "high",
1015
+ sf,
1016
+ sf,
1017
+ `Line ${lineNumber}: AsyncTask is deprecated - use Kotlin Coroutines`,
1018
+ findings
1019
+ );
1020
+ }
1021
+
1022
+ if (content.includes('Observable<') || content.includes('Single<') || content.includes('Flowable<')) {
1023
+ if (!filePath.includes('legacy') && !filePath.includes('migration')) {
1024
+ const lineNumber = content.indexOf('Observable') || content.indexOf('Single') || content.indexOf('Flowable') ?
1025
+ content.substring(0, content.indexOf('Observable') || content.indexOf('Single') || content.indexOf('Flowable')).split('\n').length : 1;
1026
+ pushFinding(
1027
+ "android.antipattern.rxjava",
1028
+ "medium",
1029
+ sf,
1030
+ sf,
1031
+ `Line ${lineNumber}: RxJava in new code - use Kotlin Flow instead`,
1032
+ findings
1033
+ );
1034
+ }
1035
+ }
1036
+
1037
+ const contextLeakPattern = /class\s+\w+\s*\([^)]*context:\s*Context\s*\)/g;
1038
+ while ((match = contextLeakPattern.exec(content)) !== null) {
1039
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1040
+ const classBlock = content.substring(match.index, match.index + 100);
1041
+ if (classBlock.includes('@Singleton') || classBlock.includes('object ')) {
1042
+ pushFinding(
1043
+ "android.memory.context_leak",
1044
+ "critical",
1045
+ sf,
1046
+ sf,
1047
+ `Line ${lineNumber}: Context reference in Singleton - use ApplicationContext to avoid leaks`,
1048
+ findings
1049
+ );
1050
+ }
1051
+ }
1052
+
1053
+ // 16. LOGGING
1054
+
1055
+ const logPattern = /Log\.[dewi]\s*\(/g;
1056
+ while ((match = logPattern.exec(content)) !== null) {
1057
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1058
+ const surroundingLines = content.split('\n').slice(Math.max(0, lineNumber - 3), lineNumber + 2).join('\n');
1059
+ if (!surroundingLines.includes('BuildConfig.DEBUG') && !surroundingLines.includes('Timber')) {
1060
+ pushFinding(
1061
+ "android.logging.production_logs",
1062
+ "medium",
1063
+ sf,
1064
+ sf,
1065
+ `Line ${lineNumber}: Log in production - wrap with if (BuildConfig.DEBUG) or use Timber`,
1066
+ findings
1067
+ );
1068
+ }
1069
+ }
1070
+
1071
+ // 17. SEALED CLASSES
1072
+
1073
+ if (content.includes('State') && content.includes('data class') && !content.includes('sealed')) {
1074
+ const stateClassPattern = /data\s+class\s+(\w*State)\s*\(/g;
1075
+ while ((match = stateClassPattern.exec(content)) !== null) {
1076
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1077
+ pushFinding(
1078
+ "android.architecture.missing_sealed_state",
1079
+ "low",
1080
+ sf,
1081
+ sf,
1082
+ `Line ${lineNumber}: State class '${match[1]}' - consider sealed class for Loading/Success/Error states`,
1083
+ findings
1084
+ );
1085
+ }
1086
+ }
1087
+
1088
+ const hasJavaFile = filePath.endsWith('.java');
1089
+ if (hasJavaFile && !filePath.includes('build/') && !filePath.includes('generated/')) {
1090
+ pushFinding(
1091
+ "android.kotlin.java_detected",
1092
+ "critical",
1093
+ sf,
1094
+ sf,
1095
+ 'Java file detected - use Kotlin 100% for new code (NO Java in new code)',
1096
+ findings
1097
+ );
1098
+ }
1099
+
1100
+ if (!content.includes('fun ') || (!content.includes('fun String.') && !content.includes('fun Int.') && !content.includes('fun List.'))) {
1101
+ if ((content.match(/fun\s+\w+/g) || []).length > 10 && !filePath.includes('ViewModel')) {
1102
+ pushFinding(
1103
+ "android.kotlin.missing_extensions",
1104
+ "low",
1105
+ sf,
1106
+ sf,
1107
+ 'File with many functions - consider extension functions for better organization',
1108
+ findings
1109
+ );
1110
+ }
1111
+ }
1112
+
1113
+ const hasScopeFunctions = content.includes('.let {') || content.includes('.run {') || content.includes('.apply {');
1114
+ const hasNullChecks = (content.match(/if\s*\(\s*\w+\s*!=\s*null\s*\)/g) || []).length;
1115
+ if (hasNullChecks > 3 && !hasScopeFunctions) {
1116
+ pushFinding(
1117
+ "android.kotlin.missing_scope_functions",
1118
+ "medium",
1119
+ sf,
1120
+ sf,
1121
+ `${hasNullChecks} null checks without scope functions - use let, run, apply for cleaner code`,
1122
+ findings
1123
+ );
1124
+ }
1125
+
1126
+ if ((content.includes('Response') || content.includes('Request') || content.includes('DTO')) &&
1127
+ content.includes('class') && !content.includes('data class')) {
1128
+ pushFinding(
1129
+ "android.kotlin.missing_data_class",
1130
+ "high",
1131
+ sf,
1132
+ sf,
1133
+ 'Response/Request/DTO without data class - use data class for DTOs and models',
1134
+ findings
1135
+ );
1136
+ }
1137
+
1138
+ const hasXMLLayout = filePath.includes('/res/layout/') && filePath.endsWith('.xml');
1139
+ if (hasXMLLayout && !filePath.includes('navigation.xml')) {
1140
+ pushFinding(
1141
+ "android.compose.xml_layout_detected",
1142
+ "high",
1143
+ sf,
1144
+ sf,
1145
+ 'XML layout detected - use Jetpack Compose 100% for new UI code',
1146
+ findings
1147
+ );
1148
+ }
1149
+
1150
+ if (content.includes('@Composable') && !content.includes('fun ')) {
1151
+ pushFinding(
1152
+ "android.compose.composable_not_function",
1153
+ "critical",
1154
+ sf,
1155
+ sf,
1156
+ '@Composable must be a function - Composable functions pattern',
1157
+ findings
1158
+ );
1159
+ }
1160
+
1161
+ if (content.includes('@Composable') && content.includes('var ') && content.includes('mutableStateOf')) {
1162
+ if (!content.includes('remember') && !content.includes('rememberSaveable')) {
1163
+ pushFinding(
1164
+ "android.compose.missing_remember",
1165
+ "high",
1166
+ sf,
1167
+ sf,
1168
+ 'mutableStateOf without remember - state will be lost on recomposition',
1169
+ findings
1170
+ );
1171
+ }
1172
+ }
1173
+
1174
+ if (content.includes('remember {') && content.includes('mutableStateOf') && !content.includes('rememberSaveable')) {
1175
+ if (filePath.includes('Screen') || filePath.includes('Activity')) {
1176
+ pushFinding(
1177
+ "android.compose.use_remembersaveable",
1178
+ "medium",
1179
+ sf,
1180
+ sf,
1181
+ 'Screen-level state with remember - use rememberSaveable to survive process death',
1182
+ findings
1183
+ );
1184
+ }
1185
+ }
1186
+
1187
+ if (content.includes('@Composable') && (content.includes('viewModel') || content.includes('repository'))) {
1188
+ if (content.includes('.collect') && !content.includes('LaunchedEffect')) {
1189
+ pushFinding(
1190
+ "android.compose.missing_launched_effect",
1191
+ "high",
1192
+ sf,
1193
+ sf,
1194
+ 'Flow.collect in Composable without LaunchedEffect - use LaunchedEffect for side effects with lifecycle',
1195
+ findings
1196
+ );
1197
+ }
1198
+ }
1199
+
1200
+ if (content.includes('@Composable') && (content.includes('Observer') || content.includes('Listener') || content.includes('register'))) {
1201
+ if (!content.includes('DisposableEffect')) {
1202
+ pushFinding(
1203
+ "android.compose.missing_disposable_effect",
1204
+ "high",
1205
+ sf,
1206
+ sf,
1207
+ 'Resource registration without DisposableEffect - implement cleanup when Composable leaves composition',
1208
+ findings
1209
+ );
1210
+ }
1211
+ }
1212
+
1213
+ if (content.includes('Modifier') && content.includes('.background(') && content.includes('.padding(')) {
1214
+ const modifierChainPattern = /\.background\([^)]+\)\.padding\(/g;
1215
+ if (modifierChainPattern.test(content)) {
1216
+ pushFinding(
1217
+ "android.compose.wrong_modifier_order",
1218
+ "medium",
1219
+ sf,
1220
+ sf,
1221
+ 'Modifier.background().padding() - wrong order, use .padding().background() (padding before background)',
1222
+ findings
1223
+ );
1224
+ }
1225
+ }
1226
+
1227
+ if (content.includes('@Composable') && !content.includes('@Preview') && !filePath.includes('ViewModel')) {
1228
+ const composableCount = (content.match(/@Composable/g) || []).length;
1229
+ if (composableCount > 0 && composableCount < 5) {
1230
+ pushFinding(
1231
+ "android.compose.missing_preview",
1232
+ "low",
1233
+ sf,
1234
+ sf,
1235
+ 'Composable without @Preview - add @Preview for development visualization',
1236
+ findings
1237
+ );
1238
+ }
1239
+ }
1240
+
1241
+ if (content.includes('import androidx.compose.material.') && !content.includes('material3')) {
1242
+ pushFinding(
1243
+ "android.material.use_material3",
1244
+ "medium",
1245
+ sf,
1246
+ sf,
1247
+ 'Using Material 2 components - migrate to Material 3 (androidx.compose.material3)',
1248
+ findings
1249
+ );
1250
+ }
1251
+
1252
+ if (content.includes('@Composable') && content.includes('Color(') && !content.includes('isSystemInDarkTheme')) {
1253
+ if ((content.match(/Color\(0x/g) || []).length > 3) {
1254
+ pushFinding(
1255
+ "android.material.missing_dark_theme",
1256
+ "medium",
1257
+ sf,
1258
+ sf,
1259
+ 'Hardcoded colors without dark theme support - use isSystemInDarkTheme() for adaptive theming',
1260
+ findings
1261
+ );
1262
+ }
1263
+ }
1264
+
1265
+ if (content.includes('@Composable') && (content.includes('Column') || content.includes('Row')) &&
1266
+ !content.includes('WindowSizeClass') && lineCount > 100) {
1267
+ pushFinding(
1268
+ "android.material.missing_adaptive_layout",
1269
+ "low",
1270
+ sf,
1271
+ sf,
1272
+ 'Complex layout without WindowSizeClass - implement responsive design for tablets',
1273
+ findings
1274
+ );
1275
+ }
1276
+
1277
+ if (content.includes('class') && content.includes('Activity') && !content.includes('MainActivity')) {
1278
+ const activityPattern = /class\s+(\w+Activity)\s*:/g;
1279
+ const activities = [];
1280
+ while ((match = activityPattern.exec(content)) !== null) {
1281
+ if (match[1] !== 'MainActivity') {
1282
+ activities.push(match[1]);
1283
+ }
1284
+ }
1285
+ if (activities.length > 0) {
1286
+ pushFinding(
1287
+ "android.architecture.multiple_activities",
1288
+ "medium",
1289
+ sf,
1290
+ sf,
1291
+ `Multiple Activities detected (${activities.join(', ')}) - use Single Activity + Composables pattern`,
1292
+ findings
1293
+ );
1294
+ }
1295
+ }
1296
+
1297
+ if (content.includes('NavHost') && !content.includes('androidx.navigation.compose')) {
1298
+ pushFinding(
1299
+ "android.navigation.use_compose_navigation",
1300
+ "high",
1301
+ sf,
1302
+ sf,
1303
+ 'NavHost without Compose Navigation - use androidx.navigation:navigation-compose',
1304
+ findings
1305
+ );
1306
+ }
1307
+
1308
+ if (content.includes('ViewModel') && content.includes('LiveData') && !content.includes('StateFlow')) {
1309
+ pushFinding(
1310
+ "android.architecture.use_stateflow",
1311
+ "high",
1312
+ sf,
1313
+ sf,
1314
+ 'ViewModel using LiveData - migrate to StateFlow for Kotlin Flow support',
1315
+ findings
1316
+ );
1317
+ }
1318
+
1319
+ if (filePath.includes('ViewModel') && !content.includes('@HiltViewModel') && content.includes('ViewModel()')) {
1320
+ pushFinding(
1321
+ "android.di.missing_hilt_viewmodel",
1322
+ "high",
1323
+ sf,
1324
+ sf,
1325
+ 'ViewModel without @HiltViewModel - use Hilt for dependency injection',
1326
+ findings
1327
+ );
1328
+ }
1329
+
1330
+ if (content.includes('class') && (content.includes('Repository') || content.includes('Service')) &&
1331
+ !content.includes('@Inject') && content.includes('constructor')) {
1332
+ pushFinding(
1333
+ "android.di.missing_inject",
1334
+ "high",
1335
+ sf,
1336
+ sf,
1337
+ 'Repository/Service constructor without @Inject - use constructor injection with Hilt',
1338
+ findings
1339
+ );
1340
+ }
1341
+
1342
+ if (content.includes('@Module') && !content.includes('@InstallIn')) {
1343
+ pushFinding(
1344
+ "android.di.missing_installin",
1345
+ "critical",
1346
+ sf,
1347
+ sf,
1348
+ '@Module without @InstallIn - specify component scope (SingletonComponent, ViewModelComponent, etc.)',
1349
+ findings
1350
+ );
1351
+ }
1352
+
1353
+ if (content.includes('ViewModel') && content.includes('launch {') && !content.includes('viewModelScope')) {
1354
+ pushFinding(
1355
+ "android.coroutines.use_viewmodelscope",
1356
+ "high",
1357
+ sf,
1358
+ sf,
1359
+ 'ViewModel launch without viewModelScope - use viewModelScope for automatic cancellation',
1360
+ findings
1361
+ );
1362
+ }
1363
+
1364
+ if (content.includes('suspend fun') && (content.includes('Room') || content.includes('Retrofit') || content.includes('File'))) {
1365
+ if (!content.includes('Dispatchers.IO') && !content.includes('withContext')) {
1366
+ pushFinding(
1367
+ "android.coroutines.missing_io_dispatcher",
1368
+ "medium",
1369
+ sf,
1370
+ sf,
1371
+ 'Network/Disk operations without Dispatchers.IO - use appropriate dispatcher',
1372
+ findings
1373
+ );
1374
+ }
1375
+ }
1376
+
1377
+ if (content.includes('async {') && !content.includes('supervisorScope') && (content.match(/async\s*\{/g) || []).length > 2) {
1378
+ pushFinding(
1379
+ "android.coroutines.use_supervisor_scope",
1380
+ "medium",
1381
+ sf,
1382
+ sf,
1383
+ 'Multiple async without supervisorScope - errors will cancel all jobs, use supervisorScope',
1384
+ findings
1385
+ );
1386
+ }
1387
+
1388
+ if (content.includes('interface') && content.includes('@GET') && !content.includes('suspend fun')) {
1389
+ pushFinding(
1390
+ "android.networking.retrofit_not_suspend",
1391
+ "high",
1392
+ sf,
1393
+ sf,
1394
+ 'Retrofit API without suspend functions - use suspend for coroutine support',
1395
+ findings
1396
+ );
1397
+ }
1398
+
1399
+ if (content.includes('Retrofit') && content.includes('Builder') && !content.includes('Interceptor')) {
1400
+ pushFinding(
1401
+ "android.networking.missing_interceptors",
1402
+ "medium",
1403
+ sf,
1404
+ sf,
1405
+ 'Retrofit without interceptors - add logging/auth interceptors for debugging and authentication',
1406
+ findings
1407
+ );
1408
+ }
1409
+
1410
+ // 165. Moshi over Gson
1411
+ if (content.includes('Gson') && !content.includes('Moshi')) {
1412
+ pushFinding(
1413
+ "android.networking.prefer_moshi",
1414
+ "low",
1415
+ sf,
1416
+ sf,
1417
+ 'Using Gson - prefer Moshi for better Kotlin support and performance',
1418
+ findings
1419
+ );
1420
+ }
1421
+
1422
+ if (content.includes('OkHttpClient') && content.includes('https') && !content.includes('CertificatePinner')) {
1423
+ if (content.includes('production') || content.includes('release')) {
1424
+ pushFinding(
1425
+ "android.networking.missing_cert_pinning",
1426
+ "critical",
1427
+ sf,
1428
+ sf,
1429
+ 'Production network without certificate pinning - implement SSL pinning for security',
1430
+ findings
1431
+ );
1432
+ }
1433
+ }
1434
+
1435
+ if (content.includes('@Dao') && content.includes('fun ') && !content.includes('suspend fun')) {
1436
+ pushFinding(
1437
+ "android.persistence.dao_not_suspend",
1438
+ "high",
1439
+ sf,
1440
+ sf,
1441
+ '@Dao methods without suspend - use suspend functions for coroutine support',
1442
+ findings
1443
+ );
1444
+ }
1445
+
1446
+ if (content.includes('@Dao') && content.includes('@Query') && !content.includes('Flow<')) {
1447
+ const queryCount = (content.match(/@Query/g) || []).length;
1448
+ if (queryCount > 0) {
1449
+ pushFinding(
1450
+ "android.persistence.use_flow_queries",
1451
+ "medium",
1452
+ sf,
1453
+ sf,
1454
+ 'Room queries without Flow - use Flow<T> for observable queries',
1455
+ findings
1456
+ );
1457
+ }
1458
+ }
1459
+
1460
+ if (content.includes('@Entity') && (content.includes('Date') || content.includes('List<') || content.includes('Map<'))) {
1461
+ if (!content.includes('@TypeConverter')) {
1462
+ pushFinding(
1463
+ "android.persistence.missing_type_converter",
1464
+ "high",
1465
+ sf,
1466
+ sf,
1467
+ 'Entity with complex types (Date, List, Map) without @TypeConverter',
1468
+ findings
1469
+ );
1470
+ }
1471
+ }
1472
+
1473
+ if (content.includes('@Dao') && (content.match(/@Query|@Insert|@Update|@Delete/g) || []).length > 2) {
1474
+ if (!content.includes('@Transaction')) {
1475
+ pushFinding(
1476
+ "android.persistence.missing_transaction",
1477
+ "medium",
1478
+ sf,
1479
+ sf,
1480
+ 'DAO with multiple operations - use @Transaction for atomic operations',
1481
+ findings
1482
+ );
1483
+ }
1484
+ }
1485
+
1486
+ if (filePath.includes('ViewModel') && content.includes('data class') && content.includes('State')) {
1487
+ if (content.includes('var ') && !content.includes('private set')) {
1488
+ pushFinding(
1489
+ "android.architecture.mutable_state",
1490
+ "high",
1491
+ sf,
1492
+ sf,
1493
+ 'Mutable state in ViewModel - use immutable data class with copy() pattern',
1494
+ findings
1495
+ );
1496
+ }
1497
+ }
1498
+
1499
+ if (content.includes('ViewModel') && (content.match(/StateFlow|LiveData/g) || []).length > 5) {
1500
+ pushFinding(
1501
+ "android.architecture.too_many_state_sources",
1502
+ "medium",
1503
+ sf,
1504
+ sf,
1505
+ 'ViewModel with many state sources - consolidate into single UiState sealed class',
1506
+ findings
1507
+ );
1508
+ }
1509
+
1510
+ if (content.includes('Glide') && content.includes('@Composable')) {
1511
+ pushFinding(
1512
+ "android.images.use_coil",
1513
+ "low",
1514
+ sf,
1515
+ sf,
1516
+ 'Using Glide in Compose - prefer Coil for Jetpack Compose integration',
1517
+ findings
1518
+ );
1519
+ }
1520
+
1521
+ if (filePath.includes('Test.kt') && content.includes('import org.junit.Test') && !content.includes('jupiter')) {
1522
+ pushFinding(
1523
+ "android.testing.use_junit5",
1524
+ "medium",
1525
+ sf,
1526
+ sf,
1527
+ 'Using JUnit4 - migrate to JUnit5 (Jupiter) for modern testing features',
1528
+ findings
1529
+ );
1530
+ }
1531
+
1532
+ if (filePath.includes('Test.kt') && content.includes('mock') && !content.includes('MockK') && !content.includes('mockk')) {
1533
+ pushFinding(
1534
+ "android.testing.use_mockk",
1535
+ "medium",
1536
+ sf,
1537
+ sf,
1538
+ 'Mocking without MockK - use MockK for Kotlin-first mocking',
1539
+ findings
1540
+ );
1541
+ }
1542
+
1543
+ if (filePath.includes('Test.kt') && content.includes('Flow<') && !content.includes('turbine')) {
1544
+ pushFinding(
1545
+ "android.testing.use_turbine",
1546
+ "low",
1547
+ sf,
1548
+ sf,
1549
+ 'Testing Flows without Turbine - use Turbine for Flow testing',
1550
+ findings
1551
+ );
1552
+ }
1553
+
1554
+ if (filePath.includes('Test.kt') && content.includes('assert') && !content.includes('assertThat')) {
1555
+ pushFinding(
1556
+ "android.testing.use_truth",
1557
+ "low",
1558
+ sf,
1559
+ sf,
1560
+ 'Using JUnit assertions - consider Truth library for more readable assertions',
1561
+ findings
1562
+ );
1563
+ }
1564
+
1565
+ if (filePath.includes('Test.kt') && content.includes('suspend fun') && !content.includes('runTest')) {
1566
+ pushFinding(
1567
+ "android.testing.use_runtest",
1568
+ "high",
1569
+ sf,
1570
+ sf,
1571
+ 'Testing suspend functions without runTest - use runTest for coroutine testing',
1572
+ findings
1573
+ );
1574
+ }
1575
+
1576
+ if (content.includes('SharedPreferences') && !content.includes('Encrypted') &&
1577
+ (content.includes('token') || content.includes('password') || content.includes('secret'))) {
1578
+ pushFinding(
1579
+ "android.security.use_encrypted_prefs",
1580
+ "critical",
1581
+ sf,
1582
+ sf,
1583
+ 'Sensitive data in SharedPreferences - use EncryptedSharedPreferences for security',
1584
+ findings
1585
+ );
1586
+ }
1587
+
1588
+ if (filePath.includes('build.gradle') && content.includes('release') && !content.includes('minifyEnabled true')) {
1589
+ pushFinding(
1590
+ "android.security.missing_proguard",
1591
+ "high",
1592
+ sf,
1593
+ sf,
1594
+ 'Release build without ProGuard/R8 - enable code obfuscation for security',
1595
+ findings
1596
+ );
1597
+ }
1598
+
1599
+ if (content.includes('Column') && content.includes('items(') && !content.includes('LazyColumn')) {
1600
+ pushFinding(
1601
+ "android.performance.use_lazy_column",
1602
+ "high",
1603
+ sf,
1604
+ sf,
1605
+ 'Column with items() - use LazyColumn for virtualized lists',
1606
+ findings
1607
+ );
1608
+ }
1609
+
1610
+ if (content.includes('LazyColumn') && content.includes('items(') && !content.includes('Paging')) {
1611
+ if ((content.match(/items\(/g) || []).length > 0 && content.includes('repository')) {
1612
+ pushFinding(
1613
+ "android.performance.use_paging",
1614
+ "medium",
1615
+ sf,
1616
+ sf,
1617
+ 'LazyColumn with repository data - consider Paging 3 for large datasets',
1618
+ findings
1619
+ );
1620
+ }
1621
+ }
1622
+
1623
+ if (filePath.includes('build.gradle') && content.includes('compose') && !content.includes('baselineProfile')) {
1624
+ pushFinding(
1625
+ "android.performance.missing_baseline_profile",
1626
+ "low",
1627
+ sf,
1628
+ sf,
1629
+ 'Compose app without Baseline Profiles - add for startup optimization',
1630
+ findings
1631
+ );
1632
+ }
1633
+
1634
+ if (content.includes('data class') && content.includes('@Composable') && !content.includes('@Stable') && !content.includes('@Immutable')) {
1635
+ if ((content.match(/data\s+class/g) || []).length > 2) {
1636
+ pushFinding(
1637
+ "android.compose_perf.missing_stability",
1638
+ "high",
1639
+ sf,
1640
+ sf,
1641
+ 'Data classes in Composable without @Stable/@Immutable - causes unnecessary recompositions',
1642
+ findings
1643
+ );
1644
+ }
1645
+ }
1646
+
1647
+ if (content.includes('StateFlow<List<') || content.includes('State<List<')) {
1648
+ if (!content.includes('kotlinx.collections.immutable')) {
1649
+ pushFinding(
1650
+ "android.compose_perf.use_immutable_collections",
1651
+ "medium",
1652
+ sf,
1653
+ sf,
1654
+ 'State with List - use ImmutableList from kotlinx.collections.immutable for stability',
1655
+ findings
1656
+ );
1657
+ }
1658
+ }
1659
+
1660
+ if (content.includes('@Composable') && (content.includes('Image') || content.includes('Icon')) &&
1661
+ !content.includes('contentDescription')) {
1662
+ const imageCount = (content.match(/Image\(|Icon\(/g) || []).length;
1663
+ if (imageCount > 2) {
1664
+ pushFinding(
1665
+ "android.accessibility.missing_content_description",
1666
+ "high",
1667
+ sf,
1668
+ sf,
1669
+ `${imageCount} images/icons without contentDescription - add for TalkBack accessibility`,
1670
+ findings
1671
+ );
1672
+ }
1673
+ }
1674
+
1675
+ if (content.includes('Modifier') && content.includes('.size(') && !content.includes('.minimumInteractiveComponentSize()')) {
1676
+ const sizePattern = /\.size\((\d+)\.dp\)/g;
1677
+ while ((match = sizePattern.exec(content)) !== null) {
1678
+ const size = parseInt(match[1]);
1679
+ if (size < 48) {
1680
+ pushFinding(
1681
+ "android.accessibility.small_touch_target",
1682
+ "medium",
1683
+ sf,
1684
+ sf,
1685
+ `Touch target ${size}dp < 48dp minimum - increase size for accessibility`,
1686
+ findings
1687
+ );
1688
+ }
1689
+ }
1690
+ }
1691
+
1692
+ // 188. strings.xml for i18n
1693
+ if (content.includes('Text(') && (content.match(/Text\("[^"]{15,}"\)/g) || []).length > 3) {
1694
+ if (!content.includes('stringResource') && !filePath.includes('Preview')) {
1695
+ pushFinding(
1696
+ "android.localization.hardcoded_strings",
1697
+ "medium",
1698
+ sf,
1699
+ sf,
1700
+ 'Hardcoded strings in Text - use stringResource(R.string.xxx) for internationalization',
1701
+ findings
1702
+ );
1703
+ }
1704
+ }
1705
+
1706
+ if (filePath.endsWith('build.gradle') && !filePath.endsWith('.kts')) {
1707
+ pushFinding(
1708
+ "android.gradle.use_kotlin_dsl",
1709
+ "low",
1710
+ sf,
1711
+ sf,
1712
+ 'Groovy Gradle script - migrate to Kotlin DSL (build.gradle.kts) for type safety',
1713
+ findings
1714
+ );
1715
+ }
1716
+
1717
+ if (filePath.includes('build.gradle') && content.includes('implementation') && !content.includes('libs.')) {
1718
+ const depCount = (content.match(/implementation\s*\(/g) || []).length;
1719
+ if (depCount > 5) {
1720
+ pushFinding(
1721
+ "android.gradle.use_version_catalogs",
1722
+ "low",
1723
+ sf,
1724
+ sf,
1725
+ `${depCount} dependencies without version catalog - use libs.versions.toml for centralized management`,
1726
+ findings
1727
+ );
1728
+ }
1729
+ }
1730
+
1731
+ });
1732
+
1733
+ // ═══════════════════════════════════════════════════════════════
1734
+ // ═══════════════════════════════════════════════════════════════
1735
+ project.getSourceFiles().forEach((sf) => {
1736
+ const filePath = sf.getFilePath();
1737
+ if (platformOf(filePath) !== "android") return;
1738
+ if (/\/ast-[^/]+\.js$/.test(filePath)) return;
1739
+
1740
+ const solidAnalyzer = new AndroidSOLIDAnalyzer();
1741
+ solidAnalyzer.analyze(sf, findings, pushFinding);
1742
+ });
1743
+
1744
+ // ═══════════════════════════════════════════════════════════════
1745
+ // ═══════════════════════════════════════════════════════════════
1746
+ project.getSourceFiles().forEach((sf) => {
1747
+ const filePath = sf.getFilePath();
1748
+ if (platformOf(filePath) !== "android") return;
1749
+ if (/\/ast-[^/]+\.js$/.test(filePath)) return;
1750
+
1751
+ const forbiddenLiteralsAnalyzer = new AndroidForbiddenLiteralsAnalyzer();
1752
+ forbiddenLiteralsAnalyzer.analyze(sf, findings, pushFinding);
1753
+ });
1754
+
1755
+ // ═══════════════════════════════════════════════════════════════
1756
+ // ═══════════════════════════════════════════════════════════════
1757
+ try {
1758
+ const kotlinFiles = project.getSourceFiles()
1759
+ .map(sf => sf.getFilePath())
1760
+ .filter(f => platformOf(f) === "android" && (f.endsWith('.kt') || f.endsWith('.kts')));
1761
+
1762
+ if (kotlinFiles.length > 0) {
1763
+ const detektFindings = runDetektAnalysis(kotlinFiles);
1764
+
1765
+ for (const finding of detektFindings) {
1766
+ findings.push({
1767
+ rule_id: finding.rule_id,
1768
+ severity: finding.severity,
1769
+ file: finding.file,
1770
+ line: finding.line,
1771
+ column: finding.column,
1772
+ message: finding.message,
1773
+ category: finding.category || 'detekt',
1774
+ debt: finding.debt || '10min'
1775
+ });
1776
+ }
1777
+ }
1778
+ } catch (error) {
1779
+ console.error('Error running detekt analysis:', error.message);
1780
+ }
1781
+ }
1782
+
1783
+ module.exports = {
1784
+ runAndroidIntelligence,
1785
+ };