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,2048 @@
1
+
2
+ const path = require('path');
3
+ const {
4
+ pushFinding,
5
+ mapToLevel,
6
+ SyntaxKind,
7
+ isTestFile,
8
+ platformOf,
9
+ hasImport,
10
+ hasDecorator,
11
+ findStringLiterals,
12
+ findIdentifiers,
13
+ findCallExpressions,
14
+ classHasMethod,
15
+ classHasProperty,
16
+ getClasses,
17
+ getFunctions,
18
+ getArrowFunctions,
19
+ getRepoRoot,
20
+ } = require(path.join(__dirname, '../ast-core'));
21
+ const { BackendArchitectureDetector } = require(path.join(__dirname, 'analyzers/BackendArchitectureDetector'));
22
+
23
+ /**
24
+ * Run Backend-specific AST intelligence analysis
25
+ * @param {Project} project - TypeScript morph project
26
+ * @param {Array} findings - Findings array to populate
27
+ * @param {string} platform - Platform identifier
28
+ */
29
+ function runBackendIntelligence(project, findings, platform) {
30
+ try {
31
+ const root = getRepoRoot();
32
+ const architectureDetector = new BackendArchitectureDetector(root);
33
+ const detectedPattern = architectureDetector.detect();
34
+ const detectionSummary = architectureDetector.getDetectionSummary();
35
+
36
+ console.error(`[Backend Architecture] Pattern detected: ${detectedPattern} (confidence: ${detectionSummary.confidence}%)`);
37
+
38
+ if (detectionSummary.warnings.length > 0) {
39
+ detectionSummary.warnings.forEach(warning => {
40
+ pushFinding('backend.architecture.detection_warning', warning.severity.toLowerCase(), null, null, warning.message + '\n\n' + warning.recommendation, findings);
41
+ });
42
+ }
43
+ } catch (error) {
44
+ console.error('[Backend Architecture] Error during architecture detection:', error.message);
45
+ }
46
+
47
+ const hasGlobalCors = project.getSourceFiles().some((sourceFile) => {
48
+ const text = sourceFile.getFullText();
49
+ return text.includes("app.enableCors(") ||
50
+ text.includes("app.use(cors(") ||
51
+ text.includes("Access-Control-Allow-Origin");
52
+ });
53
+
54
+ const godClassBaseline = (() => {
55
+ const quantile = (values, p) => {
56
+ if (!values || values.length === 0) return 0;
57
+ const sorted = [...values].filter((v) => Number.isFinite(v)).sort((a, b) => a - b);
58
+ if (sorted.length === 0) return 0;
59
+ const idx = Math.max(0, Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1));
60
+ return sorted[idx];
61
+ };
62
+
63
+ const median = (values) => {
64
+ if (!values || values.length === 0) return 0;
65
+ const sorted = [...values].filter((v) => Number.isFinite(v)).sort((a, b) => a - b);
66
+ if (sorted.length === 0) return 0;
67
+ const mid = Math.floor(sorted.length / 2);
68
+ if (sorted.length % 2 === 0) return (sorted[mid - 1] + sorted[mid]) / 2;
69
+ return sorted[mid];
70
+ };
71
+
72
+ const mad = (values) => {
73
+ const med = median(values);
74
+ const deviations = (values || []).map((v) => Math.abs(v - med));
75
+ return median(deviations);
76
+ };
77
+
78
+ const robustZ = (x, med, madValue) => {
79
+ if (!Number.isFinite(x) || !Number.isFinite(med) || !Number.isFinite(madValue) || madValue === 0) return 0;
80
+ return 0.6745 * (x - med) / madValue;
81
+ };
82
+
83
+ const concernCountOf = (cls) => {
84
+ const text = cls.getFullText();
85
+ const concerns = new Set();
86
+
87
+ if (/\bfs\.|\bfs\.promises\b|readFileSync|writeFileSync|mkdirSync|unlinkSync|readdirSync/.test(text)) concerns.add('io');
88
+ if (/\bpath\.|join\(|resolve\(|dirname\(|basename\(/.test(text)) concerns.add('path');
89
+ if (/execSync\(|spawnSync\(|spawn\(|child_process/.test(text)) concerns.add('process');
90
+ if (/\bfetch\b|axios\b|http\.|https\.|request\(/.test(text)) concerns.add('network');
91
+ if (/\bcrypto\b|encrypt|decrypt|hash|jwt|bearer|token/i.test(text)) concerns.add('security');
92
+ if (/setTimeout\(|setInterval\(|clearInterval\(|cron|schedule/i.test(text)) concerns.add('scheduling');
93
+ if (/\brepo\b|repository|prisma|typeorm|mongoose|sequelize|knex|\bdb\b|database|sql/i.test(text)) concerns.add('persistence');
94
+ if (/notification|notifier|terminal-notifier|osascript/i.test(text)) concerns.add('notifications');
95
+ if (/\bgit\b|rev-parse|git diff|git status|git log/i.test(text)) concerns.add('git');
96
+
97
+ return concerns.size;
98
+ };
99
+
100
+ const complexityOf = (cls) => {
101
+ const decisionKinds = [
102
+ SyntaxKind.IfStatement,
103
+ SyntaxKind.ForStatement,
104
+ SyntaxKind.ForInStatement,
105
+ SyntaxKind.ForOfStatement,
106
+ SyntaxKind.WhileStatement,
107
+ SyntaxKind.DoStatement,
108
+ SyntaxKind.SwitchStatement,
109
+ SyntaxKind.ConditionalExpression,
110
+ SyntaxKind.TryStatement,
111
+ SyntaxKind.CatchClause
112
+ ];
113
+ return decisionKinds.reduce((acc, kind) => acc + cls.getDescendantsOfKind(kind).length, 0);
114
+ };
115
+
116
+ const metrics = [];
117
+ project.getSourceFiles().forEach((sf) => {
118
+ if (!sf || typeof sf.getFilePath !== 'function') return;
119
+ const filePath = sf.getFilePath();
120
+ if (platformOf(filePath) !== 'backend') return;
121
+ if (/\/ast-[^/]+\.js$/.test(filePath)) return;
122
+ if (process.env.AUDIT_LIBRARY !== 'true') {
123
+ if (/scripts\/hooks-system\/infrastructure\/ast\//i.test(filePath) || /\/infrastructure\/ast\//i.test(filePath)) return;
124
+ }
125
+ if (isTestFile(filePath)) return;
126
+
127
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
128
+ const className = cls.getName() || '';
129
+ const isValueObject = /Metrics|ValueObject|VO$|Dto$|Entity$/.test(className);
130
+ const isTestClass = /Spec$|Test$|Mock/.test(className);
131
+ if (isValueObject || isTestClass) return;
132
+
133
+ const methodsCount = cls.getMethods().length;
134
+ const propertiesCount = cls.getProperties().length;
135
+ const startLine = cls.getStartLineNumber();
136
+ const endLine = cls.getEndLineNumber();
137
+ const lineCount = Math.max(0, endLine - startLine);
138
+ const complexity = complexityOf(cls);
139
+ const concerns = concernCountOf(cls);
140
+
141
+ metrics.push({ methodsCount, propertiesCount, lineCount, complexity, concerns });
142
+ });
143
+ });
144
+
145
+ if (metrics.length === 0) return null;
146
+
147
+ const pOutlier = Number(process.env.AST_GODCLASS_P_OUTLIER || 90);
148
+ const pExtreme = Number(process.env.AST_GODCLASS_P_EXTREME || 97);
149
+
150
+ const methods = metrics.map(m => m.methodsCount);
151
+ const props = metrics.map(m => m.propertiesCount);
152
+ const lines = metrics.map(m => m.lineCount);
153
+ const complexities = metrics.map(m => m.complexity);
154
+ const concerns = metrics.map(m => m.concerns);
155
+
156
+ const med = {
157
+ methodsCount: median(methods),
158
+ propertiesCount: median(props),
159
+ lineCount: median(lines),
160
+ complexity: median(complexities)
161
+ };
162
+ const madValue = {
163
+ methodsCount: mad(methods),
164
+ propertiesCount: mad(props),
165
+ lineCount: mad(lines),
166
+ complexity: mad(complexities)
167
+ };
168
+
169
+ const z = {
170
+ methodsCount: methods.map(v => robustZ(v, med.methodsCount, madValue.methodsCount)),
171
+ propertiesCount: props.map(v => robustZ(v, med.propertiesCount, madValue.propertiesCount)),
172
+ lineCount: lines.map(v => robustZ(v, med.lineCount, madValue.lineCount)),
173
+ complexity: complexities.map(v => robustZ(v, med.complexity, madValue.complexity))
174
+ };
175
+
176
+ return {
177
+ thresholds: {
178
+ outlier: {
179
+ methodsCountZ: quantile(z.methodsCount, pOutlier),
180
+ propertiesCountZ: quantile(z.propertiesCount, pOutlier),
181
+ lineCountZ: quantile(z.lineCount, pOutlier),
182
+ complexityZ: quantile(z.complexity, pOutlier),
183
+ concerns: quantile(concerns, pOutlier)
184
+ },
185
+ extreme: {
186
+ methodsCountZ: quantile(z.methodsCount, pExtreme),
187
+ propertiesCountZ: quantile(z.propertiesCount, pExtreme),
188
+ lineCountZ: quantile(z.lineCount, pExtreme),
189
+ complexityZ: quantile(z.complexity, pExtreme)
190
+ }
191
+ },
192
+ med,
193
+ mad: madValue,
194
+ robustZ
195
+ };
196
+ })();
197
+
198
+ project.getSourceFiles().forEach((sf) => {
199
+ if (!sf || typeof sf.getFilePath !== 'function') return;
200
+ const filePath = sf.getFilePath();
201
+
202
+ if (platformOf(filePath) !== "backend") return;
203
+
204
+ if (/\/ast-[^/]+\.js$/.test(filePath)) return;
205
+ if (process.env.AUDIT_LIBRARY !== 'true') {
206
+ if (/scripts\/hooks-system\/infrastructure\/ast\//i.test(filePath) || /\/infrastructure\/ast\//i.test(filePath)) return;
207
+ }
208
+
209
+ const fullText = sf.getFullText();
210
+ const insightsEnabled = process.env.AST_INSIGHTS === '1';
211
+ const isSpecFile = /\.(spec|test)\.(ts|tsx|js|jsx)$/.test(filePath);
212
+ const secretPattern = /(password|secret|key|token)\s*[:=]\s*['"`]([^'"]{8,})['"`]/gi;
213
+ const matches = Array.from(fullText.matchAll(secretPattern));
214
+
215
+ for (const match of matches) {
216
+ const fullMatch = match[0];
217
+ const secretValue = match[2];
218
+
219
+ const matchIndex = match.index || 0;
220
+ const lineStart = fullText.lastIndexOf('\n', matchIndex) + 1;
221
+ const lineEnd = fullText.indexOf('\n', matchIndex);
222
+ const fullLine = fullText.substring(lineStart, lineEnd === -1 ? undefined : lineEnd);
223
+
224
+ const isEnvVar = /process\.env\.|env\.|config\.|from.*env/i.test(fullMatch);
225
+
226
+ const isPlaceholderPattern = /^(placeholder|example|test-|mock-|fake-|dummy-|your-|xxx|abc|000|123|bearer\s)/i.test(secretValue);
227
+ const hasObviousTestWords = /(valid|invalid|wrong|expired|reset|sample|demo|user-\d|customer-\d|store-\d)/i.test(secretValue);
228
+ const isShortRepeating = secretValue.length <= 20 && /^(.)\1+$/.test(secretValue);
229
+ const isPlaceholder = isPlaceholderPattern || hasObviousTestWords || isShortRepeating;
230
+
231
+ const isComment = fullLine.includes('//') || fullLine.includes('/*');
232
+ const isTestContext = isSpecFile && /mock|jest\.fn|describe|it\(|beforeEach|afterEach/.test(fullText);
233
+ const isTestFilePath = isSpecFile || /\/(tests?|__tests__|e2e|spec|playwright)\//i.test(filePath);
234
+ const hasStorageContext = (
235
+ /localStorage|sessionStorage|AsyncStorage|getItem|setItem|removeItem/i.test(fullLine) ||
236
+ /const\s+\w*(KEY|STORAGE|CACHE|Token|Key|Storage)\s*=/i.test(fullLine)
237
+ );
238
+ const hasKeyNamingPattern = /_(?:key|token|storage|cache|slots)$/i.test(secretValue);
239
+ const hasDescriptivePrefix = /^(?:admin|user|auth|session|cache|storage|local|temp|ruralgo)_/i.test(secretValue);
240
+ const isStorageKey = (hasStorageContext || hasKeyNamingPattern || hasDescriptivePrefix) &&
241
+ !/^eyJ/.test(secretValue) && secretValue.length < 50;
242
+
243
+ const isCacheKey = secretValue.includes(':') || /^(?:products|orders|users|stores|cache|metrics|session):/i.test(secretValue);
244
+
245
+ const secretEntropyPattern = new RegExp(
246
+ '^(eyJ|sk_|pk_|live_|prod_|' +
247
+ '[a-f0-9]{' + '32,}' +
248
+ '|\\$2[aby]\\$)'
249
+ );
250
+
251
+ const matchesSecretEntropyPattern = secretEntropyPattern.test(secretValue);
252
+
253
+ const isConstantKey = /(?:const|let|var)\s+\w*(?:KEY|TOKEN|STORAGE)\s*=/i.test(fullLine) &&
254
+ secretValue.length < 30 &&
255
+ !matchesSecretEntropyPattern;
256
+ const isRolesDecorator = /ROLES_KEY\s*=\s*['"`]roles['"`]/.test(fullLine);
257
+
258
+ const isTestData = isTestFilePath && secretValue.length < 50 && !matchesSecretEntropyPattern;
259
+
260
+ if (!isEnvVar && !isPlaceholder && !isComment && !isTestContext && !isStorageKey && !isCacheKey && !isConstantKey && !isRolesDecorator && !isTestData && secretValue.length >= 8) {
261
+ pushFinding("backend.config.secrets_in_code", "critical", sf, sf, "Hardcoded secret detected - replace with environment variable (process.env)", findings);
262
+ }
263
+ }
264
+
265
+ const hasEnvSpecific = sf.getFullText().includes("process.env.NODE_ENV") ||
266
+ sf.getFullText().includes("app.get('env')") ||
267
+ sf.getFullText().includes("ConfigService");
268
+ const hasConfigUsage = sf.getFullText().includes("config") || sf.getFullText().includes("env");
269
+ if (!hasEnvSpecific && hasConfigUsage && !isTestFile(filePath)) {
270
+ pushFinding("backend.config.missing_env_separation", "warning", sf, sf, "Missing environment-specific configuration - consider NODE_ENV or ConfigService", findings);
271
+ }
272
+
273
+ const hasConfigValidation = sf.getFullText().includes("joi") ||
274
+ sf.getFullText().includes("class-validator") ||
275
+ sf.getFullText().includes("@nestjs/config");
276
+ if (!hasConfigValidation && sf.getFullText().includes("process.env")) {
277
+ pushFinding("backend.config.missing_validation", "warning", sf, sf, "Environment variables without validation - consider Joi or class-validator", findings);
278
+ }
279
+
280
+ if (godClassBaseline) {
281
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
282
+ const className = cls.getName() || '';
283
+ const isValueObject = /Metrics|ValueObject|VO$|Dto$|Entity$/.test(className);
284
+ const isTestClass = /Spec$|Test$|Mock/.test(className);
285
+ if (isValueObject || isTestClass) return;
286
+
287
+ const methodsCount = cls.getMethods().length;
288
+ const propertiesCount = cls.getProperties().length;
289
+ const startLine = cls.getStartLineNumber();
290
+ const endLine = cls.getEndLineNumber();
291
+ const lineCount = Math.max(0, endLine - startLine);
292
+
293
+ const decisionKinds = [
294
+ SyntaxKind.IfStatement,
295
+ SyntaxKind.ForStatement,
296
+ SyntaxKind.ForInStatement,
297
+ SyntaxKind.ForOfStatement,
298
+ SyntaxKind.WhileStatement,
299
+ SyntaxKind.DoStatement,
300
+ SyntaxKind.SwitchStatement,
301
+ SyntaxKind.ConditionalExpression,
302
+ SyntaxKind.TryStatement,
303
+ SyntaxKind.CatchClause
304
+ ];
305
+ const complexity = decisionKinds.reduce((acc, kind) => acc + cls.getDescendantsOfKind(kind).length, 0);
306
+
307
+ const clsText = cls.getFullText();
308
+ const concerns = new Set();
309
+ if (/\bfs\.|\bfs\.promises\b|readFileSync|writeFileSync|mkdirSync|unlinkSync|readdirSync/.test(clsText)) concerns.add('io');
310
+ if (/\bpath\.|join\(|resolve\(|dirname\(|basename\(/.test(clsText)) concerns.add('path');
311
+ if (/execSync\(|spawnSync\(|spawn\(|child_process/.test(clsText)) concerns.add('process');
312
+ if (/\bfetch\b|axios\b|http\.|https\.|request\(/.test(clsText)) concerns.add('network');
313
+ if (/\bcrypto\b|encrypt|decrypt|hash|jwt|bearer|token/i.test(clsText)) concerns.add('security');
314
+ if (/setTimeout\(|setInterval\(|clearInterval\(|cron|schedule/i.test(clsText)) concerns.add('scheduling');
315
+ if (/\brepo\b|repository|prisma|typeorm|mongoose|sequelize|knex|\bdb\b|database|sql/i.test(clsText)) concerns.add('persistence');
316
+ if (/notification|notifier|terminal-notifier|osascript/i.test(clsText)) concerns.add('notifications');
317
+ if (/\bgit\b|rev-parse|git diff|git status|git log/i.test(clsText)) concerns.add('git');
318
+ const concernCount = concerns.size;
319
+
320
+ const methodsZ = godClassBaseline.robustZ(methodsCount, godClassBaseline.med.methodsCount, godClassBaseline.mad.methodsCount);
321
+ const propsZ = godClassBaseline.robustZ(propertiesCount, godClassBaseline.med.propertiesCount, godClassBaseline.mad.propertiesCount);
322
+ const linesZ = godClassBaseline.robustZ(lineCount, godClassBaseline.med.lineCount, godClassBaseline.mad.lineCount);
323
+ const complexityZ = godClassBaseline.robustZ(complexity, godClassBaseline.med.complexity, godClassBaseline.mad.complexity);
324
+
325
+ const sizeOutlier =
326
+ methodsZ >= godClassBaseline.thresholds.outlier.methodsCountZ ||
327
+ propsZ >= godClassBaseline.thresholds.outlier.propertiesCountZ ||
328
+ linesZ >= godClassBaseline.thresholds.outlier.lineCountZ;
329
+ const complexityOutlier = complexityZ >= godClassBaseline.thresholds.outlier.complexityZ;
330
+ const concernOutlier = concernCount >= godClassBaseline.thresholds.outlier.concerns;
331
+
332
+ const isAbsoluteGod = lineCount > 600 && methodsCount > 30 && complexity > 80;
333
+ const isUnderThreshold = lineCount < 400 && methodsCount < 25 && complexity < 50;
334
+
335
+ let signalCount = 0;
336
+ if (sizeOutlier) signalCount++;
337
+ if (complexityOutlier) signalCount++;
338
+ if (concernOutlier) signalCount++;
339
+
340
+ if (!isUnderThreshold && (signalCount >= 2 || isAbsoluteGod)) {
341
+ pushFinding("backend.antipattern.god_classes", "critical", sf, cls,
342
+ `God class detected: ${methodsCount} methods, ${propertiesCount} properties, ${lineCount} lines, complexity ${complexity}, concerns ${concernCount} - VIOLATES SRP`,
343
+ findings
344
+ );
345
+ }
346
+ });
347
+ }
348
+
349
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
350
+ const name = cls.getName();
351
+ if (name && /Entity|Model|Domain/.test(name)) {
352
+ // Exclude Errors, Exceptions, and Events from Anemic Domain check
353
+ if (/Error$|Exception$|Event$/.test(name)) return;
354
+
355
+ // Exclude files in domain/errors or domain/events directories
356
+ const filePath = sf.getFilePath().replace(/\\/g, '/');
357
+ if (/\/domain\/errors\//.test(filePath) || /\/domain\/events\//.test(filePath)) return;
358
+
359
+ const methods = cls.getMethods();
360
+ const hasBusinessLogic = methods.some((method) => {
361
+ const methodName = method.getName();
362
+ return /calculate|validate|process|compute/.test(methodName);
363
+ });
364
+ if (!hasBusinessLogic && methods.length <= 2) { // Solo getters/setters
365
+ pushFinding("backend.antipattern.anemic_domain", "medium", sf, cls, `Anemic domain model: ${name} lacks business logic`, findings);
366
+ }
367
+ }
368
+ });
369
+
370
+ const hasCors = sf.getFullText().includes("cors") || sf.getFullText().includes("CORS") || sf.getFullText().includes("@CrossOrigin");
371
+ const missingCorsSeverity = hasGlobalCors ? "low" : "high";
372
+ if (!hasCors && (sf.getFullText().includes("controller") || sf.getFullText().includes("Controller"))) {
373
+ pushFinding("backend.auth.missing_cors", missingCorsSeverity, sf, sf, "Missing CORS configuration in controller - consider @CrossOrigin or global CORS config", findings);
374
+ }
375
+
376
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
377
+ const expr = call.getExpression();
378
+ if (!expr) return;
379
+ const exprText = expr.getText();
380
+ if (/log|Log|logger|Logger|console\./.test(exprText)) {
381
+ const args = call.getArguments();
382
+ args.forEach((arg) => {
383
+ const argText = arg.getText();
384
+ const argLower = argText.toLowerCase();
385
+
386
+ const isSensitiveKeyword = /password|token|secret|apikey|api_key/.test(argLower);
387
+ if (!isSensitiveKeyword) return;
388
+
389
+ const isSafeUsage =
390
+ /\.length/.test(argText) ||
391
+ /\.substring/.test(argText) ||
392
+ /\.slice/.test(argText) ||
393
+ /for user/.test(argLower) ||
394
+ /failed/.test(argLower) ||
395
+ /insufficient/.test(argLower) ||
396
+ /\$\{[^}]*\.length\}/.test(argText) ||
397
+ /template.*literal.*without.*direct.*value/.test(argLower);
398
+
399
+ const isActualValue =
400
+ /^\w+(Password|Token|Secret|Key|ApiKey)$/.test(argText) ||
401
+ /^\w+\.(password|token|secret|key|apiKey)$/.test(argText) ||
402
+ argText.match(/^(password|token|secret|key|apiKey)$/i);
403
+
404
+ if (isActualValue && !isSafeUsage) {
405
+ pushFinding("backend.logging.sensitive_data", "critical", sf, call, "Logging sensitive data detected - never log passwords, tokens, or secrets directly", findings);
406
+ }
407
+ });
408
+ }
409
+ });
410
+
411
+ const filePathNormalizedForMetrics = filePath.replace(/\\/g, "/");
412
+ const filePathNormalizedForMetricsLower = filePathNormalizedForMetrics.toLowerCase();
413
+ const isInternalAstToolingFileForMetrics = filePathNormalizedForMetricsLower.includes("/infrastructure/ast/") || filePathNormalizedForMetricsLower.includes("infrastructure/ast/") || filePathNormalizedForMetricsLower.includes("/scripts/hooks-system/infrastructure/ast/");
414
+
415
+ const fullTextLower = fullText.toLowerCase();
416
+ const hasMetrics = fullTextLower.includes("micrometer") || fullTextLower.includes("prometheus") ||
417
+ fullTextLower.includes("actuator") || fullTextLower.includes("metrics");
418
+ const looksLikeServiceOrController = fullTextLower.includes("controller") || fullTextLower.includes("service");
419
+
420
+ if (!isInternalAstToolingFileForMetrics && !hasMetrics && looksLikeServiceOrController) {
421
+ pushFinding("backend.metrics.missing_prometheus", "low", sf, sf, "Missing application metrics - consider Spring Boot Actuator or Micrometer for monitoring", findings);
422
+ }
423
+
424
+ if (isTestFile(filePath)) {
425
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
426
+ const expr = call.getExpression();
427
+ if (!expr) return;
428
+ const exprText = expr.getText();
429
+ if (/Thread\.sleep|await|delay/.test(exprText)) {
430
+ pushFinding("backend.testing.slow_tests", "medium", sf, call, "Test with sleep/delay detected - slow tests impact CI/CD performance", findings);
431
+ }
432
+ });
433
+ }
434
+
435
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
436
+ const name = cls.getName();
437
+ if (name && /Repository/.test(name) && !name.includes("Impl")) {
438
+ const hasInterface = sf.getDescendantsOfKind(SyntaxKind.InterfaceDeclaration).some((iface) => {
439
+ return iface.getName() === name.replace("Repository", "Repository");
440
+ });
441
+ if (!hasInterface) {
442
+ pushFinding("backend.repository.missing_interface", "medium", sf, cls, `Repository ${name} should implement an interface for testability`, findings);
443
+ }
444
+ }
445
+ });
446
+
447
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
448
+ const name = cls.getName();
449
+ if (name && /Repository/.test(name)) {
450
+ const methods = cls.getMethods();
451
+ methods.forEach((method) => {
452
+ const methodName = method.getName();
453
+ if (/calculate|validate|process|compute|business/.test(methodName)) {
454
+ pushFinding("backend.repository.business_logic", "high", sf, method, `Repository ${name}.${methodName} contains business logic - move to service layer`, findings);
455
+ }
456
+ });
457
+ }
458
+ });
459
+
460
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
461
+ const name = cls.getName();
462
+ if (name && /Repository/.test(name)) {
463
+ const methods = cls.getMethods();
464
+ const hasMultipleOperations = methods.some((method) => {
465
+ const body = method.getBody();
466
+ if (body) {
467
+ const calls = body.getDescendantsOfKind(SyntaxKind.CallExpression).length;
468
+ return calls > 3; // Multiple operations
469
+ }
470
+ return false;
471
+ });
472
+ if (hasMultipleOperations) {
473
+ const hasTransaction = sf.getFullText().includes("@Transactional") || sf.getFullText().includes("@Transaction");
474
+ if (!hasTransaction) {
475
+ pushFinding("backend.repository.transaction_missing", "medium", sf, cls, `Repository ${name} performs multiple operations without @Transactional`, findings);
476
+ }
477
+ }
478
+ }
479
+ });
480
+
481
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
482
+ const name = cls.getName();
483
+ if (name && /UseCase|UseCaseImpl/.test(name)) {
484
+ pushFinding("backend.usecase.explicit", "low", sf, cls, `Use case pattern detected: ${name} - good practice for business logic encapsulation`, findings);
485
+ }
486
+ });
487
+
488
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
489
+ const name = cls.getName();
490
+ if (!name || !/UseCase/.test(name)) return;
491
+ const methods = cls.getMethods();
492
+ methods.forEach((method) => {
493
+ const rtNode = method.getReturnTypeNode();
494
+ if (!rtNode) return;
495
+ const returnTypeText = rtNode.getText();
496
+ if (returnTypeText.includes("Entity") && !returnTypeText.includes("DTO")) {
497
+ pushFinding("backend.usecase.returns", "medium", sf, method, `Use case ${name}.${method.getName()} returns Entity directly - return DTOs to avoid exposing domain objects`, findings);
498
+ pushFinding("backend.usecase.returns_entity", "medium", sf, method, `Use case ${name}.${method.getName()} returns Entity directly - return DTOs to avoid exposing domain objects`, findings);
499
+ }
500
+ });
501
+ });
502
+
503
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
504
+ const name = cls.getName();
505
+ if (name && /Dto|DTO|Request|Response/.test(name)) {
506
+ const hasValidation = sf.getFullText().includes("@IsString") ||
507
+ sf.getFullText().includes("@IsEmail") ||
508
+ sf.getFullText().includes("@IsNotEmpty") ||
509
+ sf.getFullText().includes("class-validator");
510
+ if (!hasValidation) {
511
+ pushFinding("backend.dto.validation", "high", sf, cls, `DTO ${name} without validation decorators - use class-validator for input validation`, findings);
512
+ pushFinding("backend.dto.missing_validation", "high", sf, cls, `DTO ${name} without validation decorators - use class-validator for input validation`, findings);
513
+ }
514
+ }
515
+ });
516
+
517
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
518
+ const name = cls.getName();
519
+ if (name && /Dto|DTO/.test(name)) {
520
+ const hasTransform = sf.getFullText().includes("@Transform") ||
521
+ sf.getFullText().includes("@Expose") ||
522
+ sf.getFullText().includes("@Exclude") ||
523
+ sf.getFullText().includes("class-transformer");
524
+ if (!hasTransform) {
525
+ pushFinding("backend.dto.transformation", "low", sf, cls, `DTO ${name} without transformation - consider class-transformer for serialization control`, findings);
526
+ pushFinding("backend.dto.missing_transformer", "low", sf, cls, `DTO ${name} without transformation - consider class-transformer for serialization control`, findings);
527
+ }
528
+ }
529
+ });
530
+
531
+ sf.getDescendantsOfKind(SyntaxKind.ThrowStatement).forEach((throwStmt) => {
532
+ const expr = throwStmt.getExpression();
533
+ if (!expr) return;
534
+ const exprText = expr.getText();
535
+ if (exprText.includes("Error(") || exprText.includes("Exception(")) {
536
+ const isCustom = exprText.includes("Exception") &&
537
+ (exprText.includes("Validation") ||
538
+ exprText.includes("NotFound") ||
539
+ exprText.includes("Unauthorized") ||
540
+ exprText.includes("Forbidden"));
541
+ if (!isCustom) {
542
+ pushFinding("backend.error.custom_exceptions", "medium", sf, throwStmt, "Generic Error/Exception thrown - create custom exception classes for better error handling", findings);
543
+ }
544
+ }
545
+ });
546
+
547
+ sf.getDescendantsOfKind(SyntaxKind.CatchClause).forEach((catchClause) => {
548
+ const block = catchClause.getBlock();
549
+ if (block && block.getText().includes("error") || block.getText().includes("err")) {
550
+ const exposesStack = block.getText().includes("stack") || block.getText().includes("stackTrace");
551
+ if (exposesStack) {
552
+ pushFinding("backend.error.exposes", "high", sf, catchClause, "Error handler exposes stack trace - never expose internal errors to clients", findings);
553
+ }
554
+ }
555
+ });
556
+
557
+ sf.getDescendantsOfKind(SyntaxKind.CatchClause).forEach((catchClause) => {
558
+ const block = catchClause.getBlock();
559
+ if (!block) return;
560
+ const blockText = block.getText().trim();
561
+ const isEmpty = blockText === '{}' || /^\{\s*\/\/[^\n]*\s*\}$/.test(blockText) || /^\{\s*\/\*[\s\S]*?\*\/\s*\}$/.test(blockText);
562
+ if (isEmpty) {
563
+ pushFinding("backend.error.empty_catch", "high", sf, catchClause, "Empty catch block detected - handle errors properly or rethrow", findings);
564
+ }
565
+ });
566
+
567
+ if (filePath.endsWith('.ts') && !filePath.endsWith('.d.ts')) {
568
+ sf.getDescendantsOfKind(SyntaxKind.CatchClause).forEach((catchClause) => {
569
+ const varDecl = catchClause.getVariableDeclaration();
570
+ if (varDecl) {
571
+ const typeNode = varDecl.getTypeNode();
572
+ if (!typeNode) {
573
+ pushFinding(
574
+ "backend.error_handling.untyped_catch",
575
+ "high",
576
+ sf,
577
+ catchClause,
578
+ "Catch parameter MUST be typed as ': unknown' - use type guards (error instanceof HttpException/Error)",
579
+ findings
580
+ );
581
+ }
582
+ }
583
+ });
584
+ }
585
+
586
+ sf.getDescendantsOfKind(SyntaxKind.ExpressionStatement).forEach((stmt) => {
587
+ const text = stmt.getText().trim();
588
+ if (/^void\s+(err|error|e)\s*;?\s*$/.test(text)) {
589
+ pushFinding(
590
+ "backend.error_handling.void_error",
591
+ "high",
592
+ sf,
593
+ stmt,
594
+ "NEVER use 'void err' - throw custom exceptions (NotFoundException, BadRequestException) or log properly",
595
+ findings
596
+ );
597
+ }
598
+ });
599
+
600
+ sf.getDescendantsOfKind(SyntaxKind.VariableDeclaration).forEach((varDecl) => {
601
+ const typeNode = varDecl.getTypeNode();
602
+ if (typeNode && typeNode.getText() === 'unknown') {
603
+ const parent = varDecl.getParent();
604
+ const scope = varDecl.getFirstAncestorByKind(SyntaxKind.Block);
605
+ if (scope) {
606
+ const scopeText = scope.getText();
607
+ const varName = varDecl.getName();
608
+ const hasTypeGuard = new RegExp(`${varName}\\s+instanceof|typeof\\s+${varName}`).test(scopeText);
609
+ if (!hasTypeGuard) {
610
+ pushFinding(
611
+ "backend.typescript.unknown_without_guard",
612
+ "high",
613
+ sf,
614
+ varDecl,
615
+ `Variable '${varName}: unknown' used without type guards - add instanceof/typeof checks before use`,
616
+ findings
617
+ );
618
+ }
619
+ }
620
+ }
621
+ });
622
+
623
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
624
+ const expr = call.getExpression().getText();
625
+ if (/Promise\.|\.then\(|\.catch\(/.test(expr)) {
626
+ const hasAwait = sf.getFullText().includes("await ") ||
627
+ sf.getDescendantsOfKind(SyntaxKind.AwaitExpression).some((awaitExpr) =>
628
+ awaitExpr.getExpression() === call
629
+ );
630
+ if (!hasAwait) {
631
+ pushFinding("backend.async.missing_await", "high", sf, call, "Async operation without await - ensure proper async/await usage", findings);
632
+ }
633
+ }
634
+ });
635
+
636
+ sf.getDescendantsOfKind(SyntaxKind.AwaitExpression).forEach((awaitExpr) => {
637
+ const filePath = sf.getFilePath();
638
+ const ancestors = awaitExpr.getAncestors();
639
+
640
+ const insideTryCatch = ancestors.some(ancestor =>
641
+ ancestor.getKind() === SyntaxKind.TryStatement
642
+ );
643
+ if (insideTryCatch) return;
644
+
645
+ const containingFunction = ancestors.find(ancestor =>
646
+ ancestor.getKind() === SyntaxKind.FunctionDeclaration ||
647
+ ancestor.getKind() === SyntaxKind.ArrowFunction ||
648
+ ancestor.getKind() === SyntaxKind.MethodDeclaration
649
+ );
650
+ if (containingFunction) {
651
+ const funcText = containingFunction.getText();
652
+ const funcName = funcText.match(/(?:function\s+|const\s+|let\s+|var\s+)(\w+)/)?.[1] || '';
653
+ if (/catch|error|reject|handle|rescue/i.test(funcName)) return;
654
+
655
+ const isDirectReturn = awaitExpr.getParent()?.getKind() === SyntaxKind.ReturnStatement;
656
+ if (isDirectReturn && containingFunction.getKind() === SyntaxKind.ArrowFunction) {
657
+ const funcBody = funcText.trim();
658
+ const isPureWrapper = /^[^{]*=>\s*await\s+/.test(funcBody) ||
659
+ /^[^{]*=>\s*\{\s*return\s+await\s+[^;]+;\s*\}$/.test(funcBody);
660
+ if (isPureWrapper) return;
661
+ }
662
+ }
663
+
664
+ if (/\/(main|index|server|app)\.ts$/.test(filePath)) {
665
+ const isTopLevel = !ancestors.some(ancestor =>
666
+ ancestor.getKind() === SyntaxKind.FunctionDeclaration ||
667
+ ancestor.getKind() === SyntaxKind.ArrowFunction ||
668
+ ancestor.getKind() === SyntaxKind.MethodDeclaration
669
+ );
670
+ if (isTopLevel) return;
671
+ }
672
+
673
+ const statement = awaitExpr.getParent();
674
+ if (statement) {
675
+ const statementText = statement.getText();
676
+ if (/\.catch\s*\(/.test(statementText)) return; // Has .catch() handler
677
+ }
678
+
679
+ if (/middleware|guard|interceptor|filter/i.test(filePath)) {
680
+ return;
681
+ }
682
+
683
+ if (/\/(scripts?|migrations?|seeders?|fixtures?)\//i.test(filePath)) {
684
+ return;
685
+ }
686
+
687
+ pushFinding("backend.async.error_handling", "medium", sf, awaitExpr, "Await expression without try/catch - handle async errors properly", findings);
688
+ });
689
+
690
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
691
+ const expr = call.getExpression().getText();
692
+ if (expr.includes("emit") || expr.includes("publish")) {
693
+ pushFinding("backend.event.emitter", "low", sf, call, "Event emission detected - good practice for decoupled communication", findings);
694
+ }
695
+ });
696
+
697
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
698
+ const expr = call.getExpression().getText();
699
+ if (expr.includes("on(") || expr.includes("subscribe(")) {
700
+ pushFinding("backend.event.handler", "low", sf, call, "Event handler detected - ensure idempotent processing", findings);
701
+ }
702
+ });
703
+
704
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
705
+ const expr = call.getExpression().getText();
706
+ if (expr.includes(".find(") || expr.includes(".query(") || expr.includes("supabase.from(")) {
707
+ const inLoop = call.getAncestors().some((ancestor) =>
708
+ ancestor.getKind() === SyntaxKind.ForStatement ||
709
+ ancestor.getKind() === SyntaxKind.ForOfStatement ||
710
+ ancestor.getKind() === SyntaxKind.WhileStatement ||
711
+ ancestor.getKind() === SyntaxKind.ForInStatement
712
+ );
713
+ if (inLoop) {
714
+ pushFinding("backend.performance.nplus1", "high", sf, call, "Database query in loop detected - potential N+1 query problem", findings);
715
+ }
716
+ }
717
+ });
718
+
719
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
720
+ const expr = call.getExpression().getText();
721
+ if (expr.includes(".find(") || expr.includes("supabase.from(")) {
722
+ const hasLimit = sf.getFullText().includes(".limit(") ||
723
+ sf.getFullText().includes(".take(") ||
724
+ sf.getFullText().includes("LIMIT ") ||
725
+ sf.getFullText().includes("range(");
726
+ if (!hasLimit) {
727
+ pushFinding("backend.performance.pagination", "medium", sf, call, "Query without pagination - consider adding limit/range for performance", findings);
728
+ }
729
+ }
730
+ });
731
+
732
+ sf.getDescendantsOfKind(SyntaxKind.StringLiteral).forEach((str) => {
733
+ const text = str.getLiteralValue();
734
+ const sqlKeywords = ['SEL' + 'ECT ', 'INS' + 'ERT ', 'UPD' + 'ATE ', 'DEL' + 'ETE ', 'DR' + 'OP ', 'AL' + 'TER ', 'TRUN' + 'CATE '];
735
+ const sqlPattern = new RegExp(sqlKeywords.join('|'), 'i');
736
+ if (sqlPattern.test(text)) {
737
+ pushFinding("backend.database.raw_sql", "medium", sf, str, "Raw SQL detected - prefer ORM queries for type safety and security", findings);
738
+ if (/[\"'`].*\+.*[\"'`]/.test(text) || /\$\{.*\}/.test(text)) {
739
+ pushFinding("backend.db.query_not_parameterized", "high", sf, str, "Query appears to be built via string concatenation/interpolation - use parameterized queries", findings);
740
+ }
741
+ }
742
+ });
743
+
744
+ const isOrmRepositoryFile = /\/(repositories?)\//i.test(filePath) && (
745
+ fullText.includes("TypeOrmModule") ||
746
+ fullText.includes("Repository<") ||
747
+ fullText.includes("@Entity(")
748
+ );
749
+
750
+ if (isOrmRepositoryFile) {
751
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
752
+ const expr = call.getExpression().getText();
753
+ if (expr.includes(".save(") || expr.includes(".update(") || expr.includes(".delete(")) {
754
+ const multipleOps = sf.getDescendantsOfKind(SyntaxKind.CallExpression).filter((c) =>
755
+ c.getExpression().getText().includes(".save(") ||
756
+ c.getExpression().getText().includes(".update(") ||
757
+ c.getExpression().getText().includes(".delete(")
758
+ ).length > 1;
759
+
760
+ if (multipleOps) {
761
+ const hasTransaction = sf.getFullText().includes("@Transactional") ||
762
+ sf.getFullText().includes("transaction(") ||
763
+ sf.getFullText().includes("beginTransaction");
764
+ if (!hasTransaction) {
765
+ pushFinding("backend.database.transaction", "high", sf, call, "Multiple database operations without transaction - ensure atomicity", findings);
766
+ }
767
+ }
768
+ }
769
+ });
770
+ }
771
+
772
+ sf.getDescendantsOfKind(SyntaxKind.Decorator).forEach((decorator) => {
773
+ const expr = decorator.getExpression();
774
+ if (expr && expr.getText().includes("@Get") || expr.getText().includes("@Post") ||
775
+ expr.getText().includes("@Put") || expr.getText().includes("@Delete")) {
776
+ pushFinding("backend.api.restful", "low", sf, decorator, "REST endpoint detected - ensure follows RESTful conventions", findings);
777
+ const text = expr.getText();
778
+ if (!/\/(api|v[0-9]+)/i.test(text)) {
779
+ pushFinding("backend.api.missing_versioning", "medium", sf, decorator, "Route without API versioning (expected /api/vN)", findings);
780
+ }
781
+ const methodName = decorator.getParent()?.getName?.() || "";
782
+ if ((/create|add/i.test(methodName) && /@Get/.test(text)) || (/delete|remove/i.test(methodName) && /@Get/.test(text))) {
783
+ pushFinding("backend.api.bad_http_methods", "medium", sf, decorator, `Method ${methodName} may use incorrect HTTP verb`, findings);
784
+ }
785
+ if (/@Post/.test(text)) {
786
+ pushFinding("backend.api.missing_idempotency", "low", sf, decorator, "POST endpoint should consider idempotency key or safeguards", findings);
787
+ }
788
+ }
789
+ });
790
+
791
+ const swaggerFilePath = sf.getFilePath();
792
+ const isAnalyzer = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(swaggerFilePath);
793
+ const isSwaggerTestFile = /\.(spec|test)\.(js|ts)$/i.test(swaggerFilePath);
794
+ if (!isAnalyzer && !isSwaggerTestFile && sf.getFullText().includes("@Controller") && !sf.getFullText().includes("@nestjs/swagger") && !sf.getFullText().includes("@Api")) {
795
+ pushFinding("backend.api.missing_swagger", "medium", sf, sf, "Controller without Swagger decorators/imports", findings);
796
+ }
797
+
798
+ sf.getDescendantsOfKind(SyntaxKind.ReturnStatement).forEach((ret) => {
799
+ const expr = ret.getExpression();
800
+ if (!expr) return;
801
+ const exprText = expr.getText();
802
+ if (exprText.includes("status(") || exprText.includes("HttpStatus.")) {
803
+ if (exprText.includes("200") || exprText.includes("OK")) {
804
+ pushFinding("backend.api.status_codes", "low", sf, ret, "HTTP status code usage - ensure semantically correct status codes", findings);
805
+ }
806
+ }
807
+ });
808
+
809
+ const isControllerFile = /\/(controllers?)\//i.test(filePath) || sf.getFullText().includes("@Controller");
810
+ if (isControllerFile) {
811
+ sf.getDescendantsOfKind(SyntaxKind.MethodDeclaration).forEach((method) => {
812
+ const decorators = method.getDecorators();
813
+ const hasValidationPipe = decorators.some((d) =>
814
+ d.getExpression().getText().includes("UsePipes") ||
815
+ d.getExpression().getText().includes("ValidationPipe")
816
+ );
817
+ if (!hasValidationPipe && method.getParameters().length > 0) {
818
+ pushFinding("backend.api.validation", "high", sf, method, "API method without validation - use ValidationPipe for automatic input validation", findings);
819
+ }
820
+ });
821
+ }
822
+
823
+ if (insightsEnabled) {
824
+ if (sf.getFullText().includes("@nestjs/jwt") || sf.getFullText().includes("passport-jwt")) {
825
+ pushFinding("backend.auth.jwt", "low", sf, sf, "JWT authentication detected - ensure proper token validation and refresh strategy", findings);
826
+ }
827
+ }
828
+
829
+ if (isControllerFile) {
830
+ sf.getDescendantsOfKind(SyntaxKind.MethodDeclaration).forEach((method) => {
831
+ const decorators = method.getDecorators();
832
+ const hasGuard = decorators.some((d) =>
833
+ d.getExpression().getText().includes("@UseGuards") ||
834
+ d.getExpression().getText().includes("JwtAuthGuard")
835
+ );
836
+ const methodName = method.getName();
837
+ if (!hasGuard && (methodName.includes("create") || methodName.includes("update") || methodName.includes("delete"))) {
838
+ pushFinding("backend.auth.guards", "high", sf, method, "Protected operation without auth guard - ensure proper authentication", findings);
839
+ pushFinding("backend.auth.missing_guard", "high", sf, method, "Protected operation without auth guard - ensure proper authentication", findings);
840
+ }
841
+ });
842
+ }
843
+
844
+ sf.getDescendantsOfKind(SyntaxKind.Decorator).forEach((decorator) => {
845
+ if (decorator.getExpression().getText().includes("@Roles")) {
846
+ if (insightsEnabled) {
847
+ pushFinding("backend.auth.rbac", "low", sf, decorator, "Role-based access control detected - ensure proper role validation", findings);
848
+ }
849
+ } else if (sf.getFullText().includes("@Controller") && !sf.getFullText().includes("@Roles")) {
850
+ pushFinding("backend.auth.missing_roles", "medium", sf, decorator, "Controller without @Roles annotations for RBAC", findings);
851
+ }
852
+ });
853
+
854
+ if (insightsEnabled) {
855
+ if (sf.getFullText().includes("@nestjs/throttler") || sf.getFullText().includes("rate-limit")) {
856
+ pushFinding("backend.security.rate_limiting", "low", sf, sf, "Rate limiting detected - good practice for API protection", findings);
857
+ }
858
+ }
859
+
860
+ if (sf.getFullText().includes("express()") && !sf.getFullText().includes("helmet")) {
861
+ pushFinding("backend.security.missing_helmet", "high", sf, sf, "Missing Helmet security headers middleware", findings);
862
+ }
863
+
864
+ const isBusinessLogic = /\/(controllers?|services?|use-?cases?|handlers?)\//i.test(filePath) ||
865
+ /(controller|service|usecase|handler)\.ts$/i.test(filePath);
866
+ if (isBusinessLogic && !sf.getFullText().includes("winston") && !sf.getFullText().includes("audit") && !sf.getFullText().includes("Logger")) {
867
+ pushFinding("backend.security.missing_audit_logging", "medium", sf, sf, "Audit logging not detected - add structured audit logs", findings);
868
+ }
869
+
870
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
871
+ const expr = call.getExpression().getText();
872
+ if (expr.includes("req.body") || expr.includes("req.query") || expr.includes("req.params")) {
873
+ const hasSanitization = sf.getFullText().includes("sanitize") ||
874
+ sf.getFullText().includes("escape") ||
875
+ sf.getFullText().includes("validator");
876
+ if (!hasSanitization) {
877
+ pushFinding("backend.security.input_sanitization", "medium", sf, call, "User input without sanitization - prevent XSS and injection attacks", findings);
878
+ }
879
+ }
880
+ });
881
+
882
+ if (insightsEnabled) {
883
+ if (sf.getFullText().includes("redis") || sf.getFullText().includes("ioredis")) {
884
+ pushFinding("backend.caching.redis", "low", sf, sf, "Redis caching detected - ensure proper cache invalidation strategy", findings);
885
+ }
886
+ }
887
+
888
+ if (insightsEnabled) {
889
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
890
+ const expr = call.getExpression().getText();
891
+ if (expr.includes("cache.get") || expr.includes("redis.get")) {
892
+ pushFinding("backend.caching.pattern", "low", sf, call, "Cache access detected - ensure cache-aside pattern implementation", findings);
893
+ }
894
+ });
895
+ }
896
+
897
+ if (insightsEnabled) {
898
+ if (sf.getFullText().includes("health") || sf.getFullText().includes("readiness") || sf.getFullText().includes("liveness")) {
899
+ pushFinding("backend.health.checks", "low", sf, sf, "Health checks detected - ensure proper liveness/readiness probes", findings);
900
+ }
901
+ }
902
+
903
+ if (insightsEnabled) {
904
+ if (sf.getFullText().includes("winston") || sf.getFullText().includes("pino")) {
905
+ pushFinding("backend.logging.structured", "low", sf, sf, "Structured logging detected - ensure correlation IDs and proper log levels", findings);
906
+ }
907
+ }
908
+
909
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
910
+ const txt = call.getText();
911
+ if (/bcrypt\.hash\(/.test(txt)) {
912
+ const args = call.getArguments().map(a => a.getText());
913
+ const salt = args[1] || "";
914
+ if (/^[0-9]+$/.test(salt) && parseInt(salt, 10) < 10) {
915
+ pushFinding("backend.auth.weak_password_hashing", "high", sf, call, "bcrypt salt rounds < 10 detected", findings);
916
+ }
917
+ }
918
+ if (/md5\(|sha1\(/i.test(txt)) {
919
+ pushFinding("backend.auth.weak_password_hashing", "high", sf, call, "Weak hash function (MD5/SHA1) detected for passwords", findings);
920
+ }
921
+ });
922
+
923
+ if (isTestFile(filePath)) {
924
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
925
+ const expr = call.getExpression().getText();
926
+ if (expr.includes("jest.mock") || expr.includes("mock(")) {
927
+ pushFinding("backend.testing.mocks", "low", sf, call, "Mock usage in tests - prefer spies over mocks when possible", findings);
928
+ }
929
+ });
930
+ }
931
+
932
+ if (insightsEnabled) {
933
+ if (isTestFile(filePath) && sf.getFullText().includes("supertest") || sf.getFullText().includes("TestContainer")) {
934
+ pushFinding("backend.testing.integration", "low", sf, sf, "Integration testing detected - ensure proper test isolation and cleanup", findings);
935
+ }
936
+ }
937
+
938
+ if (insightsEnabled) {
939
+ if (sf.getFullText().includes("@Module") && sf.getFullText().includes("@nestjs/common")) {
940
+ pushFinding("backend.architecture.module", "info", sf, sf, "NestJS module detected - ensure proper separation of concerns", findings);
941
+ }
942
+ }
943
+
944
+ const isServiceOrRepo = /Service|Repository|Controller|Provider/.test(sf.getBaseName());
945
+ if (isServiceOrRepo) {
946
+ sf.getDescendantsOfKind(SyntaxKind.Constructor).forEach((ctor) => {
947
+ const params = ctor.getParameters();
948
+ const classDecl = ctor.getParent();
949
+ if (!classDecl || params.length === 0) return;
950
+
951
+ const depNames = [];
952
+ params.forEach((param) => {
953
+ const nameNode = typeof param.getNameNode === 'function' ? param.getNameNode() : null;
954
+ if (nameNode && typeof nameNode.getKind === 'function' && nameNode.getKind() === SyntaxKind.ObjectBindingPattern) {
955
+ const elements = typeof nameNode.getElements === 'function' ? nameNode.getElements() : [];
956
+ elements.forEach((el) => {
957
+ const elNameNode = typeof el.getNameNode === 'function' ? el.getNameNode() : null;
958
+ const depName = elNameNode ? elNameNode.getText() : null;
959
+ if (depName) depNames.push(depName);
960
+ });
961
+ return;
962
+ }
963
+
964
+ const depName = param.getName();
965
+ if (depName && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(depName)) {
966
+ depNames.push(depName);
967
+ }
968
+ });
969
+
970
+ if (depNames.length === 0) return;
971
+
972
+ const ctorBody = typeof ctor.getBody === 'function' ? ctor.getBody() : null;
973
+ const depsSet = new Set(depNames);
974
+
975
+ const assignedPropsByDep = new Map();
976
+ const recordAssignedProp = (depName, propName) => {
977
+ if (!assignedPropsByDep.has(depName)) assignedPropsByDep.set(depName, new Set());
978
+ assignedPropsByDep.get(depName).add(propName);
979
+ };
980
+
981
+ if (ctorBody) {
982
+ const binaries = ctorBody.getDescendantsOfKind(SyntaxKind.BinaryExpression);
983
+ binaries.forEach((bin) => {
984
+ const op = typeof bin.getOperatorToken === 'function' ? bin.getOperatorToken().getText() : null;
985
+ if (op !== '=') return;
986
+ const left = typeof bin.getLeft === 'function' ? bin.getLeft() : null;
987
+ const right = typeof bin.getRight === 'function' ? bin.getRight() : null;
988
+ if (!left || !right) return;
989
+
990
+ if (left.getKind() === SyntaxKind.PropertyAccessExpression && right.getKind() === SyntaxKind.Identifier) {
991
+ const leftExpr = left.getExpression();
992
+ if (leftExpr && leftExpr.getKind() === SyntaxKind.ThisExpression) {
993
+ const propName = left.getName();
994
+ const depName = right.getText();
995
+ if (depsSet.has(depName) && propName) {
996
+ recordAssignedProp(depName, propName);
997
+ }
998
+ }
999
+ }
1000
+ });
1001
+ }
1002
+
1003
+ const isThisPropWrite = (node) => {
1004
+ const parent = node.getParent();
1005
+ if (!parent) return false;
1006
+
1007
+ if (parent.getKind && parent.getKind() === SyntaxKind.BinaryExpression) {
1008
+ const op = parent.getOperatorToken?.().getText?.();
1009
+ const left = parent.getLeft?.();
1010
+ return op === '=' && left === node;
1011
+ }
1012
+
1013
+ if (parent.getKind && parent.getKind() === SyntaxKind.PostfixUnaryExpression) return true;
1014
+ if (parent.getKind && parent.getKind() === SyntaxKind.PrefixUnaryExpression) return true;
1015
+ return false;
1016
+ };
1017
+
1018
+ const allThisPropAccesses = classDecl
1019
+ .getDescendantsOfKind(SyntaxKind.PropertyAccessExpression)
1020
+ .filter((pa) => pa.getExpression()?.getKind?.() === SyntaxKind.ThisExpression);
1021
+
1022
+ const readProps = new Set(
1023
+ allThisPropAccesses
1024
+ .filter((pa) => !isThisPropWrite(pa))
1025
+ .map((pa) => pa.getName())
1026
+ .filter(Boolean)
1027
+ );
1028
+
1029
+ const directDepUseCount = new Map();
1030
+ depNames.forEach((depName) => directDepUseCount.set(depName, 0));
1031
+
1032
+ if (ctorBody) {
1033
+ const identifiers = ctorBody.getDescendantsOfKind(SyntaxKind.Identifier);
1034
+ identifiers.forEach((id) => {
1035
+ const depName = id.getText();
1036
+ if (!depsSet.has(depName)) return;
1037
+
1038
+ const bin = id.getFirstAncestorByKind(SyntaxKind.BinaryExpression);
1039
+ if (bin && bin.getRight?.() === id && bin.getOperatorToken?.().getText?.() === '=') {
1040
+ const left = bin.getLeft?.();
1041
+ if (left && left.getKind?.() === SyntaxKind.PropertyAccessExpression && left.getExpression?.().getKind?.() === SyntaxKind.ThisExpression) {
1042
+ return;
1043
+ }
1044
+ }
1045
+
1046
+ directDepUseCount.set(depName, (directDepUseCount.get(depName) || 0) + 1);
1047
+ });
1048
+ }
1049
+
1050
+ const unusedDeps = [];
1051
+ depNames.forEach((depName) => {
1052
+ const assignedProps = assignedPropsByDep.get(depName);
1053
+ const hasReadProp = assignedProps ? Array.from(assignedProps).some((p) => readProps.has(p)) : false;
1054
+ const hasDirectUse = (directDepUseCount.get(depName) || 0) > 0;
1055
+ if (!hasReadProp && !hasDirectUse) {
1056
+ unusedDeps.push(depName);
1057
+ }
1058
+ });
1059
+
1060
+ if (unusedDeps.length > 0) {
1061
+ pushFinding(
1062
+ "backend.architecture.di",
1063
+ "high",
1064
+ sf,
1065
+ ctor,
1066
+ `Unused/underused dependencies: ${unusedDeps.join(', ')} - remove or use them (ISP violation)`,
1067
+ findings
1068
+ );
1069
+ }
1070
+ });
1071
+ }
1072
+
1073
+ const filePathNormalized = filePath.replace(/\\/g, "/");
1074
+ const filePathNormalizedLower = filePathNormalized.toLowerCase();
1075
+ if (insightsEnabled) {
1076
+ if (filePathNormalized.includes("/domain/")) {
1077
+ pushFinding("backend.clean.domain", "low", sf, sf, "Domain layer file - ensure contains only business logic and entities", findings);
1078
+ }
1079
+ if (filePathNormalized.includes("/application/")) {
1080
+ pushFinding("backend.clean.application", "low", sf, sf, "Application layer file - ensure contains use cases and application logic", findings);
1081
+ }
1082
+ const isInternalAstToolingFile = filePathNormalizedLower.includes("/infrastructure/ast/") || filePathNormalizedLower.includes("/scripts/hooks-system/infrastructure/ast/");
1083
+ if (filePathNormalized.includes("/infrastructure/") && !isInternalAstToolingFile) {
1084
+ pushFinding("backend.clean.infrastructure", "low", sf, sf, "Infrastructure layer file - ensure contains external concerns and implementations", findings);
1085
+ }
1086
+ if (filePathNormalized.includes("/presentation/")) {
1087
+ pushFinding("backend.clean.presentation", "low", sf, sf, "Presentation layer file - ensure contains controllers and DTOs", findings);
1088
+ }
1089
+ }
1090
+
1091
+ sf.getDescendantsOfKind(SyntaxKind.InterfaceDeclaration).forEach((iface) => {
1092
+ const name = iface.getName();
1093
+ if (name && name.includes("Repository")) {
1094
+ if (insightsEnabled) {
1095
+ pushFinding("backend.repository.pattern", "low", sf, iface, "Repository interface detected - good abstraction for data access", findings);
1096
+ }
1097
+ }
1098
+ });
1099
+
1100
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
1101
+ const name = cls.getName();
1102
+ if (name && name.includes("Service") && !name.includes("Repository")) {
1103
+ if (insightsEnabled) {
1104
+ pushFinding("backend.service.layer", "low", sf, cls, "Service class detected - ensure orchestrates business logic without data access", findings);
1105
+ }
1106
+ }
1107
+ });
1108
+
1109
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
1110
+ const name = cls.getName();
1111
+ if (name && name.includes("Controller")) {
1112
+ if (insightsEnabled) {
1113
+ pushFinding("backend.controller.layer", "low", sf, cls, "Controller detected - ensure thin layer focused on HTTP concerns", findings);
1114
+ }
1115
+ }
1116
+ });
1117
+
1118
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
1119
+ const name = cls.getName();
1120
+ if (name && (name.includes("Dto") || name.includes("DTO") || name.includes("Request") || name.includes("Response"))) {
1121
+ if (insightsEnabled) {
1122
+ pushFinding("backend.dto.pattern", "low", sf, cls, "DTO detected - ensure used for data transfer between layers", findings);
1123
+ }
1124
+ }
1125
+ });
1126
+
1127
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
1128
+ const name = cls.getName();
1129
+ if (name && name.includes("Mapper")) {
1130
+ if (insightsEnabled) {
1131
+ pushFinding("backend.mapper.pattern", "low", sf, cls, "Mapper detected - ensure handles conversion between domain objects and DTOs", findings);
1132
+ }
1133
+ }
1134
+ });
1135
+
1136
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
1137
+ const name = cls.getName();
1138
+ if (name && name.includes("Factory")) {
1139
+ if (insightsEnabled) {
1140
+ pushFinding("backend.factory.pattern", "low", sf, cls, "Factory detected - good for complex object creation", findings);
1141
+ }
1142
+ }
1143
+ });
1144
+
1145
+ sf.getDescendantsOfKind(SyntaxKind.VariableDeclaration).forEach((varDecl) => {
1146
+ const text = varDecl.getText();
1147
+ if (text.includes("static") && text.includes("INSTANCE")) {
1148
+ pushFinding("backend.singleton.pattern", "medium", sf, varDecl, "Singleton pattern detected - consider dependency injection instead", findings);
1149
+ }
1150
+ });
1151
+
1152
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
1153
+ const expr = call.getExpression().getText();
1154
+ if (expr.includes("subscribe") || expr.includes("on(") || expr.includes("emit")) {
1155
+ if (insightsEnabled) {
1156
+ pushFinding("backend.observer.pattern", "low", sf, call, "Observer pattern usage detected - good for event-driven architecture", findings);
1157
+ }
1158
+ }
1159
+ });
1160
+
1161
+ sf.getDescendantsOfKind(SyntaxKind.InterfaceDeclaration).forEach((iface) => {
1162
+ const methods = iface.getMethods();
1163
+ if (methods.length === 1) {
1164
+ if (insightsEnabled) {
1165
+ pushFinding("backend.strategy.pattern", "low", sf, iface, "Single-method interface detected - potential strategy pattern", findings);
1166
+ }
1167
+ }
1168
+ });
1169
+
1170
+ sf.getDescendantsOfKind(SyntaxKind.ClassDeclaration).forEach((cls) => {
1171
+ const methods = cls.getMethods();
1172
+ const hasAbstract = methods.some((m) => m.getText().includes("abstract"));
1173
+ const hasOverride = methods.some((m) => m.getText().includes("override"));
1174
+ if (hasAbstract && hasOverride) {
1175
+ if (insightsEnabled) {
1176
+ pushFinding("backend.template.pattern", "low", sf, cls, "Template method pattern detected - good for algorithm customization", findings);
1177
+ }
1178
+ }
1179
+ });
1180
+
1181
+ sf.getDescendantsOfKind(SyntaxKind.Decorator).forEach((decorator) => {
1182
+ const expr = decorator.getExpression().getText();
1183
+ if (expr.includes("@") && !expr.includes("@nestjs")) {
1184
+ if (insightsEnabled) {
1185
+ pushFinding("backend.decorator.pattern", "low", sf, decorator, "Custom decorator detected - ensure follows decorator pattern principles", findings);
1186
+ }
1187
+ }
1188
+ });
1189
+
1190
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
1191
+ const expr = call.getExpression().getText();
1192
+ if (expr.includes("use(") && (expr.includes("middleware") || expr.includes("Middleware"))) {
1193
+ if (insightsEnabled) {
1194
+ pushFinding("backend.middleware.pattern", "low", sf, call, "Middleware usage detected - ensure proper request/response processing", findings);
1195
+ }
1196
+ }
1197
+ });
1198
+
1199
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
1200
+ const expr = call.getExpression().getText();
1201
+ if (expr.includes("pipe(") || expr.includes("pipeline")) {
1202
+ if (insightsEnabled) {
1203
+ pushFinding("backend.pipeline.pattern", "low", sf, call, "Pipeline pattern detected - good for data processing chains", findings);
1204
+ }
1205
+ }
1206
+ });
1207
+
1208
+ if (insightsEnabled) {
1209
+ if (sf.getFullText().includes("circuit") || sf.getFullText().includes("breaker")) {
1210
+ pushFinding("backend.circuit_breaker", "low", sf, sf, "Circuit breaker pattern detected - good for fault tolerance", findings);
1211
+ }
1212
+ }
1213
+
1214
+ if (insightsEnabled) {
1215
+ if (sf.getFullText().includes("bulkhead") || sf.getFullText().includes("isolation")) {
1216
+ pushFinding("backend.bulkhead.pattern", "low", sf, sf, "Bulkhead pattern detected - good for resource isolation", findings);
1217
+ }
1218
+
1219
+ if (sf.getFullText().includes("saga") || sf.getFullText().includes("compensation")) {
1220
+ pushFinding("backend.saga.pattern", "low", sf, sf, "Saga pattern detected - good for distributed transactions", findings);
1221
+ }
1222
+
1223
+ if (sf.getFullText().includes("Command") && sf.getFullText().includes("Query")) {
1224
+ pushFinding("backend.cqrs.pattern", "low", sf, sf, "CQRS pattern detected - ensure proper separation of read/write models", findings);
1225
+ }
1226
+
1227
+ if (sf.getFullText().includes("EventStore") || sf.getFullText().includes("event sourcing")) {
1228
+ pushFinding("backend.event_sourcing", "low", sf, sf, "Event sourcing detected - ensure proper event versioning and replay", findings);
1229
+ }
1230
+
1231
+ if (sf.getFullText().includes("axios") || sf.getFullText().includes("HttpService")) {
1232
+ pushFinding("backend.microservices.comm", "low", sf, sf, "Inter-service communication detected - ensure proper error handling and timeouts", findings);
1233
+ }
1234
+
1235
+ sf.getDescendantsOfKind(SyntaxKind.StringLiteral).forEach((str) => {
1236
+ const text = str.getLiteralValue();
1237
+ if (/\/api\/v\d+\//.test(text)) {
1238
+ pushFinding("backend.api.versioning", "low", sf, str, "API versioning detected - ensure proper version management", findings);
1239
+ }
1240
+ });
1241
+
1242
+ if (sf.getFullText().includes("@nestjs/swagger") || sf.getFullText().includes("swagger")) {
1243
+ pushFinding("backend.api.documentation", "low", sf, sf, "API documentation detected - ensure complete and accurate OpenAPI specs", findings);
1244
+ }
1245
+
1246
+ if (sf.getFullText().includes("@nestjs/graphql") || sf.getFullText().includes("graphql")) {
1247
+ pushFinding("backend.graphql.usage", "low", sf, sf, "GraphQL usage detected - ensure proper schema design and resolvers", findings);
1248
+ }
1249
+
1250
+ if (sf.getFullText().includes("@nestjs/websockets") || sf.getFullText().includes("socket.io")) {
1251
+ pushFinding("backend.websocket.usage", "low", sf, sf, "WebSocket usage detected - ensure proper connection management", findings);
1252
+ }
1253
+
1254
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
1255
+ const expr = call.getExpression().getText();
1256
+ if (expr.includes("multer") || expr.includes("upload")) {
1257
+ pushFinding("backend.file.upload", "low", sf, call, "File upload detected - ensure proper validation and security checks", findings);
1258
+ }
1259
+ });
1260
+
1261
+ if (sf.getFullText().includes("@nestjs/schedule") || sf.getFullText().includes("cron")) {
1262
+ pushFinding("backend.scheduled.tasks", "low", sf, sf, "Scheduled tasks detected - ensure proper error handling and logging", findings);
1263
+ }
1264
+
1265
+ if (sf.getFullText().includes("i18n") || sf.getFullText().includes("i18next")) {
1266
+ pushFinding("backend.i18n.support", "low", sf, sf, "Internationalization detected - ensure proper message translation and locale handling", findings);
1267
+ }
1268
+
1269
+ if (sf.getFullText().includes("feature.flag") || sf.getFullText().includes("toggle")) {
1270
+ pushFinding("backend.feature.flags", "low", sf, sf, "Feature flags detected - ensure proper flag management and cleanup", findings);
1271
+ }
1272
+
1273
+ if (sf.getFullText().includes("experiment") || sf.getFullText().includes("ab.test")) {
1274
+ pushFinding("backend.ab.testing", "low", sf, sf, "A/B testing detected - ensure proper experiment tracking and analysis", findings);
1275
+ }
1276
+ }
1277
+
1278
+ if (insightsEnabled) {
1279
+ if (sf.getFullText().includes("analytics") || sf.getFullText().includes("tracking")) {
1280
+ pushFinding("backend.analytics.tracking", "low", sf, sf, "Analytics tracking detected - ensure GDPR compliance and proper data handling", findings);
1281
+ }
1282
+
1283
+ if (sf.getFullText().includes("gdpr") || sf.getFullText().includes("consent")) {
1284
+ pushFinding("backend.gdpr.compliance", "low", sf, sf, "GDPR compliance detected - ensure proper data protection measures", findings);
1285
+ }
1286
+ }
1287
+
1288
+ if (insightsEnabled) {
1289
+ if (sf.getFullText().includes("audit") || sf.getFullText().includes("audit_log")) {
1290
+ pushFinding("backend.audit.logging", "low", sf, sf, "Audit logging detected - ensure tamper-proof and comprehensive logging", findings);
1291
+ }
1292
+
1293
+ if (sf.getFullText().includes("encrypt") || sf.getFullText().includes("crypto")) {
1294
+ pushFinding("backend.data.encryption", "low", sf, sf, "Data encryption detected - ensure proper key management and algorithm selection", findings);
1295
+ }
1296
+ }
1297
+
1298
+ if (insightsEnabled) {
1299
+ if (sf.getFullText().includes("backup") || sf.getFullText().includes("snapshot")) {
1300
+ pushFinding("backend.backup.strategy", "low", sf, sf, "Backup strategy detected - ensure regular and tested backups", findings);
1301
+ }
1302
+
1303
+ if (sf.getFullText().includes("alert") || sf.getFullText().includes("notification")) {
1304
+ pushFinding("backend.monitoring.alerts", "low", sf, sf, "Monitoring alerts detected - ensure actionable and non-spammy alerts", findings);
1305
+ }
1306
+
1307
+ if (sf.getFullText().includes("profiler") || sf.getFullText().includes("benchmark")) {
1308
+ pushFinding("backend.performance.profiling", "low", sf, sf, "Performance profiling detected - ensure regular performance monitoring", findings);
1309
+ }
1310
+
1311
+ if (sf.getFullText().includes("heap") || sf.getFullText().includes("garbage")) {
1312
+ pushFinding("backend.memory.management", "low", sf, sf, "Memory management detected - ensure proper resource cleanup", findings);
1313
+ }
1314
+
1315
+ if (sf.getFullText().includes("synchronized") || sf.getFullText().includes("mutex")) {
1316
+ pushFinding("backend.thread.safety", "low", sf, sf, "Thread safety measures detected - ensure proper synchronization", findings);
1317
+ }
1318
+
1319
+ if (sf.getFullText().includes("pool") || sf.getFullText().includes("connection.pool")) {
1320
+ pushFinding("backend.connection.pooling", "low", sf, sf, "Connection pooling detected - ensure proper pool sizing and monitoring", findings);
1321
+ }
1322
+
1323
+ sf.getDescendantsOfKind(SyntaxKind.CallExpression).forEach((call) => {
1324
+ const expr = call.getExpression().getText();
1325
+ if (expr.includes("timeout") || expr.includes("setTimeout")) {
1326
+ pushFinding("backend.timeout.management", "low", sf, call, "Timeout management detected - ensure reasonable timeout values", findings);
1327
+ }
1328
+ });
1329
+
1330
+ if (sf.getFullText().includes("retry") || sf.getFullText().includes("backoff")) {
1331
+ pushFinding("backend.retry.mechanism", "low", sf, sf, "Retry mechanism detected - ensure exponential backoff and circuit breaker", findings);
1332
+ }
1333
+
1334
+ if (sf.getFullText().includes("shutdown") || sf.getFullText().includes("SIGTERM")) {
1335
+ pushFinding("backend.graceful.shutdown", "low", sf, sf, "Graceful shutdown detected - ensure proper cleanup and request draining", findings);
1336
+ }
1337
+
1338
+ if (sf.getFullText().includes("vault") || sf.getFullText().includes("secrets")) {
1339
+ pushFinding("backend.secrets.management", "low", sf, sf, "Secrets management detected - ensure secure key rotation and access control", findings);
1340
+ }
1341
+
1342
+ if (sf.getFullText().includes("docker") || sf.getFullText().includes("Dockerfile")) {
1343
+ pushFinding("backend.containerization", "low", sf, sf, "Containerization detected - ensure proper image optimization and security", findings);
1344
+ }
1345
+
1346
+ if (sf.getFullText().includes("kubernetes") || sf.getFullText().includes("k8s")) {
1347
+ pushFinding("backend.orchestration", "low", sf, sf, "Container orchestration detected - ensure proper resource limits and health checks", findings);
1348
+ }
1349
+
1350
+ if (sf.getFullText().includes("pipeline") || sf.getFullText().includes("workflow")) {
1351
+ pushFinding("backend.cicd.pipelines", "low", sf, sf, "CI/CD pipelines detected - ensure automated testing and deployment", findings);
1352
+ }
1353
+
1354
+ if (sf.getFullText().includes("blue.green") || sf.getFullText().includes("canary")) {
1355
+ pushFinding("backend.deployment.strategy", "low", sf, sf, "Advanced deployment strategy detected - ensure proper rollback procedures", findings);
1356
+ }
1357
+
1358
+ if (sf.getFullText().includes("chaos") || sf.getFullText().includes("fault.injection")) {
1359
+ pushFinding("backend.chaos.engineering", "low", sf, sf, "Chaos engineering detected - ensure systematic testing of failure scenarios", findings);
1360
+ }
1361
+
1362
+ if (sf.getFullText().includes("istio") || sf.getFullText().includes("linkerd")) {
1363
+ pushFinding("backend.service.mesh", "low", sf, sf, "Service mesh detected - ensure proper traffic management and observability", findings);
1364
+ }
1365
+
1366
+ if (sf.getFullText().includes("gateway") || sf.getFullText().includes("kong")) {
1367
+ pushFinding("backend.api.gateway", "low", sf, sf, "API gateway detected - ensure proper routing and security policies", findings);
1368
+ }
1369
+
1370
+ if (sf.getFullText().includes("rabbitmq") || sf.getFullText().includes("kafka")) {
1371
+ pushFinding("backend.message.queues", "low", sf, sf, "Message queue detected - ensure proper message handling and dead letter queues", findings);
1372
+ }
1373
+
1374
+ if (sf.getFullText().includes("stream") || sf.getFullText().includes("reactive")) {
1375
+ pushFinding("backend.stream.processing", "low", sf, sf, "Stream processing detected - ensure proper backpressure handling", findings);
1376
+ }
1377
+
1378
+ if (sf.getFullText().includes("warehouse") || sf.getFullText().includes("redshift")) {
1379
+ pushFinding("backend.data.warehousing", "low", sf, sf, "Data warehousing detected - ensure proper ETL processes and data quality", findings);
1380
+ }
1381
+
1382
+ if (sf.getFullText().includes("tensorflow") || sf.getFullText().includes("pytorch")) {
1383
+ pushFinding("backend.ml.integration", "low", sf, sf, "Machine learning integration detected - ensure proper model versioning and monitoring", findings);
1384
+ }
1385
+
1386
+ if (sf.getFullText().includes("web3") || sf.getFullText().includes("blockchain")) {
1387
+ pushFinding("backend.blockchain.integration", "low", sf, sf, "Blockchain integration detected - ensure proper smart contract interactions", findings);
1388
+ }
1389
+
1390
+ if (sf.getFullText().includes("mqtt") || sf.getFullText().includes("iot")) {
1391
+ pushFinding("backend.iot.integration", "low", sf, sf, "IoT integration detected - ensure proper device management and security", findings);
1392
+ }
1393
+ }
1394
+ // ==========================================
1395
+ // ==========================================
1396
+
1397
+ const rawSQLPattern = /query\s*\(\s*`[^`]*\$\{[^}]+\}|execut(e|eRaw)\s*\(\s*`[^`]*\$\{/gi;
1398
+ let sqlMatch;
1399
+
1400
+ while ((sqlMatch = rawSQLPattern.exec(fullText)) !== null) {
1401
+ const lineNumber = fullText.substring(0, sqlMatch.index).split('\n').length;
1402
+ pushFinding(
1403
+ "backend.security.sql_injection",
1404
+ "critical",
1405
+ sf,
1406
+ sf,
1407
+ `🚨 CRITICAL SQL Injection Risk (line ${lineNumber}): Raw SQL with template literals. Use parameterized queries: query('SELECT * FROM users WHERE id = $1', [userId]). Never: query(\`SELECT * FROM users WHERE id = \${userId}\`). Prevents: Data breach, unauthorized access, data loss`,
1408
+ findings
1409
+ );
1410
+ }
1411
+
1412
+ const loopQueryPattern = /for\s*\([^)]+\)[^{]*\{[^}]*\.(findOne|findById|query|execute)\(/g;
1413
+ if (loopQueryPattern.test(fullText)) {
1414
+ pushFinding(
1415
+ "backend.performance.n_plus_one",
1416
+ "critical",
1417
+ sf,
1418
+ sf,
1419
+ '🚨 CRITICAL N+1 Query: Database query inside loop. Use: findByIds([ids]) or JOIN. Example: const users = await repo.findByIds(orderIds); instead of: for(order of orders) { user = await repo.findById(order.userId); }. Impact: 1000 queries = 10s response time',
1420
+ findings
1421
+ );
1422
+ }
1423
+
1424
+ const plainPasswordPattern = /password\s*[:=]\s*[^b][^c][^r][^y][^p][^t]/i;
1425
+ if (plainPasswordPattern.test(fullText) && !fullText.includes('bcrypt') && !fullText.includes('argon2') && !fullText.includes('hash')) {
1426
+ pushFinding(
1427
+ "backend.security.plain_password",
1428
+ "critical",
1429
+ sf,
1430
+ sf,
1431
+ '🚨 CRITICAL Security: Password not hashed. Use bcrypt: import * as bcrypt from \'bcrypt\'; const hash = await bcrypt.hash(password, 10); Never store plain passwords. Prevents: Account compromise, compliance violation (GDPR, PCI-DSS)',
1432
+ findings
1433
+ );
1434
+ }
1435
+
1436
+ if (filePath.includes('/auth/') || filePath.includes('/guards/')) {
1437
+ const hasJWT = fullText.includes('@nestjs/jwt') || fullText.includes('JwtService') || fullText.includes('JwtStrategy');
1438
+ const hasAuth = fullText.includes('@UseGuards') || fullText.includes('AuthGuard');
1439
+
1440
+ if (!hasJWT && hasAuth) {
1441
+ pushFinding(
1442
+ "backend.security.missing_jwt",
1443
+ "critical",
1444
+ sf,
1445
+ sf,
1446
+ '🚨 CRITICAL Auth: Auth guard without JWT strategy. Install: @nestjs/jwt @nestjs/passport passport-jwt. Implement JwtStrategy extends PassportStrategy. Use: @UseGuards(JwtAuthGuard). Prevents: Unauthorized access, session hijacking',
1447
+ findings
1448
+ );
1449
+ }
1450
+ }
1451
+
1452
+ if (filePath.includes('main.ts')) {
1453
+ const hasHelmet = fullText.includes('helmet') || fullText.includes('helmet()');
1454
+
1455
+ if (!hasHelmet) {
1456
+ pushFinding(
1457
+ "backend.security.missing_helmet",
1458
+ "critical",
1459
+ sf,
1460
+ sf,
1461
+ '🚨 CRITICAL Security Headers: Missing Helmet. Install: npm i helmet. In main.ts: app.use(helmet()). Sets 15 security headers: X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security, etc. Prevents: Clickjacking, MIME sniffing, XSS',
1462
+ findings
1463
+ );
1464
+ }
1465
+ }
1466
+
1467
+ if (filePath.includes('main.ts')) {
1468
+ const hasCORS = fullText.includes('enableCors') || fullText.includes('cors()');
1469
+ const hasWildcard = fullText.includes("origin: '*'") || fullText.includes('origin: true');
1470
+
1471
+ if (!hasCORS) {
1472
+ pushFinding(
1473
+ "backend.security.missing_cors",
1474
+ "critical",
1475
+ sf,
1476
+ sf,
1477
+ '🚨 CRITICAL CORS: Missing CORS configuration. In main.ts: app.enableCors({ origin: [\'https://yourdomain.com\'], credentials: true }). Never use origin: \'*\' in production. Prevents: Unauthorized domain access, CSRF attacks',
1478
+ findings
1479
+ );
1480
+ } else if (hasWildcard) {
1481
+ pushFinding(
1482
+ "backend.security.cors_wildcard",
1483
+ "critical",
1484
+ sf,
1485
+ sf,
1486
+ '🚨 CRITICAL CORS: Wildcard origin (*) in production. Allows ANY domain to access your API. Use specific origins: origin: [\'https://yourdomain.com\']. Prevents: CSRF, unauthorized access, data theft',
1487
+ findings
1488
+ );
1489
+ }
1490
+ }
1491
+
1492
+ if (filePath.includes('.dto.ts') || filePath.endsWith('Dto.ts')) {
1493
+ const hasValidation = fullText.includes('@IsString') ||
1494
+ fullText.includes('@IsEmail') ||
1495
+ fullText.includes('@IsNumber') ||
1496
+ fullText.includes('class-validator');
1497
+
1498
+ const hasClass = fullText.includes('export class ') && fullText.includes('Dto');
1499
+
1500
+ if (hasClass && !hasValidation) {
1501
+ pushFinding(
1502
+ "backend.security.missing_dto_validation",
1503
+ "critical",
1504
+ sf,
1505
+ sf,
1506
+ '🚨 CRITICAL Input Validation: DTO without class-validator decorators. Install: class-validator class-transformer. Use: @IsString(), @IsEmail(), @Min(), @Max(). Enable in main.ts: app.useGlobalPipes(new ValidationPipe({ whitelist: true })). Prevents: Injection attacks, invalid data, crashes',
1507
+ findings
1508
+ );
1509
+ }
1510
+ }
1511
+
1512
+ const isDomain = filePath.includes('/domain/') && !filePath.includes('Impl');
1513
+ if (isDomain) {
1514
+ const forbiddenImports = ['@nestjs/', 'express', 'fastify', 'typeorm', 'mongoose', 'prisma'];
1515
+
1516
+ forbiddenImports.forEach(forbidden => {
1517
+ if (fullText.includes(`from '${forbidden}`) || fullText.includes(`from "${forbidden}`)) {
1518
+ pushFinding(
1519
+ "backend.architecture.domain_purity",
1520
+ "critical",
1521
+ sf,
1522
+ sf,
1523
+ `🚨 CRITICAL Architecture: Domain layer importing ${forbidden}. Domain must be framework-agnostic. Move to infrastructure/. Use interfaces in domain/, implementations in infrastructure/. Prevents: Tight coupling, untestable code`,
1524
+ findings
1525
+ );
1526
+ }
1527
+ });
1528
+ }
1529
+
1530
+ const importPaths = [];
1531
+ sf.getImportDeclarations().forEach(imp => {
1532
+ const moduleSpec = imp.getModuleSpecifierValue();
1533
+ if (moduleSpec.startsWith('.') || moduleSpec.startsWith('/')) {
1534
+ importPaths.push(moduleSpec);
1535
+ }
1536
+ });
1537
+
1538
+ const currentModule = filePath.split('/').slice(-2)[0];
1539
+ importPaths.forEach(importPath => {
1540
+ if (importPath.includes(currentModule)) {
1541
+ pushFinding(
1542
+ "backend.architecture.circular_dependency",
1543
+ "critical",
1544
+ sf,
1545
+ sf,
1546
+ `🚨 CRITICAL Circular Dependency: ${currentModule} imports module that imports it back. Causes: Runtime errors, initialization failures, module.exports undefined. Refactor: Extract shared code to separate module, use dependency injection. NestJS will throw: "Circular dependency between modules"`,
1547
+ findings
1548
+ );
1549
+ }
1550
+ });
1551
+
1552
+ if (filePath.includes('/filters/') || filePath.includes('/exception')) {
1553
+ const hasStackTrace = fullText.includes('stack:') || fullText.includes('error.stack');
1554
+ const hasEnvCheck = fullText.includes('NODE_ENV') || fullText.includes('process.env');
1555
+
1556
+ if (hasStackTrace && !hasEnvCheck) {
1557
+ pushFinding(
1558
+ "backend.security.stack_trace_exposure",
1559
+ "critical",
1560
+ sf,
1561
+ sf,
1562
+ '🚨 CRITICAL Info Disclosure: Stack traces exposed in production. Add: if (process.env.NODE_ENV !== \'production\') { response.stack = error.stack; }. Production should return generic error. Prevents: Path disclosure, framework version exposure, attack surface mapping',
1563
+ findings
1564
+ );
1565
+ }
1566
+ }
1567
+
1568
+ // ==========================================
1569
+ // ==========================================
1570
+
1571
+ if (filePath.includes('.controller.ts')) {
1572
+ const lines = fullText.split('\\n');
1573
+ const methodLines = lines.filter(l => l.trim().startsWith('async ') || l.trim().startsWith('public ') || l.trim().startsWith('private '));
1574
+
1575
+ if (methodLines.length > 100) {
1576
+ pushFinding(
1577
+ "backend.architecture.fat_controller",
1578
+ "high",
1579
+ sf,
1580
+ sf,
1581
+ `🚨 HIGH: Fat Controller (${methodLines.length} methods/lines). Controllers should only route + validate. Move business logic to services/use-cases. NestJS: Controllers are thin, Services are thick. Max recommended: 50 lines per controller method.`,
1582
+ findings
1583
+ );
1584
+ }
1585
+ }
1586
+
1587
+ if (fullText.includes('.save(') && fullText.includes('.update(') && !fullText.includes('@Transaction()')) {
1588
+ const isSameFunction = fullText.match(/async\s+\w+\([^)]*\)[^{]*\{[^}]*\.save\([^}]*\.update\(/);
1589
+
1590
+ if (isSameFunction) {
1591
+ pushFinding(
1592
+ "backend.database.missing_transaction",
1593
+ "high",
1594
+ sf,
1595
+ sf,
1596
+ '🚨 HIGH: Multiple DB operations without @Transaction(). Data inconsistency risk. Wrap in transaction: @Transaction() async method() { await repo1.save(); await repo2.update(); }. Ensures: All succeed or all rollback.',
1597
+ findings
1598
+ );
1599
+ }
1600
+ }
1601
+
1602
+ if (fullText.includes('extends HttpException') || fullText.includes('extends Error')) {
1603
+ const hasFilter = fullText.includes('@Catch(');
1604
+
1605
+ if (!hasFilter && filePath.includes('/exceptions/')) {
1606
+ pushFinding(
1607
+ "backend.error_handling.missing_exception_filter",
1608
+ "high",
1609
+ sf,
1610
+ sf,
1611
+ '🚨 HIGH: Custom exception without @Catch filter. Create: @Catch(MyException) export class MyExceptionFilter implements ExceptionFilter. Consistent error responses required.',
1612
+ findings
1613
+ );
1614
+ }
1615
+ }
1616
+
1617
+ if (filePath.includes('Repository.ts') && !filePath.includes('IRepository') && !filePath.includes('Protocol')) {
1618
+ const hasInterface = fullText.includes('export interface') || fullText.includes('export abstract class');
1619
+
1620
+ if (!hasInterface && fullText.includes('export class')) {
1621
+ pushFinding(
1622
+ "backend.architecture.repository_no_interface",
1623
+ "high",
1624
+ sf,
1625
+ sf,
1626
+ '🚨 HIGH: Repository without interface. Create IRepository interface in domain/. Concrete implementation in infrastructure/. Dependency Inversion: Depend on abstractions. Testability: Mock interface, not class.',
1627
+ findings
1628
+ );
1629
+ }
1630
+ }
1631
+
1632
+ const isAppOrDomainFile = /\/(domain|application)\//i.test(filePath);
1633
+ const isServiceFile = /\/(services?|use-?cases?)\//i.test(filePath) || /Service/.test(sf.getBaseName());
1634
+ const isDomainServiceFile = isAppOrDomainFile && isServiceFile && !/Repository/.test(filePath);
1635
+ if (isDomainServiceFile && !fullText.includes('eventEmitter.emit') && !fullText.includes('@OnEvent')) {
1636
+ const persistenceReceiverRegex = /(repo|repository|prisma|typeorm|mongoose|model|collection|db|database|entitymanager|manager|client|knex|sequelize|supabase)/i;
1637
+ const persistenceWriteMethods = new Set([
1638
+ 'create',
1639
+ 'update',
1640
+ 'delete',
1641
+ 'insert',
1642
+ 'save',
1643
+ 'upsert',
1644
+ 'remove',
1645
+ 'destroy'
1646
+ ]);
1647
+
1648
+ const hasPersistenceWrite = sf
1649
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
1650
+ .some((call) => {
1651
+ const expr = call.getExpression();
1652
+ if (!expr || expr.getKind() !== SyntaxKind.PropertyAccessExpression) return false;
1653
+ const method = expr.getName();
1654
+ if (!persistenceWriteMethods.has(method)) return false;
1655
+ const receiverText = expr.getExpression()?.getText?.() || '';
1656
+ return persistenceReceiverRegex.test(receiverText);
1657
+ });
1658
+
1659
+ if (hasPersistenceWrite) {
1660
+ pushFinding(
1661
+ "backend.architecture.missing_domain_event",
1662
+ "high",
1663
+ sf,
1664
+ sf,
1665
+ '🚨 HIGH: State change without domain event. Emit events for audit/integration: this.eventEmitter.emit(\'user.created\', new UserCreatedEvent(user)). Benefits: Audit trail, microservices integration, async processing.',
1666
+ findings
1667
+ );
1668
+ }
1669
+ }
1670
+
1671
+ const isAnalyzerForPII = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(filePath);
1672
+ if (!isAnalyzerForPII && !isTestFile(filePath)) {
1673
+ const sensitiveKeywords = [
1674
+ 'password',
1675
+ 'secret',
1676
+ 'ssn',
1677
+ 'creditcard',
1678
+ 'credit_card',
1679
+ 'authorization',
1680
+ 'bearer',
1681
+ 'jwt',
1682
+ 'accesstoken',
1683
+ 'access_token',
1684
+ 'refreshtoken',
1685
+ 'refresh_token',
1686
+ 'idtoken',
1687
+ 'id_token',
1688
+ 'apikey',
1689
+ 'api_key'
1690
+ ];
1691
+ const redactionKeywords = ['redact', 'mask', 'sanitize', 'obfuscate'];
1692
+ const logCallRegex = /^(console|logger|this\.logger)\.(log|info|debug|warn|error)$/;
1693
+
1694
+ const calls = sf.getDescendantsOfKind(SyntaxKind.CallExpression);
1695
+ for (const call of calls) {
1696
+ const exprText = call.getExpression().getText();
1697
+ if (!logCallRegex.test(exprText)) continue;
1698
+
1699
+ const args = call.getArguments();
1700
+ if (!args || args.length === 0) continue;
1701
+
1702
+ const relevantArgs = args.filter((a) => {
1703
+ const kind = typeof a.getKind === 'function' ? a.getKind() : null;
1704
+ if (kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral) return false;
1705
+ return true;
1706
+ });
1707
+ if (relevantArgs.length === 0) continue;
1708
+
1709
+ const argsText = relevantArgs.map(a => a.getText()).join(' ').toLowerCase();
1710
+ if (!sensitiveKeywords.some(k => argsText.includes(k))) continue;
1711
+ if (redactionKeywords.some(k => argsText.includes(k))) continue;
1712
+
1713
+ pushFinding(
1714
+ "backend.security.pii_in_logs",
1715
+ "high",
1716
+ sf,
1717
+ call,
1718
+ '🚨 HIGH: Potential PII in logs. Never log: passwords, tokens, SSN, credit cards. Sanitize: logger.info({ userId, action }) - don\'t include sensitive fields. GDPR violation risk.',
1719
+ findings
1720
+ );
1721
+ break;
1722
+ }
1723
+ }
1724
+
1725
+ if (filePath.includes('.service.ts') && !filePath.includes('.spec.ts')) {
1726
+ const testFilePath = filePath.replace('.service.ts', '.service.spec.ts');
1727
+ const fs = require('fs');
1728
+
1729
+ if (!fs.existsSync(testFilePath)) {
1730
+ pushFinding(
1731
+ "backend.testing.missing_service_tests",
1732
+ "high",
1733
+ sf,
1734
+ sf,
1735
+ `🚨 HIGH: Service without tests. Create: ${testFilePath}. Test coverage required for business logic. Use makeSUT pattern, AAA (Arrange-Act-Assert). Target: >95% coverage.`,
1736
+ findings
1737
+ );
1738
+ }
1739
+ }
1740
+
1741
+ if (filePath.includes('.controller.ts')) {
1742
+ const hasListEndpoint = fullText.includes('findAll') || fullText.includes('getAll') || fullText.includes('list');
1743
+ const hasPagination = fullText.includes('page') || fullText.includes('limit') || fullText.includes('offset') || fullText.includes('@Query');
1744
+
1745
+ if (hasListEndpoint && !hasPagination) {
1746
+ pushFinding(
1747
+ "backend.api.missing_pagination",
1748
+ "high",
1749
+ sf,
1750
+ sf,
1751
+ '🚨 HIGH: List endpoint without pagination. Add: @Query(\'page\') page: number, @Query(\'limit\') limit: number. Return: { data: items, total, page, limit }. Prevents: Memory issues, slow responses, timeout.',
1752
+ findings
1753
+ );
1754
+ }
1755
+ }
1756
+
1757
+ if (filePath.includes('.controller.ts')) {
1758
+ const swaggerIsAnalyzer = /infrastructure\/ast\/|analyzers\/|detectors\/|scanner|analyzer|detector/i.test(filePath);
1759
+ const swaggerIsTestFile = /\.(spec|test)\.(js|ts)$/i.test(filePath);
1760
+ if (!swaggerIsAnalyzer && !swaggerIsTestFile) {
1761
+ const hasSwaggerDecorators = fullText.includes('@ApiTags') || fullText.includes('@ApiOperation');
1762
+ const hasEndpoints = fullText.includes('@Get') || fullText.includes('@Post') || fullText.includes('@Put');
1763
+
1764
+ if (hasEndpoints && !hasSwaggerDecorators) {
1765
+ pushFinding(
1766
+ "backend.api.missing_swagger",
1767
+ "medium",
1768
+ sf,
1769
+ sf,
1770
+ 'Controller without Swagger documentation. Add: @ApiTags(\'users\'), @ApiOperation({ summary: \'...\' }), @ApiResponse(). Benefits: Auto-generated API docs, TypeScript types for frontend.',
1771
+ findings
1772
+ );
1773
+ }
1774
+ }
1775
+ }
1776
+
1777
+ if (filePath.includes('.controller.ts')) {
1778
+ const hasVersioning = fullText.includes('@Version(') || fullText.includes('/v1/') || fullText.includes('/v2/');
1779
+ const isMainController = fullText.includes('@Controller(') && !filePath.includes('.spec.ts');
1780
+
1781
+ if (isMainController && !hasVersioning) {
1782
+ pushFinding(
1783
+ "backend.api.missing_versioning",
1784
+ "medium",
1785
+ sf,
1786
+ sf,
1787
+ 'Controller without API versioning. Add: @Controller(\'v1/users\'). Or enable global versioning in main.ts. Prevents breaking changes for existing clients.',
1788
+ findings
1789
+ );
1790
+ }
1791
+ }
1792
+
1793
+ if (filePath.includes('health') || fullText.includes('HealthCheck')) {
1794
+ const hasDatabase = fullText.includes('database') || fullText.includes('TypeOrmHealthIndicator');
1795
+ const hasMemory = fullText.includes('memory') || fullText.includes('MemoryHealthIndicator');
1796
+ const hasDisk = fullText.includes('disk') || fullText.includes('DiskHealthIndicator');
1797
+
1798
+ if (!hasDatabase || !hasMemory) {
1799
+ pushFinding(
1800
+ "backend.monitoring.incomplete_health_check",
1801
+ "medium",
1802
+ sf,
1803
+ sf,
1804
+ 'Incomplete health check. Add indicators: database, memory, disk. Use @nestjs/terminus. Kubernetes needs /health liveness and /health/ready readiness probes.',
1805
+ findings
1806
+ );
1807
+ }
1808
+ }
1809
+
1810
+ if (filePath.includes('middleware') || filePath.includes('interceptor')) {
1811
+ const hasRequestId = fullText.includes('requestId') || fullText.includes('x-request-id') || fullText.includes('correlationId');
1812
+
1813
+ if (!hasRequestId && fullText.includes('logger')) {
1814
+ pushFinding(
1815
+ "backend.observability.missing_request_id",
1816
+ "medium",
1817
+ sf,
1818
+ sf,
1819
+ 'Logger without request ID. Generate: const requestId = uuidv4(); Add to headers: res.set(\'X-Request-ID\', requestId). Include in all logs. Traces requests across microservices.',
1820
+ findings
1821
+ );
1822
+ }
1823
+ }
1824
+
1825
+ if (filePath.includes('main.ts')) {
1826
+ const hasCompression = fullText.includes('compression(') || fullText.includes('@nestjs/platform-fastify');
1827
+
1828
+ if (!hasCompression) {
1829
+ pushFinding(
1830
+ "backend.performance.missing_compression",
1831
+ "medium",
1832
+ sf,
1833
+ sf,
1834
+ 'Missing GZIP compression. Install: npm i compression. Add in main.ts: app.use(compression()). Reduces response size by 70-90%. Faster API responses.',
1835
+ findings
1836
+ );
1837
+ }
1838
+ }
1839
+
1840
+ if (filePath.includes('config') || filePath.includes('env')) {
1841
+ const hasValidation = fullText.includes('Joi') || fullText.includes('class-validator') || fullText.includes('validate');
1842
+ const hasEnvVars = fullText.includes('process.env');
1843
+
1844
+ if (hasEnvVars && !hasValidation) {
1845
+ pushFinding(
1846
+ "backend.config.missing_env_validation",
1847
+ "medium",
1848
+ sf,
1849
+ sf,
1850
+ 'Environment variables without validation. Use Joi: validationSchema: Joi.object({ NODE_ENV: Joi.string().valid(\'dev\', \'prod\').required() }). Fails fast on missing/invalid config.',
1851
+ findings
1852
+ );
1853
+ }
1854
+ }
1855
+
1856
+
1857
+ if (fullText.includes('metrics') && !fullText.includes('prom-client') && !fullText.includes('@Prometheus')) {
1858
+ pushFinding(
1859
+ "backend.observability.missing_prometheus",
1860
+ "low",
1861
+ sf,
1862
+ sf,
1863
+ 'Metrics without Prometheus. Install: npm i prom-client @willsoto/nestjs-prometheus. Expose: /metrics endpoint. Monitor: request_duration, error_rate, throughput.',
1864
+ findings
1865
+ );
1866
+ }
1867
+
1868
+ if (filePath.includes('prometheus') || filePath.includes('metrics')) {
1869
+ const hasDashboardComment = fullText.includes('Grafana') || fullText.includes('dashboard');
1870
+
1871
+ if (!hasDashboardComment) {
1872
+ pushFinding(
1873
+ "backend.observability.missing_grafana_setup",
1874
+ "low",
1875
+ sf,
1876
+ sf,
1877
+ 'Prometheus metrics without Grafana dashboard. Export metrics.json. Import to Grafana. Create: API latency, error rate, throughput dashboards. Visualize production health.',
1878
+ findings
1879
+ );
1880
+ }
1881
+ }
1882
+
1883
+ if (filePath.includes('monitoring') || filePath.includes('alert')) {
1884
+ const hasAlertManager = fullText.includes('AlertManager') || fullText.includes('webhook') || fullText.includes('notification');
1885
+
1886
+ if (!hasAlertManager) {
1887
+ pushFinding(
1888
+ "backend.observability.missing_alerting",
1889
+ "low",
1890
+ sf,
1891
+ sf,
1892
+ 'Monitoring without alerts. Configure Prometheus AlertManager or PagerDuty. Alert on: error_rate > 5%, latency_p99 > 1s, cpu > 80%. Get notified BEFORE users complain.',
1893
+ findings
1894
+ );
1895
+ }
1896
+ }
1897
+
1898
+ if (filePath.includes('logger') && !fullText.includes('elasticsearch') && !fullText.includes('logstash')) {
1899
+ const hasStructuredLogging = fullText.includes('JSON.stringify') || fullText.includes('winston');
1900
+
1901
+ if (hasStructuredLogging) {
1902
+ pushFinding(
1903
+ "backend.observability.missing_log_aggregation",
1904
+ "low",
1905
+ sf,
1906
+ sf,
1907
+ 'Structured logs without aggregation. Send to: ELK Stack (Elasticsearch, Logstash, Kibana) or Datadog. Search logs across servers. Debug production issues faster.',
1908
+ findings
1909
+ );
1910
+ }
1911
+ }
1912
+
1913
+ if (filePath.includes('main.ts') && !fullText.includes('newrelic') && !fullText.includes('datadog') && !fullText.includes('@sentry/node')) {
1914
+ pushFinding(
1915
+ "backend.observability.missing_apm",
1916
+ "low",
1917
+ sf,
1918
+ sf,
1919
+ 'No APM integration. Install: New Relic, Datadog, or Elastic APM. Track: slow queries, external API calls, memory leaks. Find performance bottlenecks.',
1920
+ findings
1921
+ );
1922
+ }
1923
+
1924
+ if (filePath.includes('e2e') || filePath.includes('test')) {
1925
+ const hasLoadTest = fullText.includes('artillery') || fullText.includes('k6') || fullText.includes('jmeter');
1926
+
1927
+ if (!hasLoadTest && filePath.includes('e2e')) {
1928
+ pushFinding(
1929
+ "backend.testing.missing_load_tests",
1930
+ "low",
1931
+ sf,
1932
+ sf,
1933
+ 'E2E tests without load testing. Add: artillery or k6. Test: 100 concurrent users, 1000 requests/second. Find: rate limits, connection pool issues, memory leaks.',
1934
+ findings
1935
+ );
1936
+ }
1937
+ }
1938
+
1939
+ if (filePath.includes('database') || filePath.includes('typeorm')) {
1940
+ const hasBackup = fullText.includes('backup') || fullText.includes('pg_dump') || fullText.includes('snapshot');
1941
+
1942
+ if (!hasBackup && fullText.includes('createConnection')) {
1943
+ pushFinding(
1944
+ "backend.reliability.missing_backup_strategy",
1945
+ "low",
1946
+ sf,
1947
+ sf,
1948
+ 'Database connection without backup strategy. Configure: Daily automated backups, 30-day retention, test restore monthly. Use: pg_dump, AWS RDS snapshots, or backup service.',
1949
+ findings
1950
+ );
1951
+ }
1952
+ }
1953
+
1954
+ if (filePath.includes('config') && fullText.includes('database')) {
1955
+ const hasReplication = fullText.includes('replication') || fullText.includes('replica') || fullText.includes('standby');
1956
+
1957
+ if (!hasReplication) {
1958
+ pushFinding(
1959
+ "backend.reliability.missing_dr_plan",
1960
+ "low",
1961
+ sf,
1962
+ sf,
1963
+ 'Database config without disaster recovery. Setup: Read replicas, failover automation, cross-region backup. Target: RTO < 1 hour, RPO < 15 minutes. Survive data center outage.',
1964
+ findings
1965
+ );
1966
+ }
1967
+ }
1968
+
1969
+ if (filePath.includes('main.ts') || filePath.includes('app.module')) {
1970
+ const hasThrottler = fullText.includes('@nestjs/throttler') || fullText.includes('ThrottlerModule');
1971
+
1972
+ if (!hasThrottler) {
1973
+ pushFinding(
1974
+ "backend.security.missing_rate_limiting",
1975
+ "low",
1976
+ sf,
1977
+ sf,
1978
+ 'No rate limiting. Install: @nestjs/throttler. Configure: 10 requests/second per IP. Prevents: DDoS, brute force, API abuse. Saves infrastructure costs.',
1979
+ findings
1980
+ );
1981
+ }
1982
+ }
1983
+
1984
+ if (fullText.includes('axios') || fullText.includes('fetch') || fullText.includes('HttpService')) {
1985
+ const hasCircuitBreaker = fullText.includes('opossum') || fullText.includes('CircuitBreaker');
1986
+
1987
+ if (!hasCircuitBreaker && filePath.includes('.service.ts')) {
1988
+ pushFinding(
1989
+ "backend.reliability.missing_circuit_breaker",
1990
+ "low",
1991
+ sf,
1992
+ sf,
1993
+ 'External API call without circuit breaker. Install: opossum. Prevents: Cascading failures, timeout avalanche. Opens circuit after 5 failures, retries after 30s.',
1994
+ findings
1995
+ );
1996
+ }
1997
+ }
1998
+
1999
+ if (fullText.includes('async') && fullText.includes('await') && fullText.includes('for')) {
2000
+ const hasBulkhead = fullText.includes('Promise.all') || fullText.includes('p-limit');
2001
+
2002
+ if (!hasBulkhead) {
2003
+ pushFinding(
2004
+ "backend.reliability.missing_bulkhead",
2005
+ "low",
2006
+ sf,
2007
+ sf,
2008
+ 'Parallel async operations without bulkhead. Use: p-limit to control concurrency. const limit = pLimit(10). Prevents: Thread pool exhaustion, connection pool depletion.',
2009
+ findings
2010
+ );
2011
+ }
2012
+ }
2013
+
2014
+ if ((fullText.includes('axios') || fullText.includes('fetch')) && !fullText.includes('retry')) {
2015
+ const isExternalCall = fullText.includes('http://') || fullText.includes('https://');
2016
+
2017
+ if (isExternalCall) {
2018
+ pushFinding(
2019
+ "backend.reliability.missing_retry_policy",
2020
+ "low",
2021
+ sf,
2022
+ sf,
2023
+ 'External API call without retry. Use: axios-retry or custom exponential backoff. Retry 3 times with delays: 1s, 2s, 4s. Handles transient network failures.',
2024
+ findings
2025
+ );
2026
+ }
2027
+ }
2028
+
2029
+ if (filePath.includes('Dockerfile') || filePath.includes('.yml') || filePath.includes('deploy')) {
2030
+ const hasStrategy = fullText.includes('blue-green') || fullText.includes('canary') || fullText.includes('rolling');
2031
+
2032
+ if (!hasStrategy && (fullText.includes('deploy') || fullText.includes('kubernetes'))) {
2033
+ pushFinding(
2034
+ "backend.devops.missing_deployment_strategy",
2035
+ "low",
2036
+ sf,
2037
+ sf,
2038
+ 'Deployment config without strategy. Use: Blue-green (zero downtime), Canary (gradual rollout), or Rolling update. Kubernetes: set strategy.type, maxSurge, maxUnavailable.',
2039
+ findings
2040
+ );
2041
+ }
2042
+ }
2043
+ });
2044
+ }
2045
+
2046
+ module.exports = {
2047
+ runBackendIntelligence,
2048
+ };