oc-pilot 0.5.1 → 0.88.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 (481) hide show
  1. package/CHANGELOG.md +683 -0
  2. package/CONTRIBUTING.md +4 -2
  3. package/README.md +588 -20
  4. package/README.ru.md +572 -10
  5. package/dist/cli.js +185 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cli.test.js +1211 -1
  8. package/dist/cli.test.js.map +1 -1
  9. package/dist/commands/anti-regression.js +97 -0
  10. package/dist/commands/anti-regression.js.map +1 -0
  11. package/dist/commands/arch-diff.js +80 -0
  12. package/dist/commands/arch-diff.js.map +1 -0
  13. package/dist/commands/authz.js +167 -0
  14. package/dist/commands/authz.js.map +1 -0
  15. package/dist/commands/autopsy.js +64 -0
  16. package/dist/commands/autopsy.js.map +1 -0
  17. package/dist/commands/bankruptcy.js +60 -0
  18. package/dist/commands/bankruptcy.js.map +1 -0
  19. package/dist/commands/battle.js +119 -0
  20. package/dist/commands/battle.js.map +1 -0
  21. package/dist/commands/boundaries.js +114 -0
  22. package/dist/commands/boundaries.js.map +1 -0
  23. package/dist/commands/bounty.js +56 -0
  24. package/dist/commands/bounty.js.map +1 -0
  25. package/dist/commands/brag.js +62 -0
  26. package/dist/commands/brag.js.map +1 -0
  27. package/dist/commands/cache.js +62 -0
  28. package/dist/commands/cache.js.map +1 -0
  29. package/dist/commands/cinema.js +61 -0
  30. package/dist/commands/cinema.js.map +1 -0
  31. package/dist/commands/cleanup.js +110 -0
  32. package/dist/commands/cleanup.js.map +1 -0
  33. package/dist/commands/commit.js +177 -0
  34. package/dist/commands/commit.js.map +1 -0
  35. package/dist/commands/commit.test.js +160 -0
  36. package/dist/commands/commit.test.js.map +1 -0
  37. package/dist/commands/compliance.js +121 -0
  38. package/dist/commands/compliance.js.map +1 -0
  39. package/dist/commands/contract-drift.js +97 -0
  40. package/dist/commands/contract-drift.js.map +1 -0
  41. package/dist/commands/coverage.js +137 -0
  42. package/dist/commands/coverage.js.map +1 -0
  43. package/dist/commands/day.js +116 -0
  44. package/dist/commands/day.js.map +1 -0
  45. package/dist/commands/day.test.js +45 -0
  46. package/dist/commands/day.test.js.map +1 -0
  47. package/dist/commands/deps.js +245 -0
  48. package/dist/commands/deps.js.map +1 -0
  49. package/dist/commands/docgen.js +4 -2
  50. package/dist/commands/docgen.js.map +1 -1
  51. package/dist/commands/doctor.js +146 -2
  52. package/dist/commands/doctor.js.map +1 -1
  53. package/dist/commands/doctor.test.js +69 -0
  54. package/dist/commands/doctor.test.js.map +1 -0
  55. package/dist/commands/echo.js +78 -0
  56. package/dist/commands/echo.js.map +1 -0
  57. package/dist/commands/ecosystem-verify.js +113 -0
  58. package/dist/commands/ecosystem-verify.js.map +1 -0
  59. package/dist/commands/env-sync.js +102 -0
  60. package/dist/commands/env-sync.js.map +1 -0
  61. package/dist/commands/explain.js +115 -0
  62. package/dist/commands/explain.js.map +1 -0
  63. package/dist/commands/fix.js +145 -3
  64. package/dist/commands/fix.js.map +1 -1
  65. package/dist/commands/flaky.js +111 -0
  66. package/dist/commands/flaky.js.map +1 -0
  67. package/dist/commands/fuzz.js +119 -0
  68. package/dist/commands/fuzz.js.map +1 -0
  69. package/dist/commands/hivemind.js +124 -0
  70. package/dist/commands/hivemind.js.map +1 -0
  71. package/dist/commands/impact.js +73 -0
  72. package/dist/commands/impact.js.map +1 -0
  73. package/dist/commands/init.js +73 -11
  74. package/dist/commands/init.js.map +1 -1
  75. package/dist/commands/init.test.js +138 -0
  76. package/dist/commands/init.test.js.map +1 -0
  77. package/dist/commands/install-hook.js +161 -0
  78. package/dist/commands/install-hook.js.map +1 -0
  79. package/dist/commands/install-hook.test.js +100 -0
  80. package/dist/commands/install-hook.test.js.map +1 -0
  81. package/dist/commands/invariants.js +103 -0
  82. package/dist/commands/invariants.js.map +1 -0
  83. package/dist/commands/mock-leak.js +97 -0
  84. package/dist/commands/mock-leak.js.map +1 -0
  85. package/dist/commands/mutate.js +99 -0
  86. package/dist/commands/mutate.js.map +1 -0
  87. package/dist/commands/pack.js +144 -0
  88. package/dist/commands/pack.js.map +1 -0
  89. package/dist/commands/preflight.js +137 -0
  90. package/dist/commands/preflight.js.map +1 -0
  91. package/dist/commands/query.js +57 -0
  92. package/dist/commands/query.js.map +1 -0
  93. package/dist/commands/ready.js +152 -0
  94. package/dist/commands/ready.js.map +1 -0
  95. package/dist/commands/receipt.js +289 -0
  96. package/dist/commands/receipt.js.map +1 -0
  97. package/dist/commands/replay.js +112 -0
  98. package/dist/commands/replay.js.map +1 -0
  99. package/dist/commands/replay.test.js +125 -0
  100. package/dist/commands/replay.test.js.map +1 -0
  101. package/dist/commands/restore.js +54 -0
  102. package/dist/commands/restore.js.map +1 -0
  103. package/dist/commands/review.js +263 -4
  104. package/dist/commands/review.js.map +1 -1
  105. package/dist/commands/review.test.js +160 -0
  106. package/dist/commands/review.test.js.map +1 -0
  107. package/dist/commands/reviewers.js +86 -0
  108. package/dist/commands/reviewers.js.map +1 -0
  109. package/dist/commands/roast.js +102 -0
  110. package/dist/commands/roast.js.map +1 -0
  111. package/dist/commands/sandbox.js +82 -0
  112. package/dist/commands/sandbox.js.map +1 -0
  113. package/dist/commands/sast.js +162 -0
  114. package/dist/commands/sast.js.map +1 -0
  115. package/dist/commands/scan.js +47 -12
  116. package/dist/commands/scan.js.map +1 -1
  117. package/dist/commands/slop.js +145 -0
  118. package/dist/commands/slop.js.map +1 -0
  119. package/dist/commands/slop.test.js +135 -0
  120. package/dist/commands/slop.test.js.map +1 -0
  121. package/dist/commands/stats.js +61 -0
  122. package/dist/commands/stats.js.map +1 -0
  123. package/dist/commands/test-gen.js +372 -0
  124. package/dist/commands/test-gen.js.map +1 -0
  125. package/dist/commands/test-guard.js +89 -0
  126. package/dist/commands/test-guard.js.map +1 -0
  127. package/dist/commands/test-plan.js +133 -0
  128. package/dist/commands/test-plan.js.map +1 -0
  129. package/dist/commands/zoom-symbol.js +52 -0
  130. package/dist/commands/zoom-symbol.js.map +1 -0
  131. package/dist/core/agent-rules.js +35 -0
  132. package/dist/core/agent-rules.js.map +1 -0
  133. package/dist/core/anthropic.test.js +116 -0
  134. package/dist/core/anthropic.test.js.map +1 -0
  135. package/dist/core/anti-regression.js +161 -0
  136. package/dist/core/anti-regression.js.map +1 -0
  137. package/dist/core/anti-regression.test.js +154 -0
  138. package/dist/core/anti-regression.test.js.map +1 -0
  139. package/dist/core/aql.js +220 -0
  140. package/dist/core/aql.js.map +1 -0
  141. package/dist/core/aql.test.js +64 -0
  142. package/dist/core/aql.test.js.map +1 -0
  143. package/dist/core/arch-diff.js +122 -0
  144. package/dist/core/arch-diff.js.map +1 -0
  145. package/dist/core/arch-diff.test.js +93 -0
  146. package/dist/core/arch-diff.test.js.map +1 -0
  147. package/dist/core/authz.js +292 -0
  148. package/dist/core/authz.js.map +1 -0
  149. package/dist/core/authz.test.js +115 -0
  150. package/dist/core/authz.test.js.map +1 -0
  151. package/dist/core/autopsy.js +230 -0
  152. package/dist/core/autopsy.js.map +1 -0
  153. package/dist/core/autopsy.test.js +106 -0
  154. package/dist/core/autopsy.test.js.map +1 -0
  155. package/dist/core/bankruptcy.js +232 -0
  156. package/dist/core/bankruptcy.js.map +1 -0
  157. package/dist/core/bankruptcy.test.js +91 -0
  158. package/dist/core/bankruptcy.test.js.map +1 -0
  159. package/dist/core/baseline.js +131 -0
  160. package/dist/core/baseline.js.map +1 -0
  161. package/dist/core/baseline.test.js +91 -0
  162. package/dist/core/baseline.test.js.map +1 -0
  163. package/dist/core/battle.js +213 -0
  164. package/dist/core/battle.js.map +1 -0
  165. package/dist/core/battle.test.js +116 -0
  166. package/dist/core/battle.test.js.map +1 -0
  167. package/dist/core/boundaries.js +200 -0
  168. package/dist/core/boundaries.js.map +1 -0
  169. package/dist/core/boundaries.test.js +62 -0
  170. package/dist/core/boundaries.test.js.map +1 -0
  171. package/dist/core/bounty.js +128 -0
  172. package/dist/core/bounty.js.map +1 -0
  173. package/dist/core/bounty.test.js +82 -0
  174. package/dist/core/bounty.test.js.map +1 -0
  175. package/dist/core/brag.js +174 -0
  176. package/dist/core/brag.js.map +1 -0
  177. package/dist/core/brag.test.js +43 -0
  178. package/dist/core/brag.test.js.map +1 -0
  179. package/dist/core/cache.js +189 -0
  180. package/dist/core/cache.js.map +1 -0
  181. package/dist/core/cache.test.js +159 -0
  182. package/dist/core/cache.test.js.map +1 -0
  183. package/dist/core/cinema.js +242 -0
  184. package/dist/core/cinema.js.map +1 -0
  185. package/dist/core/cinema.test.js +73 -0
  186. package/dist/core/cinema.test.js.map +1 -0
  187. package/dist/core/cleanup.js +107 -0
  188. package/dist/core/cleanup.js.map +1 -0
  189. package/dist/core/compliance.js +193 -0
  190. package/dist/core/compliance.js.map +1 -0
  191. package/dist/core/compliance.test.js +123 -0
  192. package/dist/core/compliance.test.js.map +1 -0
  193. package/dist/core/config.js +78 -0
  194. package/dist/core/config.js.map +1 -1
  195. package/dist/core/config.test.js +66 -0
  196. package/dist/core/config.test.js.map +1 -1
  197. package/dist/core/contract-drift.js +296 -0
  198. package/dist/core/contract-drift.js.map +1 -0
  199. package/dist/core/contract-drift.test.js +166 -0
  200. package/dist/core/contract-drift.test.js.map +1 -0
  201. package/dist/core/cost.test.js +103 -0
  202. package/dist/core/cost.test.js.map +1 -0
  203. package/dist/core/coverage.js +335 -0
  204. package/dist/core/coverage.js.map +1 -0
  205. package/dist/core/coverage.test.js +190 -0
  206. package/dist/core/coverage.test.js.map +1 -0
  207. package/dist/core/deps-fix.js +169 -0
  208. package/dist/core/deps-fix.js.map +1 -0
  209. package/dist/core/deps-fix.test.js +176 -0
  210. package/dist/core/deps-fix.test.js.map +1 -0
  211. package/dist/core/deps-health.js +353 -0
  212. package/dist/core/deps-health.js.map +1 -0
  213. package/dist/core/deps-health.test.js +230 -0
  214. package/dist/core/deps-health.test.js.map +1 -0
  215. package/dist/core/deps-why.js +260 -0
  216. package/dist/core/deps-why.js.map +1 -0
  217. package/dist/core/deps-why.test.js +175 -0
  218. package/dist/core/deps-why.test.js.map +1 -0
  219. package/dist/core/deps.js +179 -0
  220. package/dist/core/deps.js.map +1 -0
  221. package/dist/core/deps.test.js +88 -0
  222. package/dist/core/deps.test.js.map +1 -0
  223. package/dist/core/diff-source.js +4 -2
  224. package/dist/core/diff-source.js.map +1 -1
  225. package/dist/core/echo.js +151 -0
  226. package/dist/core/echo.js.map +1 -0
  227. package/dist/core/echo.test.js +90 -0
  228. package/dist/core/echo.test.js.map +1 -0
  229. package/dist/core/ecosystem-verify.js +331 -0
  230. package/dist/core/ecosystem-verify.js.map +1 -0
  231. package/dist/core/ecosystem-verify.test.js +158 -0
  232. package/dist/core/ecosystem-verify.test.js.map +1 -0
  233. package/dist/core/engine-preset.test.js +60 -0
  234. package/dist/core/engine-preset.test.js.map +1 -0
  235. package/dist/core/engine.js +278 -32
  236. package/dist/core/engine.js.map +1 -1
  237. package/dist/core/engine.test.js +63 -0
  238. package/dist/core/engine.test.js.map +1 -1
  239. package/dist/core/env-sync.js +122 -0
  240. package/dist/core/env-sync.js.map +1 -0
  241. package/dist/core/env-sync.test.js +99 -0
  242. package/dist/core/env-sync.test.js.map +1 -0
  243. package/dist/core/file-context.js +4 -2
  244. package/dist/core/file-context.js.map +1 -1
  245. package/dist/core/findings-baseline.js +60 -0
  246. package/dist/core/findings-baseline.js.map +1 -0
  247. package/dist/core/findings-baseline.test.js +77 -0
  248. package/dist/core/findings-baseline.test.js.map +1 -0
  249. package/dist/core/findings.js +68 -21
  250. package/dist/core/findings.js.map +1 -1
  251. package/dist/core/flaky.js +137 -0
  252. package/dist/core/flaky.js.map +1 -0
  253. package/dist/core/flaky.test.js +97 -0
  254. package/dist/core/flaky.test.js.map +1 -0
  255. package/dist/core/format.js +5 -3
  256. package/dist/core/format.js.map +1 -1
  257. package/dist/core/fuzz.js +306 -0
  258. package/dist/core/fuzz.js.map +1 -0
  259. package/dist/core/fuzz.test.js +121 -0
  260. package/dist/core/fuzz.test.js.map +1 -0
  261. package/dist/core/git.js +22 -0
  262. package/dist/core/git.js.map +1 -1
  263. package/dist/core/gitlab.js +176 -0
  264. package/dist/core/gitlab.js.map +1 -0
  265. package/dist/core/gitlab.test.js +199 -0
  266. package/dist/core/gitlab.test.js.map +1 -0
  267. package/dist/core/hivemind.js +250 -0
  268. package/dist/core/hivemind.js.map +1 -0
  269. package/dist/core/hivemind.test.js +79 -0
  270. package/dist/core/hivemind.test.js.map +1 -0
  271. package/dist/core/hologram.js +463 -0
  272. package/dist/core/hologram.js.map +1 -0
  273. package/dist/core/hologram.test.js +124 -0
  274. package/dist/core/hologram.test.js.map +1 -0
  275. package/dist/core/impact.js +240 -0
  276. package/dist/core/impact.js.map +1 -0
  277. package/dist/core/impact.test.js +105 -0
  278. package/dist/core/impact.test.js.map +1 -0
  279. package/dist/core/import-graph.js +170 -0
  280. package/dist/core/import-graph.js.map +1 -0
  281. package/dist/core/import-graph.test.js +52 -0
  282. package/dist/core/import-graph.test.js.map +1 -0
  283. package/dist/core/injection.js +55 -0
  284. package/dist/core/injection.js.map +1 -0
  285. package/dist/core/injection.test.js +45 -0
  286. package/dist/core/injection.test.js.map +1 -0
  287. package/dist/core/invariants.js +286 -0
  288. package/dist/core/invariants.js.map +1 -0
  289. package/dist/core/invariants.test.js +87 -0
  290. package/dist/core/invariants.test.js.map +1 -0
  291. package/dist/core/last-review.js +92 -0
  292. package/dist/core/last-review.js.map +1 -0
  293. package/dist/core/last-review.test.js +97 -0
  294. package/dist/core/last-review.test.js.map +1 -0
  295. package/dist/core/lockfile-support.test.js +112 -0
  296. package/dist/core/lockfile-support.test.js.map +1 -0
  297. package/dist/core/lockfile.js +284 -0
  298. package/dist/core/lockfile.js.map +1 -0
  299. package/dist/core/lockfile.test.js +203 -0
  300. package/dist/core/lockfile.test.js.map +1 -0
  301. package/dist/core/mock-leak.js +120 -0
  302. package/dist/core/mock-leak.js.map +1 -0
  303. package/dist/core/mock-leak.test.js +79 -0
  304. package/dist/core/mock-leak.test.js.map +1 -0
  305. package/dist/core/mutation.js +206 -0
  306. package/dist/core/mutation.js.map +1 -0
  307. package/dist/core/mutation.test.js +104 -0
  308. package/dist/core/mutation.test.js.map +1 -0
  309. package/dist/core/opencode.js +15 -2
  310. package/dist/core/opencode.js.map +1 -1
  311. package/dist/core/opencode.test.js +10 -1
  312. package/dist/core/opencode.test.js.map +1 -1
  313. package/dist/core/options.js +51 -3
  314. package/dist/core/options.js.map +1 -1
  315. package/dist/core/osv.js +202 -0
  316. package/dist/core/osv.js.map +1 -0
  317. package/dist/core/osv.test.js +173 -0
  318. package/dist/core/osv.test.js.map +1 -0
  319. package/dist/core/output-format.test.js +102 -0
  320. package/dist/core/output-format.test.js.map +1 -0
  321. package/dist/core/pack.js +212 -0
  322. package/dist/core/pack.js.map +1 -0
  323. package/dist/core/pack.test.js +129 -0
  324. package/dist/core/pack.test.js.map +1 -0
  325. package/dist/core/phantom.js +155 -0
  326. package/dist/core/phantom.js.map +1 -0
  327. package/dist/core/phantom.test.js +58 -0
  328. package/dist/core/phantom.test.js.map +1 -0
  329. package/dist/core/pr-comment.js +116 -0
  330. package/dist/core/pr-comment.js.map +1 -0
  331. package/dist/core/pr-comment.test.js +53 -0
  332. package/dist/core/pr-comment.test.js.map +1 -0
  333. package/dist/core/pr-review.js +160 -0
  334. package/dist/core/pr-review.js.map +1 -0
  335. package/dist/core/pr-review.test.js +62 -0
  336. package/dist/core/pr-review.test.js.map +1 -0
  337. package/dist/core/preflight-run.js +106 -0
  338. package/dist/core/preflight-run.js.map +1 -0
  339. package/dist/core/preflight.js +173 -0
  340. package/dist/core/preflight.js.map +1 -0
  341. package/dist/core/preflight.test.js +187 -0
  342. package/dist/core/preflight.test.js.map +1 -0
  343. package/dist/core/presets.js +408 -0
  344. package/dist/core/presets.js.map +1 -0
  345. package/dist/core/presets.test.js +79 -0
  346. package/dist/core/presets.test.js.map +1 -0
  347. package/dist/core/prompt.golden.test.js +74 -0
  348. package/dist/core/prompt.golden.test.js.map +1 -0
  349. package/dist/core/prompt.js +43 -4
  350. package/dist/core/prompt.js.map +1 -1
  351. package/dist/core/prompt.test.js +16 -0
  352. package/dist/core/prompt.test.js.map +1 -1
  353. package/dist/core/readiness-fix.js +111 -0
  354. package/dist/core/readiness-fix.js.map +1 -0
  355. package/dist/core/readiness-fix.test.js +99 -0
  356. package/dist/core/readiness-fix.test.js.map +1 -0
  357. package/dist/core/readiness.js +189 -0
  358. package/dist/core/readiness.js.map +1 -0
  359. package/dist/core/readiness.test.js +199 -0
  360. package/dist/core/readiness.test.js.map +1 -0
  361. package/dist/core/receipt.js +193 -0
  362. package/dist/core/receipt.js.map +1 -0
  363. package/dist/core/receipt.test.js +193 -0
  364. package/dist/core/receipt.test.js.map +1 -0
  365. package/dist/core/reviewers.js +179 -0
  366. package/dist/core/reviewers.js.map +1 -0
  367. package/dist/core/reviewers.test.js +105 -0
  368. package/dist/core/reviewers.test.js.map +1 -0
  369. package/dist/core/roast.js +27 -0
  370. package/dist/core/roast.js.map +1 -0
  371. package/dist/core/roast.test.js +19 -0
  372. package/dist/core/roast.test.js.map +1 -0
  373. package/dist/core/router.js +8 -0
  374. package/dist/core/router.js.map +1 -0
  375. package/dist/core/router.test.js +157 -0
  376. package/dist/core/router.test.js.map +1 -0
  377. package/dist/core/rules.js +2 -2
  378. package/dist/core/rules.js.map +1 -1
  379. package/dist/core/run.js +322 -20
  380. package/dist/core/run.js.map +1 -1
  381. package/dist/core/sarif.js +125 -0
  382. package/dist/core/sarif.js.map +1 -0
  383. package/dist/core/sarif.test.js +60 -0
  384. package/dist/core/sarif.test.js.map +1 -0
  385. package/dist/core/sast.js +324 -0
  386. package/dist/core/sast.js.map +1 -0
  387. package/dist/core/sast.test.js +105 -0
  388. package/dist/core/sast.test.js.map +1 -0
  389. package/dist/core/sbom.js +121 -0
  390. package/dist/core/sbom.js.map +1 -0
  391. package/dist/core/sbom.test.js +81 -0
  392. package/dist/core/sbom.test.js.map +1 -0
  393. package/dist/core/second-opinion.js +88 -0
  394. package/dist/core/second-opinion.js.map +1 -0
  395. package/dist/core/second-opinion.test.js +89 -0
  396. package/dist/core/second-opinion.test.js.map +1 -0
  397. package/dist/core/secrets.js +120 -5
  398. package/dist/core/secrets.js.map +1 -1
  399. package/dist/core/secrets.test.js +74 -1
  400. package/dist/core/secrets.test.js.map +1 -1
  401. package/dist/core/shadow.js +94 -0
  402. package/dist/core/shadow.js.map +1 -0
  403. package/dist/core/shadow.test.js +50 -0
  404. package/dist/core/shadow.test.js.map +1 -0
  405. package/dist/core/slicing.js +110 -0
  406. package/dist/core/slicing.js.map +1 -0
  407. package/dist/core/slicing.test.js +287 -0
  408. package/dist/core/slicing.test.js.map +1 -0
  409. package/dist/core/slop/churn.js +49 -0
  410. package/dist/core/slop/churn.js.map +1 -0
  411. package/dist/core/slop/collect.js +186 -0
  412. package/dist/core/slop/collect.js.map +1 -0
  413. package/dist/core/slop/dead-exports.js +101 -0
  414. package/dist/core/slop/dead-exports.js.map +1 -0
  415. package/dist/core/slop/duplicates.js +155 -0
  416. package/dist/core/slop/duplicates.js.map +1 -0
  417. package/dist/core/slop/index.js +72 -0
  418. package/dist/core/slop/index.js.map +1 -0
  419. package/dist/core/slop/model.js +2 -0
  420. package/dist/core/slop/model.js.map +1 -0
  421. package/dist/core/slop/score.js +185 -0
  422. package/dist/core/slop/score.js.map +1 -0
  423. package/dist/core/slop/slop.test.js +160 -0
  424. package/dist/core/slop/slop.test.js.map +1 -0
  425. package/dist/core/slop/source.js +129 -0
  426. package/dist/core/slop/source.js.map +1 -0
  427. package/dist/core/slop/test-debt.js +178 -0
  428. package/dist/core/slop/test-debt.js.map +1 -0
  429. package/dist/core/slop/test-debt.test.js +114 -0
  430. package/dist/core/slop/test-debt.test.js.map +1 -0
  431. package/dist/core/slop/todos.js +117 -0
  432. package/dist/core/slop/todos.js.map +1 -0
  433. package/dist/core/snapshot.js +214 -0
  434. package/dist/core/snapshot.js.map +1 -0
  435. package/dist/core/snapshot.test.js +101 -0
  436. package/dist/core/snapshot.test.js.map +1 -0
  437. package/dist/core/stats.js +119 -0
  438. package/dist/core/stats.js.map +1 -0
  439. package/dist/core/stats.test.js +86 -0
  440. package/dist/core/stats.test.js.map +1 -0
  441. package/dist/core/suppressions.js +96 -0
  442. package/dist/core/suppressions.js.map +1 -0
  443. package/dist/core/suppressions.test.js +90 -0
  444. package/dist/core/suppressions.test.js.map +1 -0
  445. package/dist/core/test-delta.js +53 -0
  446. package/dist/core/test-delta.js.map +1 -0
  447. package/dist/core/test-delta.test.js +41 -0
  448. package/dist/core/test-delta.test.js.map +1 -0
  449. package/dist/core/test-plan.js +212 -0
  450. package/dist/core/test-plan.js.map +1 -0
  451. package/dist/core/test-plan.test.js +94 -0
  452. package/dist/core/test-plan.test.js.map +1 -0
  453. package/dist/core/test-weakening.js +191 -0
  454. package/dist/core/test-weakening.js.map +1 -0
  455. package/dist/core/test-weakening.test.js +114 -0
  456. package/dist/core/test-weakening.test.js.map +1 -0
  457. package/dist/core/tree-scan.js +77 -0
  458. package/dist/core/tree-scan.js.map +1 -0
  459. package/dist/core/tree-scan.test.js +58 -0
  460. package/dist/core/tree-scan.test.js.map +1 -0
  461. package/dist/mcp-prompts.test.js +133 -0
  462. package/dist/mcp-prompts.test.js.map +1 -0
  463. package/dist/mcp.js +3303 -240
  464. package/dist/mcp.js.map +1 -1
  465. package/dist/mcp.test.js +1739 -1
  466. package/dist/mcp.test.js.map +1 -1
  467. package/dist/skills.test.js +221 -0
  468. package/dist/skills.test.js.map +1 -0
  469. package/dist/version.js +1 -1
  470. package/dist/version.js.map +1 -1
  471. package/docs/integrations.md +151 -7
  472. package/package.json +2 -1
  473. package/prompts/oc-pilot.en.md +8 -1
  474. package/prompts/oc-pilot.ru.md +8 -1
  475. package/server.json +2 -2
  476. package/skills/agent-self-review/SKILL.md +87 -0
  477. package/skills/commit-discipline/SKILL.md +86 -0
  478. package/skills/oc-pilot/SKILL.md +86 -0
  479. package/skills/secure-diff-gate/SKILL.md +101 -0
  480. package/skills/stack-review-packs/SKILL.md +76 -0
  481. package/skills/test-failure-triage/SKILL.md +88 -0
@@ -0,0 +1,116 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import test from "node:test";
6
+ import { runBattle } from "./battle.js";
7
+ const DIFF = "diff --git a/src/app.ts b/src/app.ts\n--- a/src/app.ts\n+++ b/src/app.ts\n@@ -1 +1 @@\n-old\n+new\n";
8
+ function fakeRunner(responses, calls = []) {
9
+ return (args) => {
10
+ calls.push(args);
11
+ const command = args[0] ?? "";
12
+ const payload = responses[command];
13
+ return { exitCode: payload === undefined ? 0 : 0, json: (payload ?? {}), stderr: "" };
14
+ };
15
+ }
16
+ test("runBattle passes when the patch has no high findings and tests pass", () => {
17
+ const dir = mkdtempSync(join(tmpdir(), "oc-pilot-battle-"));
18
+ try {
19
+ const calls = [];
20
+ const runner = fakeRunner({
21
+ fix: {
22
+ patch: DIFF,
23
+ files: ["src/app.ts"],
24
+ applied: false,
25
+ tests: { exitCode: 0, timedOut: false },
26
+ },
27
+ review: { findings: [] },
28
+ sast: { findings: [] },
29
+ authz: { findings: [] },
30
+ "test-guard": { findings: [] },
31
+ }, calls);
32
+ const report = runBattle(dir, { task: "fix the retry", diff: DIFF, runner });
33
+ assert.equal(report.status, "pass");
34
+ assert.equal(report.rounds.length, 1);
35
+ assert.equal(report.rounds[0]?.findings.length, 0);
36
+ assert.equal(report.rounds[0]?.tests?.passed, true);
37
+ assert.ok(calls.some((args) => args[0] === "review" && args.includes("--diff")));
38
+ }
39
+ finally {
40
+ rmSync(dir, { recursive: true, force: true });
41
+ }
42
+ });
43
+ test("runBattle fails without apply when the hacker finds a critical issue", () => {
44
+ const dir = mkdtempSync(join(tmpdir(), "oc-pilot-battle-"));
45
+ try {
46
+ const runner = fakeRunner({
47
+ fix: { patch: DIFF, files: ["src/app.ts"], applied: false, tests: null },
48
+ review: { findings: [{ severity: "critical", text: "command injection", location: "src/app.ts:1" }] },
49
+ sast: { findings: [] },
50
+ authz: { findings: [] },
51
+ "test-guard": { findings: [] },
52
+ });
53
+ const report = runBattle(dir, { task: "fix the retry", diff: DIFF, runner });
54
+ assert.equal(report.status, "fail");
55
+ assert.match(report.reason, /--apply/);
56
+ assert.equal(report.rounds[0]?.findings[0]?.source, "review");
57
+ }
58
+ finally {
59
+ rmSync(dir, { recursive: true, force: true });
60
+ }
61
+ });
62
+ test("runBattle stops when the coder proposes no patch", () => {
63
+ const dir = mkdtempSync(join(tmpdir(), "oc-pilot-battle-"));
64
+ try {
65
+ const runner = fakeRunner({ fix: { applied: false, error: "no patch" } });
66
+ const report = runBattle(dir, { task: "fix the retry", diff: DIFF, runner });
67
+ assert.equal(report.status, "stopped");
68
+ assert.equal(report.rounds.length, 1);
69
+ assert.match(report.reason, /no patch/);
70
+ }
71
+ finally {
72
+ rmSync(dir, { recursive: true, force: true });
73
+ }
74
+ });
75
+ test("runBattle retries on the working tree after an applied patch", () => {
76
+ const dir = mkdtempSync(join(tmpdir(), "oc-pilot-battle-"));
77
+ try {
78
+ const calls = [];
79
+ let reviewRound = 0;
80
+ const runner = (args) => {
81
+ calls.push(args);
82
+ const command = args[0];
83
+ if (command === "fix") {
84
+ return {
85
+ exitCode: 0,
86
+ json: { patch: DIFF, files: ["src/app.ts"], applied: true, tests: { exitCode: 0, timedOut: false } },
87
+ stderr: "",
88
+ };
89
+ }
90
+ if (command === "review") {
91
+ reviewRound += 1;
92
+ return {
93
+ exitCode: 0,
94
+ json: {
95
+ findings: reviewRound === 1
96
+ ? [{ severity: "high", text: "missing auth", location: "src/app.ts:2" }]
97
+ : [],
98
+ },
99
+ stderr: "",
100
+ };
101
+ }
102
+ return { exitCode: 0, json: { findings: [] }, stderr: "" };
103
+ };
104
+ const report = runBattle(dir, { task: "fix the retry", diff: DIFF, apply: true, rounds: 2, runner });
105
+ assert.equal(report.status, "pass");
106
+ assert.equal(report.rounds.length, 2);
107
+ const coderCalls = calls.filter((args) => args[0] === "fix");
108
+ assert.equal(coderCalls.length, 2);
109
+ assert.ok(coderCalls[0]?.includes("--diff"));
110
+ assert.ok(!coderCalls[1]?.includes("--diff"), "round 2 should use the working tree");
111
+ }
112
+ finally {
113
+ rmSync(dir, { recursive: true, force: true });
114
+ }
115
+ });
116
+ //# sourceMappingURL=battle.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"battle.test.js","sourceRoot":"","sources":["../../src/core/battle.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,200 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join, normalize } from "node:path";
3
+ import { splitDiffBlocks } from "./diff.js";
4
+ import { PilotError } from "./errors.js";
5
+ import { isCodeFile, isGeneratedPath, isTestFile } from "./slop/source.js";
6
+ export const DEFAULT_BOUNDARY_CONFIG = join(".oc-pilot", "architecture.json");
7
+ export const STARTER_BOUNDARY_CONFIG = `${JSON.stringify({
8
+ rules: [
9
+ {
10
+ from: "src/ui/**",
11
+ deny: ["src/db/**"],
12
+ reason: "UI не должен импортировать слой БД напрямую",
13
+ },
14
+ {
15
+ from: "src/routes/**",
16
+ deny: ["src/db/**"],
17
+ reason: "Маршруты должны ходить в БД через сервисы",
18
+ },
19
+ ],
20
+ }, null, 2)}\n`;
21
+ /** Converts a small glob subset (**, *, ?) to a regular expression. */
22
+ export function globToRegExp(pattern) {
23
+ const source = pattern.replace(/\\/g, "/").replace(/^\.\//, "");
24
+ let out = "^";
25
+ for (let index = 0; index < source.length; index += 1) {
26
+ const char = source[index];
27
+ if (char === "*") {
28
+ if (source[index + 1] === "*") {
29
+ index += 1;
30
+ if (source[index + 1] === "/") {
31
+ index += 1;
32
+ out += "(?:.*/)?";
33
+ }
34
+ else {
35
+ out += ".*";
36
+ }
37
+ }
38
+ else {
39
+ out += "[^/]*";
40
+ }
41
+ continue;
42
+ }
43
+ if (char === "?") {
44
+ out += "[^/]";
45
+ continue;
46
+ }
47
+ out += /[.+^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
48
+ }
49
+ return new RegExp(`${out}$`);
50
+ }
51
+ /** Matches a repo path or a package specifier against a rule pattern. */
52
+ export function matchesBoundary(pattern, value) {
53
+ const normalized = value.replace(/\\/g, "/");
54
+ if (!pattern.includes("/") && !pattern.includes("*") && !pattern.includes("?")) {
55
+ return normalized === pattern || normalized.startsWith(`${pattern}/`);
56
+ }
57
+ return globToRegExp(pattern).test(normalized);
58
+ }
59
+ /** Validates the architecture config and returns a typed rule list. */
60
+ export function parseBoundaryConfig(text, path) {
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(text);
64
+ }
65
+ catch {
66
+ throw new PilotError(`Не удалось разобрать ${path}`, {
67
+ hint: "Файл архитектуры должен быть корректным JSON.",
68
+ exitCode: 2,
69
+ });
70
+ }
71
+ const rules = parsed?.rules;
72
+ if (!Array.isArray(rules) || rules.length === 0) {
73
+ throw new PilotError(`В ${path} нет правил архитектуры`, {
74
+ hint: 'Ожидается {"rules":[{"from":"src/ui/**","deny":["src/db/**"]}]}.',
75
+ exitCode: 2,
76
+ });
77
+ }
78
+ return {
79
+ rules: rules.map((rule, index) => {
80
+ const value = rule;
81
+ if (typeof value?.from !== "string" || !value.from.trim()) {
82
+ throw new PilotError(`Правило ${String(index + 1)} в ${path} без from`, { exitCode: 2 });
83
+ }
84
+ if (!Array.isArray(value.deny) || value.deny.some((item) => typeof item !== "string")) {
85
+ throw new PilotError(`Правило ${String(index + 1)} в ${path} без списка deny`, { exitCode: 2 });
86
+ }
87
+ return {
88
+ from: value.from,
89
+ deny: value.deny,
90
+ ...(typeof value.reason === "string" ? { reason: value.reason } : {}),
91
+ };
92
+ }),
93
+ };
94
+ }
95
+ /** Loads an explicit config or the conventional .oc-pilot/architecture.json. */
96
+ export function loadBoundaryConfig(cwd, explicit) {
97
+ const path = explicit ?? DEFAULT_BOUNDARY_CONFIG;
98
+ const absolute = join(cwd, path);
99
+ if (!existsSync(absolute)) {
100
+ if (explicit) {
101
+ throw new PilotError(`Файл архитектуры не найден: ${path}`, { exitCode: 2 });
102
+ }
103
+ return undefined;
104
+ }
105
+ return parseBoundaryConfig(readFileSync(absolute, "utf8"), path);
106
+ }
107
+ const IMPORT_PATTERNS = [
108
+ /^\s*import\s+(?:type\s+)?(?:[^"']+?\s+from\s+)?["']([^"']+)["']/,
109
+ /^\s*export\s+(?:type\s+)?[^"']*?\s+from\s+["']([^"']+)["']/,
110
+ /\brequire\(\s*["']([^"']+)["']\s*\)/,
111
+ /\bimport\(\s*["']([^"']+)["']\s*\)/,
112
+ ];
113
+ /** Resolves a relative or alias import to a repo-relative path. */
114
+ export function resolveImport(file, specifier) {
115
+ if (specifier.startsWith("@/"))
116
+ return `src/${specifier.slice(2)}`;
117
+ if (specifier.startsWith(".")) {
118
+ const resolved = normalize(join(dirname(file), specifier)).replace(/\\/g, "/");
119
+ return resolved.replace(/^\.\//, "");
120
+ }
121
+ return specifier;
122
+ }
123
+ /** Extracts added imports from a unified diff, skipping tests and generated code. */
124
+ export function addedImports(diff) {
125
+ const imports = [];
126
+ for (const block of splitDiffBlocks(diff)) {
127
+ if (block.path === "(diff)" || block.path === "(unknown)")
128
+ continue;
129
+ if (!isCodeFile(block.path) || isTestFile(block.path) || isGeneratedPath(block.path))
130
+ continue;
131
+ let newLine = 0;
132
+ for (const line of block.text.split("\n")) {
133
+ const hunk = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
134
+ if (hunk) {
135
+ newLine = Number(hunk[1]);
136
+ continue;
137
+ }
138
+ if (line.startsWith("+++") || line.startsWith("---"))
139
+ continue;
140
+ if (line.startsWith("+")) {
141
+ const text = line.slice(1);
142
+ if (!/\bimport\s+type\b/.test(text) && !/\bexport\s+type\b/.test(text)) {
143
+ for (const pattern of IMPORT_PATTERNS) {
144
+ const match = pattern.exec(text);
145
+ if (match?.[1]) {
146
+ imports.push({ file: block.path, line: newLine, specifier: match[1] });
147
+ break;
148
+ }
149
+ }
150
+ }
151
+ newLine += 1;
152
+ }
153
+ else if (line.startsWith(" ") || line === "") {
154
+ newLine += 1;
155
+ }
156
+ }
157
+ }
158
+ return imports;
159
+ }
160
+ /** Reports imports that cross a denied architectural boundary. */
161
+ export function analyzeBoundaries(diff, config) {
162
+ const imports = addedImports(diff);
163
+ const violations = [];
164
+ const seen = new Set();
165
+ for (const use of imports) {
166
+ const resolved = resolveImport(use.file, use.specifier);
167
+ for (const rule of config.rules) {
168
+ if (!matchesBoundary(rule.from, use.file))
169
+ continue;
170
+ if (!rule.deny.some((pattern) => matchesBoundary(pattern, resolved) || matchesBoundary(pattern, use.specifier))) {
171
+ continue;
172
+ }
173
+ const key = `${use.file}:${String(use.line)}:${rule.from}:${use.specifier}`;
174
+ if (seen.has(key))
175
+ continue;
176
+ seen.add(key);
177
+ violations.push({ file: use.file, line: use.line, imported: use.specifier, resolved, rule });
178
+ }
179
+ }
180
+ violations.sort((left, right) => left.file.localeCompare(right.file) || left.line - right.line);
181
+ return {
182
+ rules: config.rules.length,
183
+ violations,
184
+ scannedFiles: new Set(imports.map((use) => use.file)).size,
185
+ status: violations.length > 0 ? "fail" : "pass",
186
+ };
187
+ }
188
+ /** Russian summary capped at ten violations. */
189
+ export function describeBoundaryViolations(violations) {
190
+ if (violations.length === 0)
191
+ return "Нарушений границ не найдено.";
192
+ const lines = violations.slice(0, 10).map((violation) => {
193
+ const reason = violation.rule.reason ? ` — ${violation.rule.reason}` : "";
194
+ return `- ${violation.file}:${String(violation.line)}: ${violation.imported} запрещён правилом ${violation.rule.from}${reason}`;
195
+ });
196
+ if (violations.length > 10)
197
+ lines.push(`…и ещё ${String(violations.length - 10)}`);
198
+ return lines.join("\n");
199
+ }
200
+ //# sourceMappingURL=boundaries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundaries.js","sourceRoot":"","sources":["../../src/core/boundaries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { addedImports, analyzeBoundaries, globToRegExp, matchesBoundary, parseBoundaryConfig, resolveImport, } from "./boundaries.js";
4
+ const DIFF = [
5
+ "diff --git a/src/ui/app.ts b/src/ui/app.ts",
6
+ "--- a/src/ui/app.ts",
7
+ "+++ b/src/ui/app.ts",
8
+ "@@ -1,1 +1,5 @@",
9
+ ' import { Button } from "@/components/button";',
10
+ '+import { db } from "../db/user";',
11
+ '+import type { User } from "../db/types";',
12
+ '+import React from "react";',
13
+ '+const legacy = require("../db/legacy");',
14
+ "",
15
+ ].join("\n");
16
+ test("globToRegExp and matchesBoundary cover the glob subset", () => {
17
+ assert.ok(globToRegExp("src/ui/**").test("src/ui/app.ts"));
18
+ assert.ok(globToRegExp("src/**/test.ts").test("src/a/b/test.ts"));
19
+ assert.ok(globToRegExp("src/**/test.ts").test("src/test.ts"));
20
+ assert.ok(!globToRegExp("src/ui/**").test("src/lib/app.ts"));
21
+ assert.ok(matchesBoundary("src/ui/**", "src/ui/app.ts"));
22
+ assert.ok(matchesBoundary("react", "react/jsx-runtime"));
23
+ assert.ok(!matchesBoundary("react", "react-dom"));
24
+ });
25
+ test("resolveImport maps aliases and relative paths", () => {
26
+ assert.equal(resolveImport("src/ui/app.ts", "@/db/user"), "src/db/user");
27
+ assert.equal(resolveImport("src/ui/app.ts", "../db/user"), "src/db/user");
28
+ assert.equal(resolveImport("src/ui/app.ts", "react"), "react");
29
+ });
30
+ test("parseBoundaryConfig validates rules and keeps reasons", () => {
31
+ const config = parseBoundaryConfig(JSON.stringify({
32
+ rules: [{ from: "src/ui/**", deny: ["src/db/**"], reason: "UI не ходит в БД" }],
33
+ }), ".oc-pilot/architecture.json");
34
+ assert.equal(config.rules.length, 1);
35
+ assert.equal(config.rules[0]?.reason, "UI не ходит в БД");
36
+ assert.throws(() => parseBoundaryConfig("{}", "architecture.json"), /нет правил/);
37
+ assert.throws(() => parseBoundaryConfig(JSON.stringify({ rules: [{ from: "src/ui/**" }] }), "architecture.json"), /без списка deny/);
38
+ });
39
+ test("addedImports reads added imports and skips type-only imports", () => {
40
+ const imports = addedImports(DIFF);
41
+ assert.deepEqual(imports.map((use) => `${use.file}:${String(use.line)}:${use.specifier}`), ["src/ui/app.ts:2:../db/user", "src/ui/app.ts:4:react", "src/ui/app.ts:5:../db/legacy"]);
42
+ });
43
+ test("analyzeBoundaries reports denied imports and passes allowed ones", () => {
44
+ const config = parseBoundaryConfig(JSON.stringify({
45
+ rules: [{ from: "src/ui/**", deny: ["src/db/**"], reason: "UI не ходит в БД" }],
46
+ }), ".oc-pilot/architecture.json");
47
+ const report = analyzeBoundaries(DIFF, config);
48
+ assert.equal(report.status, "fail");
49
+ assert.equal(report.scannedFiles, 1);
50
+ assert.deepEqual(report.violations.map((violation) => `${violation.file}:${String(violation.line)}:${violation.resolved}`), ["src/ui/app.ts:2:src/db/user", "src/ui/app.ts:5:src/db/legacy"]);
51
+ const allowed = analyzeBoundaries(DIFF, parseBoundaryConfig(JSON.stringify({ rules: [{ from: "src/ui/**", deny: ["src/services/**"] }] }), "architecture.json"));
52
+ assert.equal(allowed.status, "pass");
53
+ assert.equal(allowed.violations.length, 0);
54
+ });
55
+ test("analyzeBoundaries matches a denied package by its raw specifier", () => {
56
+ const config = parseBoundaryConfig(JSON.stringify({ rules: [{ from: "src/ui/**", deny: ["react"] }] }), "architecture.json");
57
+ const report = analyzeBoundaries(DIFF, config);
58
+ assert.equal(report.violations.length, 1);
59
+ assert.equal(report.violations[0]?.imported, "react");
60
+ assert.equal(report.violations[0]?.resolved, "react");
61
+ });
62
+ //# sourceMappingURL=boundaries.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"boundaries.test.js","sourceRoot":"","sources":["../../src/core/boundaries.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import { readRepository } from "./slop/collect.js";
2
+ import { analyzeRepository } from "./slop/index.js";
3
+ import { scanSource } from "./sast.js";
4
+ const SAST_WEIGHT = { high: 5, medium: 3, low: 1 };
5
+ function candidate(map, file) {
6
+ const existing = map.get(file);
7
+ if (existing)
8
+ return existing;
9
+ const created = { file, score: 0, reasons: [] };
10
+ map.set(file, created);
11
+ return created;
12
+ }
13
+ function add(map, file, reason) {
14
+ const entry = candidate(map, file);
15
+ entry.score += reason.weight;
16
+ entry.reasons.push(reason);
17
+ }
18
+ /** Ranks files by the mess they carry: security findings, duplication, dead code, TODOs and churn. */
19
+ export function buildBounty(cwd, options = {}) {
20
+ const limit = Math.min(20, Math.max(1, options.limit ?? 5));
21
+ const sinceDays = Math.min(365, Math.max(1, options.sinceDays ?? 30));
22
+ const slop = analyzeRepository(cwd, { sinceDays, blame: true, limit: 500 });
23
+ const scores = new Map();
24
+ for (const file of slop.duplicates) {
25
+ for (const side of [file.first, file.second]) {
26
+ add(scores, side.file, {
27
+ kind: "duplicate",
28
+ weight: 3,
29
+ detail: `дубликат блока (${String(file.tokens)} токенов)`,
30
+ });
31
+ }
32
+ }
33
+ for (const dead of slop.deadExports) {
34
+ add(scores, dead.file, {
35
+ kind: "dead-export",
36
+ weight: 2,
37
+ detail: `мёртвый экспорт ${dead.name}`,
38
+ });
39
+ }
40
+ for (const todo of slop.todos) {
41
+ const age = todo.ageDays ?? 0;
42
+ const weight = 1 + Math.min(3, Math.floor(age / 30));
43
+ add(scores, todo.file, {
44
+ kind: "todo",
45
+ weight,
46
+ detail: `TODO${age > 0 ? ` возрастом ${String(age)} дн.` : ""}: ${todo.text.slice(0, 80)}`,
47
+ });
48
+ }
49
+ for (const churn of slop.churn) {
50
+ if (churn.commits < 5)
51
+ continue;
52
+ add(scores, churn.file, {
53
+ kind: "churn",
54
+ weight: Math.min(5, Math.floor(churn.commits / 5)),
55
+ detail: `${String(churn.commits)} коммитов без достаточного числа тестов`,
56
+ });
57
+ }
58
+ let scannedFiles = 0;
59
+ try {
60
+ const repository = readRepository(cwd).files;
61
+ scannedFiles = repository.length;
62
+ for (const file of repository) {
63
+ for (const finding of scanSource(file.path, file.text).findings) {
64
+ add(scores, finding.file, {
65
+ kind: "sast",
66
+ weight: SAST_WEIGHT[finding.severity],
67
+ detail: `${finding.severity}: ${finding.rule} в строке ${String(finding.line)}`,
68
+ });
69
+ }
70
+ }
71
+ }
72
+ catch {
73
+ // The poster still works from the slop data when sources cannot be read.
74
+ }
75
+ const candidates = [...scores.values()]
76
+ .filter((entry) => entry.score > 0)
77
+ .sort((left, right) => right.score - left.score || left.file.localeCompare(right.file))
78
+ .slice(0, limit);
79
+ const worst = candidates[0];
80
+ const report = {
81
+ file: worst?.file ?? null,
82
+ score: worst?.score ?? 0,
83
+ candidates,
84
+ poster: "",
85
+ scannedFiles,
86
+ sinceDays,
87
+ };
88
+ report.poster = renderPoster(report);
89
+ return report;
90
+ }
91
+ function pad(value, width) {
92
+ return value.length >= width ? value.slice(0, width) : value + " ".repeat(width - value.length);
93
+ }
94
+ export function renderPoster(report) {
95
+ const lines = [
96
+ "+--------------------------------------------------------------+",
97
+ "| W A N T E D |",
98
+ "+--------------------------------------------------------------+",
99
+ ];
100
+ if (!report.file) {
101
+ lines.push("| Ни один файл не попал в розыск. Пока чисто. |");
102
+ lines.push("+--------------------------------------------------------------+");
103
+ return `${lines.join("\n")}\n`;
104
+ }
105
+ lines.push(`| ${pad(report.file, 58)}|`);
106
+ lines.push(`| ${pad(`Награда: ${String(report.score)} очков позора`, 58)}|`);
107
+ lines.push("+--------------------------------------------------------------+");
108
+ for (const reason of report.candidates[0].reasons.slice(0, 6)) {
109
+ lines.push(`| ${pad(`- ${reason.detail}`, 58)}|`);
110
+ }
111
+ lines.push("+--------------------------------------------------------------+");
112
+ if (report.candidates.length > 1) {
113
+ const others = report.candidates
114
+ .slice(1, 4)
115
+ .map((entry) => `${entry.file} (${String(entry.score)})`)
116
+ .join(", ");
117
+ lines.push(`| ${pad(`Сообщники: ${others}`, 58)}|`);
118
+ lines.push("+--------------------------------------------------------------+");
119
+ }
120
+ return `${lines.join("\n")}\n`;
121
+ }
122
+ export function describeBounty(report) {
123
+ if (!report.file) {
124
+ return `Розыск не требуется: ${String(report.scannedFiles)} файл(ов) проверено.`;
125
+ }
126
+ return `Худший файл: ${report.file} (${String(report.score)} очков, ${String(report.candidates[0].reasons.length)} причин).`;
127
+ }
128
+ //# sourceMappingURL=bounty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounty.js","sourceRoot":"","sources":["../../src/core/bounty.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import assert from "node:assert/strict";
2
+ import { spawnSync } from "node:child_process";
3
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import test from "node:test";
7
+ import { buildBounty, describeBounty } from "./bounty.js";
8
+ function git(args, cwd) {
9
+ const result = spawnSync("git", args, { cwd, encoding: "utf8" });
10
+ if (result.status !== 0) {
11
+ throw new Error(`git ${args.join(" ")} failed: ${result.stderr}`);
12
+ }
13
+ }
14
+ function makeRepo(files) {
15
+ const dir = mkdtempSync(join(tmpdir(), "oc-pilot-bounty-"));
16
+ git(["init", "-q"], dir);
17
+ git(["config", "user.email", "test@example.com"], dir);
18
+ git(["config", "user.name", "Test"], dir);
19
+ git(["config", "core.autocrlf", "false"], dir);
20
+ for (const [path, content] of Object.entries(files)) {
21
+ const full = join(dir, path);
22
+ mkdirSync(join(full, ".."), { recursive: true });
23
+ writeFileSync(full, content, "utf8");
24
+ }
25
+ git(["add", "."], dir);
26
+ git(["commit", "-m", "init"], dir);
27
+ return dir;
28
+ }
29
+ test("buildBounty ranks the file with a SAST finding and an old TODO", () => {
30
+ const dir = makeRepo({
31
+ "src/app.js": [
32
+ 'const { exec } = require("child_process");',
33
+ "// TODO: remove the debug command",
34
+ "exec(req.query.cmd);",
35
+ "",
36
+ ].join("\n"),
37
+ "src/clean.js": "export const value = 1;\n",
38
+ });
39
+ try {
40
+ const report = buildBounty(dir);
41
+ assert.equal(report.file, "src/app.js");
42
+ assert.ok(report.score > 0, `score ${String(report.score)}`);
43
+ const kinds = report.candidates[0]?.reasons.map((reason) => reason.kind) ?? [];
44
+ assert.ok(kinds.includes("sast"), `kinds: ${kinds.join(",")}`);
45
+ assert.ok(kinds.includes("todo"), `kinds: ${kinds.join(",")}`);
46
+ assert.match(report.poster, /W\s*A\s*N\s*T\s*E\s*D/);
47
+ assert.match(report.poster, /src\/app\.js/);
48
+ assert.match(describeBounty(report), /Худший файл: src\/app\.js/);
49
+ }
50
+ finally {
51
+ rmSync(dir, { recursive: true, force: true });
52
+ }
53
+ });
54
+ test("buildBounty is deterministic for equal scores", () => {
55
+ const dir = makeRepo({
56
+ "src/b.js": "// TODO: second\nexport const b = 2;\n",
57
+ "src/a.js": "// TODO: first\nexport const a = 1;\n",
58
+ });
59
+ try {
60
+ const first = buildBounty(dir);
61
+ const second = buildBounty(dir);
62
+ assert.deepEqual(first.candidates.map((candidate) => candidate.file), second.candidates.map((candidate) => candidate.file));
63
+ assert.equal(first.file, "src/a.js");
64
+ }
65
+ finally {
66
+ rmSync(dir, { recursive: true, force: true });
67
+ }
68
+ });
69
+ test("buildBounty reports a clean repository", () => {
70
+ const dir = makeRepo({ "README.md": "# demo\n" });
71
+ try {
72
+ const report = buildBounty(dir);
73
+ assert.equal(report.file, null);
74
+ assert.equal(report.score, 0);
75
+ assert.match(report.poster, /Пока чисто/);
76
+ assert.match(describeBounty(report), /Розыск не требуется/);
77
+ }
78
+ finally {
79
+ rmSync(dir, { recursive: true, force: true });
80
+ }
81
+ });
82
+ //# sourceMappingURL=bounty.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounty.test.js","sourceRoot":"","sources":["../../src/core/bounty.test.ts"],"names":[],"mappings":"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"}