vaspera 2.9.2 → 2.10.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 (327) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +58 -1
  3. package/dist/__tests__/autofix/branch-manager.test.d.ts +2 -0
  4. package/dist/__tests__/autofix/branch-manager.test.d.ts.map +1 -0
  5. package/dist/__tests__/autofix/branch-manager.test.js +60 -0
  6. package/dist/__tests__/autofix/branch-manager.test.js.map +1 -0
  7. package/dist/__tests__/autofix/commit-generator.test.d.ts +2 -0
  8. package/dist/__tests__/autofix/commit-generator.test.d.ts.map +1 -0
  9. package/dist/__tests__/autofix/commit-generator.test.js +147 -0
  10. package/dist/__tests__/autofix/commit-generator.test.js.map +1 -0
  11. package/dist/__tests__/autofix/constitution.test.d.ts +9 -0
  12. package/dist/__tests__/autofix/constitution.test.d.ts.map +1 -0
  13. package/dist/__tests__/autofix/constitution.test.js +421 -0
  14. package/dist/__tests__/autofix/constitution.test.js.map +1 -0
  15. package/dist/__tests__/autofix/pr-generator.test.d.ts +2 -0
  16. package/dist/__tests__/autofix/pr-generator.test.d.ts.map +1 -0
  17. package/dist/__tests__/autofix/pr-generator.test.js +152 -0
  18. package/dist/__tests__/autofix/pr-generator.test.js.map +1 -0
  19. package/dist/__tests__/property-test-helpers.d.ts +87 -0
  20. package/dist/__tests__/property-test-helpers.d.ts.map +1 -0
  21. package/dist/__tests__/property-test-helpers.js +136 -0
  22. package/dist/__tests__/property-test-helpers.js.map +1 -0
  23. package/dist/__tests__/scanners/ai-code/ai-detector.test.d.ts +2 -0
  24. package/dist/__tests__/scanners/ai-code/ai-detector.test.d.ts.map +1 -0
  25. package/dist/__tests__/scanners/ai-code/ai-detector.test.js +188 -0
  26. package/dist/__tests__/scanners/ai-code/ai-detector.test.js.map +1 -0
  27. package/dist/__tests__/scanners/ai-code/confidence-scorer.test.d.ts +2 -0
  28. package/dist/__tests__/scanners/ai-code/confidence-scorer.test.d.ts.map +1 -0
  29. package/dist/__tests__/scanners/ai-code/confidence-scorer.test.js +363 -0
  30. package/dist/__tests__/scanners/ai-code/confidence-scorer.test.js.map +1 -0
  31. package/dist/__tests__/scanners/ai-code/hallucination-checker.test.d.ts +2 -0
  32. package/dist/__tests__/scanners/ai-code/hallucination-checker.test.d.ts.map +1 -0
  33. package/dist/__tests__/scanners/ai-code/hallucination-checker.test.js +226 -0
  34. package/dist/__tests__/scanners/ai-code/hallucination-checker.test.js.map +1 -0
  35. package/dist/__tests__/scanners/ai-code/index.test.d.ts +2 -0
  36. package/dist/__tests__/scanners/ai-code/index.test.d.ts.map +1 -0
  37. package/dist/__tests__/scanners/ai-code/index.test.js +214 -0
  38. package/dist/__tests__/scanners/ai-code/index.test.js.map +1 -0
  39. package/dist/__tests__/scanners/dast/index.test.d.ts +2 -0
  40. package/dist/__tests__/scanners/dast/index.test.d.ts.map +1 -0
  41. package/dist/__tests__/scanners/dast/index.test.js +183 -0
  42. package/dist/__tests__/scanners/dast/index.test.js.map +1 -0
  43. package/dist/__tests__/scanners/dast/nuclei.test.d.ts +2 -0
  44. package/dist/__tests__/scanners/dast/nuclei.test.d.ts.map +1 -0
  45. package/dist/__tests__/scanners/dast/nuclei.test.js +166 -0
  46. package/dist/__tests__/scanners/dast/nuclei.test.js.map +1 -0
  47. package/dist/__tests__/scanners/dast/zap.test.d.ts +2 -0
  48. package/dist/__tests__/scanners/dast/zap.test.d.ts.map +1 -0
  49. package/dist/__tests__/scanners/dast/zap.test.js +158 -0
  50. package/dist/__tests__/scanners/dast/zap.test.js.map +1 -0
  51. package/dist/__tests__/scanners/deploy/health-checker.test.d.ts +2 -0
  52. package/dist/__tests__/scanners/deploy/health-checker.test.d.ts.map +1 -0
  53. package/dist/__tests__/scanners/deploy/health-checker.test.js +67 -0
  54. package/dist/__tests__/scanners/deploy/health-checker.test.js.map +1 -0
  55. package/dist/__tests__/scanners/deploy/index.test.d.ts +2 -0
  56. package/dist/__tests__/scanners/deploy/index.test.d.ts.map +1 -0
  57. package/dist/__tests__/scanners/deploy/index.test.js +84 -0
  58. package/dist/__tests__/scanners/deploy/index.test.js.map +1 -0
  59. package/dist/__tests__/scanners/deploy/provider-detector.test.d.ts +2 -0
  60. package/dist/__tests__/scanners/deploy/provider-detector.test.d.ts.map +1 -0
  61. package/dist/__tests__/scanners/deploy/provider-detector.test.js +88 -0
  62. package/dist/__tests__/scanners/deploy/provider-detector.test.js.map +1 -0
  63. package/dist/__tests__/scanners/deploy/types.test.d.ts +2 -0
  64. package/dist/__tests__/scanners/deploy/types.test.d.ts.map +1 -0
  65. package/dist/__tests__/scanners/deploy/types.test.js +126 -0
  66. package/dist/__tests__/scanners/deploy/types.test.js.map +1 -0
  67. package/dist/__tests__/scanners/fp-feedback.test.d.ts +2 -0
  68. package/dist/__tests__/scanners/fp-feedback.test.d.ts.map +1 -0
  69. package/dist/__tests__/scanners/fp-feedback.test.js +202 -0
  70. package/dist/__tests__/scanners/fp-feedback.test.js.map +1 -0
  71. package/dist/__tests__/scanners/fp-filter.property.test.d.ts +9 -0
  72. package/dist/__tests__/scanners/fp-filter.property.test.d.ts.map +1 -0
  73. package/dist/__tests__/scanners/fp-filter.property.test.js +253 -0
  74. package/dist/__tests__/scanners/fp-filter.property.test.js.map +1 -0
  75. package/dist/__tests__/scanners/fp-filter.test.d.ts +2 -0
  76. package/dist/__tests__/scanners/fp-filter.test.d.ts.map +1 -0
  77. package/dist/__tests__/scanners/fp-filter.test.js +234 -0
  78. package/dist/__tests__/scanners/fp-filter.test.js.map +1 -0
  79. package/dist/__tests__/scanners/fp-tracker.test.d.ts +2 -0
  80. package/dist/__tests__/scanners/fp-tracker.test.d.ts.map +1 -0
  81. package/dist/__tests__/scanners/fp-tracker.test.js +262 -0
  82. package/dist/__tests__/scanners/fp-tracker.test.js.map +1 -0
  83. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts +10 -0
  84. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.d.ts.map +1 -0
  85. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js +238 -0
  86. package/dist/__tests__/scanners/logic/endpoint-analyzer.property.test.js.map +1 -0
  87. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts +2 -0
  88. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.d.ts.map +1 -0
  89. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js +55 -0
  90. package/dist/__tests__/scanners/logic/endpoint-analyzer.test.js.map +1 -0
  91. package/dist/__tests__/scanners/logic/index.test.d.ts +2 -0
  92. package/dist/__tests__/scanners/logic/index.test.d.ts.map +1 -0
  93. package/dist/__tests__/scanners/logic/index.test.js +165 -0
  94. package/dist/__tests__/scanners/logic/index.test.js.map +1 -0
  95. package/dist/__tests__/scanners/logic/types.test.d.ts +2 -0
  96. package/dist/__tests__/scanners/logic/types.test.d.ts.map +1 -0
  97. package/dist/__tests__/scanners/logic/types.test.js +85 -0
  98. package/dist/__tests__/scanners/logic/types.test.js.map +1 -0
  99. package/dist/__tests__/scanners/runtime/app-launcher.test.d.ts +2 -0
  100. package/dist/__tests__/scanners/runtime/app-launcher.test.d.ts.map +1 -0
  101. package/dist/__tests__/scanners/runtime/app-launcher.test.js +94 -0
  102. package/dist/__tests__/scanners/runtime/app-launcher.test.js.map +1 -0
  103. package/dist/__tests__/scanners/runtime/golden-path-runner.test.d.ts +2 -0
  104. package/dist/__tests__/scanners/runtime/golden-path-runner.test.d.ts.map +1 -0
  105. package/dist/__tests__/scanners/runtime/golden-path-runner.test.js +195 -0
  106. package/dist/__tests__/scanners/runtime/golden-path-runner.test.js.map +1 -0
  107. package/dist/__tests__/scanners/runtime/index.test.d.ts +2 -0
  108. package/dist/__tests__/scanners/runtime/index.test.d.ts.map +1 -0
  109. package/dist/__tests__/scanners/runtime/index.test.js +120 -0
  110. package/dist/__tests__/scanners/runtime/index.test.js.map +1 -0
  111. package/dist/__tests__/scanners/runtime/types.test.d.ts +2 -0
  112. package/dist/__tests__/scanners/runtime/types.test.d.ts.map +1 -0
  113. package/dist/__tests__/scanners/runtime/types.test.js +126 -0
  114. package/dist/__tests__/scanners/runtime/types.test.js.map +1 -0
  115. package/dist/__tests__/scanners/scale/bottleneck-detector.test.d.ts +2 -0
  116. package/dist/__tests__/scanners/scale/bottleneck-detector.test.d.ts.map +1 -0
  117. package/dist/__tests__/scanners/scale/bottleneck-detector.test.js +187 -0
  118. package/dist/__tests__/scanners/scale/bottleneck-detector.test.js.map +1 -0
  119. package/dist/__tests__/scanners/scale/index.test.d.ts +2 -0
  120. package/dist/__tests__/scanners/scale/index.test.d.ts.map +1 -0
  121. package/dist/__tests__/scanners/scale/index.test.js +87 -0
  122. package/dist/__tests__/scanners/scale/index.test.js.map +1 -0
  123. package/dist/__tests__/scanners/scale/load-profiler.test.d.ts +2 -0
  124. package/dist/__tests__/scanners/scale/load-profiler.test.d.ts.map +1 -0
  125. package/dist/__tests__/scanners/scale/load-profiler.test.js +122 -0
  126. package/dist/__tests__/scanners/scale/load-profiler.test.js.map +1 -0
  127. package/dist/__tests__/scanners/scale/types.test.d.ts +2 -0
  128. package/dist/__tests__/scanners/scale/types.test.d.ts.map +1 -0
  129. package/dist/__tests__/scanners/scale/types.test.js +129 -0
  130. package/dist/__tests__/scanners/scale/types.test.js.map +1 -0
  131. package/dist/action/pr-comment.test.js +4 -0
  132. package/dist/action/pr-comment.test.js.map +1 -1
  133. package/dist/action/sarif-upload.test.js +4 -0
  134. package/dist/action/sarif-upload.test.js.map +1 -1
  135. package/dist/autofix/branch-manager.d.ts +115 -0
  136. package/dist/autofix/branch-manager.d.ts.map +1 -0
  137. package/dist/autofix/branch-manager.js +308 -0
  138. package/dist/autofix/branch-manager.js.map +1 -0
  139. package/dist/autofix/commit-generator.d.ts +55 -0
  140. package/dist/autofix/commit-generator.d.ts.map +1 -0
  141. package/dist/autofix/commit-generator.js +277 -0
  142. package/dist/autofix/commit-generator.js.map +1 -0
  143. package/dist/autofix/constitution.d.ts +77 -0
  144. package/dist/autofix/constitution.d.ts.map +1 -0
  145. package/dist/autofix/constitution.js +261 -0
  146. package/dist/autofix/constitution.js.map +1 -0
  147. package/dist/autofix/constitution.schema.d.ts +441 -0
  148. package/dist/autofix/constitution.schema.d.ts.map +1 -0
  149. package/dist/autofix/constitution.schema.js +144 -0
  150. package/dist/autofix/constitution.schema.js.map +1 -0
  151. package/dist/autofix/index.d.ts +13 -0
  152. package/dist/autofix/index.d.ts.map +1 -0
  153. package/dist/autofix/index.js +15 -0
  154. package/dist/autofix/index.js.map +1 -0
  155. package/dist/autofix/pr-generator.d.ts +57 -0
  156. package/dist/autofix/pr-generator.d.ts.map +1 -0
  157. package/dist/autofix/pr-generator.js +597 -0
  158. package/dist/autofix/pr-generator.js.map +1 -0
  159. package/dist/autofix/types.d.ts +151 -0
  160. package/dist/autofix/types.d.ts.map +1 -0
  161. package/dist/autofix/types.js +22 -0
  162. package/dist/autofix/types.js.map +1 -0
  163. package/dist/eval/fixtures.d.ts +20 -0
  164. package/dist/eval/fixtures.d.ts.map +1 -1
  165. package/dist/eval/fixtures.js +430 -0
  166. package/dist/eval/fixtures.js.map +1 -1
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +874 -0
  169. package/dist/index.js.map +1 -1
  170. package/dist/install-skills.d.ts +11 -0
  171. package/dist/install-skills.d.ts.map +1 -0
  172. package/dist/install-skills.js +81 -0
  173. package/dist/install-skills.js.map +1 -0
  174. package/dist/scanners/ai-code/ai-detector.d.ts +25 -0
  175. package/dist/scanners/ai-code/ai-detector.d.ts.map +1 -0
  176. package/dist/scanners/ai-code/ai-detector.js +192 -0
  177. package/dist/scanners/ai-code/ai-detector.js.map +1 -0
  178. package/dist/scanners/ai-code/confidence-scorer.d.ts +40 -0
  179. package/dist/scanners/ai-code/confidence-scorer.d.ts.map +1 -0
  180. package/dist/scanners/ai-code/confidence-scorer.js +148 -0
  181. package/dist/scanners/ai-code/confidence-scorer.js.map +1 -0
  182. package/dist/scanners/ai-code/hallucination-checker.d.ts +36 -0
  183. package/dist/scanners/ai-code/hallucination-checker.d.ts.map +1 -0
  184. package/dist/scanners/ai-code/hallucination-checker.js +298 -0
  185. package/dist/scanners/ai-code/hallucination-checker.js.map +1 -0
  186. package/dist/scanners/ai-code/index.d.ts +30 -0
  187. package/dist/scanners/ai-code/index.d.ts.map +1 -0
  188. package/dist/scanners/ai-code/index.js +224 -0
  189. package/dist/scanners/ai-code/index.js.map +1 -0
  190. package/dist/scanners/ai-code/types.d.ts +192 -0
  191. package/dist/scanners/ai-code/types.d.ts.map +1 -0
  192. package/dist/scanners/ai-code/types.js +37 -0
  193. package/dist/scanners/ai-code/types.js.map +1 -0
  194. package/dist/scanners/cache.d.ts.map +1 -1
  195. package/dist/scanners/cache.js +4 -0
  196. package/dist/scanners/cache.js.map +1 -1
  197. package/dist/scanners/dast/index.d.ts +39 -0
  198. package/dist/scanners/dast/index.d.ts.map +1 -0
  199. package/dist/scanners/dast/index.js +259 -0
  200. package/dist/scanners/dast/index.js.map +1 -0
  201. package/dist/scanners/dast/nuclei.d.ts +26 -0
  202. package/dist/scanners/dast/nuclei.d.ts.map +1 -0
  203. package/dist/scanners/dast/nuclei.js +354 -0
  204. package/dist/scanners/dast/nuclei.js.map +1 -0
  205. package/dist/scanners/dast/types.d.ts +306 -0
  206. package/dist/scanners/dast/types.d.ts.map +1 -0
  207. package/dist/scanners/dast/types.js +52 -0
  208. package/dist/scanners/dast/types.js.map +1 -0
  209. package/dist/scanners/dast/zap.d.ts +26 -0
  210. package/dist/scanners/dast/zap.d.ts.map +1 -0
  211. package/dist/scanners/dast/zap.js +453 -0
  212. package/dist/scanners/dast/zap.js.map +1 -0
  213. package/dist/scanners/deploy/health-checker.d.ts +38 -0
  214. package/dist/scanners/deploy/health-checker.d.ts.map +1 -0
  215. package/dist/scanners/deploy/health-checker.js +272 -0
  216. package/dist/scanners/deploy/health-checker.js.map +1 -0
  217. package/dist/scanners/deploy/index.d.ts +44 -0
  218. package/dist/scanners/deploy/index.d.ts.map +1 -0
  219. package/dist/scanners/deploy/index.js +208 -0
  220. package/dist/scanners/deploy/index.js.map +1 -0
  221. package/dist/scanners/deploy/provider-detector.d.ts +25 -0
  222. package/dist/scanners/deploy/provider-detector.d.ts.map +1 -0
  223. package/dist/scanners/deploy/provider-detector.js +177 -0
  224. package/dist/scanners/deploy/provider-detector.js.map +1 -0
  225. package/dist/scanners/deploy/types.d.ts +406 -0
  226. package/dist/scanners/deploy/types.d.ts.map +1 -0
  227. package/dist/scanners/deploy/types.js +58 -0
  228. package/dist/scanners/deploy/types.js.map +1 -0
  229. package/dist/scanners/deploy/vercel-integration.d.ts +52 -0
  230. package/dist/scanners/deploy/vercel-integration.d.ts.map +1 -0
  231. package/dist/scanners/deploy/vercel-integration.js +280 -0
  232. package/dist/scanners/deploy/vercel-integration.js.map +1 -0
  233. package/dist/scanners/fp-feedback.d.ts +140 -0
  234. package/dist/scanners/fp-feedback.d.ts.map +1 -0
  235. package/dist/scanners/fp-feedback.js +292 -0
  236. package/dist/scanners/fp-feedback.js.map +1 -0
  237. package/dist/scanners/fp-filter.d.ts +94 -0
  238. package/dist/scanners/fp-filter.d.ts.map +1 -0
  239. package/dist/scanners/fp-filter.js +397 -0
  240. package/dist/scanners/fp-filter.js.map +1 -0
  241. package/dist/scanners/fp-tracker.d.ts +125 -0
  242. package/dist/scanners/fp-tracker.d.ts.map +1 -0
  243. package/dist/scanners/fp-tracker.js +330 -0
  244. package/dist/scanners/fp-tracker.js.map +1 -0
  245. package/dist/scanners/index.d.ts.map +1 -1
  246. package/dist/scanners/index.js +56 -0
  247. package/dist/scanners/index.js.map +1 -1
  248. package/dist/scanners/index.test.js +6 -6
  249. package/dist/scanners/index.test.js.map +1 -1
  250. package/dist/scanners/logic/auth-flow-analyzer.d.ts +18 -0
  251. package/dist/scanners/logic/auth-flow-analyzer.d.ts.map +1 -0
  252. package/dist/scanners/logic/auth-flow-analyzer.js +384 -0
  253. package/dist/scanners/logic/auth-flow-analyzer.js.map +1 -0
  254. package/dist/scanners/logic/endpoint-analyzer.d.ts +29 -0
  255. package/dist/scanners/logic/endpoint-analyzer.d.ts.map +1 -0
  256. package/dist/scanners/logic/endpoint-analyzer.js +528 -0
  257. package/dist/scanners/logic/endpoint-analyzer.js.map +1 -0
  258. package/dist/scanners/logic/index.d.ts +41 -0
  259. package/dist/scanners/logic/index.d.ts.map +1 -0
  260. package/dist/scanners/logic/index.js +268 -0
  261. package/dist/scanners/logic/index.js.map +1 -0
  262. package/dist/scanners/logic/types.d.ts +254 -0
  263. package/dist/scanners/logic/types.d.ts.map +1 -0
  264. package/dist/scanners/logic/types.js +142 -0
  265. package/dist/scanners/logic/types.js.map +1 -0
  266. package/dist/scanners/runtime/app-launcher.d.ts +33 -0
  267. package/dist/scanners/runtime/app-launcher.d.ts.map +1 -0
  268. package/dist/scanners/runtime/app-launcher.js +419 -0
  269. package/dist/scanners/runtime/app-launcher.js.map +1 -0
  270. package/dist/scanners/runtime/golden-path-runner.d.ts +48 -0
  271. package/dist/scanners/runtime/golden-path-runner.d.ts.map +1 -0
  272. package/dist/scanners/runtime/golden-path-runner.js +373 -0
  273. package/dist/scanners/runtime/golden-path-runner.js.map +1 -0
  274. package/dist/scanners/runtime/index.d.ts +41 -0
  275. package/dist/scanners/runtime/index.d.ts.map +1 -0
  276. package/dist/scanners/runtime/index.js +164 -0
  277. package/dist/scanners/runtime/index.js.map +1 -0
  278. package/dist/scanners/runtime/playwright-executor.d.ts +50 -0
  279. package/dist/scanners/runtime/playwright-executor.d.ts.map +1 -0
  280. package/dist/scanners/runtime/playwright-executor.js +387 -0
  281. package/dist/scanners/runtime/playwright-executor.js.map +1 -0
  282. package/dist/scanners/runtime/types.d.ts +215 -0
  283. package/dist/scanners/runtime/types.d.ts.map +1 -0
  284. package/dist/scanners/runtime/types.js +40 -0
  285. package/dist/scanners/runtime/types.js.map +1 -0
  286. package/dist/scanners/scale/bottleneck-detector.d.ts +17 -0
  287. package/dist/scanners/scale/bottleneck-detector.d.ts.map +1 -0
  288. package/dist/scanners/scale/bottleneck-detector.js +250 -0
  289. package/dist/scanners/scale/bottleneck-detector.js.map +1 -0
  290. package/dist/scanners/scale/capacity-estimator.d.ts +17 -0
  291. package/dist/scanners/scale/capacity-estimator.d.ts.map +1 -0
  292. package/dist/scanners/scale/capacity-estimator.js +197 -0
  293. package/dist/scanners/scale/capacity-estimator.js.map +1 -0
  294. package/dist/scanners/scale/index.d.ts +37 -0
  295. package/dist/scanners/scale/index.d.ts.map +1 -0
  296. package/dist/scanners/scale/index.js +101 -0
  297. package/dist/scanners/scale/index.js.map +1 -0
  298. package/dist/scanners/scale/load-profiler.d.ts +48 -0
  299. package/dist/scanners/scale/load-profiler.d.ts.map +1 -0
  300. package/dist/scanners/scale/load-profiler.js +377 -0
  301. package/dist/scanners/scale/load-profiler.js.map +1 -0
  302. package/dist/scanners/scale/types.d.ts +529 -0
  303. package/dist/scanners/scale/types.d.ts.map +1 -0
  304. package/dist/scanners/scale/types.js +57 -0
  305. package/dist/scanners/scale/types.js.map +1 -0
  306. package/dist/scanners/secrets.d.ts.map +1 -1
  307. package/dist/scanners/secrets.js +13 -2
  308. package/dist/scanners/secrets.js.map +1 -1
  309. package/dist/scanners/types.d.ts +1 -1
  310. package/dist/scanners/types.d.ts.map +1 -1
  311. package/dist/scanners/types.js +4 -0
  312. package/dist/scanners/types.js.map +1 -1
  313. package/package.json +8 -4
  314. package/skills/vaspera-add-tests/SKILL.md +102 -0
  315. package/skills/vaspera-ai-verify/SKILL.md +166 -0
  316. package/skills/vaspera-audit/SKILL.md +67 -0
  317. package/skills/vaspera-certify/SKILL.md +130 -0
  318. package/skills/vaspera-deploy/SKILL.md +152 -0
  319. package/skills/vaspera-fix-critical/SKILL.md +52 -0
  320. package/skills/vaspera-fix-high/SKILL.md +81 -0
  321. package/skills/vaspera-fix-medium/SKILL.md +56 -0
  322. package/skills/vaspera-fix-rls/SKILL.md +85 -0
  323. package/skills/vaspera-harden/SKILL.md +102 -0
  324. package/skills/vaspera-help/SKILL.md +61 -0
  325. package/skills/vaspera-load-test/SKILL.md +167 -0
  326. package/skills/vaspera-verify/SKILL.md +70 -0
  327. package/skills/vaspera-verify-e2e/SKILL.md +117 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * PR Generator
3
+ *
4
+ * Orchestrates the creation of autofix pull requests.
5
+ * Uses gh CLI for GitHub operations (works locally and in CI).
6
+ *
7
+ * @module autofix/pr-generator
8
+ */
9
+ import type { Finding, Severity } from "../certification/types.js";
10
+ import type { AutofixPRConfig, AutofixPRResult, AutofixGroup, BatchPRResult } from "./types.js";
11
+ import { type Constitution } from "./constitution.js";
12
+ /**
13
+ * Check if gh CLI is available
14
+ */
15
+ export declare function isGhCliAvailable(): Promise<boolean>;
16
+ /**
17
+ * Check if authenticated with gh CLI
18
+ */
19
+ export declare function isGhAuthenticated(): Promise<boolean>;
20
+ /**
21
+ * Group findings into PR groups based on config
22
+ */
23
+ export declare function groupFindings(findings: Finding[], groupBy: AutofixPRConfig["groupBy"], branchPrefix: string): AutofixGroup[];
24
+ /**
25
+ * Create a single autofix PR for a group of findings
26
+ */
27
+ export declare function createAutofixPR(config: AutofixPRConfig, group: AutofixGroup, constitution?: Constitution): Promise<AutofixPRResult>;
28
+ /**
29
+ * Create autofix PRs for all findings
30
+ */
31
+ export declare function createAutofixPRs(config: Partial<AutofixPRConfig> & {
32
+ projectPath: string;
33
+ findings: Finding[];
34
+ }, constitution?: Constitution): Promise<BatchPRResult>;
35
+ /**
36
+ * Preview autofix PRs without creating them
37
+ */
38
+ export declare function previewAutofixPRs(projectPath: string, findings: Finding[], options?: {
39
+ groupBy?: AutofixPRConfig["groupBy"];
40
+ includeUnsafe?: boolean;
41
+ }): Promise<{
42
+ groups: Array<{
43
+ groupId: string;
44
+ branchName: string;
45
+ findings: number;
46
+ severity?: Severity;
47
+ fixable: number;
48
+ unfixable: number;
49
+ estimatedChanges: Array<{
50
+ file: string;
51
+ findingId: string;
52
+ canAutoFix: boolean;
53
+ risk: string;
54
+ }>;
55
+ }>;
56
+ }>;
57
+ //# sourceMappingURL=pr-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-generator.d.ts","sourceRoot":"","sources":["../../src/autofix/pr-generator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAGnE,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,aAAa,EAEd,MAAM,YAAY,CAAC;AA4BpB,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAYzD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAY1D;AAwED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,EACnC,YAAY,EAAE,MAAM,GACnB,YAAY,EAAE,CA2EhB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,YAAY,EACnB,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,eAAe,CAAC,CAwM1B;AAqED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,EAC/E,YAAY,CAAC,EAAE,YAAY,GAC1B,OAAO,CAAC,aAAa,CAAC,CAmLxB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,CAAC,EAAE;IACR,OAAO,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GACA,OAAO,CAAC;IACT,MAAM,EAAE,KAAK,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,gBAAgB,EAAE,KAAK,CAAC;YACtB,IAAI,EAAE,MAAM,CAAC;YACb,SAAS,EAAE,MAAM,CAAC;YAClB,UAAU,EAAE,OAAO,CAAC;YACpB,IAAI,EAAE,MAAM,CAAC;SACd,CAAC,CAAC;KACJ,CAAC,CAAC;CACJ,CAAC,CA8DD"}
@@ -0,0 +1,597 @@
1
+ /**
2
+ * PR Generator
3
+ *
4
+ * Orchestrates the creation of autofix pull requests.
5
+ * Uses gh CLI for GitHub operations (works locally and in CI).
6
+ *
7
+ * @module autofix/pr-generator
8
+ */
9
+ import spawn from "cross-spawn";
10
+ import { applyFix, previewFix, FIX_PATTERNS } from "../certification/autofix.js";
11
+ import { git, isGitRepo, getCurrentBranch, getDefaultBranch, createBranch, checkoutBranch, stageFiles, pushBranch, getRemoteUrl, parseGitHubRemote, generateBranchName, ensureCleanWorkingTree, restoreOriginalState, } from "./branch-manager.js";
12
+ import { generateCommitMessage, generateCommitBody, createCommit, generatePRTitle, generatePRBody, } from "./commit-generator.js";
13
+ import { logger } from "../logger.js";
14
+ import { loadConstitution, isPathAllowed, isPatternApproved, } from "./constitution.js";
15
+ /**
16
+ * Check if gh CLI is available
17
+ */
18
+ export async function isGhCliAvailable() {
19
+ return new Promise((resolve) => {
20
+ const child = spawn("gh", ["--version"], { timeout: 5000 });
21
+ child.on("close", (code) => {
22
+ resolve(code === 0);
23
+ });
24
+ child.on("error", () => {
25
+ resolve(false);
26
+ });
27
+ });
28
+ }
29
+ /**
30
+ * Check if authenticated with gh CLI
31
+ */
32
+ export async function isGhAuthenticated() {
33
+ return new Promise((resolve) => {
34
+ const child = spawn("gh", ["auth", "status"], { timeout: 10000 });
35
+ child.on("close", (code) => {
36
+ resolve(code === 0);
37
+ });
38
+ child.on("error", () => {
39
+ resolve(false);
40
+ });
41
+ });
42
+ }
43
+ /**
44
+ * Create a PR using gh CLI
45
+ */
46
+ async function createPRWithGh(cwd, title, body, baseBranch, headBranch, labels = []) {
47
+ return new Promise((resolve) => {
48
+ const args = [
49
+ "pr",
50
+ "create",
51
+ "--title",
52
+ title,
53
+ "--body",
54
+ body,
55
+ "--base",
56
+ baseBranch,
57
+ "--head",
58
+ headBranch,
59
+ ];
60
+ if (labels.length > 0) {
61
+ args.push("--label", labels.join(","));
62
+ }
63
+ logger.info("gh.create_pr", { title, baseBranch, headBranch });
64
+ const child = spawn("gh", args, {
65
+ cwd,
66
+ timeout: 60000,
67
+ });
68
+ let stdout = "";
69
+ let stderr = "";
70
+ child.stdout?.on("data", (data) => {
71
+ stdout += data.toString();
72
+ });
73
+ child.stderr?.on("data", (data) => {
74
+ stderr += data.toString();
75
+ });
76
+ child.on("close", (code) => {
77
+ if (code === 0) {
78
+ // stdout contains the PR URL
79
+ const prUrl = stdout.trim();
80
+ // Extract PR number from URL
81
+ const prNumberMatch = prUrl.match(/\/pull\/(\d+)/);
82
+ const prNumber = prNumberMatch ? parseInt(prNumberMatch[1], 10) : 0;
83
+ logger.info("gh.pr_created", { prNumber, prUrl });
84
+ resolve({ prNumber, prUrl });
85
+ }
86
+ else {
87
+ logger.error("gh.pr_create_failed", { stderr, exitCode: code });
88
+ resolve({ error: stderr || "Failed to create PR" });
89
+ }
90
+ });
91
+ child.on("error", (error) => {
92
+ logger.error("gh.spawn_error", { error: String(error) });
93
+ resolve({ error: String(error) });
94
+ });
95
+ });
96
+ }
97
+ /**
98
+ * Group findings into PR groups based on config
99
+ */
100
+ export function groupFindings(findings, groupBy, branchPrefix) {
101
+ const groups = [];
102
+ switch (groupBy) {
103
+ case "severity": {
104
+ const severityOrder = ["critical", "high", "medium", "low", "info"];
105
+ for (const severity of severityOrder) {
106
+ const matching = findings.filter((f) => f.severity === severity);
107
+ if (matching.length > 0) {
108
+ groups.push({
109
+ groupId: `severity-${severity}`,
110
+ branchName: generateBranchName(branchPrefix, severity),
111
+ findings: matching,
112
+ severity,
113
+ });
114
+ }
115
+ }
116
+ break;
117
+ }
118
+ case "file": {
119
+ const byFile = new Map();
120
+ for (const finding of findings) {
121
+ if (finding.file) {
122
+ const existing = byFile.get(finding.file) || [];
123
+ existing.push(finding);
124
+ byFile.set(finding.file, existing);
125
+ }
126
+ }
127
+ for (const [file, fileFindings] of byFile) {
128
+ const shortFile = file.split("/").pop() || file;
129
+ groups.push({
130
+ groupId: `file-${file}`,
131
+ branchName: generateBranchName(branchPrefix, shortFile),
132
+ findings: fileFindings,
133
+ file,
134
+ });
135
+ }
136
+ break;
137
+ }
138
+ case "pattern": {
139
+ const byPattern = new Map();
140
+ for (const finding of findings) {
141
+ const patternId = finding.category || "other";
142
+ const existing = byPattern.get(patternId) || [];
143
+ existing.push(finding);
144
+ byPattern.set(patternId, existing);
145
+ }
146
+ for (const [patternId, patternFindings] of byPattern) {
147
+ groups.push({
148
+ groupId: `pattern-${patternId}`,
149
+ branchName: generateBranchName(branchPrefix, patternId),
150
+ findings: patternFindings,
151
+ patternId,
152
+ });
153
+ }
154
+ break;
155
+ }
156
+ case "single": {
157
+ for (let i = 0; i < findings.length; i++) {
158
+ const finding = findings[i];
159
+ groups.push({
160
+ groupId: `single-${finding.id}`,
161
+ branchName: generateBranchName(branchPrefix, finding.id),
162
+ findings: [finding],
163
+ severity: finding.severity,
164
+ });
165
+ }
166
+ break;
167
+ }
168
+ }
169
+ return groups;
170
+ }
171
+ /**
172
+ * Create a single autofix PR for a group of findings
173
+ */
174
+ export async function createAutofixPR(config, group, constitution) {
175
+ const { projectPath, baseBranch, dryRun, signCommits, includeUnsafe, remoteName } = config;
176
+ // Store original state
177
+ const originalBranch = await getCurrentBranch(projectPath);
178
+ if (!originalBranch) {
179
+ return {
180
+ branch: group.branchName,
181
+ fixesApplied: [],
182
+ filesModified: [],
183
+ dryRun,
184
+ error: "Could not determine current branch",
185
+ };
186
+ }
187
+ // Ensure clean working tree
188
+ const { clean, stashed } = await ensureCleanWorkingTree(projectPath);
189
+ if (!clean) {
190
+ return {
191
+ branch: group.branchName,
192
+ fixesApplied: [],
193
+ filesModified: [],
194
+ dryRun,
195
+ error: "Working tree has uncommitted changes. Please commit or stash them first.",
196
+ };
197
+ }
198
+ try {
199
+ // Create and checkout the new branch
200
+ if (!dryRun) {
201
+ const branchResult = await createBranch(projectPath, group.branchName, baseBranch);
202
+ if (!branchResult.success) {
203
+ await restoreOriginalState(projectPath, originalBranch, stashed);
204
+ return {
205
+ branch: group.branchName,
206
+ fixesApplied: [],
207
+ filesModified: [],
208
+ dryRun,
209
+ error: `Failed to create branch: ${branchResult.stderr}`,
210
+ };
211
+ }
212
+ }
213
+ // Apply fixes
214
+ const fixResults = [];
215
+ const filesModified = new Set();
216
+ for (const finding of group.findings) {
217
+ // Preview first
218
+ const preview = await previewFix(projectPath, finding);
219
+ if (!preview.canAutoFix) {
220
+ continue;
221
+ }
222
+ if (preview.risk === "high" && !includeUnsafe) {
223
+ logger.info("autofix.skipped_unsafe", { findingId: finding.id });
224
+ continue;
225
+ }
226
+ // Apply the fix
227
+ const result = await applyFix(projectPath, finding, dryRun);
228
+ fixResults.push(result);
229
+ if (result.applied || result.diff) {
230
+ filesModified.add(result.file);
231
+ }
232
+ }
233
+ if (fixResults.length === 0) {
234
+ if (!dryRun) {
235
+ await restoreOriginalState(projectPath, originalBranch, stashed);
236
+ }
237
+ return {
238
+ branch: group.branchName,
239
+ fixesApplied: [],
240
+ filesModified: [],
241
+ dryRun,
242
+ error: "No fixes could be applied",
243
+ severity: group.severity,
244
+ };
245
+ }
246
+ if (dryRun) {
247
+ return {
248
+ branch: group.branchName,
249
+ fixesApplied: fixResults,
250
+ filesModified: Array.from(filesModified),
251
+ dryRun: true,
252
+ severity: group.severity,
253
+ };
254
+ }
255
+ // Stage the modified files
256
+ const stageResult = await stageFiles(projectPath, Array.from(filesModified));
257
+ if (!stageResult.success) {
258
+ await restoreOriginalState(projectPath, originalBranch, stashed);
259
+ return {
260
+ branch: group.branchName,
261
+ fixesApplied: fixResults,
262
+ filesModified: Array.from(filesModified),
263
+ dryRun,
264
+ error: `Failed to stage files: ${stageResult.stderr}`,
265
+ severity: group.severity,
266
+ };
267
+ }
268
+ // Create commit
269
+ const commitMessage = generateCommitMessage(fixResults, group.severity);
270
+ const commitBody = generateCommitBody(fixResults, group.severity);
271
+ const commitResult = await createCommit(projectPath, commitMessage, commitBody);
272
+ if ("error" in commitResult) {
273
+ await restoreOriginalState(projectPath, originalBranch, stashed);
274
+ return {
275
+ branch: group.branchName,
276
+ fixesApplied: fixResults,
277
+ filesModified: Array.from(filesModified),
278
+ dryRun,
279
+ error: commitResult.error,
280
+ severity: group.severity,
281
+ };
282
+ }
283
+ // Push branch
284
+ const pushResult = await pushBranch(projectPath, group.branchName, remoteName);
285
+ if (!pushResult.success) {
286
+ await restoreOriginalState(projectPath, originalBranch, stashed);
287
+ return {
288
+ branch: group.branchName,
289
+ fixesApplied: fixResults,
290
+ filesModified: Array.from(filesModified),
291
+ commitSha: commitResult.sha,
292
+ dryRun,
293
+ error: `Failed to push branch: ${pushResult.stderr}`,
294
+ severity: group.severity,
295
+ };
296
+ }
297
+ // Create PR with constitution labels
298
+ const prLabels = constitution?.prRules.requiredLabels || ["autofix", "security"];
299
+ const prTitle = generatePRTitle(fixResults, group.severity, config.titleTemplate);
300
+ const prBody = generatePRBody(fixResults, group.severity, {
301
+ includeBeforeAfter: true,
302
+ });
303
+ const prResult = await createPRWithGh(projectPath, prTitle, prBody, baseBranch, group.branchName, prLabels);
304
+ // Restore original branch
305
+ await checkoutBranch(projectPath, originalBranch);
306
+ if (stashed) {
307
+ await git(["stash", "pop"], { cwd: projectPath });
308
+ }
309
+ if ("error" in prResult) {
310
+ return {
311
+ branch: group.branchName,
312
+ fixesApplied: fixResults,
313
+ filesModified: Array.from(filesModified),
314
+ commitSha: commitResult.sha,
315
+ dryRun,
316
+ error: prResult.error,
317
+ severity: group.severity,
318
+ };
319
+ }
320
+ return {
321
+ prNumber: prResult.prNumber,
322
+ prUrl: prResult.prUrl,
323
+ branch: group.branchName,
324
+ fixesApplied: fixResults,
325
+ filesModified: Array.from(filesModified),
326
+ commitSha: commitResult.sha,
327
+ dryRun,
328
+ severity: group.severity,
329
+ };
330
+ }
331
+ catch (error) {
332
+ // Always try to restore original state on error
333
+ try {
334
+ await restoreOriginalState(projectPath, originalBranch, stashed);
335
+ }
336
+ catch {
337
+ // Ignore restoration errors
338
+ }
339
+ return {
340
+ branch: group.branchName,
341
+ fixesApplied: [],
342
+ filesModified: [],
343
+ dryRun,
344
+ error: String(error),
345
+ severity: group.severity,
346
+ };
347
+ }
348
+ }
349
+ /**
350
+ * Find the fix pattern for a finding
351
+ */
352
+ function findPatternForFinding(finding) {
353
+ // Try matching by category first
354
+ if (finding.category) {
355
+ const categoryStr = typeof finding.category === "string" ? finding.category : String(finding.category);
356
+ const pattern = FIX_PATTERNS.find((p) => p.patternId === categoryStr || p.patternId.includes(categoryStr));
357
+ if (pattern)
358
+ return pattern;
359
+ }
360
+ // Try matching by scanner source
361
+ if (finding.scanner_source) {
362
+ const pattern = FIX_PATTERNS.find((p) => p.patternId.includes(finding.scanner_source || ""));
363
+ if (pattern)
364
+ return pattern;
365
+ }
366
+ return undefined;
367
+ }
368
+ /**
369
+ * Filter findings based on constitution rules
370
+ */
371
+ function filterFindingsByConstitution(findings, constitution) {
372
+ const allowed = [];
373
+ const blocked = [];
374
+ for (const finding of findings) {
375
+ // Check path restrictions
376
+ if (finding.file) {
377
+ const pathCheck = isPathAllowed(constitution, finding.file);
378
+ if (!pathCheck.allowed) {
379
+ blocked.push({ finding, reason: pathCheck.reason || "Path not allowed" });
380
+ continue;
381
+ }
382
+ }
383
+ // Check pattern approval
384
+ const pattern = findPatternForFinding(finding);
385
+ if (pattern) {
386
+ const severity = (finding.severity || "medium");
387
+ const approval = isPatternApproved(constitution, pattern, {
388
+ filePath: finding.file || "",
389
+ severity,
390
+ });
391
+ if (!approval.approved) {
392
+ blocked.push({
393
+ finding,
394
+ reason: approval.reason || "Pattern not approved",
395
+ });
396
+ continue;
397
+ }
398
+ }
399
+ allowed.push(finding);
400
+ }
401
+ return { allowed, blocked };
402
+ }
403
+ /**
404
+ * Create autofix PRs for all findings
405
+ */
406
+ export async function createAutofixPRs(config, constitution) {
407
+ // Load constitution if not provided
408
+ const effectiveConstitution = constitution || await loadConstitution(config.projectPath);
409
+ const fullConfig = {
410
+ branchPrefix: "vaspera/autofix",
411
+ baseBranch: "main",
412
+ // Use constitution's groupBy if specified
413
+ groupBy: effectiveConstitution.prRules.groupBy,
414
+ // Use constitution's dryRunDefault
415
+ dryRun: effectiveConstitution.safety.dryRunDefault,
416
+ signCommits: false,
417
+ // Use aggressive risk tolerance to include unsafe
418
+ includeUnsafe: effectiveConstitution.riskTolerance === "aggressive",
419
+ remoteName: "origin",
420
+ ...config,
421
+ };
422
+ const { projectPath, findings, groupBy, dryRun } = fullConfig;
423
+ // Filter findings based on constitution
424
+ const { allowed: allowedFindings, blocked } = filterFindingsByConstitution(findings, effectiveConstitution);
425
+ logger.info("constitution.filtered_findings", {
426
+ total: findings.length,
427
+ allowed: allowedFindings.length,
428
+ blocked: blocked.length,
429
+ });
430
+ // Add blocked findings to unfixable list
431
+ const unfixable = blocked.map(({ finding, reason }) => ({ findingId: finding.id, reason }));
432
+ // Check if any findings remain
433
+ if (allowedFindings.length === 0) {
434
+ return {
435
+ totalFindings: findings.length,
436
+ prsCreated: [],
437
+ unfixable,
438
+ success: unfixable.length === 0,
439
+ summary: { bySeverity: {}, byFile: {}, byPattern: {} },
440
+ };
441
+ }
442
+ // Validate prerequisites
443
+ if (!(await isGitRepo(projectPath))) {
444
+ return {
445
+ totalFindings: findings.length,
446
+ prsCreated: [],
447
+ unfixable: [...unfixable, ...allowedFindings.map((f) => ({ findingId: f.id, reason: "Not a git repository" }))],
448
+ success: false,
449
+ summary: { bySeverity: {}, byFile: {}, byPattern: {} },
450
+ };
451
+ }
452
+ if (!dryRun) {
453
+ const ghAvailable = await isGhCliAvailable();
454
+ if (!ghAvailable) {
455
+ return {
456
+ totalFindings: findings.length,
457
+ prsCreated: [],
458
+ unfixable: [...unfixable, ...allowedFindings.map((f) => ({ findingId: f.id, reason: "gh CLI not available" }))],
459
+ success: false,
460
+ summary: { bySeverity: {}, byFile: {}, byPattern: {} },
461
+ };
462
+ }
463
+ const ghAuthenticated = await isGhAuthenticated();
464
+ if (!ghAuthenticated) {
465
+ return {
466
+ totalFindings: findings.length,
467
+ prsCreated: [],
468
+ unfixable: [...unfixable, ...allowedFindings.map((f) => ({ findingId: f.id, reason: "gh CLI not authenticated" }))],
469
+ success: false,
470
+ summary: { bySeverity: {}, byFile: {}, byPattern: {} },
471
+ };
472
+ }
473
+ const remoteUrl = await getRemoteUrl(projectPath, fullConfig.remoteName);
474
+ if (!remoteUrl || !parseGitHubRemote(remoteUrl)) {
475
+ return {
476
+ totalFindings: findings.length,
477
+ prsCreated: [],
478
+ unfixable: [...unfixable, ...allowedFindings.map((f) => ({ findingId: f.id, reason: "Not a GitHub repository" }))],
479
+ success: false,
480
+ summary: { bySeverity: {}, byFile: {}, byPattern: {} },
481
+ };
482
+ }
483
+ }
484
+ // Get default branch if not specified
485
+ if (fullConfig.baseBranch === "main") {
486
+ fullConfig.baseBranch = await getDefaultBranch(projectPath);
487
+ }
488
+ // Check max files limit from constitution
489
+ const maxFiles = effectiveConstitution.safety.maxFilesPerRun;
490
+ const uniqueFiles = new Set(allowedFindings.map((f) => f.file).filter(Boolean));
491
+ if (uniqueFiles.size > maxFiles) {
492
+ logger.warn("constitution.max_files_exceeded", {
493
+ files: uniqueFiles.size,
494
+ maxFiles,
495
+ });
496
+ // Continue but log warning - could also return error here
497
+ }
498
+ // Check max PRs limit from constitution
499
+ const maxPRs = effectiveConstitution.prRules.maxPRsPerRun;
500
+ // Group findings
501
+ let groups = groupFindings(allowedFindings, groupBy, fullConfig.branchPrefix);
502
+ // Limit to maxPRsPerRun
503
+ if (groups.length > maxPRs) {
504
+ logger.warn("constitution.max_prs_exceeded", {
505
+ groups: groups.length,
506
+ maxPRs,
507
+ });
508
+ groups = groups.slice(0, maxPRs);
509
+ }
510
+ logger.info("autofix.creating_prs", {
511
+ totalFindings: findings.length,
512
+ allowedFindings: allowedFindings.length,
513
+ groups: groups.length,
514
+ groupBy,
515
+ dryRun,
516
+ });
517
+ // Create PRs for each group, passing constitution for labels
518
+ const prsCreated = [];
519
+ for (const group of groups) {
520
+ const result = await createAutofixPR(fullConfig, group, effectiveConstitution);
521
+ prsCreated.push(result);
522
+ if (result.error) {
523
+ for (const finding of group.findings) {
524
+ unfixable.push({ findingId: finding.id, reason: result.error });
525
+ }
526
+ }
527
+ }
528
+ // Build summary
529
+ const summary = {
530
+ bySeverity: {},
531
+ byFile: {},
532
+ byPattern: {},
533
+ };
534
+ for (const pr of prsCreated) {
535
+ if (pr.severity) {
536
+ summary.bySeverity[pr.severity] = (summary.bySeverity[pr.severity] || 0) + pr.fixesApplied.length;
537
+ }
538
+ for (const file of pr.filesModified) {
539
+ summary.byFile[file] = (summary.byFile[file] || 0) + 1;
540
+ }
541
+ }
542
+ const allSuccessful = prsCreated.every((pr) => !pr.error);
543
+ logger.info("autofix.batch_complete", {
544
+ prsCreated: prsCreated.length,
545
+ successfulPrs: prsCreated.filter((pr) => !pr.error).length,
546
+ totalFixes: prsCreated.reduce((sum, pr) => sum + pr.fixesApplied.length, 0),
547
+ unfixable: unfixable.length,
548
+ });
549
+ return {
550
+ totalFindings: findings.length,
551
+ prsCreated,
552
+ unfixable,
553
+ success: allSuccessful,
554
+ summary,
555
+ };
556
+ }
557
+ /**
558
+ * Preview autofix PRs without creating them
559
+ */
560
+ export async function previewAutofixPRs(projectPath, findings, options) {
561
+ const groupBy = options?.groupBy ?? "severity";
562
+ const includeUnsafe = options?.includeUnsafe ?? false;
563
+ const groups = groupFindings(findings, groupBy, "vaspera/autofix");
564
+ const result = [];
565
+ for (const group of groups) {
566
+ const changes = [];
567
+ let fixable = 0;
568
+ let unfixable = 0;
569
+ for (const finding of group.findings) {
570
+ const preview = await previewFix(projectPath, finding);
571
+ const canFix = preview.canAutoFix && (includeUnsafe || preview.risk !== "high");
572
+ changes.push({
573
+ file: finding.file || "unknown",
574
+ findingId: finding.id,
575
+ canAutoFix: canFix,
576
+ risk: preview.risk,
577
+ });
578
+ if (canFix) {
579
+ fixable++;
580
+ }
581
+ else {
582
+ unfixable++;
583
+ }
584
+ }
585
+ result.push({
586
+ groupId: group.groupId,
587
+ branchName: group.branchName,
588
+ findings: group.findings.length,
589
+ severity: group.severity,
590
+ fixable,
591
+ unfixable,
592
+ estimatedChanges: changes,
593
+ });
594
+ }
595
+ return { groups: result };
596
+ }
597
+ //# sourceMappingURL=pr-generator.js.map