ai-first-cli 1.3.0 → 1.3.5

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 (527) hide show
  1. package/.ai-dev/index.db +0 -0
  2. package/.github/workflows/publish.yml +4 -1
  3. package/BETA_EVALUATION_REPORT.md +151 -0
  4. package/CHANGELOG.md +178 -0
  5. package/PHASE1_USER_SIMULATION.md +56 -0
  6. package/PHASE2_USER_SIMULATION.md +81 -0
  7. package/PHASE3_USER_SIMULATION.md +176 -0
  8. package/README.es.md +18 -0
  9. package/README.md +80 -1
  10. package/ai/graph/knowledge-graph.json +10 -0
  11. package/ai-context/ai_context.md +130 -0
  12. package/{test-projects/react-app/.ai-dev → ai-context}/ai_rules.md +10 -5
  13. package/ai-context/architecture.md +136 -0
  14. package/ai-context/context/features/src.json +69 -0
  15. package/ai-context/context/features/test-projects.json +69 -0
  16. package/ai-context/context/flows/App.json +17 -0
  17. package/ai-context/context/flows/DashboardPage.json +14 -0
  18. package/ai-context/context/flows/LoginPage.json +14 -0
  19. package/ai-context/context/flows/admin.json +10 -0
  20. package/ai-context/context/flows/ai-first.json +9 -0
  21. package/ai-context/context/flows/androidresources.json +11 -0
  22. package/ai-context/context/flows/auth.json +13 -0
  23. package/ai-context/context/flows/authController.json +14 -0
  24. package/ai-context/context/flows/doctor.json +9 -0
  25. package/ai-context/context/flows/entrypoints.json +9 -0
  26. package/ai-context/context/flows/explore.json +9 -0
  27. package/ai-context/context/flows/fastapiAdapter.json +14 -0
  28. package/ai-context/context/flows/fastapiadapter.json +11 -0
  29. package/ai-context/context/flows/index.json +19 -0
  30. package/ai-context/context/flows/indexer.json +9 -0
  31. package/ai-context/context/flows/indexstate.json +9 -0
  32. package/ai-context/context/flows/init.json +22 -0
  33. package/ai-context/context/flows/main.json +18 -0
  34. package/ai-context/context/flows/mainactivity.json +9 -0
  35. package/ai-context/context/flows/models.json +15 -0
  36. package/ai-context/context/flows/posts.json +15 -0
  37. package/ai-context/context/flows/repoMapper.json +20 -0
  38. package/ai-context/context/flows/repomapper.json +11 -0
  39. package/ai-context/context/flows/routes.json +15 -0
  40. package/ai-context/context/flows/serializers.json +10 -0
  41. package/ai-context/context/flows/user.json +23 -0
  42. package/ai-context/context/flows/views.json +12 -0
  43. package/{test-projects/react-app/.ai-dev → ai-context}/conventions.md +3 -2
  44. package/ai-context/dependencies.json +3360 -0
  45. package/ai-context/entrypoints.md +45 -0
  46. package/ai-context/index-state.json +196 -0
  47. package/ai-context/modules.json +901 -0
  48. package/ai-context/project.json +33 -0
  49. package/ai-context/repo_map.json +8857 -0
  50. package/ai-context/repo_map.md +2002 -0
  51. package/{test-projects/flask-app/.ai-dev → ai-context}/schema.json +1 -1
  52. package/ai-context/summary.md +46 -0
  53. package/ai-context/symbols.json +82467 -0
  54. package/{test-projects/react-app/.ai-dev → ai-context}/tech_stack.md +15 -7
  55. package/ai-context-evaluation-report-1774223059505.md +206 -0
  56. package/dist/analyzers/architecture.d.ts.map +1 -1
  57. package/dist/analyzers/architecture.js +6 -0
  58. package/dist/analyzers/architecture.js.map +1 -1
  59. package/dist/analyzers/entrypoints.d.ts.map +1 -1
  60. package/dist/analyzers/entrypoints.js +105 -0
  61. package/dist/analyzers/entrypoints.js.map +1 -1
  62. package/dist/analyzers/symbols.d.ts.map +1 -1
  63. package/dist/analyzers/symbols.js +72 -1
  64. package/dist/analyzers/symbols.js.map +1 -1
  65. package/dist/analyzers/techStack.d.ts +8 -0
  66. package/dist/analyzers/techStack.d.ts.map +1 -1
  67. package/dist/analyzers/techStack.js +75 -0
  68. package/dist/analyzers/techStack.js.map +1 -1
  69. package/dist/scripts/ai-context-evaluator.js +367 -0
  70. package/package.json +1 -1
  71. package/quick-evaluation-report-1774396002305.md +64 -0
  72. package/quick-evaluator.ts +200 -0
  73. package/scripts/ai-context-evaluator.ts +440 -0
  74. package/src/analyzers/architecture.ts +8 -0
  75. package/src/analyzers/entrypoints.ts +115 -0
  76. package/src/analyzers/symbols.ts +77 -1
  77. package/src/analyzers/techStack.ts +93 -0
  78. package/test_adapters.mjs +11 -11
  79. package/tests/apex-parser.test.ts +193 -0
  80. package/tests/cli-commands-batch1.test.ts +808 -0
  81. package/tests/cli-commands-batch2.test.ts +1113 -0
  82. package/tests/cli-commands-batch3.test.ts +1128 -0
  83. package/tests/cli-index.test.ts +1007 -0
  84. package/tests/cli-init.test.ts +761 -0
  85. package/tests/salesforce-apex-classes.test.ts +713 -0
  86. package/tests/salesforce-apex-triggers.test.ts +871 -0
  87. package/tests/salesforce-custom-objects.test.ts +918 -0
  88. package/tests/salesforce-flows.test.ts +710 -0
  89. package/tests/salesforce-lwc.test.ts +963 -0
  90. package/tests/salesforce-sfdx-integration.test.ts +1125 -0
  91. package/CONTRIBUTING.md +0 -89
  92. package/FLOW.md +0 -129
  93. package/install.sh +0 -188
  94. package/run-all-tests.sh +0 -184
  95. package/test-projects/django-app/.ai-dev/ai_context.md +0 -92
  96. package/test-projects/django-app/.ai-dev/ai_rules.md +0 -47
  97. package/test-projects/django-app/.ai-dev/architecture.md +0 -57
  98. package/test-projects/django-app/.ai-dev/cache.json +0 -169
  99. package/test-projects/django-app/.ai-dev/context/flows/views.json +0 -10
  100. package/test-projects/django-app/.ai-dev/conventions.md +0 -51
  101. package/test-projects/django-app/.ai-dev/dependencies.json +0 -312
  102. package/test-projects/django-app/.ai-dev/entrypoints.md +0 -4
  103. package/test-projects/django-app/.ai-dev/files.json +0 -209
  104. package/test-projects/django-app/.ai-dev/graph/knowledge-graph.json +0 -36
  105. package/test-projects/django-app/.ai-dev/graph/module-graph.json +0 -145
  106. package/test-projects/django-app/.ai-dev/graph/symbol-graph.json +0 -1488
  107. package/test-projects/django-app/.ai-dev/graph/symbol-references.json +0 -1
  108. package/test-projects/django-app/.ai-dev/index-state.json +0 -294
  109. package/test-projects/django-app/.ai-dev/modules.json +0 -35
  110. package/test-projects/django-app/.ai-dev/project.json +0 -11
  111. package/test-projects/django-app/.ai-dev/repo_map.json +0 -412
  112. package/test-projects/django-app/.ai-dev/repo_map.md +0 -105
  113. package/test-projects/django-app/.ai-dev/schema.json +0 -5
  114. package/test-projects/django-app/.ai-dev/summary.md +0 -15
  115. package/test-projects/django-app/.ai-dev/symbols.json +0 -1
  116. package/test-projects/django-app/.ai-dev/tech_stack.md +0 -32
  117. package/test-projects/django-app/README.md +0 -91
  118. package/test-projects/django-app/blog/__init__.py +0 -0
  119. package/test-projects/django-app/blog/admin.py +0 -31
  120. package/test-projects/django-app/blog/models.py +0 -55
  121. package/test-projects/django-app/blog/serializers.py +0 -69
  122. package/test-projects/django-app/blog/urls.py +0 -14
  123. package/test-projects/django-app/blog/views.py +0 -96
  124. package/test-projects/django-app/django_app/__init__.py +0 -0
  125. package/test-projects/django-app/django_app/settings.py +0 -90
  126. package/test-projects/django-app/django_app/urls.py +0 -11
  127. package/test-projects/django-app/django_app/wsgi.py +0 -9
  128. package/test-projects/django-app/manage.py +0 -23
  129. package/test-projects/django-app/requirements.txt +0 -3
  130. package/test-projects/django-app/users/__init__.py +0 -0
  131. package/test-projects/django-app/users/admin.py +0 -42
  132. package/test-projects/django-app/users/models.py +0 -54
  133. package/test-projects/django-app/users/serializers.py +0 -113
  134. package/test-projects/django-app/users/urls.py +0 -13
  135. package/test-projects/django-app/users/views.py +0 -135
  136. package/test-projects/express-api/.ai-dev/ai_context.md +0 -112
  137. package/test-projects/express-api/.ai-dev/ai_rules.md +0 -50
  138. package/test-projects/express-api/.ai-dev/architecture.md +0 -62
  139. package/test-projects/express-api/.ai-dev/context/features/controllers.json +0 -13
  140. package/test-projects/express-api/.ai-dev/context/features/services.json +0 -13
  141. package/test-projects/express-api/.ai-dev/context/flows/auth.json +0 -12
  142. package/test-projects/express-api/.ai-dev/context/flows/user.json +0 -13
  143. package/test-projects/express-api/.ai-dev/conventions.md +0 -51
  144. package/test-projects/express-api/.ai-dev/dependencies.json +0 -54
  145. package/test-projects/express-api/.ai-dev/entrypoints.md +0 -17
  146. package/test-projects/express-api/.ai-dev/modules.json +0 -30
  147. package/test-projects/express-api/.ai-dev/project.json +0 -15
  148. package/test-projects/express-api/.ai-dev/repo_map.json +0 -100
  149. package/test-projects/express-api/.ai-dev/repo_map.md +0 -36
  150. package/test-projects/express-api/.ai-dev/schema.json +0 -5
  151. package/test-projects/express-api/.ai-dev/summary.md +0 -14
  152. package/test-projects/express-api/.ai-dev/symbols.json +0 -7
  153. package/test-projects/express-api/.ai-dev/tech_stack.md +0 -38
  154. package/test-projects/express-api/.ai-dev/tools.json +0 -10
  155. package/test-projects/express-api/controllers/authController.js +0 -32
  156. package/test-projects/express-api/controllers/userController.js +0 -51
  157. package/test-projects/express-api/index.js +0 -30
  158. package/test-projects/express-api/middleware/authMiddleware.js +0 -30
  159. package/test-projects/express-api/models/userRepository.js +0 -25
  160. package/test-projects/express-api/package.json +0 -18
  161. package/test-projects/express-api/services/authService.js +0 -17
  162. package/test-projects/express-api/services/userService.js +0 -28
  163. package/test-projects/fastapi-app/.ai-dev/ai_context.md +0 -89
  164. package/test-projects/fastapi-app/.ai-dev/ai_rules.md +0 -47
  165. package/test-projects/fastapi-app/.ai-dev/architecture.md +0 -39
  166. package/test-projects/fastapi-app/.ai-dev/cache.json +0 -125
  167. package/test-projects/fastapi-app/.ai-dev/conventions.md +0 -51
  168. package/test-projects/fastapi-app/.ai-dev/dependencies.json +0 -244
  169. package/test-projects/fastapi-app/.ai-dev/entrypoints.md +0 -4
  170. package/test-projects/fastapi-app/.ai-dev/files.json +0 -154
  171. package/test-projects/fastapi-app/.ai-dev/graph/knowledge-graph.json +0 -15
  172. package/test-projects/fastapi-app/.ai-dev/graph/module-graph.json +0 -78
  173. package/test-projects/fastapi-app/.ai-dev/graph/symbol-graph.json +0 -1724
  174. package/test-projects/fastapi-app/.ai-dev/graph/symbol-references.json +0 -51
  175. package/test-projects/fastapi-app/.ai-dev/index-state.json +0 -217
  176. package/test-projects/fastapi-app/.ai-dev/modules.json +0 -16
  177. package/test-projects/fastapi-app/.ai-dev/project.json +0 -9
  178. package/test-projects/fastapi-app/.ai-dev/repo_map.json +0 -298
  179. package/test-projects/fastapi-app/.ai-dev/repo_map.md +0 -74
  180. package/test-projects/fastapi-app/.ai-dev/schema.json +0 -5
  181. package/test-projects/fastapi-app/.ai-dev/summary.md +0 -12
  182. package/test-projects/fastapi-app/.ai-dev/symbols.json +0 -1
  183. package/test-projects/fastapi-app/.ai-dev/tech_stack.md +0 -32
  184. package/test-projects/fastapi-app/.ai-dev/tools.json +0 -10
  185. package/test-projects/fastapi-app/README.md +0 -118
  186. package/test-projects/fastapi-app/app/database.py +0 -21
  187. package/test-projects/fastapi-app/app/dependencies.py +0 -107
  188. package/test-projects/fastapi-app/app/main.py +0 -47
  189. package/test-projects/fastapi-app/app/models.py +0 -149
  190. package/test-projects/fastapi-app/app/routers/auth.py +0 -117
  191. package/test-projects/fastapi-app/app/routers/posts.py +0 -272
  192. package/test-projects/fastapi-app/app/schemas.py +0 -191
  193. package/test-projects/fastapi-app/requirements.txt +0 -10
  194. package/test-projects/flask-app/.ai-dev/ai_context.md +0 -94
  195. package/test-projects/flask-app/.ai-dev/ai_rules.md +0 -47
  196. package/test-projects/flask-app/.ai-dev/architecture.md +0 -49
  197. package/test-projects/flask-app/.ai-dev/cache.json +0 -157
  198. package/test-projects/flask-app/.ai-dev/context/features/app.json +0 -25
  199. package/test-projects/flask-app/.ai-dev/context/flows/routes.json +0 -14
  200. package/test-projects/flask-app/.ai-dev/conventions.md +0 -51
  201. package/test-projects/flask-app/.ai-dev/dependencies.json +0 -298
  202. package/test-projects/flask-app/.ai-dev/entrypoints.md +0 -4
  203. package/test-projects/flask-app/.ai-dev/files.json +0 -194
  204. package/test-projects/flask-app/.ai-dev/graph/knowledge-graph.json +0 -60
  205. package/test-projects/flask-app/.ai-dev/graph/module-graph.json +0 -95
  206. package/test-projects/flask-app/.ai-dev/graph/symbol-graph.json +0 -1448
  207. package/test-projects/flask-app/.ai-dev/graph/symbol-references.json +0 -45
  208. package/test-projects/flask-app/.ai-dev/index-state.json +0 -273
  209. package/test-projects/flask-app/.ai-dev/modules.json +0 -21
  210. package/test-projects/flask-app/.ai-dev/project.json +0 -13
  211. package/test-projects/flask-app/.ai-dev/repo_map.json +0 -400
  212. package/test-projects/flask-app/.ai-dev/repo_map.md +0 -98
  213. package/test-projects/flask-app/.ai-dev/summary.md +0 -13
  214. package/test-projects/flask-app/.ai-dev/symbols.json +0 -1
  215. package/test-projects/flask-app/.ai-dev/tech_stack.md +0 -32
  216. package/test-projects/flask-app/.ai-dev/tools.json +0 -10
  217. package/test-projects/flask-app/README.md +0 -129
  218. package/test-projects/flask-app/app/__init__.py +0 -46
  219. package/test-projects/flask-app/app/api/__init__.py +0 -7
  220. package/test-projects/flask-app/app/api/routes.py +0 -122
  221. package/test-projects/flask-app/app/auth/__init__.py +0 -7
  222. package/test-projects/flask-app/app/auth/forms.py +0 -52
  223. package/test-projects/flask-app/app/auth/routes.py +0 -68
  224. package/test-projects/flask-app/app/blog/__init__.py +0 -7
  225. package/test-projects/flask-app/app/blog/forms.py +0 -35
  226. package/test-projects/flask-app/app/blog/routes.py +0 -140
  227. package/test-projects/flask-app/app/main/__init__.py +0 -7
  228. package/test-projects/flask-app/app/main/routes.py +0 -88
  229. package/test-projects/flask-app/app/models.py +0 -177
  230. package/test-projects/flask-app/config.py +0 -64
  231. package/test-projects/flask-app/requirements.txt +0 -10
  232. package/test-projects/laravel-app/.ai-dev/ai_context.md +0 -97
  233. package/test-projects/laravel-app/.ai-dev/ai_rules.md +0 -47
  234. package/test-projects/laravel-app/.ai-dev/architecture.md +0 -60
  235. package/test-projects/laravel-app/.ai-dev/cache.json +0 -161
  236. package/test-projects/laravel-app/.ai-dev/context/features/app.json +0 -21
  237. package/test-projects/laravel-app/.ai-dev/context/flows/.json +0 -9
  238. package/test-projects/laravel-app/.ai-dev/context/flows/category.json +0 -12
  239. package/test-projects/laravel-app/.ai-dev/context/flows/comment.json +0 -12
  240. package/test-projects/laravel-app/.ai-dev/context/flows/post.json +0 -12
  241. package/test-projects/laravel-app/.ai-dev/context/flows/unnamed.json +0 -9
  242. package/test-projects/laravel-app/.ai-dev/conventions.md +0 -51
  243. package/test-projects/laravel-app/.ai-dev/dependencies.json +0 -6
  244. package/test-projects/laravel-app/.ai-dev/entrypoints.md +0 -4
  245. package/test-projects/laravel-app/.ai-dev/files.json +0 -199
  246. package/test-projects/laravel-app/.ai-dev/graph/knowledge-graph.json +0 -98
  247. package/test-projects/laravel-app/.ai-dev/graph/module-graph.json +0 -30
  248. package/test-projects/laravel-app/.ai-dev/graph/symbol-graph.json +0 -5
  249. package/test-projects/laravel-app/.ai-dev/graph/symbol-references.json +0 -1
  250. package/test-projects/laravel-app/.ai-dev/index-state.json +0 -280
  251. package/test-projects/laravel-app/.ai-dev/modules.json +0 -29
  252. package/test-projects/laravel-app/.ai-dev/project.json +0 -17
  253. package/test-projects/laravel-app/.ai-dev/repo_map.json +0 -419
  254. package/test-projects/laravel-app/.ai-dev/repo_map.md +0 -106
  255. package/test-projects/laravel-app/.ai-dev/schema.json +0 -5
  256. package/test-projects/laravel-app/.ai-dev/summary.md +0 -15
  257. package/test-projects/laravel-app/.ai-dev/symbols.json +0 -1
  258. package/test-projects/laravel-app/.ai-dev/tech_stack.md +0 -34
  259. package/test-projects/laravel-app/.ai-dev/tools.json +0 -10
  260. package/test-projects/laravel-app/README.md +0 -107
  261. package/test-projects/laravel-app/app/Http/Controllers/Api/CategoryController.php +0 -88
  262. package/test-projects/laravel-app/app/Http/Controllers/Api/CommentController.php +0 -56
  263. package/test-projects/laravel-app/app/Http/Controllers/Api/PostController.php +0 -174
  264. package/test-projects/laravel-app/app/Http/Controllers/Controller.php +0 -12
  265. package/test-projects/laravel-app/app/Models/Category.php +0 -34
  266. package/test-projects/laravel-app/app/Models/Comment.php +0 -51
  267. package/test-projects/laravel-app/app/Models/Post.php +0 -108
  268. package/test-projects/laravel-app/app/Models/User.php +0 -85
  269. package/test-projects/laravel-app/bootstrap/app.php +0 -25
  270. package/test-projects/laravel-app/composer.json +0 -35
  271. package/test-projects/laravel-app/routes/api.php +0 -40
  272. package/test-projects/nestjs-backend/.ai-dev/ai_context.md +0 -111
  273. package/test-projects/nestjs-backend/.ai-dev/ai_rules.md +0 -52
  274. package/test-projects/nestjs-backend/.ai-dev/architecture.md +0 -49
  275. package/test-projects/nestjs-backend/.ai-dev/cache.json +0 -169
  276. package/test-projects/nestjs-backend/.ai-dev/context/features/src.json +0 -23
  277. package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.controller.json +0 -14
  278. package/test-projects/nestjs-backend/.ai-dev/context/flows/auth.json +0 -10
  279. package/test-projects/nestjs-backend/.ai-dev/context/flows/users..json +0 -10
  280. package/test-projects/nestjs-backend/.ai-dev/context/flows/users.controller.json +0 -14
  281. package/test-projects/nestjs-backend/.ai-dev/context/flows/users.json +0 -10
  282. package/test-projects/nestjs-backend/.ai-dev/conventions.md +0 -52
  283. package/test-projects/nestjs-backend/.ai-dev/dependencies.json +0 -152
  284. package/test-projects/nestjs-backend/.ai-dev/entrypoints.md +0 -18
  285. package/test-projects/nestjs-backend/.ai-dev/files.json +0 -209
  286. package/test-projects/nestjs-backend/.ai-dev/graph/knowledge-graph.json +0 -132
  287. package/test-projects/nestjs-backend/.ai-dev/graph/module-graph.json +0 -29
  288. package/test-projects/nestjs-backend/.ai-dev/graph/symbol-graph.json +0 -304
  289. package/test-projects/nestjs-backend/.ai-dev/graph/symbol-references.json +0 -5
  290. package/test-projects/nestjs-backend/.ai-dev/index-state.json +0 -294
  291. package/test-projects/nestjs-backend/.ai-dev/modules.json +0 -19
  292. package/test-projects/nestjs-backend/.ai-dev/project.json +0 -18
  293. package/test-projects/nestjs-backend/.ai-dev/repo_map.json +0 -427
  294. package/test-projects/nestjs-backend/.ai-dev/repo_map.md +0 -104
  295. package/test-projects/nestjs-backend/.ai-dev/schema.json +0 -5
  296. package/test-projects/nestjs-backend/.ai-dev/summary.md +0 -13
  297. package/test-projects/nestjs-backend/.ai-dev/symbols.json +0 -1
  298. package/test-projects/nestjs-backend/.ai-dev/tech_stack.md +0 -38
  299. package/test-projects/nestjs-backend/.ai-dev/tools.json +0 -10
  300. package/test-projects/nestjs-backend/package.json +0 -22
  301. package/test-projects/nestjs-backend/src/app.module.ts +0 -8
  302. package/test-projects/nestjs-backend/src/auth/auth.controller.ts +0 -22
  303. package/test-projects/nestjs-backend/src/auth/auth.module.ts +0 -11
  304. package/test-projects/nestjs-backend/src/auth/auth.service.ts +0 -28
  305. package/test-projects/nestjs-backend/src/auth/dto/login.dto.ts +0 -4
  306. package/test-projects/nestjs-backend/src/auth/strategies/jwt.strategy.ts +0 -18
  307. package/test-projects/nestjs-backend/src/main.ts +0 -9
  308. package/test-projects/nestjs-backend/src/users/users.controller.ts +0 -32
  309. package/test-projects/nestjs-backend/src/users/users.module.ts +0 -10
  310. package/test-projects/nestjs-backend/src/users/users.service.ts +0 -42
  311. package/test-projects/nestjs-backend/tsconfig.json +0 -21
  312. package/test-projects/python-cli/.ai-dev/ai_context.md +0 -95
  313. package/test-projects/python-cli/.ai-dev/ai_rules.md +0 -47
  314. package/test-projects/python-cli/.ai-dev/architecture.md +0 -55
  315. package/test-projects/python-cli/.ai-dev/cache.json +0 -149
  316. package/test-projects/python-cli/.ai-dev/context/features/cli.json +0 -16
  317. package/test-projects/python-cli/.ai-dev/context/flows/list_.json +0 -9
  318. package/test-projects/python-cli/.ai-dev/context/flows/remove_.json +0 -9
  319. package/test-projects/python-cli/.ai-dev/conventions.md +0 -51
  320. package/test-projects/python-cli/.ai-dev/dependencies.json +0 -66
  321. package/test-projects/python-cli/.ai-dev/entrypoints.md +0 -4
  322. package/test-projects/python-cli/.ai-dev/files.json +0 -184
  323. package/test-projects/python-cli/.ai-dev/graph/knowledge-graph.json +0 -83
  324. package/test-projects/python-cli/.ai-dev/graph/module-graph.json +0 -31
  325. package/test-projects/python-cli/.ai-dev/graph/symbol-graph.json +0 -358
  326. package/test-projects/python-cli/.ai-dev/graph/symbol-references.json +0 -11
  327. package/test-projects/python-cli/.ai-dev/index-state.json +0 -259
  328. package/test-projects/python-cli/.ai-dev/modules.json +0 -21
  329. package/test-projects/python-cli/.ai-dev/project.json +0 -15
  330. package/test-projects/python-cli/.ai-dev/repo_map.json +0 -367
  331. package/test-projects/python-cli/.ai-dev/repo_map.md +0 -93
  332. package/test-projects/python-cli/.ai-dev/schema.json +0 -5
  333. package/test-projects/python-cli/.ai-dev/summary.md +0 -14
  334. package/test-projects/python-cli/.ai-dev/symbols.json +0 -1
  335. package/test-projects/python-cli/.ai-dev/tech_stack.md +0 -32
  336. package/test-projects/python-cli/.ai-dev/tools.json +0 -10
  337. package/test-projects/python-cli/__init__.py +0 -1
  338. package/test-projects/python-cli/cli/__init__.py +0 -1
  339. package/test-projects/python-cli/cli/add_command.py +0 -6
  340. package/test-projects/python-cli/cli/list_command.py +0 -7
  341. package/test-projects/python-cli/cli/remove_command.py +0 -6
  342. package/test-projects/python-cli/main.py +0 -34
  343. package/test-projects/python-cli/models/__init__.py +0 -2
  344. package/test-projects/python-cli/models/task.py +0 -19
  345. package/test-projects/python-cli/models/task_repository.py +0 -44
  346. package/test-projects/rails-app/.ai-dev/ai_context.md +0 -94
  347. package/test-projects/rails-app/.ai-dev/ai_rules.md +0 -47
  348. package/test-projects/rails-app/.ai-dev/architecture.md +0 -49
  349. package/test-projects/rails-app/.ai-dev/cache.json +0 -193
  350. package/test-projects/rails-app/.ai-dev/context/features/app.json +0 -24
  351. package/test-projects/rails-app/.ai-dev/context/features/config.json +0 -13
  352. package/test-projects/rails-app/.ai-dev/context/flows/application.json +0 -9
  353. package/test-projects/rails-app/.ai-dev/context/flows/application_.json +0 -9
  354. package/test-projects/rails-app/.ai-dev/context/flows/comments.json +0 -11
  355. package/test-projects/rails-app/.ai-dev/context/flows/comments_.json +0 -11
  356. package/test-projects/rails-app/.ai-dev/context/flows/posts.json +0 -11
  357. package/test-projects/rails-app/.ai-dev/context/flows/posts_.json +0 -11
  358. package/test-projects/rails-app/.ai-dev/context/flows/routes.json +0 -9
  359. package/test-projects/rails-app/.ai-dev/context/flows/users.json +0 -11
  360. package/test-projects/rails-app/.ai-dev/context/flows/users_.json +0 -11
  361. package/test-projects/rails-app/.ai-dev/conventions.md +0 -51
  362. package/test-projects/rails-app/.ai-dev/dependencies.json +0 -6
  363. package/test-projects/rails-app/.ai-dev/entrypoints.md +0 -4
  364. package/test-projects/rails-app/.ai-dev/files.json +0 -239
  365. package/test-projects/rails-app/.ai-dev/graph/knowledge-graph.json +0 -130
  366. package/test-projects/rails-app/.ai-dev/graph/module-graph.json +0 -27
  367. package/test-projects/rails-app/.ai-dev/graph/symbol-graph.json +0 -5
  368. package/test-projects/rails-app/.ai-dev/graph/symbol-references.json +0 -1
  369. package/test-projects/rails-app/.ai-dev/index-state.json +0 -336
  370. package/test-projects/rails-app/.ai-dev/modules.json +0 -26
  371. package/test-projects/rails-app/.ai-dev/project.json +0 -22
  372. package/test-projects/rails-app/.ai-dev/repo_map.json +0 -486
  373. package/test-projects/rails-app/.ai-dev/repo_map.md +0 -117
  374. package/test-projects/rails-app/.ai-dev/schema.json +0 -5
  375. package/test-projects/rails-app/.ai-dev/summary.md +0 -13
  376. package/test-projects/rails-app/.ai-dev/symbols.json +0 -1
  377. package/test-projects/rails-app/.ai-dev/tech_stack.md +0 -32
  378. package/test-projects/rails-app/.ai-dev/tools.json +0 -10
  379. package/test-projects/rails-app/Gemfile +0 -38
  380. package/test-projects/rails-app/README.md +0 -140
  381. package/test-projects/rails-app/Rakefile +0 -8
  382. package/test-projects/rails-app/app/controllers/api/comments_controller.rb +0 -75
  383. package/test-projects/rails-app/app/controllers/api/posts_controller.rb +0 -68
  384. package/test-projects/rails-app/app/controllers/api/users_controller.rb +0 -54
  385. package/test-projects/rails-app/app/controllers/application_controller.rb +0 -31
  386. package/test-projects/rails-app/app/models/comment.rb +0 -34
  387. package/test-projects/rails-app/app/models/post.rb +0 -36
  388. package/test-projects/rails-app/app/models/user.rb +0 -28
  389. package/test-projects/rails-app/app/services/post_service.rb +0 -92
  390. package/test-projects/rails-app/app/services/user_service.rb +0 -76
  391. package/test-projects/rails-app/config/application.rb +0 -27
  392. package/test-projects/rails-app/config/environment.rb +0 -7
  393. package/test-projects/rails-app/config/routes.rb +0 -15
  394. package/test-projects/react-app/.ai-dev/ai_context.md +0 -96
  395. package/test-projects/react-app/.ai-dev/architecture.md +0 -39
  396. package/test-projects/react-app/.ai-dev/cache.json +0 -153
  397. package/test-projects/react-app/.ai-dev/context/features/src.json +0 -18
  398. package/test-projects/react-app/.ai-dev/context/flows/UsersPage.json +0 -14
  399. package/test-projects/react-app/.ai-dev/context/flows/dashboard.json +0 -9
  400. package/test-projects/react-app/.ai-dev/context/flows/login.json +0 -9
  401. package/test-projects/react-app/.ai-dev/context/flows/users.json +0 -9
  402. package/test-projects/react-app/.ai-dev/dependencies.json +0 -128
  403. package/test-projects/react-app/.ai-dev/entrypoints.md +0 -4
  404. package/test-projects/react-app/.ai-dev/files.json +0 -189
  405. package/test-projects/react-app/.ai-dev/graph/knowledge-graph.json +0 -112
  406. package/test-projects/react-app/.ai-dev/graph/module-graph.json +0 -31
  407. package/test-projects/react-app/.ai-dev/graph/symbol-graph.json +0 -868
  408. package/test-projects/react-app/.ai-dev/graph/symbol-references.json +0 -31
  409. package/test-projects/react-app/.ai-dev/index-state.json +0 -266
  410. package/test-projects/react-app/.ai-dev/modules.json +0 -17
  411. package/test-projects/react-app/.ai-dev/project.json +0 -16
  412. package/test-projects/react-app/.ai-dev/repo_map.json +0 -391
  413. package/test-projects/react-app/.ai-dev/repo_map.md +0 -94
  414. package/test-projects/react-app/.ai-dev/schema.json +0 -5
  415. package/test-projects/react-app/.ai-dev/summary.md +0 -13
  416. package/test-projects/react-app/.ai-dev/symbols.json +0 -1
  417. package/test-projects/react-app/.ai-dev/tools.json +0 -10
  418. package/test-projects/react-app/package.json +0 -16
  419. package/test-projects/react-app/src/App.tsx +0 -21
  420. package/test-projects/react-app/src/context/AuthContext.tsx +0 -41
  421. package/test-projects/react-app/src/hooks/useAuth.ts +0 -10
  422. package/test-projects/react-app/src/main.tsx +0 -10
  423. package/test-projects/react-app/src/pages/DashboardPage.tsx +0 -17
  424. package/test-projects/react-app/src/pages/LoginPage.tsx +0 -41
  425. package/test-projects/react-app/src/pages/UsersPage.tsx +0 -36
  426. package/test-projects/react-app/src/services/userService.ts +0 -37
  427. package/test-projects/salesforce-cli/.ai-dev/ai_context.md +0 -89
  428. package/test-projects/salesforce-cli/.ai-dev/ai_rules.md +0 -47
  429. package/test-projects/salesforce-cli/.ai-dev/architecture.md +0 -39
  430. package/test-projects/salesforce-cli/.ai-dev/cache.json +0 -125
  431. package/test-projects/salesforce-cli/.ai-dev/context/features/force-app.json +0 -14
  432. package/test-projects/salesforce-cli/.ai-dev/context/flows/account.json +0 -9
  433. package/test-projects/salesforce-cli/.ai-dev/context/flows/opportunity.json +0 -9
  434. package/test-projects/salesforce-cli/.ai-dev/conventions.md +0 -51
  435. package/test-projects/salesforce-cli/.ai-dev/dependencies.json +0 -6
  436. package/test-projects/salesforce-cli/.ai-dev/entrypoints.md +0 -4
  437. package/test-projects/salesforce-cli/.ai-dev/files.json +0 -154
  438. package/test-projects/salesforce-cli/.ai-dev/graph/knowledge-graph.json +0 -64
  439. package/test-projects/salesforce-cli/.ai-dev/graph/module-graph.json +0 -13
  440. package/test-projects/salesforce-cli/.ai-dev/graph/symbol-graph.json +0 -148
  441. package/test-projects/salesforce-cli/.ai-dev/graph/symbol-references.json +0 -1
  442. package/test-projects/salesforce-cli/.ai-dev/index-state.json +0 -217
  443. package/test-projects/salesforce-cli/.ai-dev/modules.json +0 -12
  444. package/test-projects/salesforce-cli/.ai-dev/project.json +0 -14
  445. package/test-projects/salesforce-cli/.ai-dev/repo_map.json +0 -328
  446. package/test-projects/salesforce-cli/.ai-dev/repo_map.md +0 -80
  447. package/test-projects/salesforce-cli/.ai-dev/schema.json +0 -5
  448. package/test-projects/salesforce-cli/.ai-dev/summary.md +0 -13
  449. package/test-projects/salesforce-cli/.ai-dev/symbols.json +0 -1
  450. package/test-projects/salesforce-cli/.ai-dev/tech_stack.md +0 -31
  451. package/test-projects/salesforce-cli/.ai-dev/tools.json +0 -10
  452. package/test-projects/salesforce-cli/.forceignore +0 -27
  453. package/test-projects/salesforce-cli/force-app/main/default/classes/AccountController.cls +0 -24
  454. package/test-projects/salesforce-cli/force-app/main/default/classes/OpportunityController.cls +0 -25
  455. package/test-projects/salesforce-cli/force-app/main/default/objects/Project__c.object.xml +0 -45
  456. package/test-projects/salesforce-cli/force-app/main/default/triggers/AccountTrigger.trigger +0 -33
  457. package/test-projects/salesforce-cli/sfdx-project.json +0 -11
  458. package/test-projects/spring-boot-app/.ai-dev/ai_context.md +0 -91
  459. package/test-projects/spring-boot-app/.ai-dev/ai_rules.md +0 -48
  460. package/test-projects/spring-boot-app/.ai-dev/architecture.md +0 -39
  461. package/test-projects/spring-boot-app/.ai-dev/cache.json +0 -173
  462. package/test-projects/spring-boot-app/.ai-dev/context/features/src.json +0 -26
  463. package/test-projects/spring-boot-app/.ai-dev/context/flows/PostController.json +0 -19
  464. package/test-projects/spring-boot-app/.ai-dev/context/flows/UserController.json +0 -19
  465. package/test-projects/spring-boot-app/.ai-dev/context/flows/comment.json +0 -11
  466. package/test-projects/spring-boot-app/.ai-dev/context/flows/post.json +0 -14
  467. package/test-projects/spring-boot-app/.ai-dev/context/flows/user.json +0 -14
  468. package/test-projects/spring-boot-app/.ai-dev/conventions.md +0 -52
  469. package/test-projects/spring-boot-app/.ai-dev/dependencies.json +0 -326
  470. package/test-projects/spring-boot-app/.ai-dev/entrypoints.md +0 -4
  471. package/test-projects/spring-boot-app/.ai-dev/files.json +0 -214
  472. package/test-projects/spring-boot-app/.ai-dev/graph/knowledge-graph.json +0 -231
  473. package/test-projects/spring-boot-app/.ai-dev/graph/module-graph.json +0 -22
  474. package/test-projects/spring-boot-app/.ai-dev/graph/symbol-graph.json +0 -794
  475. package/test-projects/spring-boot-app/.ai-dev/graph/symbol-references.json +0 -70
  476. package/test-projects/spring-boot-app/.ai-dev/index-state.json +0 -301
  477. package/test-projects/spring-boot-app/.ai-dev/modules.json +0 -21
  478. package/test-projects/spring-boot-app/.ai-dev/project.json +0 -17
  479. package/test-projects/spring-boot-app/.ai-dev/repo_map.json +0 -461
  480. package/test-projects/spring-boot-app/.ai-dev/repo_map.md +0 -109
  481. package/test-projects/spring-boot-app/.ai-dev/schema.json +0 -5
  482. package/test-projects/spring-boot-app/.ai-dev/summary.md +0 -12
  483. package/test-projects/spring-boot-app/.ai-dev/symbols.json +0 -1
  484. package/test-projects/spring-boot-app/.ai-dev/tech_stack.md +0 -32
  485. package/test-projects/spring-boot-app/.ai-dev/tools.json +0 -10
  486. package/test-projects/spring-boot-app/.classpath +0 -57
  487. package/test-projects/spring-boot-app/.factorypath +0 -69
  488. package/test-projects/spring-boot-app/.project +0 -34
  489. package/test-projects/spring-boot-app/.settings/org.eclipse.core.resources.prefs +0 -4
  490. package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.apt.core.prefs +0 -4
  491. package/test-projects/spring-boot-app/.settings/org.eclipse.jdt.core.prefs +0 -10
  492. package/test-projects/spring-boot-app/.settings/org.eclipse.m2e.core.prefs +0 -4
  493. package/test-projects/spring-boot-app/README.md +0 -122
  494. package/test-projects/spring-boot-app/pom.xml +0 -79
  495. package/test-projects/spring-boot-app/src/main/java/com/example/demo/DemoApplication.java +0 -12
  496. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/CommentController.java +0 -89
  497. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/PostController.java +0 -92
  498. package/test-projects/spring-boot-app/src/main/java/com/example/demo/controllers/UserController.java +0 -84
  499. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Comment.java +0 -38
  500. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/Post.java +0 -56
  501. package/test-projects/spring-boot-app/src/main/java/com/example/demo/models/User.java +0 -44
  502. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/CommentRepository.java +0 -21
  503. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/PostRepository.java +0 -18
  504. package/test-projects/spring-boot-app/src/main/java/com/example/demo/repositories/UserRepository.java +0 -15
  505. package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/PostService.java +0 -83
  506. package/test-projects/spring-boot-app/src/main/java/com/example/demo/services/UserService.java +0 -62
  507. package/test-projects/spring-boot-app/src/main/resources/application.properties +0 -22
  508. package/test-projects/spring-boot-app/target/classes/com/example/demo/DemoApplication.class +0 -0
  509. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentCreateRequest.class +0 -0
  510. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController$CommentUpdateRequest.class +0 -0
  511. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/CommentController.class +0 -0
  512. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostCreateRequest.class +0 -0
  513. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController$PostUpdateRequest.class +0 -0
  514. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/PostController.class +0 -0
  515. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserCreateRequest.class +0 -0
  516. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController$UserUpdateRequest.class +0 -0
  517. package/test-projects/spring-boot-app/target/classes/com/example/demo/controllers/UserController.class +0 -0
  518. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Comment.class +0 -0
  519. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/Post.class +0 -0
  520. package/test-projects/spring-boot-app/target/classes/com/example/demo/models/User.class +0 -0
  521. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/CommentRepository.class +0 -0
  522. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/PostRepository.class +0 -0
  523. package/test-projects/spring-boot-app/target/classes/com/example/demo/repositories/UserRepository.class +0 -0
  524. package/test-projects/spring-boot-app/target/classes/com/example/demo/services/PostService.class +0 -0
  525. package/test-projects/spring-boot-app/target/classes/com/example/demo/services/UserService.class +0 -0
  526. package/tests/e2e/run-e2e.sh +0 -88
  527. /package/{test-projects/django-app/.ai-dev → ai-context}/tools.json +0 -0
@@ -0,0 +1,871 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { extractSymbols } from "../src/analyzers/symbols";
3
+ import { FileInfo } from "../src/core/repoScanner";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import os from "os";
7
+
8
+ const SALESFORCE_ENTERPRISE_PATH = path.join(process.cwd(), "test-projects/salesforce-enterprise");
9
+
10
+ describe("Salesforce Apex Triggers Detection", () => {
11
+ let tempDir: string;
12
+
13
+ beforeAll(() => {
14
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "apex-triggers-test-"));
15
+ });
16
+
17
+ afterAll(() => {
18
+ fs.rmSync(tempDir, { recursive: true, force: true });
19
+ });
20
+
21
+ const createFileInfo = (filePath: string, extension: string, content: string): FileInfo => {
22
+ const fullPath = path.join(tempDir, filePath);
23
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
24
+ fs.writeFileSync(fullPath, content);
25
+
26
+ return {
27
+ path: fullPath,
28
+ relativePath: filePath,
29
+ extension,
30
+ name: filePath.split("/").pop()?.replace(`.${extension}`, "") || "",
31
+ };
32
+ };
33
+
34
+ // =========================================================================
35
+ // INTEGRATION TESTS - Using real Salesforce Enterprise test project
36
+ // =========================================================================
37
+
38
+ describe("Integration: Real Salesforce Enterprise Project", () => {
39
+ it("should detect trigger files in the enterprise project", () => {
40
+ const triggersDir = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers");
41
+ const files = fs.readdirSync(triggersDir).filter(f => f.endsWith(".trigger"));
42
+
43
+ expect(files).toHaveLength(1);
44
+ expect(files).toContain("OpportunityTrigger.trigger");
45
+ });
46
+
47
+ it("should extract OpportunityTrigger symbol from real file", () => {
48
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
49
+ const content = fs.readFileSync(triggerPath, "utf-8");
50
+
51
+ const file: FileInfo = {
52
+ path: triggerPath,
53
+ relativePath: "force-app/main/default/triggers/OpportunityTrigger.trigger",
54
+ extension: "trigger",
55
+ name: "OpportunityTrigger",
56
+ };
57
+
58
+ const result = extractSymbols([file]);
59
+ const triggerSymbol = result.symbols.find(s => s.name === "OpportunityTrigger");
60
+
61
+ expect(triggerSymbol).toBeDefined();
62
+ expect(triggerSymbol?.type).toBe("function");
63
+ expect(triggerSymbol?.export).toBe(true);
64
+ });
65
+ });
66
+
67
+ // =========================================================================
68
+ // OpportunityTrigger - Main trigger under test
69
+ // =========================================================================
70
+
71
+ describe("OpportunityTrigger", () => {
72
+ it("should have trigger name 'OpportunityTrigger'", () => {
73
+ const content = fs.readFileSync(
74
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
75
+ "utf-8"
76
+ );
77
+
78
+ const file = createFileInfo("force-app/main/default/triggers/OpportunityTrigger.trigger", "trigger", content);
79
+ const result = extractSymbols([file]);
80
+
81
+ const triggerSymbol = result.symbols.find(s => s.name === "OpportunityTrigger");
82
+ expect(triggerSymbol).toBeDefined();
83
+ expect(triggerSymbol?.name).toBe("OpportunityTrigger");
84
+ });
85
+
86
+ it("should fire on 'Opportunity' object", () => {
87
+ const content = fs.readFileSync(
88
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
89
+ "utf-8"
90
+ );
91
+
92
+ expect(content).toContain("trigger OpportunityTrigger on Opportunity");
93
+ });
94
+
95
+ it("should detect all 7 trigger events", () => {
96
+ const content = fs.readFileSync(
97
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
98
+ "utf-8"
99
+ );
100
+
101
+ // All 7 events: before insert, before update, before delete, after insert, after update, after delete, after undelete
102
+ expect(content).toContain("before insert");
103
+ expect(content).toContain("before update");
104
+ expect(content).toContain("before delete");
105
+ expect(content).toContain("after insert");
106
+ expect(content).toContain("after update");
107
+ expect(content).toContain("after delete");
108
+ expect(content).toContain("after undelete");
109
+ });
110
+
111
+ it("should have all 7 events in single trigger definition", () => {
112
+ const content = fs.readFileSync(
113
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
114
+ "utf-8"
115
+ );
116
+
117
+ // The trigger definition should contain all events
118
+ const triggerDefinitionMatch = content.match(/trigger\s+\w+\s+on\s+\w+\s*\(([^)]+)\)/);
119
+ expect(triggerDefinitionMatch).toBeDefined();
120
+
121
+ const events = triggerDefinitionMatch?.[1] || "";
122
+ expect(events).toContain("before insert");
123
+ expect(events).toContain("before update");
124
+ expect(events).toContain("before delete");
125
+ expect(events).toContain("after insert");
126
+ expect(events).toContain("after update");
127
+ expect(events).toContain("after delete");
128
+ expect(events).toContain("after undelete");
129
+ });
130
+
131
+ it("should use handler pattern with OpportunityTriggerHandler", () => {
132
+ const content = fs.readFileSync(
133
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
134
+ "utf-8"
135
+ );
136
+
137
+ expect(content).toContain("OpportunityTriggerHandler");
138
+ expect(content).toContain("new OpportunityTriggerHandler()");
139
+ });
140
+
141
+ it("should have bypass logic with TriggerManagement.isTriggerBypassed", () => {
142
+ const content = fs.readFileSync(
143
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
144
+ "utf-8"
145
+ );
146
+
147
+ expect(content).toContain("TriggerManagement.isTriggerBypassed");
148
+ expect(content).toContain("isTriggerBypassed('Opportunity')");
149
+ });
150
+
151
+ it("should use Trigger.isBefore and Trigger.isAfter context variables", () => {
152
+ const content = fs.readFileSync(
153
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
154
+ "utf-8"
155
+ );
156
+
157
+ expect(content).toContain("Trigger.isBefore");
158
+ expect(content).toContain("Trigger.isAfter");
159
+ });
160
+
161
+ it("should use Trigger.isInsert, Trigger.isUpdate, Trigger.isDelete, Trigger.isUndelete", () => {
162
+ const content = fs.readFileSync(
163
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
164
+ "utf-8"
165
+ );
166
+
167
+ expect(content).toContain("Trigger.isInsert");
168
+ expect(content).toContain("Trigger.isUpdate");
169
+ expect(content).toContain("Trigger.isDelete");
170
+ expect(content).toContain("Trigger.isUndelete");
171
+ });
172
+
173
+ it("should use Trigger.new and Trigger.oldMap context variables", () => {
174
+ const content = fs.readFileSync(
175
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
176
+ "utf-8"
177
+ );
178
+
179
+ expect(content).toContain("Trigger.new");
180
+ expect(content).toContain("Trigger.oldMap");
181
+ });
182
+
183
+ it("should have before insert handler", () => {
184
+ const content = fs.readFileSync(
185
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
186
+ "utf-8"
187
+ );
188
+
189
+ expect(content).toContain("onBeforeInsert");
190
+ expect(content).toContain("Trigger.new");
191
+ });
192
+
193
+ it("should have before update handler", () => {
194
+ const content = fs.readFileSync(
195
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
196
+ "utf-8"
197
+ );
198
+
199
+ expect(content).toContain("onBeforeUpdate");
200
+ expect(content).toContain("Trigger.new");
201
+ expect(content).toContain("Trigger.oldMap");
202
+ });
203
+
204
+ it("should have before delete handler", () => {
205
+ const content = fs.readFileSync(
206
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
207
+ "utf-8"
208
+ );
209
+
210
+ expect(content).toContain("onBeforeDelete");
211
+ expect(content).toContain("Trigger.oldMap");
212
+ });
213
+
214
+ it("should have after insert handler", () => {
215
+ const content = fs.readFileSync(
216
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
217
+ "utf-8"
218
+ );
219
+
220
+ expect(content).toContain("onAfterInsert");
221
+ expect(content).toContain("Trigger.new");
222
+ });
223
+
224
+ it("should have after update handler", () => {
225
+ const content = fs.readFileSync(
226
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
227
+ "utf-8"
228
+ );
229
+
230
+ expect(content).toContain("onAfterUpdate");
231
+ expect(content).toContain("Trigger.new");
232
+ expect(content).toContain("Trigger.oldMap");
233
+ });
234
+
235
+ it("should have after delete handler", () => {
236
+ const content = fs.readFileSync(
237
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
238
+ "utf-8"
239
+ );
240
+
241
+ expect(content).toContain("onAfterDelete");
242
+ expect(content).toContain("Trigger.oldMap");
243
+ });
244
+
245
+ it("should have after undelete handler", () => {
246
+ const content = fs.readFileSync(
247
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
248
+ "utf-8"
249
+ );
250
+
251
+ expect(content).toContain("onAfterUndelete");
252
+ expect(content).toContain("Trigger.new");
253
+ });
254
+ });
255
+
256
+ // =========================================================================
257
+ // UNIT TESTS - Apex trigger parsing edge cases
258
+ // =========================================================================
259
+
260
+ describe("Unit: Apex trigger parsing edge cases", () => {
261
+ it("should parse trigger with single event", () => {
262
+ const content = `trigger AccountTrigger on Account (before insert) {
263
+ // handler logic
264
+ }`;
265
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
266
+ const result = extractSymbols([file]);
267
+
268
+ const triggerSymbol = result.symbols.find(s => s.name === "AccountTrigger");
269
+ expect(triggerSymbol).toBeDefined();
270
+ expect(triggerSymbol?.type).toBe("function");
271
+ expect(triggerSymbol?.export).toBe(true);
272
+ });
273
+
274
+ it("should parse trigger with multiple events on same line", () => {
275
+ const content = `trigger ContactTrigger on Contact (before insert, before update, after insert, after update) {
276
+ // handler logic
277
+ }`;
278
+ const file = createFileInfo("ContactTrigger.trigger", "trigger", content);
279
+ const result = extractSymbols([file]);
280
+
281
+ const triggerSymbol = result.symbols.find(s => s.name === "ContactTrigger");
282
+ expect(triggerSymbol).toBeDefined();
283
+ expect(triggerSymbol?.type).toBe("function");
284
+ });
285
+
286
+ it("should parse trigger with before events only", () => {
287
+ const content = `trigger CaseTrigger on Case (before insert, before update, before delete) {
288
+ // before event handlers
289
+ }`;
290
+ const file = createFileInfo("CaseTrigger.trigger", "trigger", content);
291
+ const result = extractSymbols([file]);
292
+
293
+ const triggerSymbol = result.symbols.find(s => s.name === "CaseTrigger");
294
+ expect(triggerSymbol).toBeDefined();
295
+ expect(triggerSymbol?.type).toBe("function");
296
+ });
297
+
298
+ it("should parse trigger with after events only", () => {
299
+ const content = `trigger LeadTrigger on Lead (after insert, after update, after delete, after undelete) {
300
+ // after event handlers
301
+ }`;
302
+ const file = createFileInfo("LeadTrigger.trigger", "trigger", content);
303
+ const result = extractSymbols([file]);
304
+
305
+ const triggerSymbol = result.symbols.find(s => s.name === "LeadTrigger");
306
+ expect(triggerSymbol).toBeDefined();
307
+ expect(triggerSymbol?.type).toBe("function");
308
+ });
309
+
310
+ it("should parse trigger on different SObjects (Account)", () => {
311
+ const content = `trigger AccountTrigger on Account (before insert, before update) {
312
+ for(Account a : Trigger.new) {
313
+ // process
314
+ }
315
+ }`;
316
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
317
+ const result = extractSymbols([file]);
318
+
319
+ const triggerSymbol = result.symbols.find(s => s.name === "AccountTrigger");
320
+ expect(triggerSymbol).toBeDefined();
321
+
322
+ const content2 = fs.readFileSync(file.path, "utf-8");
323
+ expect(content2).toContain("on Account");
324
+ });
325
+
326
+ it("should parse trigger on different SObjects (Contact)", () => {
327
+ const content = `trigger ContactTrigger on Contact (before insert, before update) {
328
+ for(Contact c : Trigger.new) {
329
+ // process
330
+ }
331
+ }`;
332
+ const file = createFileInfo("ContactTrigger.trigger", "trigger", content);
333
+ const result = extractSymbols([file]);
334
+
335
+ const triggerSymbol = result.symbols.find(s => s.name === "ContactTrigger");
336
+ expect(triggerSymbol).toBeDefined();
337
+
338
+ const content2 = fs.readFileSync(file.path, "utf-8");
339
+ expect(content2).toContain("on Contact");
340
+ });
341
+
342
+ it("should parse trigger on custom object", () => {
343
+ const content = `trigger CustomObjectTrigger on CustomObject__c (before insert) {
344
+ // custom object trigger
345
+ }`;
346
+ const file = createFileInfo("CustomObjectTrigger.trigger", "trigger", content);
347
+ const result = extractSymbols([file]);
348
+
349
+ const triggerSymbol = result.symbols.find(s => s.name === "CustomObjectTrigger");
350
+ expect(triggerSymbol).toBeDefined();
351
+ });
352
+
353
+ it("should detect trigger with handler instantiation", () => {
354
+ const content = `trigger OrderTrigger on Order (before insert) {
355
+ OrderTriggerHandler handler = new OrderTriggerHandler();
356
+ if (Trigger.isBefore) {
357
+ handler.onBeforeInsert(Trigger.new);
358
+ }
359
+ }`;
360
+ const file = createFileInfo("OrderTrigger.trigger", "trigger", content);
361
+ const result = extractSymbols([file]);
362
+
363
+ const triggerSymbol = result.symbols.find(s => s.name === "OrderTrigger");
364
+ expect(triggerSymbol).toBeDefined();
365
+
366
+ const fileContent = fs.readFileSync(file.path, "utf-8");
367
+ expect(fileContent).toContain("new OrderTriggerHandler()");
368
+ });
369
+
370
+ it("should detect trigger with bypass check at start", () => {
371
+ const content = `trigger ProductTrigger on Product2 (before insert) {
372
+ if (TriggerManagement.isTriggerBypassed('Product2')) {
373
+ return;
374
+ }
375
+ // trigger logic
376
+ }`;
377
+ const file = createFileInfo("ProductTrigger.trigger", "trigger", content);
378
+ const result = extractSymbols([file]);
379
+
380
+ const triggerSymbol = result.symbols.find(s => s.name === "ProductTrigger");
381
+ expect(triggerSymbol).toBeDefined();
382
+
383
+ const fileContent = fs.readFileSync(file.path, "utf-8");
384
+ expect(fileContent).toContain("isTriggerBypassed");
385
+ });
386
+
387
+ it("should parse trigger with Trigger.new", () => {
388
+ const content = `trigger AssetTrigger on Asset (before insert, before update) {
389
+ for(Asset a : Trigger.new) {
390
+ a.Description = 'Updated';
391
+ }
392
+ }`;
393
+ const file = createFileInfo("AssetTrigger.trigger", "trigger", content);
394
+ const result = extractSymbols([file]);
395
+
396
+ expect(result.symbols.find(s => s.name === "AssetTrigger")).toBeDefined();
397
+ });
398
+
399
+ it("should parse trigger with Trigger.oldMap", () => {
400
+ const content = `trigger CampaignTrigger on Campaign (before delete) {
401
+ Map<Id, Campaign> oldMap = Trigger.oldMap;
402
+ // process deletions
403
+ }`;
404
+ const file = createFileInfo("CampaignTrigger.trigger", "trigger", content);
405
+ const result = extractSymbols([file]);
406
+
407
+ expect(result.symbols.find(s => s.name === "CampaignTrigger")).toBeDefined();
408
+ });
409
+
410
+ it("should detect trigger as entrypoint type", () => {
411
+ const content = fs.readFileSync(
412
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
413
+ "utf-8"
414
+ );
415
+
416
+ // Trigger files are entrypoints in Salesforce
417
+ expect(content).toContain("trigger");
418
+ expect(content).toContain("Trigger.isBefore");
419
+ expect(content).toContain("Trigger.isAfter");
420
+ });
421
+ });
422
+
423
+ // =========================================================================
424
+ // TRIGGER EVENTS VERIFICATION
425
+ // =========================================================================
426
+
427
+ describe("Trigger Events Verification", () => {
428
+ it("should correctly identify before insert event", () => {
429
+ const content = `trigger AccountTrigger on Account (before insert) {
430
+ if (Trigger.isBefore && Trigger.isInsert) {
431
+ // before insert logic
432
+ }
433
+ }`;
434
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
435
+ const fileContent = fs.readFileSync(file.path, "utf-8");
436
+
437
+ expect(fileContent).toContain("before insert");
438
+ expect(fileContent).toContain("Trigger.isBefore");
439
+ expect(fileContent).toContain("Trigger.isInsert");
440
+ });
441
+
442
+ it("should correctly identify before update event", () => {
443
+ const content = `trigger AccountTrigger on Account (before update) {
444
+ if (Trigger.isBefore && Trigger.isUpdate) {
445
+ // before update logic
446
+ }
447
+ }`;
448
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
449
+ const fileContent = fs.readFileSync(file.path, "utf-8");
450
+
451
+ expect(fileContent).toContain("before update");
452
+ expect(fileContent).toContain("Trigger.isBefore");
453
+ expect(fileContent).toContain("Trigger.isUpdate");
454
+ });
455
+
456
+ it("should correctly identify before delete event", () => {
457
+ const content = `trigger AccountTrigger on Account (before delete) {
458
+ if (Trigger.isBefore && Trigger.isDelete) {
459
+ // before delete logic
460
+ }
461
+ }`;
462
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
463
+ const fileContent = fs.readFileSync(file.path, "utf-8");
464
+
465
+ expect(fileContent).toContain("before delete");
466
+ expect(fileContent).toContain("Trigger.isBefore");
467
+ expect(fileContent).toContain("Trigger.isDelete");
468
+ });
469
+
470
+ it("should correctly identify after insert event", () => {
471
+ const content = `trigger AccountTrigger on Account (after insert) {
472
+ if (Trigger.isAfter && Trigger.isInsert) {
473
+ // after insert logic
474
+ }
475
+ }`;
476
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
477
+ const fileContent = fs.readFileSync(file.path, "utf-8");
478
+
479
+ expect(fileContent).toContain("after insert");
480
+ expect(fileContent).toContain("Trigger.isAfter");
481
+ expect(fileContent).toContain("Trigger.isInsert");
482
+ });
483
+
484
+ it("should correctly identify after update event", () => {
485
+ const content = `trigger AccountTrigger on Account (after update) {
486
+ if (Trigger.isAfter && Trigger.isUpdate) {
487
+ // after update logic
488
+ }
489
+ }`;
490
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
491
+ const fileContent = fs.readFileSync(file.path, "utf-8");
492
+
493
+ expect(fileContent).toContain("after update");
494
+ expect(fileContent).toContain("Trigger.isAfter");
495
+ expect(fileContent).toContain("Trigger.isUpdate");
496
+ });
497
+
498
+ it("should correctly identify after delete event", () => {
499
+ const content = `trigger AccountTrigger on Account (after delete) {
500
+ if (Trigger.isAfter && Trigger.isDelete) {
501
+ // after delete logic
502
+ }
503
+ }`;
504
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
505
+ const fileContent = fs.readFileSync(file.path, "utf-8");
506
+
507
+ expect(fileContent).toContain("after delete");
508
+ expect(fileContent).toContain("Trigger.isAfter");
509
+ expect(fileContent).toContain("Trigger.isDelete");
510
+ });
511
+
512
+ it("should correctly identify after undelete event", () => {
513
+ const content = `trigger AccountTrigger on Account (after undelete) {
514
+ if (Trigger.isAfter && Trigger.isUndelete) {
515
+ // after undelete logic
516
+ }
517
+ }`;
518
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
519
+ const fileContent = fs.readFileSync(file.path, "utf-8");
520
+
521
+ expect(fileContent).toContain("after undelete");
522
+ expect(fileContent).toContain("Trigger.isAfter");
523
+ expect(fileContent).toContain("Trigger.isUndelete");
524
+ });
525
+ });
526
+
527
+ // =========================================================================
528
+ // TRIGGER CONTEXT VARIABLES VERIFICATION
529
+ // =========================================================================
530
+
531
+ describe("Trigger Context Variables Verification", () => {
532
+ it("should verify Trigger.new is used correctly", () => {
533
+ const content = fs.readFileSync(
534
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
535
+ "utf-8"
536
+ );
537
+
538
+ expect(content).toMatch(/Trigger\.new/);
539
+ });
540
+
541
+ it("should verify Trigger.old is used correctly", () => {
542
+ const content = `trigger AccountTrigger on Account (before update) {
543
+ for(Account oldAcc : Trigger.old) {
544
+ // use old values
545
+ }
546
+ }`;
547
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
548
+ const fileContent = fs.readFileSync(file.path, "utf-8");
549
+
550
+ expect(fileContent).toMatch(/Trigger\.old/);
551
+ });
552
+
553
+ it("should verify Trigger.oldMap is used correctly", () => {
554
+ const content = fs.readFileSync(
555
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
556
+ "utf-8"
557
+ );
558
+
559
+ expect(content).toMatch(/Trigger\.oldMap/);
560
+ });
561
+
562
+ it("should verify Trigger.newMap is used correctly", () => {
563
+ const content = `trigger AccountTrigger on Account (after update) {
564
+ Map<Id, Account> newMap = Trigger.newMap;
565
+ // process with new map
566
+ }`;
567
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
568
+ const fileContent = fs.readFileSync(file.path, "utf-8");
569
+
570
+ expect(fileContent).toMatch(/Trigger\.newMap/);
571
+ });
572
+
573
+ it("should verify Trigger.isExecuting is used", () => {
574
+ const content = `trigger AccountTrigger on Account (before insert) {
575
+ if (Trigger.isExecuting) {
576
+ // running in trigger context
577
+ }
578
+ }`;
579
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
580
+ const fileContent = fs.readFileSync(file.path, "utf-8");
581
+
582
+ expect(fileContent).toMatch(/Trigger\.isExecuting/);
583
+ });
584
+
585
+ it("should verify Trigger.operationType is used", () => {
586
+ const content = `trigger AccountTrigger on Account (before insert) {
587
+ System.Debug('Operation: ' + Trigger.operationType);
588
+ }`;
589
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
590
+ const fileContent = fs.readFileSync(file.path, "utf-8");
591
+
592
+ expect(fileContent).toMatch(/Trigger\.operationType/);
593
+ });
594
+ });
595
+
596
+ // =========================================================================
597
+ // TRIGGER HANDLER PATTERN VERIFICATION
598
+ // =========================================================================
599
+
600
+ describe("Trigger Handler Pattern Verification", () => {
601
+ it("should have trigger handler class for OpportunityTrigger", () => {
602
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
603
+ expect(fs.existsSync(handlerPath)).toBe(true);
604
+
605
+ const content = fs.readFileSync(handlerPath, "utf-8");
606
+ expect(content).toContain("class OpportunityTriggerHandler");
607
+ });
608
+
609
+ it("should verify handler has onBeforeInsert method", () => {
610
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
611
+ const content = fs.readFileSync(handlerPath, "utf-8");
612
+
613
+ expect(content).toContain("public void onBeforeInsert");
614
+ expect(content).toContain("onBeforeInsert");
615
+ });
616
+
617
+ it("should verify handler has onBeforeUpdate method", () => {
618
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
619
+ const content = fs.readFileSync(handlerPath, "utf-8");
620
+
621
+ expect(content).toContain("public void onBeforeUpdate");
622
+ expect(content).toContain("onBeforeUpdate");
623
+ });
624
+
625
+ it("should verify handler has onBeforeDelete method", () => {
626
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
627
+ const content = fs.readFileSync(handlerPath, "utf-8");
628
+
629
+ expect(content).toContain("public void onBeforeDelete");
630
+ expect(content).toContain("onBeforeDelete");
631
+ });
632
+
633
+ it("should verify handler has onAfterInsert method", () => {
634
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
635
+ const content = fs.readFileSync(handlerPath, "utf-8");
636
+
637
+ expect(content).toContain("public void onAfterInsert");
638
+ expect(content).toContain("onAfterInsert");
639
+ });
640
+
641
+ it("should verify handler has onAfterUpdate method", () => {
642
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
643
+ const content = fs.readFileSync(handlerPath, "utf-8");
644
+
645
+ expect(content).toContain("public void onAfterUpdate");
646
+ expect(content).toContain("onAfterUpdate");
647
+ });
648
+
649
+ it("should verify handler has onAfterDelete method", () => {
650
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
651
+ const content = fs.readFileSync(handlerPath, "utf-8");
652
+
653
+ expect(content).toContain("public void onAfterDelete");
654
+ expect(content).toContain("onAfterDelete");
655
+ });
656
+
657
+ it("should verify handler has onAfterUndelete method", () => {
658
+ const handlerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/OpportunityTriggerHandler.cls");
659
+ const content = fs.readFileSync(handlerPath, "utf-8");
660
+
661
+ expect(content).toContain("public void onAfterUndelete");
662
+ expect(content).toContain("onAfterUndelete");
663
+ });
664
+
665
+ it("should use handler pattern in trigger", () => {
666
+ const content = fs.readFileSync(
667
+ path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger"),
668
+ "utf-8"
669
+ );
670
+
671
+ // Verify handler instantiation and usage pattern
672
+ expect(content).toContain("OpportunityTriggerHandler handler = new OpportunityTriggerHandler()");
673
+ });
674
+ });
675
+
676
+ // =========================================================================
677
+ // TRIGGER BYPASS MANAGEMENT VERIFICATION
678
+ // =========================================================================
679
+
680
+ describe("Trigger Bypass Management Verification", () => {
681
+ it("should have TriggerManagement class", () => {
682
+ const managementPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/TriggerManagement.cls");
683
+ expect(fs.existsSync(managementPath)).toBe(true);
684
+
685
+ const content = fs.readFileSync(managementPath, "utf-8");
686
+ expect(content).toContain("class TriggerManagement");
687
+ });
688
+
689
+ it("should have isTriggerBypassed method", () => {
690
+ const managementPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/classes/TriggerManagement.cls");
691
+ const content = fs.readFileSync(managementPath, "utf-8");
692
+
693
+ expect(content).toContain("isTriggerBypassed");
694
+ });
695
+
696
+ it("should use isTriggerBypassed in trigger", () => {
697
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
698
+ const content = fs.readFileSync(triggerPath, "utf-8");
699
+
700
+ expect(content).toContain("TriggerManagement.isTriggerBypassed('Opportunity')");
701
+ });
702
+
703
+ it("should return early when trigger is bypassed", () => {
704
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
705
+ const content = fs.readFileSync(triggerPath, "utf-8");
706
+
707
+ expect(content).toContain("return;");
708
+ });
709
+ });
710
+
711
+ // =========================================================================
712
+ // SYMBOL EXTRACTION ACCURACY
713
+ // =========================================================================
714
+
715
+ describe("Symbol Extraction Accuracy", () => {
716
+ it("should extract OpportunityTrigger as function symbol", () => {
717
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
718
+ const content = fs.readFileSync(triggerPath, "utf-8");
719
+
720
+ const file: FileInfo = {
721
+ path: triggerPath,
722
+ relativePath: "force-app/main/default/triggers/OpportunityTrigger.trigger",
723
+ extension: "trigger",
724
+ name: "OpportunityTrigger",
725
+ };
726
+
727
+ const result = extractSymbols([file]);
728
+ const triggerSymbols = result.symbols.filter(s => s.type === "function");
729
+
730
+ expect(triggerSymbols.length).toBeGreaterThanOrEqual(1);
731
+ expect(triggerSymbols.some(s => s.name === "OpportunityTrigger")).toBe(true);
732
+ });
733
+
734
+ it("should have correct line number for OpportunityTrigger", () => {
735
+ const triggerPath = path.join(SALESFORCE_ENTERPRISE_PATH, "force-app/main/default/triggers/OpportunityTrigger.trigger");
736
+ const content = fs.readFileSync(triggerPath, "utf-8");
737
+
738
+ const file: FileInfo = {
739
+ path: triggerPath,
740
+ relativePath: "force-app/main/default/triggers/OpportunityTrigger.trigger",
741
+ extension: "trigger",
742
+ name: "OpportunityTrigger",
743
+ };
744
+
745
+ const result = extractSymbols([file]);
746
+ const triggerSymbol = result.symbols.find(s => s.name === "OpportunityTrigger");
747
+
748
+ expect(triggerSymbol).toBeDefined();
749
+ expect(triggerSymbol?.line).toBe(7); // trigger definition starts at line 7
750
+ });
751
+
752
+ it("should extract all trigger symbols from multiple trigger files", () => {
753
+ const trigger1 = `trigger AccountTrigger on Account (before insert) {}`;
754
+ const trigger2 = `trigger ContactTrigger on Contact (before insert) {}`;
755
+ const trigger3 = `trigger LeadTrigger on Lead (before insert) {}`;
756
+
757
+ const files = [
758
+ createFileInfo("AccountTrigger.trigger", "trigger", trigger1),
759
+ createFileInfo("ContactTrigger.trigger", "trigger", trigger2),
760
+ createFileInfo("LeadTrigger.trigger", "trigger", trigger3),
761
+ ];
762
+
763
+ const result = extractSymbols(files);
764
+ const triggerSymbols = result.symbols.filter(s => s.type === "function");
765
+
766
+ expect(triggerSymbols.length).toBe(3);
767
+ expect(triggerSymbols.some(s => s.name === "AccountTrigger")).toBe(true);
768
+ expect(triggerSymbols.some(s => s.name === "ContactTrigger")).toBe(true);
769
+ expect(triggerSymbols.some(s => s.name === "LeadTrigger")).toBe(true);
770
+ });
771
+ });
772
+
773
+ // =========================================================================
774
+ // EDGE CASES
775
+ // =========================================================================
776
+
777
+ describe("Edge Cases", () => {
778
+ it("should handle trigger without comments", () => {
779
+ const content = `trigger SimpleTrigger on Account (before insert) {
780
+ // no comments
781
+ }`;
782
+ const file = createFileInfo("SimpleTrigger.trigger", "trigger", content);
783
+ const result = extractSymbols([file]);
784
+
785
+ expect(result.symbols.find(s => s.name === "SimpleTrigger")).toBeDefined();
786
+ });
787
+
788
+ it("should handle trigger with multi-line events definition", () => {
789
+ const content = `trigger AccountTrigger on Account (
790
+ before insert,
791
+ before update,
792
+ before delete,
793
+ after insert,
794
+ after update,
795
+ after delete,
796
+ after undelete
797
+ ) {
798
+ // all events
799
+ }`;
800
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
801
+ const result = extractSymbols([file]);
802
+
803
+ expect(result.symbols.find(s => s.name === "AccountTrigger")).toBeDefined();
804
+ });
805
+
806
+ it("should handle trigger on object with namespace prefix", () => {
807
+ const content = `trigger CustomTrigger on MyNamespace__CustomObject__c (before insert) {
808
+ // namespaced object
809
+ }`;
810
+ const file = createFileInfo("CustomTrigger.trigger", "trigger", content);
811
+ const result = extractSymbols([file]);
812
+
813
+ expect(result.symbols.find(s => s.name === "CustomTrigger")).toBeDefined();
814
+ });
815
+
816
+ it("should handle trigger with no explicit handler (inline logic)", () => {
817
+ const content = `trigger InlineTrigger on Account (before insert) {
818
+ for(Account a : Trigger.new) {
819
+ a.Description = 'Auto-generated';
820
+ }
821
+ }`;
822
+ const file = createFileInfo("InlineTrigger.trigger", "trigger", content);
823
+ const result = extractSymbols([file]);
824
+
825
+ expect(result.symbols.find(s => s.name === "InlineTrigger")).toBeDefined();
826
+ });
827
+
828
+ it("should handle trigger with try-catch block", () => {
829
+ const content = `trigger SafeTrigger on Account (before insert) {
830
+ try {
831
+ // might fail
832
+ } catch (Exception e) {
833
+ System.debug(e.getMessage());
834
+ }
835
+ }`;
836
+ const file = createFileInfo("SafeTrigger.trigger", "trigger", content);
837
+ const result = extractSymbols([file]);
838
+
839
+ expect(result.symbols.find(s => s.name === "SafeTrigger")).toBeDefined();
840
+ });
841
+
842
+ it("should handle trigger with SOQL query", () => {
843
+ const content = `trigger AccountTrigger on Account (before insert) {
844
+ List<Account> related = [SELECT Id FROM Account WHERE ParentId = null LIMIT 1];
845
+ }`;
846
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
847
+ const result = extractSymbols([file]);
848
+
849
+ expect(result.symbols.find(s => s.name === "AccountTrigger")).toBeDefined();
850
+ });
851
+
852
+ it("should handle trigger with DML statement", () => {
853
+ const content = `trigger AccountTrigger on Account (before insert) {
854
+ insert new Account(Name = 'Test');
855
+ }`;
856
+ const file = createFileInfo("AccountTrigger.trigger", "trigger", content);
857
+ const result = extractSymbols([file]);
858
+
859
+ expect(result.symbols.find(s => s.name === "AccountTrigger")).toBeDefined();
860
+ });
861
+
862
+ it("should handle empty trigger body", () => {
863
+ const content = `trigger EmptyTrigger on Account (before insert) {
864
+ }`;
865
+ const file = createFileInfo("EmptyTrigger.trigger", "trigger", content);
866
+ const result = extractSymbols([file]);
867
+
868
+ expect(result.symbols.find(s => s.name === "EmptyTrigger")).toBeDefined();
869
+ });
870
+ });
871
+ });