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,2176 @@
1
+
2
+ const path = require('path');
3
+ const glob = require('glob');
4
+ const { pushFinding, mapToLevel, SyntaxKind, isTestFile, platformOf, getRepoRoot } = require(path.join(__dirname, '../ast-core'));
5
+ const { iOSEnterpriseAnalyzer } = require(path.join(__dirname, 'analyzers/iOSEnterpriseAnalyzer'));
6
+ const { iOSArchitectureDetector } = require(path.join(__dirname, 'analyzers/iOSArchitectureDetector'));
7
+ const { iOSArchitectureRules } = require(path.join(__dirname, 'analyzers/iOSArchitectureRules'));
8
+ const { iOSPerformanceRules } = require(path.join(__dirname, 'analyzers/iOSPerformanceRules'));
9
+ const { iOSSwiftUIAdvancedRules } = require(path.join(__dirname, 'analyzers/iOSSwiftUIAdvancedRules'));
10
+ const { iOSSPMRules } = require(path.join(__dirname, 'analyzers/iOSSPMRules'));
11
+ const { iOSTestingAdvancedRules } = require(path.join(__dirname, 'analyzers/iOSTestingAdvancedRules'));
12
+ const { runSwiftLintNative } = require(path.join(__dirname, 'native-bridge'));
13
+ const { iOSNetworkingAdvancedRules } = require(path.join(__dirname, 'analyzers/iOSNetworkingAdvancedRules'));
14
+ const { iOSCICDRules } = require(path.join(__dirname, 'analyzers/iOSCICDRules'));
15
+ const { iOSForbiddenLiteralsAnalyzer } = require(path.join(__dirname, 'analyzers/iOSForbiddenLiteralsAnalyzer'));
16
+ const { iOSASTIntelligentAnalyzer } = require(path.join(__dirname, 'analyzers/iOSASTIntelligentAnalyzer'));
17
+
18
+ /**
19
+ * Run iOS-specific AST intelligence analysis
20
+ * Uses both TypeScript AST (for .ts/.tsx) and SourceKitten (for .swift)
21
+ * @param {Project} project - TypeScript morph project
22
+ * @param {Array} findings - Findings array to populate
23
+ * @param {string} platform - Platform identifier
24
+ */
25
+ async function runIOSIntelligence(project, findings, platform) {
26
+
27
+ console.error(`[iOS AST Intelligence] Running SourceKitten-based analysis...`);
28
+ const astAnalyzer = new iOSASTIntelligentAnalyzer(findings);
29
+ const root = getRepoRoot();
30
+ const swiftFilesForAST = glob.sync('**/*.swift', {
31
+ cwd: root,
32
+ ignore: ['**/node_modules/**', '**/build/**', '**/Pods/**', '**/.build/**', '**/CustomLintRules/**'],
33
+ absolute: true,
34
+ });
35
+
36
+ for (const swiftFile of swiftFilesForAST) {
37
+ astAnalyzer.analyzeFile(swiftFile);
38
+ }
39
+
40
+ astAnalyzer.finalizeGodClassDetection();
41
+ console.error(`[iOS AST Intelligence] Analyzed ${swiftFilesForAST.length} Swift files with SourceKitten AST`);
42
+
43
+ await runSwiftLintNative(findings);
44
+
45
+ project.getSourceFiles().forEach((sf) => {
46
+ if (!sf || typeof sf.getFilePath !== 'function') return;
47
+ const filePath = sf.getFilePath();
48
+
49
+ if (/\/ast-[^/]+\.js$/.test(filePath)) return;
50
+
51
+ if (platformOf(filePath) !== "ios") return;
52
+
53
+ sf.getDescendantsOfKind(SyntaxKind.NonNullExpression).forEach((expr) => {
54
+ pushFinding("ios.force_unwrapping", "high", sf, expr, "Force unwrapping (!) detected - use if let or guard let instead", findings);
55
+ });
56
+
57
+ const completionHandlerFilePath = sf.getFilePath();
58
+ const isAnalyzer = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(completionHandlerFilePath);
59
+ const isCompletionTestFile = /\.(spec|test)\.(js|ts|swift)$/i.test(completionHandlerFilePath);
60
+ if (!isAnalyzer && !isCompletionTestFile) {
61
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
62
+ const args = call.getArguments();
63
+ args.forEach((arg) => {
64
+ const argText = typeof arg?.getText === "function" ? arg.getText() : "";
65
+ if (argText.includes("completion:")) {
66
+ pushFinding("ios.completion_handlers", "medium", sf, call, "Completion handler detected - use async/await instead", findings);
67
+ }
68
+ });
69
+ });
70
+ }
71
+
72
+ const currentFilePath = sf.getFilePath();
73
+ const isAnalyzerForStoryboards = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(currentFilePath);
74
+ if (!isAnalyzerForStoryboards && (sf.getFullText().includes("storyboard") || sf.getFullText().includes("xib") || sf.getFullText().includes("nib"))) {
75
+ pushFinding("ios.storyboards", "high", sf, sf, "Storyboard/XIB detected - use programmatic UI for better version control", findings);
76
+ }
77
+
78
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
79
+ const name = cls.getName();
80
+ if (!name || !name.includes("ViewController")) return;
81
+ const lines = cls.getEnd() - cls.getStart();
82
+ if (lines > 300) {
83
+ pushFinding("ios.massive_viewcontrollers", "high", sf, cls, `Massive ViewController detected (${lines} lines) - break down into smaller components`, findings);
84
+ }
85
+ });
86
+ });
87
+
88
+ try {
89
+ const root = getRepoRoot();
90
+ const swiftFiles = glob.sync('**/*.swift', {
91
+ cwd: root,
92
+ ignore: ['**/node_modules/**', '**/build/**', '**/Pods/**', '**/.build/**'],
93
+ absolute: true,
94
+ });
95
+
96
+ if (swiftFiles.length > 0) {
97
+ console.error(`[iOS Enterprise] Analyzing ${swiftFiles.length} Swift files with SourceKitten...`);
98
+
99
+ const repoRoot = getRepoRoot();
100
+ const architectureDetector = new iOSArchitectureDetector(repoRoot);
101
+ const detectedPattern = architectureDetector.detect();
102
+ const detectionSummary = architectureDetector.getDetectionSummary();
103
+
104
+ console.error(`[iOS Architecture] Pattern detected: ${detectedPattern} (confidence: ${detectionSummary.confidence}%)`);
105
+
106
+ if (detectionSummary.warnings.length > 0) {
107
+ detectionSummary.warnings.forEach(warning => {
108
+ pushFinding(findings, {
109
+ ruleId: 'ios.architecture.detection_warning',
110
+ severity: warning.severity.toLowerCase(),
111
+ message: warning.message,
112
+ filePath: 'PROJECT_ROOT',
113
+ line: 1,
114
+ suggestion: warning.recommendation
115
+ });
116
+ });
117
+ }
118
+
119
+ const architectureRules = new iOSArchitectureRules(findings, detectedPattern);
120
+ architectureRules.runRules(swiftFiles);
121
+
122
+ console.error(`[iOS Performance] Analyzing SwiftUI performance...`);
123
+ const performanceRules = new iOSPerformanceRules(findings);
124
+ swiftFiles.forEach(swiftFile => {
125
+ performanceRules.analyzeFile(swiftFile, null);
126
+ });
127
+
128
+ console.error(`[iOS SwiftUI Advanced] Analyzing advanced patterns...`);
129
+ const swiftUIAdvanced = new iOSSwiftUIAdvancedRules(findings);
130
+ swiftFiles.forEach(swiftFile => {
131
+ swiftUIAdvanced.analyzeFile(swiftFile, null);
132
+ });
133
+
134
+ console.error(`[iOS SPM] Analyzing code organization...`);
135
+ const spmRules = new iOSSPMRules(findings, repoRoot);
136
+ spmRules.analyze();
137
+
138
+ console.error(`[iOS Testing] Analyzing testing patterns...`);
139
+ const testingRules = new iOSTestingAdvancedRules(findings, repoRoot);
140
+ testingRules.analyze();
141
+
142
+ console.error(`[iOS Networking] Analyzing networking layer...`);
143
+ const networkingRules = new iOSNetworkingAdvancedRules(findings, repoRoot);
144
+ networkingRules.analyze();
145
+
146
+ console.error(`[iOS CI/CD] Analyzing CI/CD configuration...`);
147
+ const cicdRules = new iOSCICDRules(findings, repoRoot);
148
+ cicdRules.analyze();
149
+
150
+ const analyzer = new iOSEnterpriseAnalyzer();
151
+
152
+ for (const swiftFile of swiftFiles) {
153
+ await analyzer.analyzeFile(swiftFile, findings);
154
+ }
155
+
156
+ console.error(`[iOS Enterprise] Completed Swift analysis`);
157
+ }
158
+ } catch (error) {
159
+ console.error(`[iOS Enterprise] Error during Swift analysis:`, error.message);
160
+ }
161
+
162
+ // ═══════════════════════════════════════════════════════════════
163
+ // ═══════════════════════════════════════════════════════════════
164
+ project.getSourceFiles().forEach((sf) => {
165
+ if (!sf || typeof sf.getFilePath !== 'function') return;
166
+ const filePath = sf.getFilePath();
167
+ if (platformOf(filePath) !== "ios") return;
168
+ if (/\/ast-[^/]+\.js$/.test(filePath)) return;
169
+
170
+ const forbiddenLiteralsAnalyzer = new iOSForbiddenLiteralsAnalyzer();
171
+ forbiddenLiteralsAnalyzer.analyze(sf, findings, pushFinding);
172
+ });
173
+
174
+ project.getSourceFiles().forEach((sf) => {
175
+ if (!sf || typeof sf.getFilePath !== 'function') return;
176
+ const filePath = sf.getFilePath();
177
+ if (platformOf(filePath) !== "ios" || !filePath.endsWith('.swift')) return;
178
+
179
+ const content = sf.getFullText();
180
+
181
+ const emptyCatchPattern = /catch\s*\{\s*\}/g;
182
+ let match;
183
+ while ((match = emptyCatchPattern.exec(content)) !== null) {
184
+ const lineNumber = content.substring(0, match.index).split('\n').length;
185
+ pushFinding(
186
+ "ios.error_handling.empty_catch",
187
+ "high",
188
+ sf,
189
+ sf,
190
+ `Line ${lineNumber}: Empty catch block - handle error with 'catch let error' or propagate with throws`,
191
+ findings
192
+ );
193
+ }
194
+
195
+ if (content.includes('_ = error') || content.includes('_ = err')) {
196
+ const errorIndex = content.indexOf('_ = error') !== -1 ? content.indexOf('_ = error') : content.indexOf('_ = err');
197
+ const lineNumber = content.substring(0, errorIndex).split('\n').length;
198
+ pushFinding(
199
+ "ios.error_handling.silenced_error",
200
+ "high",
201
+ sf,
202
+ sf,
203
+ `Line ${lineNumber}: NEVER silence errors with '_ = error' - handle with type check or propagate with throws`,
204
+ findings
205
+ );
206
+ }
207
+
208
+ const forceTryPattern = /try!\s+/g;
209
+ while ((match = forceTryPattern.exec(content)) !== null) {
210
+ const lineNumber = content.substring(0, match.index).split('\n').length;
211
+ pushFinding(
212
+ "ios.error_handling.force_try",
213
+ "high",
214
+ sf,
215
+ sf,
216
+ `Line ${lineNumber}: NEVER use 'try!' - use proper do-catch or try? with nil handling`,
217
+ findings
218
+ );
219
+ }
220
+
221
+ const anyTypePattern = /:\s*Any\b/g;
222
+ while ((match = anyTypePattern.exec(content)) !== null) {
223
+ const lineNumber = content.substring(0, match.index).split('\n').length;
224
+ const lineText = content.split('\n')[lineNumber - 1];
225
+ const varName = lineText.match(/\b(\w+)\s*:\s*Any\b/)?.[1];
226
+ if (varName) {
227
+ const subsequentLines = content.split('\n').slice(lineNumber, lineNumber + 10).join('\n');
228
+ const hasTypeCheck = new RegExp(`${varName}\\s+is\\s+|${varName}\\s+as\\?|if\\s+let.*${varName}`).test(subsequentLines);
229
+ if (!hasTypeCheck) {
230
+ pushFinding(
231
+ "ios.typescript.any_without_guard",
232
+ "high",
233
+ sf,
234
+ sf,
235
+ `Line ${lineNumber}: Variable '${varName}: Any' used without type checking - use 'is', 'as?', or 'if let' guards`,
236
+ findings
237
+ );
238
+ }
239
+ }
240
+ }
241
+
242
+ // ==========================================
243
+ // ==========================================
244
+
245
+
246
+ const delegatePattern = /var\s+(\w*[Dd]elegate\w*)\s*:\s*\w+(?!\?)(?!\s*\?)/g;
247
+ while ((match = delegatePattern.exec(content)) !== null) {
248
+ const lineNumber = content.substring(0, match.index).split('\n').length;
249
+ const lineText = content.split('\n')[lineNumber - 1];
250
+ if (!lineText.includes('weak')) {
251
+ pushFinding(
252
+ "ios.memory.delegate_not_weak",
253
+ "high",
254
+ sf,
255
+ sf,
256
+ `Line ${lineNumber}: Delegate '${match[1]}' should be weak to avoid retain cycles`,
257
+ findings
258
+ );
259
+ }
260
+ }
261
+
262
+ const closurePattern = /\{\s*(?!\[weak|!\[unowned)[^}]{30,}\bself\b/g;
263
+ while ((match = closurePattern.exec(content)) !== null) {
264
+ const lineNumber = content.substring(0, match.index).split('\n').length;
265
+ const context = content.substring(match.index - 100, match.index + 100);
266
+ if (context.includes('escaping') || context.includes('@escaping')) {
267
+ pushFinding(
268
+ "ios.memory.closure_retain_cycle",
269
+ "high",
270
+ sf,
271
+ sf,
272
+ `Line ${lineNumber}: Escaping closure captures self - use [weak self] or [unowned self]`,
273
+ findings
274
+ );
275
+ }
276
+ }
277
+
278
+ // 2. CONCURRENCY
279
+
280
+ if (content.includes('DispatchQueue')) {
281
+ const lines = content.split('\n');
282
+ lines.forEach((line, idx) => {
283
+ if (line.includes('DispatchQueue') && !line.includes('//')) {
284
+ pushFinding(
285
+ "ios.concurrency.dispatch_queue",
286
+ "medium",
287
+ sf,
288
+ sf,
289
+ `Line ${idx + 1}: DispatchQueue detected - prefer async/await and actors for new code`,
290
+ findings
291
+ );
292
+ }
293
+ });
294
+ }
295
+
296
+ const uiUpdatePattern = /func\s+\w+.*\{[^}]*\b(self\.|view\.|layer\.|bounds|frame|backgroundColor)/g;
297
+ while ((match = uiUpdatePattern.exec(content)) !== null) {
298
+ const lineNumber = content.substring(0, match.index).split('\n').length;
299
+ const funcBlock = content.substring(match.index, match.index + 200);
300
+ if (!funcBlock.includes('@MainActor') && funcBlock.includes('async')) {
301
+ pushFinding(
302
+ "ios.concurrency.missing_main_actor",
303
+ "high",
304
+ sf,
305
+ sf,
306
+ `Line ${lineNumber}: Async function with UI updates - add @MainActor annotation`,
307
+ findings
308
+ );
309
+ }
310
+ }
311
+
312
+
313
+ const implicitUnwrapPattern = /var\s+\w+\s*:\s*\w+!/g;
314
+ while ((match = implicitUnwrapPattern.exec(content)) !== null) {
315
+ const lineNumber = content.substring(0, match.index).split('\n').length;
316
+ const lineText = content.split('\n')[lineNumber - 1];
317
+ if (!lineText.includes('@IBOutlet')) {
318
+ pushFinding(
319
+ "ios.optionals.implicitly_unwrapped",
320
+ "medium",
321
+ sf,
322
+ sf,
323
+ `Line ${lineNumber}: Implicitly unwrapped optional (!) - use regular optional (?) unless IBOutlet`,
324
+ findings
325
+ );
326
+ }
327
+ }
328
+
329
+ // 4. ARCHITECTURE
330
+
331
+ const singletonPattern = /static\s+(let|var)\s+(shared|instance)\s*=/g;
332
+ while ((match = singletonPattern.exec(content)) !== null) {
333
+ const lineNumber = content.substring(0, match.index).split('\n').length;
334
+ if (!content.includes('URLSession.shared') && !content.includes('NotificationCenter.default')) {
335
+ pushFinding(
336
+ "ios.architecture.singleton",
337
+ "high",
338
+ sf,
339
+ sf,
340
+ `Line ${lineNumber}: Singleton detected - use Dependency Injection instead`,
341
+ findings
342
+ );
343
+ }
344
+ }
345
+
346
+ const viewControllerPattern = /class\s+(\w*ViewController|\w*View)\s*:/g;
347
+ while ((match = viewControllerPattern.exec(content)) !== null) {
348
+ const className = match[1];
349
+ const classStart = match.index;
350
+ const classEnd = content.indexOf('\n}\n', classStart);
351
+ if (classEnd > -1) {
352
+ const lines = content.substring(classStart, classEnd).split('\n').length;
353
+ if (lines > 300) {
354
+ pushFinding(
355
+ "ios.architecture.massive_view",
356
+ "high",
357
+ sf,
358
+ sf,
359
+ `${className} has ${lines} lines - break down into smaller components (max 300)`,
360
+ findings
361
+ );
362
+ }
363
+ }
364
+ }
365
+
366
+ if (content.includes(': View') || content.includes('SwiftUI.View')) {
367
+ const businessLogicPatterns = ['URLSession', 'Alamofire', 'fetch', 'api.', 'UserDefaults', 'CoreData'];
368
+ businessLogicPatterns.forEach(pattern => {
369
+ if (content.includes(pattern)) {
370
+ const lineNumber = content.indexOf(pattern) ? content.substring(0, content.indexOf(pattern)).split('\n').length : 1;
371
+ pushFinding(
372
+ "ios.architecture.business_logic_in_view",
373
+ "high",
374
+ sf,
375
+ sf,
376
+ `Line ${lineNumber}: Business logic in View - move to ViewModel or UseCase`,
377
+ findings
378
+ );
379
+ }
380
+ });
381
+ }
382
+
383
+ // 5. SECURITY
384
+
385
+ const secretPatterns = [
386
+ /api[_-]?key\s*=\s*["'][^"']+["']/gi,
387
+ /secret\s*=\s*["'][^"']+["']/gi,
388
+ /password\s*=\s*["'][^"']+["']/gi,
389
+ /token\s*=\s*["'][^"']+["']/gi
390
+ ];
391
+ secretPatterns.forEach(pattern => {
392
+ let match;
393
+ while ((match = pattern.exec(content)) !== null) {
394
+ const lineNumber = content.substring(0, match.index).split('\n').length;
395
+ pushFinding(
396
+ "ios.security.hardcoded_secret",
397
+ "critical",
398
+ sf,
399
+ sf,
400
+ `Line ${lineNumber}: Hardcoded secret detected - use Keychain or environment variables`,
401
+ findings
402
+ );
403
+ }
404
+ });
405
+
406
+ if (content.includes('UserDefaults') && (content.includes('password') || content.includes('token') || content.includes('secret'))) {
407
+ const lineNumber = content.indexOf('UserDefaults') ? content.substring(0, content.indexOf('UserDefaults')).split('\n').length : 1;
408
+ pushFinding(
409
+ "ios.security.userdefaults_sensitive",
410
+ "critical",
411
+ sf,
412
+ sf,
413
+ `Line ${lineNumber}: Sensitive data in UserDefaults - use Keychain instead`,
414
+ findings
415
+ );
416
+ }
417
+
418
+ const httpPattern = /http:\/\/(?!localhost|127\.0\.0\.1)/g;
419
+ while ((match = httpPattern.exec(content)) !== null) {
420
+ const lineNumber = content.substring(0, match.index).split('\n').length;
421
+ pushFinding(
422
+ "ios.security.http_url",
423
+ "high",
424
+ sf,
425
+ sf,
426
+ `Line ${lineNumber}: HTTP URL detected - use HTTPS for security`,
427
+ findings
428
+ );
429
+ }
430
+
431
+ // 6. LOCALIZATION
432
+
433
+ const hardcodedStringPattern = /Text\s*\(\s*"[^"]+"\s*\)|\.title\s*=\s*"[^"]+"|\.text\s*=\s*"[^"]+"/g;
434
+ let hardcodedCount = 0;
435
+ while ((match = hardcodedStringPattern.exec(content)) !== null && hardcodedCount < 5) {
436
+ const lineNumber = content.substring(0, match.index).split('\n').length;
437
+ const stringContent = match[0];
438
+ if (!stringContent.includes('NSLocalizedString') && !stringContent.match(/"(OK|Cancel|Yes|No|Error)"/)) {
439
+ pushFinding(
440
+ "ios.i18n.hardcoded_string",
441
+ "medium",
442
+ sf,
443
+ sf,
444
+ `Line ${lineNumber}: Hardcoded string - use NSLocalizedString for i18n`,
445
+ findings
446
+ );
447
+ hardcodedCount++;
448
+ }
449
+ }
450
+
451
+ // 7. PERFORMANCE
452
+
453
+ if (content.includes('DispatchQueue.global') || content.includes('.background')) {
454
+ const uiPatterns = ['view.', 'layer.', 'backgroundColor', 'addSubview', 'removeFromSuperview'];
455
+ uiPatterns.forEach(pattern => {
456
+ const dispatchIndex = content.indexOf('DispatchQueue.global');
457
+ if (dispatchIndex > -1) {
458
+ const subsequentCode = content.substring(dispatchIndex, dispatchIndex + 500);
459
+ if (subsequentCode.includes(pattern)) {
460
+ const lineNumber = content.substring(0, dispatchIndex).split('\n').length;
461
+ pushFinding(
462
+ "ios.performance.ui_on_background",
463
+ "high",
464
+ sf,
465
+ sf,
466
+ `Line ${lineNumber}: UI update on background thread - wrap in DispatchQueue.main or @MainActor`,
467
+ findings
468
+ );
469
+ }
470
+ }
471
+ });
472
+ }
473
+
474
+
475
+ let geometryReaderCount = (content.match(/GeometryReader/g) || []).length;
476
+ if (geometryReaderCount > 3) {
477
+ pushFinding(
478
+ "ios.swiftui.geometry_reader_overuse",
479
+ "medium",
480
+ sf,
481
+ sf,
482
+ `File has ${geometryReaderCount} GeometryReader usages - use sparingly (impacts performance)`,
483
+ findings
484
+ );
485
+ }
486
+
487
+ if (content.includes('ObservableObject')) {
488
+ const varPattern = /var\s+\w+\s*:/g;
489
+ const publishedPattern = /@Published/g;
490
+ const varCount = (content.match(varPattern) || []).length;
491
+ const publishedCount = (content.match(publishedPattern) || []).length;
492
+ if (varCount > publishedCount + 2) {
493
+ pushFinding(
494
+ "ios.swiftui.missing_published",
495
+ "medium",
496
+ sf,
497
+ sf,
498
+ `ObservableObject with ${varCount - publishedCount} non-@Published vars - state changes won't trigger UI updates`,
499
+ findings
500
+ );
501
+ }
502
+ }
503
+
504
+ const stateObjectPattern = /@StateObject.*=.*\(/g;
505
+ while ((match = stateObjectPattern.exec(content)) !== null) {
506
+ const lineNumber = content.substring(0, match.index).split('\n').length;
507
+ const nextLines = content.split('\n').slice(lineNumber, lineNumber + 3).join('\n');
508
+ if (nextLines.match(/init\s*\(/)) {
509
+ pushFinding(
510
+ "ios.swiftui.stateobject_in_init",
511
+ "high",
512
+ sf,
513
+ sf,
514
+ `Line ${lineNumber}: @StateObject created in init - will recreate on every init, use @ObservedObject instead`,
515
+ findings
516
+ );
517
+ }
518
+ }
519
+
520
+ // 9. TESTING
521
+
522
+ if (content.includes('ViewModel') && !filePath.includes('Test') && !filePath.includes('Mock')) {
523
+ const className = content.match(/class\s+(\w+ViewModel)/)?.[1];
524
+ if (className) {
525
+ const testPath = filePath.replace(/\.swift$/, 'Tests.swift');
526
+ pushFinding(
527
+ "ios.testing.missing_viewmodel_tests",
528
+ "medium",
529
+ sf,
530
+ sf,
531
+ `ViewModel '${className}' - ensure test file exists: ${testPath}`,
532
+ findings
533
+ );
534
+ }
535
+ }
536
+
537
+ if (!filePath.includes('Test') && content.includes('XCTAssert')) {
538
+ const lineNumber = content.indexOf('XCTAssert') ? content.substring(0, content.indexOf('XCTAssert')).split('\n').length : 1;
539
+ pushFinding(
540
+ "ios.testing.xctest_in_production",
541
+ "high",
542
+ sf,
543
+ sf,
544
+ `Line ${lineNumber}: XCTest assertions in production code - move to test files`,
545
+ findings
546
+ );
547
+ }
548
+
549
+
550
+ const classInheritancePattern = /class\s+\w+\s*:\s*\w+[^,{]*\{/g;
551
+ while ((match = classInheritancePattern.exec(content)) !== null) {
552
+ const lineNumber = content.substring(0, match.index).split('\n').length;
553
+ const lineText = content.split('\n')[lineNumber - 1];
554
+ if (!lineText.includes('Protocol') && !lineText.includes('ObservableObject') &&
555
+ !lineText.includes('UIViewController') && !lineText.includes('UIView') &&
556
+ !lineText.includes('NSObject')) {
557
+ pushFinding(
558
+ "ios.architecture.class_inheritance",
559
+ "medium",
560
+ sf,
561
+ sf,
562
+ `Line ${lineNumber}: Class inheritance detected - prefer protocol composition`,
563
+ findings
564
+ );
565
+ }
566
+ }
567
+
568
+ // 11. VALUE TYPES
569
+
570
+ // class when struct would work
571
+ const classPattern = /class\s+(\w+)\s*(?::\s*Codable|:\s*Equatable|:\s*Hashable)?\s*\{/g;
572
+ while ((match = classPattern.exec(content)) !== null) {
573
+ const className = match[1];
574
+ const lineNumber = content.substring(0, match.index).split('\n').length;
575
+ if (!className.includes('ViewModel') && !className.includes('Controller') &&
576
+ !className.includes('Manager') && !className.includes('Service')) {
577
+ const classBlock = content.substring(match.index, content.indexOf('\n}\n', match.index) || match.index + 500);
578
+ if (!classBlock.includes('deinit') && !classBlock.includes('init?(')) {
579
+ pushFinding(
580
+ "ios.architecture.class_over_struct",
581
+ "low",
582
+ sf,
583
+ sf,
584
+ `Line ${lineNumber}: Class '${className}' might be better as struct - use struct unless you need identity`,
585
+ findings
586
+ );
587
+ }
588
+ }
589
+ }
590
+
591
+
592
+ const statePattern = /@State\s+var\s+(\w+)/g;
593
+ while ((match = statePattern.exec(content)) !== null) {
594
+ const lineNumber = content.substring(0, match.index).split('\n').length;
595
+ const lineText = content.split('\n')[lineNumber - 1];
596
+ if (!lineText.includes('private')) {
597
+ pushFinding(
598
+ "ios.swiftui.state_not_private",
599
+ "medium",
600
+ sf,
601
+ sf,
602
+ `Line ${lineNumber}: @State var '${match[1]}' should be private - state should not be shared directly`,
603
+ findings
604
+ );
605
+ }
606
+ }
607
+
608
+ // 13. NETWORKING
609
+
610
+ if (content.includes('URLSession') || content.includes('Alamofire')) {
611
+ const noErrorHandling = /\.dataTask|\.request.*\{(?!.*catch|.*error)[\s\S]{50,200}\}/g;
612
+ while ((match = noErrorHandling.exec(content)) !== null) {
613
+ const lineNumber = content.substring(0, match.index).split('\n').length;
614
+ pushFinding(
615
+ "ios.networking.missing_error_handling",
616
+ "high",
617
+ sf,
618
+ sf,
619
+ `Line ${lineNumber}: Network call without error handling - add do-catch or Result type`,
620
+ findings
621
+ );
622
+ }
623
+ }
624
+
625
+
626
+ const lineCount = content.split('\n').length;
627
+ if (lineCount > 500 && !filePath.includes('Generated')) {
628
+ pushFinding(
629
+ "ios.organization.file_too_large",
630
+ "medium",
631
+ sf,
632
+ sf,
633
+ `File has ${lineCount} lines - split into smaller files (max 500 lines)`,
634
+ findings
635
+ );
636
+ }
637
+
638
+ if (lineCount > 150 && !content.includes('MARK:')) {
639
+ pushFinding(
640
+ "ios.organization.missing_marks",
641
+ "low",
642
+ sf,
643
+ sf,
644
+ 'Large file without MARK: comments - add section markers for better organization',
645
+ findings
646
+ );
647
+ }
648
+
649
+ // 15. MAGIC NUMBERS
650
+
651
+ const magicNumberPattern = /\b\d{3,}\b/g;
652
+ let magicCount = 0;
653
+ while ((match = magicNumberPattern.exec(content)) !== null && magicCount < 5) {
654
+ const lineNumber = content.substring(0, match.index).split('\n').length;
655
+ const lineText = content.split('\n')[lineNumber - 1];
656
+ if (!lineText.includes('//') && !lineText.includes('let') && !lineText.includes('case')) {
657
+ pushFinding(
658
+ "ios.code_quality.magic_number",
659
+ "low",
660
+ sf,
661
+ sf,
662
+ `Line ${lineNumber}: Magic number ${match[0]} - use named constant`,
663
+ findings
664
+ );
665
+ magicCount++;
666
+ }
667
+ }
668
+
669
+ // 16. ACCESSIBILITY
670
+
671
+ if (content.includes('Image(') || content.includes('Button(')) {
672
+ const noAccessibilityPattern = /(Image|Button)\([^)]+\)(?![^.]*\.accessibilityLabel)/g;
673
+ let accessibilityCount = 0;
674
+ while ((match = noAccessibilityPattern.exec(content)) !== null && accessibilityCount < 3) {
675
+ const lineNumber = content.substring(0, match.index).split('\n').length;
676
+ pushFinding(
677
+ "ios.accessibility.missing_label",
678
+ "medium",
679
+ sf,
680
+ sf,
681
+ `Line ${lineNumber}: ${match[1]} without accessibility label - add .accessibilityLabel()`,
682
+ findings
683
+ );
684
+ accessibilityCount++;
685
+ }
686
+ }
687
+
688
+ // 17. COMBINE
689
+
690
+ const combinePattern = /\.sink\s*\{|\.assign\s*\(/g;
691
+ let combineCount = (content.match(combinePattern) || []).length;
692
+ if (combineCount > 5 && !content.includes('// Combine required')) {
693
+ pushFinding(
694
+ "ios.combine.overuse",
695
+ "low",
696
+ sf,
697
+ sf,
698
+ `File has ${combineCount} Combine operators - consider async/await for simpler async operations`,
699
+ findings
700
+ );
701
+ }
702
+
703
+ if (content.includes('.sink') && !content.includes('Set<AnyCancellable>') && !content.includes('AnyCancellable')) {
704
+ const lineNumber = content.indexOf('.sink') ? content.substring(0, content.indexOf('.sink')).split('\n').length : 1;
705
+ pushFinding(
706
+ "ios.combine.missing_cancellable_storage",
707
+ "high",
708
+ sf,
709
+ sf,
710
+ `Line ${lineNumber}: Combine subscription without AnyCancellable storage - will leak memory`,
711
+ findings
712
+ );
713
+ }
714
+
715
+
716
+ const manualInstantiationPattern = /=\s*\w+(Service|Repository|Manager|Client|API)\s*\(\)/g;
717
+ while ((match = manualInstantiationPattern.exec(content)) !== null) {
718
+ const lineNumber = content.substring(0, match.index).split('\n').length;
719
+ const lineText = content.split('\n')[lineNumber - 1];
720
+ if (!lineText.includes('init') && !lineText.includes('@Injected') && !lineText.includes('Environment')) {
721
+ pushFinding(
722
+ "ios.di.manual_instantiation",
723
+ "high",
724
+ sf,
725
+ sf,
726
+ `Line ${lineNumber}: Manual dependency instantiation - inject via initializer or @EnvironmentObject`,
727
+ findings
728
+ );
729
+ }
730
+ }
731
+
732
+ if (content.includes('class') && content.match(/init\([^)]{50,}\)/)) {
733
+ const complexInitPattern = /init\([^)]{50,}\)/g;
734
+ while ((match = complexInitPattern.exec(content)) !== null) {
735
+ const lineNumber = content.substring(0, match.index).split('\n').length;
736
+ pushFinding(
737
+ "ios.di.complex_init",
738
+ "medium",
739
+ sf,
740
+ sf,
741
+ `Line ${lineNumber}: Complex initializer with many parameters - consider Factory pattern`,
742
+ findings
743
+ );
744
+ }
745
+ }
746
+
747
+ // 19. PERSISTENCE
748
+
749
+ if (content.includes('NSFetchRequest') && content.includes('NSManagedObject')) {
750
+ const rawFetchPattern = /NSFetchRequest<NSManagedObject>/g;
751
+ while ((match = rawFetchPattern.exec(content)) !== null) {
752
+ const lineNumber = content.substring(0, match.index).split('\n').length;
753
+ pushFinding(
754
+ "ios.persistence.raw_fetch",
755
+ "medium",
756
+ sf,
757
+ sf,
758
+ `Line ${lineNumber}: Raw NSFetchRequest<NSManagedObject> - use typed fetch NSFetchRequest<YourEntity>`,
759
+ findings
760
+ );
761
+ }
762
+ }
763
+
764
+ if (content.includes('@Model') || content.includes('SwiftData')) {
765
+ if (!content.includes('@available(iOS 17') && !content.includes('if #available(iOS 17')) {
766
+ const lineNumber = content.indexOf('@Model') || content.indexOf('SwiftData');
767
+ if (lineNumber > -1) {
768
+ pushFinding(
769
+ "ios.persistence.swiftdata_availability",
770
+ "high",
771
+ sf,
772
+ sf,
773
+ `SwiftData requires iOS 17+ - add @available(iOS 17, *) check`,
774
+ findings
775
+ );
776
+ }
777
+ }
778
+ }
779
+
780
+
781
+ const storyboardPatterns = [
782
+ /UIStoryboard\s*\(/g,
783
+ /instantiateViewController/g,
784
+ /UINib\s*\(/g,
785
+ /loadNibNamed/g,
786
+ /@IBOutlet.*weak.*UI/g
787
+ ];
788
+ storyboardPatterns.forEach(pattern => {
789
+ let match;
790
+ while ((match = pattern.exec(content)) !== null) {
791
+ const lineNumber = content.substring(0, match.index).split('\n').length;
792
+ pushFinding(
793
+ "ios.uikit.storyboards",
794
+ "medium",
795
+ sf,
796
+ sf,
797
+ `Line ${lineNumber}: Storyboard/XIB usage - prefer programmatic UI (better version control & testing)`,
798
+ findings
799
+ );
800
+ }
801
+ });
802
+
803
+ if (content.includes('UIView') && content.includes('.frame =')) {
804
+ const framePattern = /\.frame\s*=\s*CGRect/g;
805
+ let frameCount = 0;
806
+ while ((match = framePattern.exec(content)) !== null && frameCount < 3) {
807
+ const lineNumber = content.substring(0, match.index).split('\n').length;
808
+ pushFinding(
809
+ "ios.uikit.frame_layout",
810
+ "medium",
811
+ sf,
812
+ sf,
813
+ `Line ${lineNumber}: Manual frame layout - use Auto Layout or SwiftUI instead`,
814
+ findings
815
+ );
816
+ frameCount++;
817
+ }
818
+ }
819
+
820
+
821
+ if (filePath.includes('Test') && content.includes('XCTest')) {
822
+ if (!content.includes('makeSUT') && !content.includes('func make')) {
823
+ pushFinding(
824
+ "ios.testing.missing_make_sut",
825
+ "medium",
826
+ sf,
827
+ sf,
828
+ 'Test file without makeSUT factory - extract SUT creation for reusability',
829
+ findings
830
+ );
831
+ }
832
+
833
+ if (!content.includes('trackForMemoryLeaks') && !content.includes('addTeardownBlock')) {
834
+ pushFinding(
835
+ "ios.testing.missing_leak_tracking",
836
+ "medium",
837
+ sf,
838
+ sf,
839
+ 'Test file without memory leak tracking - add trackForMemoryLeaks helper',
840
+ findings
841
+ );
842
+ }
843
+ }
844
+
845
+ if (!filePath.includes('Test') && (content.includes('Mock') || content.includes('Spy') || content.includes('Stub'))) {
846
+ const mockPattern = /\b(Mock|Spy|Stub)\w+/g;
847
+ while ((match = mockPattern.exec(content)) !== null) {
848
+ const lineNumber = content.substring(0, match.index).split('\n').length;
849
+ pushFinding(
850
+ "ios.testing.mock_in_production",
851
+ "critical",
852
+ sf,
853
+ sf,
854
+ `Line ${lineNumber}: Test double '${match[0]}' in production code - move to test target`,
855
+ findings
856
+ );
857
+ }
858
+ }
859
+
860
+
861
+ if (content.includes('Coordinator')) {
862
+ const childCoordinatorPattern = /var\s+\w*[Cc]oordinators?\w*\s*:\s*\[/g;
863
+ while ((match = childCoordinatorPattern.exec(content)) !== null) {
864
+ const lineNumber = content.substring(0, match.index).split('\n').length;
865
+ const lineText = content.split('\n')[lineNumber - 1];
866
+ if (!lineText.includes('weak')) {
867
+ pushFinding(
868
+ "ios.architecture.coordinator_strong_children",
869
+ "high",
870
+ sf,
871
+ sf,
872
+ `Line ${lineNumber}: Child coordinators array should use weak references to avoid retain cycles`,
873
+ findings
874
+ );
875
+ }
876
+ }
877
+ }
878
+
879
+ if (content.includes('Presenter') && content.includes('Interactor') && content.includes('Router')) {
880
+ const lineCount = content.split('\n').length;
881
+ if (lineCount < 100) {
882
+ pushFinding(
883
+ "ios.architecture.viper_overkill",
884
+ "low",
885
+ sf,
886
+ sf,
887
+ 'VIPER pattern for small feature (<100 lines) - consider simpler MVVM',
888
+ findings
889
+ );
890
+ }
891
+ }
892
+
893
+
894
+ if (content.includes('UILabel') || content.includes('UITextView') || content.includes('UITextField')) {
895
+ if (!content.includes('adjustsFontForContentSizeCategory') && !content.includes('.font = .preferredFont')) {
896
+ pushFinding(
897
+ "ios.accessibility.dynamic_type",
898
+ "medium",
899
+ sf,
900
+ sf,
901
+ 'UI text elements without Dynamic Type support - use .preferredFont(forTextStyle:)',
902
+ findings
903
+ );
904
+ }
905
+ }
906
+
907
+ if (content.includes('isAccessibilityElement = true') && !content.includes('accessibilityTraits')) {
908
+ const lineNumber = content.indexOf('isAccessibilityElement = true');
909
+ if (lineNumber > -1) {
910
+ pushFinding(
911
+ "ios.accessibility.missing_traits",
912
+ "medium",
913
+ sf,
914
+ sf,
915
+ 'Accessibility element without traits - add .accessibilityTraits for better VoiceOver experience',
916
+ findings
917
+ );
918
+ }
919
+ }
920
+
921
+ if (content.includes('animate') || content.includes('UIView.transition')) {
922
+ if (!content.includes('UIAccessibility.isReduceMotionEnabled') && !content.includes('@Environment(\\.accessibilityReduceMotion)')) {
923
+ pushFinding(
924
+ "ios.accessibility.reduce_motion",
925
+ "medium",
926
+ sf,
927
+ sf,
928
+ 'Animation without Reduce Motion check - respect user preference with UIAccessibility.isReduceMotionEnabled',
929
+ findings
930
+ );
931
+ }
932
+ }
933
+
934
+
935
+ if ((content.includes('URLSession') || content.includes('Alamofire')) && content.includes('func')) {
936
+ if (!content.includes('retry') && !content.includes('maxRetries') && !filePath.includes('Mock')) {
937
+ pushFinding(
938
+ "ios.networking.missing_retry",
939
+ "medium",
940
+ sf,
941
+ sf,
942
+ 'Network layer without retry logic - add exponential backoff for failed requests',
943
+ findings
944
+ );
945
+ }
946
+ }
947
+
948
+ if (content.includes('URLSession') && (content.includes('api') || content.includes('backend'))) {
949
+ if (!content.includes('pinning') && !content.includes('URLSessionDelegate')) {
950
+ pushFinding(
951
+ "ios.networking.missing_ssl_pinning",
952
+ "high",
953
+ sf,
954
+ sf,
955
+ 'API client without SSL pinning - add certificate pinning for sensitive endpoints',
956
+ findings
957
+ );
958
+ }
959
+ }
960
+
961
+ if ((content.includes('URLSession') || content.includes('Alamofire')) && !content.includes('Reachability') && !content.includes('NWPathMonitor')) {
962
+ pushFinding(
963
+ "ios.networking.missing_reachability",
964
+ "medium",
965
+ sf,
966
+ sf,
967
+ 'Network layer without reachability check - add Network framework monitoring',
968
+ findings
969
+ );
970
+ }
971
+
972
+
973
+ if (content.includes('public') && filePath.includes('Sources/')) {
974
+ const publicPattern = /public\s+(class|struct|enum|func|var|let)/g;
975
+ const publicCount = (content.match(publicPattern) || []).length;
976
+ if (publicCount > 20) {
977
+ pushFinding(
978
+ "ios.spm.excessive_public_api",
979
+ "medium",
980
+ sf,
981
+ sf,
982
+ `Package exposes ${publicCount} public symbols - reduce public API surface, prefer internal`,
983
+ findings
984
+ );
985
+ }
986
+ }
987
+
988
+ const isModularProject = content.includes('import ') && (content.match(/import \w+/g) || []).length > 10;
989
+ if (isModularProject && !filePath.includes('Package.swift')) {
990
+ const fs = require('fs');
991
+ const packagePath = path.join(getRepoRoot(), 'Package.swift');
992
+ if (!fs.existsSync(packagePath)) {
993
+ pushFinding(
994
+ "ios.spm.missing_package_swift",
995
+ "low",
996
+ sf,
997
+ sf,
998
+ 'Project with many imports - consider SPM modularization with Package.swift',
999
+ findings
1000
+ );
1001
+ }
1002
+ }
1003
+
1004
+
1005
+ const forceCastPattern = /as!\s+/g;
1006
+ while ((match = forceCastPattern.exec(content)) !== null) {
1007
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1008
+ pushFinding(
1009
+ "ios.code_quality.force_cast",
1010
+ "high",
1011
+ sf,
1012
+ sf,
1013
+ `Line ${lineNumber}: Force cast 'as!' detected - use 'as?' with nil handling instead`,
1014
+ findings
1015
+ );
1016
+ }
1017
+
1018
+ if (!filePath.includes('Test')) {
1019
+ const todoPattern = /\/\/\s*(TODO|FIXME):/g;
1020
+ let todoCount = 0;
1021
+ while ((match = todoPattern.exec(content)) !== null && todoCount < 3) {
1022
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1023
+ pushFinding(
1024
+ "ios.code_quality.todo_fixme",
1025
+ "low",
1026
+ sf,
1027
+ sf,
1028
+ `Line ${lineNumber}: ${match[1]} comment - resolve before production release`,
1029
+ findings
1030
+ );
1031
+ todoCount++;
1032
+ }
1033
+ }
1034
+
1035
+ if (content.includes('#warning') || content.includes('@available(*, deprecated')) {
1036
+ pushFinding(
1037
+ "ios.code_quality.warnings_present",
1038
+ "medium",
1039
+ sf,
1040
+ sf,
1041
+ 'Build warnings present - resolve all warnings before production deployment',
1042
+ findings
1043
+ );
1044
+ }
1045
+
1046
+ // ==========================================
1047
+ // ==========================================
1048
+
1049
+
1050
+ const typePattern = /\b(class|struct|enum|protocol)\s+\w+/g;
1051
+ const classCount = (content.match(typePattern) || []).length;
1052
+ if (classCount > 3 && !filePath.includes('Generated')) {
1053
+ pushFinding(
1054
+ "ios.solid.srp_multiple_types",
1055
+ "high",
1056
+ sf,
1057
+ sf,
1058
+ `File defines ${classCount} types - split into separate files (SRP: one responsibility per file)`,
1059
+ findings
1060
+ );
1061
+ }
1062
+
1063
+ if (content.includes('class') || content.includes('struct')) {
1064
+ const funcPattern = /func\s+\w+/g;
1065
+ const funcCount = (content.match(funcPattern) || []).length;
1066
+ if (funcCount > 20) {
1067
+ pushFinding(
1068
+ "ios.solid.srp_god_class",
1069
+ "critical",
1070
+ sf,
1071
+ sf,
1072
+ `Type has ${funcCount} methods - split responsibilities (SRP: classes should have one reason to change)`,
1073
+ findings
1074
+ );
1075
+ }
1076
+ }
1077
+
1078
+
1079
+ // switch/if-else chains that should be polymorphism
1080
+ const switchPattern = /switch\s+\w+\s*\{[^}]{200,}\}/g;
1081
+ let switchCount = 0;
1082
+ while ((match = switchPattern.exec(content)) !== null && switchCount < 3) {
1083
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1084
+ const caseCount = (match[0].match(/case/g) || []).length;
1085
+ if (caseCount > 5) {
1086
+ pushFinding(
1087
+ "ios.solid.ocp_switch_polymorphism",
1088
+ "high",
1089
+ sf,
1090
+ sf,
1091
+ `Line ${lineNumber}: Large switch (${caseCount} cases) - consider protocol + extensions (OCP: open for extension, closed for modification)`,
1092
+ findings
1093
+ );
1094
+ switchCount++;
1095
+ }
1096
+ }
1097
+
1098
+ if (content.includes('extension') && content.includes('override')) {
1099
+ pushFinding(
1100
+ "ios.solid.ocp_modification",
1101
+ "medium",
1102
+ sf,
1103
+ sf,
1104
+ 'Extension with override - prefer protocol extensions or composition (OCP: extend, not modify)',
1105
+ findings
1106
+ );
1107
+ }
1108
+
1109
+
1110
+ const overridePattern = /override\s+func\s+(\w+)[^{]*\{[^}]*throw/g;
1111
+ while ((match = overridePattern.exec(content)) !== null) {
1112
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1113
+ pushFinding(
1114
+ "ios.solid.lsp_throws_violation",
1115
+ "high",
1116
+ sf,
1117
+ sf,
1118
+ `Line ${lineNumber}: Override throws error - ensure parent signature matches (LSP: subtypes must be substitutable)`,
1119
+ findings
1120
+ );
1121
+ }
1122
+
1123
+ if (content.includes('override') && content.includes('precondition(') || content.includes('assert(')) {
1124
+ const overrideBlocks = content.match(/override[^}]+\{[^}]+\}/g) || [];
1125
+ overrideBlocks.forEach(block => {
1126
+ if (block.includes('precondition') || block.includes('assert')) {
1127
+ const lineNumber = content.indexOf(block) > -1 ? content.substring(0, content.indexOf(block)).split('\n').length : 1;
1128
+ pushFinding(
1129
+ "ios.solid.lsp_precondition",
1130
+ "high",
1131
+ sf,
1132
+ sf,
1133
+ `Line ${lineNumber}: Override adds preconditions - weakens contract (LSP: don't strengthen preconditions)`,
1134
+ findings
1135
+ );
1136
+ }
1137
+ });
1138
+ }
1139
+
1140
+
1141
+ const protocolPattern = /protocol\s+(\w+)[^{]*\{([^}]+)\}/g;
1142
+ while ((match = protocolPattern.exec(content)) !== null) {
1143
+ const protocolName = match[1];
1144
+ const protocolBody = match[2];
1145
+ const reqCount = (protocolBody.match(/func\s+/g) || []).length + (protocolBody.match(/var\s+/g) || []).length;
1146
+ if (reqCount > 10) {
1147
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1148
+ pushFinding(
1149
+ "ios.solid.isp_fat_protocol",
1150
+ "high",
1151
+ sf,
1152
+ sf,
1153
+ `Line ${lineNumber}: Protocol '${protocolName}' has ${reqCount} requirements - split into smaller protocols (ISP: clients shouldn't depend on unused methods)`,
1154
+ findings
1155
+ );
1156
+ }
1157
+ }
1158
+
1159
+
1160
+ if ((content.includes('ViewModel') || content.includes('UseCase')) &&
1161
+ (content.includes('URLSession') || content.includes('UserDefaults') || content.includes('CoreData'))) {
1162
+ const lineNumber = content.indexOf('URLSession') || content.indexOf('UserDefaults') || content.indexOf('CoreData');
1163
+ if (lineNumber > -1) {
1164
+ const actualLine = content.substring(0, lineNumber).split('\n').length;
1165
+ pushFinding(
1166
+ "ios.solid.dip_concrete_dependency",
1167
+ "critical",
1168
+ sf,
1169
+ sf,
1170
+ `Line ${actualLine}: High-level module depends on concrete implementation - inject protocol instead (DIP: depend on abstractions)`,
1171
+ findings
1172
+ );
1173
+ }
1174
+ }
1175
+
1176
+ if ((content.includes('class') || content.includes('struct')) &&
1177
+ (content.match(/Repository|Service|Manager|Client/) && !content.match(/Protocol/))) {
1178
+ const className = content.match(/(class|struct)\s+(\w+(?:Repository|Service|Manager|Client))/)?.[2];
1179
+ if (className && !content.includes(`protocol ${className.replace(/Impl$/, '')}Protocol`)) {
1180
+ pushFinding(
1181
+ "ios.solid.dip_missing_abstraction",
1182
+ "high",
1183
+ sf,
1184
+ sf,
1185
+ `${className} without protocol abstraction - create protocol for testability (DIP: high-level shouldn't know low-level)`,
1186
+ findings
1187
+ );
1188
+ }
1189
+ }
1190
+
1191
+ // ==========================================
1192
+ // ==========================================
1193
+
1194
+
1195
+ if (filePath.includes('/Domain/')) {
1196
+ const forbiddenImports = ['UIKit', 'SwiftUI', 'Alamofire', 'CoreData', 'UserDefaults'];
1197
+ forbiddenImports.forEach(forbidden => {
1198
+ if (content.includes(`import ${forbidden}`)) {
1199
+ pushFinding(
1200
+ "ios.clean_arch.domain_dependency",
1201
+ "critical",
1202
+ sf,
1203
+ sf,
1204
+ `Domain layer imports ${forbidden} - Domain must be framework-agnostic (Clean Arch: dependencies point inward)`,
1205
+ findings
1206
+ );
1207
+ }
1208
+ });
1209
+ }
1210
+
1211
+ if (filePath.includes('/Application/') && filePath.includes('ViewModel')) {
1212
+ if (content.includes('URLSession') || content.includes('Alamofire') || content.includes('CoreData')) {
1213
+ pushFinding(
1214
+ "ios.clean_arch.application_dependency",
1215
+ "high",
1216
+ sf,
1217
+ sf,
1218
+ 'ViewModel depends on infrastructure details - inject repository protocol instead (Clean Arch: Application uses Domain protocols)',
1219
+ findings
1220
+ );
1221
+ }
1222
+ }
1223
+
1224
+ if (filePath.includes('/Presentation/') || content.includes(': View') || content.includes(': UIViewController')) {
1225
+ const businessPatterns = ['URLSession', 'fetch(', 'save(', 'delete(', 'update('];
1226
+ businessPatterns.forEach(pattern => {
1227
+ if (content.includes(pattern)) {
1228
+ const lineNumber = content.indexOf(pattern) > -1 ? content.substring(0, content.indexOf(pattern)).split('\n').length : 1;
1229
+ pushFinding(
1230
+ "ios.clean_arch.presentation_business_logic",
1231
+ "critical",
1232
+ sf,
1233
+ sf,
1234
+ `Line ${lineNumber}: Business logic in Presentation - move to UseCase/ViewModel (Clean Arch: Presentation only coordinates)`,
1235
+ findings
1236
+ );
1237
+ }
1238
+ });
1239
+ }
1240
+
1241
+
1242
+ if (filePath.includes('/Utilities/') || filePath.includes('/Helpers/')) {
1243
+ pushFinding(
1244
+ "ios.clean_arch.forbidden_directory",
1245
+ "critical",
1246
+ sf,
1247
+ sf,
1248
+ 'Utilities/Helpers directory forbidden - move to Domain/ or Infrastructure/ (Clean Arch: no utility dumping ground)',
1249
+ findings
1250
+ );
1251
+ }
1252
+
1253
+ if (filePath.endsWith('.swift') && !filePath.includes('/Domain/') && !filePath.includes('/Application/') &&
1254
+ !filePath.includes('/Infrastructure/') && !filePath.includes('/Presentation/') &&
1255
+ !filePath.includes('/Tests/') && !filePath.includes('AppDelegate')) {
1256
+ pushFinding(
1257
+ "ios.clean_arch.root_code",
1258
+ "high",
1259
+ sf,
1260
+ sf,
1261
+ 'Swift file in project root - organize into Domain/Application/Infrastructure/Presentation layers',
1262
+ findings
1263
+ );
1264
+ }
1265
+
1266
+
1267
+ if (content.includes('Repository') && !content.includes('Protocol') && !filePath.includes('/Infrastructure/')) {
1268
+ pushFinding(
1269
+ "ios.clean_arch.repository_location",
1270
+ "high",
1271
+ sf,
1272
+ sf,
1273
+ 'Repository implementation outside Infrastructure/ - move to Infrastructure/Repositories/',
1274
+ findings
1275
+ );
1276
+ }
1277
+
1278
+ if (content.includes('protocol') && content.includes('Repository') && !filePath.includes('/Domain/')) {
1279
+ pushFinding(
1280
+ "ios.clean_arch.repository_protocol_location",
1281
+ "high",
1282
+ sf,
1283
+ sf,
1284
+ 'Repository protocol outside Domain/ - move to Domain/Repositories/',
1285
+ findings
1286
+ );
1287
+ }
1288
+
1289
+ // ==========================================
1290
+ // ==========================================
1291
+
1292
+
1293
+ if (filePath.includes('Test') && content.includes('XCTest')) {
1294
+ const testFunctions = content.match(/func\s+test\w+\(\)/g) || [];
1295
+ testFunctions.forEach(testFunc => {
1296
+ const funcName = testFunc.match(/func\s+(test\w+)/)?.[1];
1297
+ if (funcName && !funcName.includes('_') && funcName.length > 15) {
1298
+ const lineNumber = content.indexOf(testFunc) > -1 ? content.substring(0, content.indexOf(testFunc)).split('\n').length : 1;
1299
+ pushFinding(
1300
+ "ios.bdd.test_naming",
1301
+ "medium",
1302
+ sf,
1303
+ sf,
1304
+ `Line ${lineNumber}: Test '${funcName}' - use BDD naming: test_givenX_whenY_thenZ or testGivenXWhenYThenZ`,
1305
+ findings
1306
+ );
1307
+ }
1308
+ });
1309
+
1310
+ if (content.includes('Mock') && !content.includes('Spy')) {
1311
+ pushFinding(
1312
+ "ios.bdd.prefer_spies",
1313
+ "low",
1314
+ sf,
1315
+ sf,
1316
+ 'Test uses Mocks - prefer Spies (verify real behavior) over Mocks (BDD: test behavior, not implementation)',
1317
+ findings
1318
+ );
1319
+ }
1320
+ }
1321
+
1322
+ // ==========================================
1323
+ // ==========================================
1324
+
1325
+
1326
+ const commentPattern = /\/\/(?!\s*MARK:)(?!\s*TODO:)(?!\s*FIXME:)(?!\s*swiftlint)[^\n]{20,}/g;
1327
+ let commentCount = 0;
1328
+ while ((match = commentPattern.exec(content)) !== null && commentCount < 5) {
1329
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1330
+ const commentText = match[0].substring(0, 50);
1331
+ pushFinding(
1332
+ "ios.code_quality.comment",
1333
+ "medium",
1334
+ sf,
1335
+ sf,
1336
+ `Line ${lineNumber}: Comment detected '${commentText}...' - refactor to self-descriptive code (No comments rule)`,
1337
+ findings
1338
+ );
1339
+ commentCount++;
1340
+ }
1341
+
1342
+
1343
+ const varPattern = /\bvar\s+\w+/g;
1344
+ const letPattern = /\blet\s+\w+/g;
1345
+ const varCount = (content.match(varPattern) || []).length;
1346
+ const letCount = (content.match(letPattern) || []).length;
1347
+ if (varCount > letCount && varCount > 10) {
1348
+ pushFinding(
1349
+ "ios.value_types.prefer_let",
1350
+ "medium",
1351
+ sf,
1352
+ sf,
1353
+ `File has ${varCount} 'var' vs ${letCount} 'let' - prefer immutability with 'let' (Value Types: immutability first)`,
1354
+ findings
1355
+ );
1356
+ }
1357
+
1358
+
1359
+ if (content.includes('struct') && !content.includes('Equatable') && !content.includes('Hashable')) {
1360
+ const structPattern = /struct\s+(\w+)/g;
1361
+ let structCount = 0;
1362
+ while ((match = structPattern.exec(content)) !== null && structCount < 3) {
1363
+ const structName = match[1];
1364
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1365
+ if (!structName.includes('Private') && !structName.includes('Internal')) {
1366
+ pushFinding(
1367
+ "ios.value_types.missing_protocols",
1368
+ "low",
1369
+ sf,
1370
+ sf,
1371
+ `Line ${lineNumber}: struct '${structName}' - add Equatable/Hashable conformance for value semantics`,
1372
+ findings
1373
+ );
1374
+ structCount++;
1375
+ }
1376
+ }
1377
+ }
1378
+
1379
+
1380
+ const pyramidPattern = /if\s+[^{]+\{[^}]*if\s+[^{]+\{[^}]*if\s+[^{]+\{/g;
1381
+ while ((match = pyramidPattern.exec(content)) !== null) {
1382
+ const lineNumber = content.substring(0, match.index).split('\n').length;
1383
+ pushFinding(
1384
+ "ios.code_quality.pyramid_doom",
1385
+ "high",
1386
+ sf,
1387
+ sf,
1388
+ `Line ${lineNumber}: Nested if statements (pyramid of doom) - use guard clauses for early returns`,
1389
+ findings
1390
+ );
1391
+ }
1392
+
1393
+
1394
+ if ((filePath.includes('/Models/') || filePath.includes('/Views/') || filePath.includes('/Controllers/')) &&
1395
+ !filePath.includes('/Features/') && !filePath.includes('/Domain/')) {
1396
+ pushFinding(
1397
+ "ios.ddd.technical_grouping",
1398
+ "low",
1399
+ sf,
1400
+ sf,
1401
+ 'Technical grouping (Models/Views/Controllers) - consider feature-first organization (DDD: group by domain)',
1402
+ findings
1403
+ );
1404
+ }
1405
+
1406
+ // 41. struct vs class (prefer value types)
1407
+ if (content.includes('class') && content.includes('Codable')) {
1408
+ const classPattern = /class\s+(\w+).*:.*Codable/g;
1409
+ while ((match = classPattern.exec(content)) !== null) {
1410
+ const className = match[1];
1411
+ if (!className.includes('ViewModel') && !className.includes('Controller')) {
1412
+ pushFinding(
1413
+ "ios.value_types.prefer_struct",
1414
+ "medium",
1415
+ sf,
1416
+ sf,
1417
+ `Class '${className}' with Codable - consider struct for value semantics (struct por defecto)`,
1418
+ findings
1419
+ );
1420
+ }
1421
+ }
1422
+ }
1423
+
1424
+ if (content.includes('ObservableObject') && content.includes('var ') && !content.includes('@Published')) {
1425
+ pushFinding(
1426
+ "ios.swiftui.missing_published",
1427
+ "high",
1428
+ sf,
1429
+ sf,
1430
+ 'ObservableObject without @Published properties - ViewModels need @Published for reactive updates',
1431
+ findings
1432
+ );
1433
+ }
1434
+
1435
+ if ((content.includes('UIView') || content.includes('View:')) && content.includes('DispatchQueue.main') && !content.includes('@MainActor')) {
1436
+ pushFinding(
1437
+ "ios.concurrency.missing_main_actor",
1438
+ "high",
1439
+ sf,
1440
+ sf,
1441
+ 'UI updates with DispatchQueue.main instead of @MainActor - use Swift Concurrency @MainActor',
1442
+ findings
1443
+ );
1444
+ }
1445
+
1446
+ // 44. actor keyword usage (thread-safe state)
1447
+ if (content.includes('class') && content.includes('queue') && !content.includes('actor') && !content.includes('@MainActor')) {
1448
+ const hasDispatchQueue = content.includes('DispatchQueue') || content.includes('serialQueue');
1449
+ const hasState = (content.match(/var\s+\w+/g) || []).length > 3;
1450
+ if (hasDispatchQueue && hasState) {
1451
+ pushFinding(
1452
+ "ios.concurrency.use_actor",
1453
+ "medium",
1454
+ sf,
1455
+ sf,
1456
+ 'Manual thread synchronization with queues - consider actor for thread-safe state management',
1457
+ findings
1458
+ );
1459
+ }
1460
+ }
1461
+
1462
+ if (content.includes('async') && content.includes('struct') && !content.includes('Sendable') && !content.includes('@unchecked')) {
1463
+ const asyncPattern = /func\s+\w+\([^)]*\)\s+async/g;
1464
+ if ((content.match(asyncPattern) || []).length > 2) {
1465
+ pushFinding(
1466
+ "ios.concurrency.missing_sendable",
1467
+ "medium",
1468
+ sf,
1469
+ sf,
1470
+ 'Async functions without Sendable conformance - add Sendable for thread-safe async operations',
1471
+ findings
1472
+ );
1473
+ }
1474
+ }
1475
+
1476
+ if (content.includes('import Alamofire') && !content.includes('// justified')) {
1477
+ pushFinding(
1478
+ "ios.networking.prefer_urlsession",
1479
+ "low",
1480
+ sf,
1481
+ sf,
1482
+ 'Using Alamofire - prefer native URLSession with async/await unless justified',
1483
+ findings
1484
+ );
1485
+ }
1486
+
1487
+ if ((content.includes('Response') || content.includes('Request')) && content.includes('struct') && !content.includes('Codable') && !content.includes('Decodable')) {
1488
+ const modelPattern = /struct\s+(\w+(Response|Request|DTO))\s/g;
1489
+ while ((match = modelPattern.exec(content)) !== null) {
1490
+ const modelName = match[1];
1491
+ pushFinding(
1492
+ "ios.networking.missing_codable",
1493
+ "high",
1494
+ sf,
1495
+ sf,
1496
+ `${modelName} struct without Codable - network models need Codable for JSON serialization`,
1497
+ findings
1498
+ );
1499
+ }
1500
+ }
1501
+
1502
+ if (content.includes('UserDefaults') && (content.includes('password') || content.includes('token') || content.includes('secret'))) {
1503
+ pushFinding(
1504
+ "ios.security.sensitive_data_userdefaults",
1505
+ "critical",
1506
+ sf,
1507
+ sf,
1508
+ 'Sensitive data in UserDefaults - use Keychain for passwords/tokens (Security: Keychain for sensitive data)',
1509
+ findings
1510
+ );
1511
+ }
1512
+
1513
+ if (content.includes('URLSession') && content.includes('https') && !content.includes('ServerTrustPolicy') && !content.includes('pinning')) {
1514
+ if (content.includes('production') || content.includes('release')) {
1515
+ pushFinding(
1516
+ "ios.security.missing_ssl_pinning",
1517
+ "high",
1518
+ sf,
1519
+ sf,
1520
+ 'Production network code without SSL pinning - implement certificate pinning for security',
1521
+ findings
1522
+ );
1523
+ }
1524
+ }
1525
+
1526
+ if (content.includes('catch') && !content.includes('log') && !content.includes('print') && !content.includes('Logger')) {
1527
+ const catchPattern = /catch\s*\{[^}]{0,50}\}/g;
1528
+ const matches = content.match(catchPattern) || [];
1529
+ if (matches.length > 0) {
1530
+ pushFinding(
1531
+ "ios.error_handling.silent_catch",
1532
+ "high",
1533
+ sf,
1534
+ sf,
1535
+ `${matches.length} catch blocks without logging - errors should be logged for debugging`,
1536
+ findings
1537
+ );
1538
+ }
1539
+ }
1540
+
1541
+ if ((content.includes('Button') || content.includes('Image') || content.includes('Text')) &&
1542
+ content.includes('View') && !content.includes('accessibility') && !filePath.includes('Preview')) {
1543
+ pushFinding(
1544
+ "ios.accessibility.missing_labels",
1545
+ "medium",
1546
+ sf,
1547
+ sf,
1548
+ 'SwiftUI views without accessibility modifiers - add .accessibilityLabel() for VoiceOver support',
1549
+ findings
1550
+ );
1551
+ }
1552
+
1553
+ const swiftUIStringPattern = /Text\("(?!.*NSLocalizedString)[^"]{10,}"\)/g;
1554
+ let stringMatches = content.match(swiftUIStringPattern) || [];
1555
+ if (stringMatches.length > 5 && !filePath.includes('Test') && !filePath.includes('Preview')) {
1556
+ pushFinding(
1557
+ "ios.localization.hardcoded_strings",
1558
+ "medium",
1559
+ sf,
1560
+ sf,
1561
+ `${stringMatches.length} hardcoded strings - use NSLocalizedString for i18n support`,
1562
+ findings
1563
+ );
1564
+ }
1565
+
1566
+ if (content.includes('NavigationLink') && content.includes('destination:') && !content.includes('Coordinator') && !content.includes('Router')) {
1567
+ const navLinkCount = (content.match(/NavigationLink/g) || []).length;
1568
+ if (navLinkCount > 3) {
1569
+ pushFinding(
1570
+ "ios.architecture.missing_coordinator",
1571
+ "medium",
1572
+ sf,
1573
+ sf,
1574
+ `${navLinkCount} NavigationLinks without Coordinator - consider Coordinator pattern for complex navigation`,
1575
+ findings
1576
+ );
1577
+ }
1578
+ }
1579
+
1580
+ if (filePath.includes('ViewModel') && (content.includes('import SwiftUI') || content.includes('import UIKit'))) {
1581
+ if (content.includes('Color') || content.includes('Font') || content.includes('Image')) {
1582
+ pushFinding(
1583
+ "ios.mvvm.viewmodel_ui_coupling",
1584
+ "high",
1585
+ sf,
1586
+ sf,
1587
+ 'ViewModel importing SwiftUI/UIKit with UI types - ViewModels should be UI-independent',
1588
+ findings
1589
+ );
1590
+ }
1591
+ }
1592
+
1593
+ if (filePath.includes('Repository') && content.includes('class') && !content.includes('protocol')) {
1594
+ pushFinding(
1595
+ "ios.architecture.missing_repository_protocol",
1596
+ "high",
1597
+ sf,
1598
+ sf,
1599
+ 'Repository implementation without protocol - define protocol in Domain layer for testability',
1600
+ findings
1601
+ );
1602
+ }
1603
+
1604
+ if (filePath.includes('ViewModel') && (content.match(/func\s+\w+.*async.*throws/g) || []).length > 3) {
1605
+ if (!content.includes('UseCase') && !content.includes('Interactor')) {
1606
+ pushFinding(
1607
+ "ios.architecture.missing_use_case",
1608
+ "medium",
1609
+ sf,
1610
+ sf,
1611
+ 'ViewModel with complex async logic - extract to Use Cases for better separation of concerns',
1612
+ findings
1613
+ );
1614
+ }
1615
+ }
1616
+
1617
+ if (content.includes('Task {') && !content.includes('async let') && !content.includes('withTaskGroup')) {
1618
+ const taskCount = (content.match(/Task\s*\{/g) || []).length;
1619
+ if (taskCount > 3) {
1620
+ pushFinding(
1621
+ "ios.concurrency.unstructured_tasks",
1622
+ "medium",
1623
+ sf,
1624
+ sf,
1625
+ `${taskCount} unstructured Tasks - consider TaskGroup for structured concurrency`,
1626
+ findings
1627
+ );
1628
+ }
1629
+ }
1630
+
1631
+ if (content.includes('.sink') && !content.includes('AnyCancellable') && !content.includes('store(in:)')) {
1632
+ pushFinding(
1633
+ "ios.combine.missing_cancellable_storage",
1634
+ "high",
1635
+ sf,
1636
+ sf,
1637
+ 'Combine subscription without cancellable storage - store in Set<AnyCancellable> to prevent memory leaks',
1638
+ findings
1639
+ );
1640
+ }
1641
+
1642
+ if (content.includes('List') && content.includes('ForEach') && !content.includes('Lazy')) {
1643
+ const listCount = (content.match(/List\s*\{/g) || []).length;
1644
+ if (listCount > 0 && content.includes('.id(')) {
1645
+ pushFinding(
1646
+ "ios.performance.use_lazy_stack",
1647
+ "low",
1648
+ sf,
1649
+ sf,
1650
+ 'List with many items - consider LazyVStack/LazyHStack for performance',
1651
+ findings
1652
+ );
1653
+ }
1654
+ }
1655
+
1656
+ if (filePath.includes('Tests') && content.includes('import ') && !content.includes('@testable')) {
1657
+ if (content.includes('XCTest')) {
1658
+ pushFinding(
1659
+ "ios.testing.missing_testable",
1660
+ "low",
1661
+ sf,
1662
+ sf,
1663
+ 'Test file without @testable import - use @testable for accessing internal types',
1664
+ findings
1665
+ );
1666
+ }
1667
+ }
1668
+
1669
+ if (content.includes('protocol') && !content.includes('extension') && (content.match(/func\s+\w+/g) || []).length > 3) {
1670
+ pushFinding(
1671
+ "ios.pop.missing_protocol_extension",
1672
+ "low",
1673
+ sf,
1674
+ sf,
1675
+ 'Protocol with many methods - consider protocol extensions for default implementations (Protocol-Oriented Programming)',
1676
+ findings
1677
+ );
1678
+ }
1679
+
1680
+ if (content.includes('class') && content.includes(':') && !content.includes('protocol')) {
1681
+ const inheritancePattern = /class\s+\w+\s*:\s*(\w+)(?:\s|,|<)/g;
1682
+ const matches = [];
1683
+ while ((match = inheritancePattern.exec(content)) !== null) {
1684
+ const baseClass = match[1];
1685
+ if (baseClass !== 'NSObject' && baseClass !== 'ObservableObject' && !baseClass.includes('ViewController')) {
1686
+ matches.push(baseClass);
1687
+ }
1688
+ }
1689
+ if (matches.length > 0) {
1690
+ pushFinding(
1691
+ "ios.pop.prefer_protocol_composition",
1692
+ "medium",
1693
+ sf,
1694
+ sf,
1695
+ `Class inheritance from ${matches[0]} - prefer protocol composition over inheritance (POP: Protocols over Inheritance)`,
1696
+ findings
1697
+ );
1698
+ }
1699
+ }
1700
+
1701
+ if (content.includes('protocol') && content.includes('<') && !content.includes('associatedtype')) {
1702
+ pushFinding(
1703
+ "ios.pop.use_associated_types",
1704
+ "low",
1705
+ sf,
1706
+ sf,
1707
+ 'Generic protocol without associatedtype - use associated types for type-safe generics',
1708
+ findings
1709
+ );
1710
+ }
1711
+
1712
+ if ((content.match(/protocol\s+\w+/g) || []).length > 2 && !content.includes('&') && !content.includes(',')) {
1713
+ pushFinding(
1714
+ "ios.pop.missing_protocol_composition",
1715
+ "low",
1716
+ sf,
1717
+ sf,
1718
+ 'Multiple protocols defined - consider protocol composition (Protocol & AnotherProtocol)',
1719
+ findings
1720
+ );
1721
+ }
1722
+
1723
+ if (content.includes('protocol') && content.includes('Delegate') && !content.includes('AnyObject')) {
1724
+ const delegatePattern = /protocol\s+\w+Delegate/g;
1725
+ if ((content.match(delegatePattern) || []).length > 0 && !content.includes(': AnyObject')) {
1726
+ pushFinding(
1727
+ "ios.memory.delegate_not_anyobject",
1728
+ "high",
1729
+ sf,
1730
+ sf,
1731
+ 'Delegate protocol without AnyObject conformance - add ": AnyObject" to enable weak references',
1732
+ findings
1733
+ );
1734
+ }
1735
+ }
1736
+
1737
+ if (content.includes('class') && (content.includes('Timer') || content.includes('NotificationCenter') || content.includes('observer')) && !content.includes('deinit')) {
1738
+ pushFinding(
1739
+ "ios.memory.missing_deinit",
1740
+ "medium",
1741
+ sf,
1742
+ sf,
1743
+ 'Class with resources (Timer/NotificationCenter) without deinit - implement deinit for cleanup',
1744
+ findings
1745
+ );
1746
+ }
1747
+
1748
+ if (content.includes('struct') && (content.match(/var\s+\w+:\s*\[/g) || []).length > 2) {
1749
+ const structPattern = /struct\s+(\w+)/g;
1750
+ while ((match = structPattern.exec(content)) !== null) {
1751
+ const structName = match[1];
1752
+ if (!content.includes('isKnownUniquelyReferenced')) {
1753
+ pushFinding(
1754
+ "ios.value_types.large_struct_cow",
1755
+ "low",
1756
+ sf,
1757
+ sf,
1758
+ `Struct '${structName}' with collections - consider implementing copy-on-write for performance`,
1759
+ findings
1760
+ );
1761
+ break;
1762
+ }
1763
+ }
1764
+ }
1765
+
1766
+ if (content.includes('enum') && content.includes('Error') && !content.includes(': Error') && !content.includes(': LocalizedError')) {
1767
+ const errorEnumPattern = /enum\s+(\w+Error)/g;
1768
+ while ((match = errorEnumPattern.exec(content)) !== null) {
1769
+ const enumName = match[1];
1770
+ pushFinding(
1771
+ "ios.error_handling.enum_not_error",
1772
+ "high",
1773
+ sf,
1774
+ sf,
1775
+ `Enum '${enumName}' should conform to Error protocol for proper error handling`,
1776
+ findings
1777
+ );
1778
+ }
1779
+ }
1780
+
1781
+ if (content.includes('import Combine') && content.includes('Publisher') && !content.includes('async')) {
1782
+ const publisherCount = (content.match(/Publisher/g) || []).length;
1783
+ if (publisherCount > 5) {
1784
+ pushFinding(
1785
+ "ios.combine.overuse",
1786
+ "low",
1787
+ sf,
1788
+ sf,
1789
+ `${publisherCount} Publishers - consider async/await for simpler async code (Combine: async/await more simple for single values)`,
1790
+ findings
1791
+ );
1792
+ }
1793
+ }
1794
+
1795
+ if (content.includes('URLSession') && content.includes('dataTask') && !content.includes('retry') && !content.includes('attempt')) {
1796
+ pushFinding(
1797
+ "ios.networking.missing_retry",
1798
+ "medium",
1799
+ sf,
1800
+ sf,
1801
+ 'Network requests without retry logic - implement exponential backoff for failed requests',
1802
+ findings
1803
+ );
1804
+ }
1805
+
1806
+ if (content.includes('Keychain') && !content.includes('SecItemAdd') && !content.includes('KeychainSwift')) {
1807
+ pushFinding(
1808
+ "ios.security.keychain_usage",
1809
+ "low",
1810
+ sf,
1811
+ sf,
1812
+ 'Keychain mentioned but not using Security framework APIs - use SecItemAdd/SecItemCopyMatching',
1813
+ findings
1814
+ );
1815
+ }
1816
+
1817
+ if (content.includes('import CoreData') && content.includes('NSManagedObject') && !content.includes('NSPersistentContainer')) {
1818
+ pushFinding(
1819
+ "ios.persistence.outdated_coredata",
1820
+ "medium",
1821
+ sf,
1822
+ sf,
1823
+ 'Core Data without NSPersistentContainer - use modern Core Data stack',
1824
+ findings
1825
+ );
1826
+ }
1827
+
1828
+ if (content.includes('import CoreData') && !content.includes('SwiftData') && content.includes('@available(iOS 17')) {
1829
+ pushFinding(
1830
+ "ios.persistence.use_swiftdata",
1831
+ "low",
1832
+ sf,
1833
+ sf,
1834
+ 'iOS 17+ with Core Data - consider SwiftData for modern declarative persistence',
1835
+ findings
1836
+ );
1837
+ }
1838
+
1839
+ if (content.includes('ObservedObject') && !content.includes('StateObject') && content.includes('ViewModel')) {
1840
+ pushFinding(
1841
+ "ios.swiftui.wrong_property_wrapper",
1842
+ "high",
1843
+ sf,
1844
+ sf,
1845
+ '@ObservedObject for ViewModel - use @StateObject for ownership to prevent recreation',
1846
+ findings
1847
+ );
1848
+ }
1849
+
1850
+ if ((content.match(/GeometryReader/g) || []).length > 2) {
1851
+ pushFinding(
1852
+ "ios.swiftui.geometryreader_overuse",
1853
+ "low",
1854
+ sf,
1855
+ sf,
1856
+ 'Multiple GeometryReader uses - use with moderation, prefer layout priorities',
1857
+ findings
1858
+ );
1859
+ }
1860
+
1861
+ if ((content.match(/\.padding\(\)\.background\(\)\.cornerRadius\(\)/g) || []).length > 2) {
1862
+ pushFinding(
1863
+ "ios.swiftui.extract_view_modifier",
1864
+ "low",
1865
+ sf,
1866
+ sf,
1867
+ 'Repeated modifier chains - extract to custom ViewModifier for reusability',
1868
+ findings
1869
+ );
1870
+ }
1871
+
1872
+ if (content.includes('UIViewController') && lineCount > 300) {
1873
+ pushFinding(
1874
+ "ios.uikit.massive_viewcontroller",
1875
+ "high",
1876
+ sf,
1877
+ sf,
1878
+ `UIViewController with ${lineCount} lines (limit: 300) - extract logic to ViewModel/Coordinator (MVVM pattern)`,
1879
+ findings
1880
+ );
1881
+ }
1882
+
1883
+ if (filePath.includes('ViewController.swift') && !content.includes('loadView') && content.includes('viewDidLoad')) {
1884
+ if (content.includes('storyboard') || content.includes('nib')) {
1885
+ pushFinding(
1886
+ "ios.uikit.storyboard_usage",
1887
+ "medium",
1888
+ sf,
1889
+ sf,
1890
+ 'ViewController using Storyboards/XIBs - prefer programmatic UI for better version control',
1891
+ findings
1892
+ );
1893
+ }
1894
+ }
1895
+
1896
+ if (filePath.includes('.swift') && !filePath.includes('Package.swift') && !filePath.includes('Tests')) {
1897
+ const hasFeature = filePath.includes('/Features/') || filePath.includes('/Modules/');
1898
+ const totalSourceFiles = project?.getSourceFiles()?.length || 0;
1899
+ const isLargeProject = totalSourceFiles > 100;
1900
+ if (isLargeProject && !hasFeature && !filePath.includes('/Shared/')) {
1901
+ pushFinding(
1902
+ "ios.code_organization.missing_modularization",
1903
+ "low",
1904
+ sf,
1905
+ sf,
1906
+ 'Large project without SPM modules - consider feature modules (Orders, Users, Auth as packages)',
1907
+ findings
1908
+ );
1909
+ }
1910
+ }
1911
+
1912
+ if (lineCount > 100 && !content.includes('// MARK:') && !content.includes('MARK: -')) {
1913
+ pushFinding(
1914
+ "ios.code_organization.missing_mark",
1915
+ "low",
1916
+ sf,
1917
+ sf,
1918
+ `File with ${lineCount} lines without MARK: - sections - use MARK: for code organization`,
1919
+ findings
1920
+ );
1921
+ }
1922
+
1923
+ const fileName = filePath.split('/').pop();
1924
+ if (fileName && /^[a-z]/.test(fileName) && fileName.endsWith('.swift')) {
1925
+ pushFinding(
1926
+ "ios.code_organization.file_naming",
1927
+ "low",
1928
+ sf,
1929
+ sf,
1930
+ `File name '${fileName}' starts with lowercase - use PascalCase for type files`,
1931
+ findings
1932
+ );
1933
+ }
1934
+
1935
+ if (filePath.includes('Tests') && content.includes('func test')) {
1936
+ const testPattern = /func\s+(test\w+)/g;
1937
+ const tests = [];
1938
+ while ((match = testPattern.exec(content)) !== null) {
1939
+ tests.push(match[1]);
1940
+ }
1941
+
1942
+ const badTests = tests.filter(t =>
1943
+ !t.includes('_') &&
1944
+ !t.toLowerCase().includes('given') &&
1945
+ !t.toLowerCase().includes('when') &&
1946
+ !t.toLowerCase().includes('then') &&
1947
+ !t.toLowerCase().includes('should')
1948
+ );
1949
+
1950
+ if (badTests.length > 0) {
1951
+ pushFinding(
1952
+ "ios.testing.test_naming",
1953
+ "low",
1954
+ sf,
1955
+ sf,
1956
+ `${badTests.length} tests with unclear names - use Given_When_Then or should pattern (BDD: test naming)`,
1957
+ findings
1958
+ );
1959
+ }
1960
+ }
1961
+
1962
+ if (filePath.includes('Tests') && content.includes('XCTest') && !content.includes('makeSUT') && !content.includes('func sut')) {
1963
+ const testCount = (content.match(/func\s+test/g) || []).length;
1964
+ if (testCount > 2) {
1965
+ pushFinding(
1966
+ "ios.testing.missing_makesut",
1967
+ "medium",
1968
+ sf,
1969
+ sf,
1970
+ 'Tests without makeSUT factory - use makeSUT pattern for System Under Test creation',
1971
+ findings
1972
+ );
1973
+ }
1974
+ }
1975
+
1976
+ if (filePath.includes('Tests') && content.includes('deinit') && !content.includes('trackForMemoryLeaks') && !content.includes('addTeardownBlock')) {
1977
+ pushFinding(
1978
+ "ios.testing.missing_memory_leak_tracking",
1979
+ "medium",
1980
+ sf,
1981
+ sf,
1982
+ 'Tests checking deinit without trackForMemoryLeaks helper - implement helper for consistent leak detection',
1983
+ findings
1984
+ );
1985
+ }
1986
+
1987
+ if (filePath.includes('Tests') && (content.includes('class Mock') || content.includes('class Stub'))) {
1988
+ if (!content.includes('class Spy')) {
1989
+ pushFinding(
1990
+ "ios.testing.prefer_spies",
1991
+ "low",
1992
+ sf,
1993
+ sf,
1994
+ 'Using Mocks/Stubs - prefer Spies for verifying real behavior (Spies > Mocks)',
1995
+ findings
1996
+ );
1997
+ }
1998
+ }
1999
+
2000
+ if (filePath.includes('Tests') && content.includes('sleep') || content.includes('wait')) {
2001
+ pushFinding(
2002
+ "ios.testing.slow_tests",
2003
+ "medium",
2004
+ sf,
2005
+ sf,
2006
+ 'Test with sleep/wait - tests should be fast (<10ms unit tests)',
2007
+ findings
2008
+ );
2009
+ }
2010
+
2011
+ if (filePath.includes('UITests') && content.includes('XCUIApplication') && !content.includes('identifier')) {
2012
+ pushFinding(
2013
+ "ios.ui_testing.missing_identifiers",
2014
+ "medium",
2015
+ sf,
2016
+ sf,
2017
+ 'UI tests without accessibility identifiers - add identifiers for reliable element location',
2018
+ findings
2019
+ );
2020
+ }
2021
+
2022
+ if (filePath.includes('UITests') && (content.match(/XCUIElement/g) || []).length > 5 && !content.includes('Page')) {
2023
+ pushFinding(
2024
+ "ios.ui_testing.missing_page_object",
2025
+ "low",
2026
+ sf,
2027
+ sf,
2028
+ 'UI test with many XCUIElements - use Page Object Pattern for encapsulation',
2029
+ findings
2030
+ );
2031
+ }
2032
+
2033
+ if (content.includes('LocalAuthentication') && !content.includes('LAContext') && !content.includes('biometryType')) {
2034
+ pushFinding(
2035
+ "ios.security.incomplete_biometric",
2036
+ "medium",
2037
+ sf,
2038
+ sf,
2039
+ 'LocalAuthentication import without LAContext - implement complete biometric authentication',
2040
+ findings
2041
+ );
2042
+ }
2043
+
2044
+ if (content.includes('http://') && !content.includes('localhost') && !filePath.includes('Test')) {
2045
+ pushFinding(
2046
+ "ios.security.http_not_https",
2047
+ "critical",
2048
+ sf,
2049
+ sf,
2050
+ 'HTTP URL detected - use HTTPS for App Transport Security (ATS: HTTPS por defecto)',
2051
+ findings
2052
+ );
2053
+ }
2054
+
2055
+ if ((content.includes('View:') || content.includes('UIView')) && !content.includes('accessibility') && lineCount > 50) {
2056
+ pushFinding(
2057
+ "ios.accessibility.voiceover_support",
2058
+ "low",
2059
+ sf,
2060
+ sf,
2061
+ 'Complex view without accessibility modifiers - test with VoiceOver and add labels/traits',
2062
+ findings
2063
+ );
2064
+ }
2065
+
2066
+ if (content.includes('UIFont') && content.includes('systemFont') && !content.includes('preferredFont')) {
2067
+ pushFinding(
2068
+ "ios.accessibility.missing_dynamic_type",
2069
+ "medium",
2070
+ sf,
2071
+ sf,
2072
+ 'Using systemFont instead of preferredFont - use preferredFont for Dynamic Type support',
2073
+ findings
2074
+ );
2075
+ }
2076
+
2077
+ if ((content.match(/"[^"]{20,}"/g) || []).length > 3 && !content.includes('NSLocalizedString') && !filePath.includes('Test')) {
2078
+ pushFinding(
2079
+ "ios.localization.missing_nslocalizedstring",
2080
+ "medium",
2081
+ sf,
2082
+ sf,
2083
+ 'Long strings without NSLocalizedString - use NSLocalizedString for internationalization',
2084
+ findings
2085
+ );
2086
+ }
2087
+
2088
+ if (content.includes('Date()') && content.includes('description') && !content.includes('DateFormatter')) {
2089
+ pushFinding(
2090
+ "ios.localization.missing_dateformatter",
2091
+ "medium",
2092
+ sf,
2093
+ sf,
2094
+ 'Date.description for display - use DateFormatter for localized dates',
2095
+ findings
2096
+ );
2097
+ }
2098
+
2099
+ if (filePath.includes('ViewModel') && content.includes('navigation') && !content.includes('Coordinator')) {
2100
+ pushFinding(
2101
+ "ios.architecture.viewmodel_navigation",
2102
+ "medium",
2103
+ sf,
2104
+ sf,
2105
+ 'ViewModel with navigation logic - use Coordinator pattern (MVVM-C) for navigation',
2106
+ findings
2107
+ );
2108
+ }
2109
+
2110
+ if (content.includes('struct') && content.includes(': View') && !content.includes(': Equatable') && lineCount > 50) {
2111
+ pushFinding(
2112
+ "ios.swiftui.missing_equatable",
2113
+ "low",
2114
+ sf,
2115
+ sf,
2116
+ 'Complex View without Equatable conformance - add Equatable for render optimization',
2117
+ findings
2118
+ );
2119
+ }
2120
+
2121
+ if (content.includes('VStack') && content.includes('ForEach') && !content.includes('Lazy')) {
2122
+ const forEachPattern = /ForEach\([^)]*\)/g;
2123
+ const iterations = content.match(forEachPattern) || [];
2124
+ if (iterations.length > 0 && content.includes('.count >')) {
2125
+ pushFinding(
2126
+ "ios.performance.use_lazy_stack",
2127
+ "medium",
2128
+ sf,
2129
+ sf,
2130
+ 'VStack with ForEach over large array - use LazyVStack for performance',
2131
+ findings
2132
+ );
2133
+ }
2134
+ }
2135
+
2136
+ if (content.includes('Image(') && !content.includes('resizable') && !content.includes('frame')) {
2137
+ const imageCount = (content.match(/Image\(/g) || []).length;
2138
+ if (imageCount > 3) {
2139
+ pushFinding(
2140
+ "ios.performance.image_not_optimized",
2141
+ "low",
2142
+ sf,
2143
+ sf,
2144
+ `${imageCount} images without resizable/frame - optimize image rendering with .resizable().frame()`,
2145
+ findings
2146
+ );
2147
+ }
2148
+ }
2149
+
2150
+ if (content.includes('URLSession') && content.includes('dataTask') && content.includes('DispatchQueue.main')) {
2151
+ pushFinding(
2152
+ "ios.performance.unnecessary_main_dispatch",
2153
+ "low",
2154
+ sf,
2155
+ sf,
2156
+ 'Manual DispatchQueue.main with URLSession - network callbacks already on background thread',
2157
+ findings
2158
+ );
2159
+ }
2160
+
2161
+ const fastlaneFileName = filePath.split('/').pop();
2162
+ if (fastlaneFileName === 'Fastfile' || content.includes('fastlane')) {
2163
+ pushFinding(
2164
+ "ios.cicd.fastlane_detected",
2165
+ "info",
2166
+ sf,
2167
+ sf,
2168
+ 'Fastlane configuration detected - good practice for CI/CD automation',
2169
+ findings
2170
+ );
2171
+ }
2172
+
2173
+ });
2174
+ }
2175
+
2176
+ module.exports = { runIOSIntelligence };