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
package/README.md CHANGED
@@ -1,12 +1,16 @@
1
1
  # oc-pilot
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/oc-pilot?style=flat-square&color=black)](https://www.npmjs.com/package/oc-pilot)
4
+ [![Node.js](https://img.shields.io/node/v/oc-pilot?style=flat-square&color=black)](https://nodejs.org)
5
+ [![License](https://img.shields.io/badge/license-MIT-black?style=flat-square)](LICENSE)
6
+
3
7
  Local AI code review, failing-test analysis, documentation generation, and secret scanning for agent CLIs.
4
8
 
5
9
  `oc-pilot` is a small CLI and MCP server for developers who write code with AI agents. It reviews an uncommitted diff, follows the project beyond the changed lines, catches leaked credentials before they reach a model, explains a failing test, and returns findings as human-readable Markdown or machine-readable JSON.
6
10
 
7
11
  It works with OpenCode out of the box, with any OpenAI-compatible API (OpenAI, DeepSeek, Groq, OpenRouter, Ollama), and with any MCP-capable agent: Claude Code, OpenAI Codex CLI, Cursor CLI, Gemini CLI, and others.
8
12
 
9
- [Русская документация](README.ru.md)
13
+ [English](README.md) | [Русская документация](README.ru.md) | [MCP integrations](docs/integrations.md) | [Security](SECURITY.md) | [Contributing](CONTRIBUTING.md) | [Changelog](CHANGELOG.md)
10
14
 
11
15
  ## Why
12
16
 
@@ -28,19 +32,19 @@ Agents are good at producing code and bad at reviewing their own work. A normal
28
32
 
29
33
  ## Install
30
34
 
31
- From GitHub, pinned to a release tag (works before the first npm release):
35
+ From npm. The latest published release is **0.5.1**:
32
36
 
33
37
  ```bash
34
- npm install -g https://github.com/MyNameIsNikki/oc-pilot/archive/refs/tags/v0.5.1.tar.gz
38
+ npm install -g oc-pilot
35
39
  ```
36
40
 
37
- From npm, once published:
41
+ From the repository, pinned to a release tag. Tag **v0.5.2** is a version-only release with identical runtime behavior:
38
42
 
39
43
  ```bash
40
- npm install -g oc-pilot
44
+ npm install -g https://github.com/MyNameIsNikki/oc-pilot/archive/refs/tags/v0.5.2.tar.gz
41
45
  ```
42
46
 
43
- For development:
47
+ From source:
44
48
 
45
49
  ```bash
46
50
  git clone https://github.com/MyNameIsNikki/oc-pilot.git
@@ -58,7 +62,7 @@ oc-pilot-mcp --version
58
62
  oc-pilot mcp --version
59
63
  ```
60
64
 
61
- For development, run `node dist/cli.js` and `node dist/mcp.js` directly.
65
+ During development, run `node dist/cli.js` and `node dist/mcp.js` directly.
62
66
 
63
67
  ## Quick start (OpenCode)
64
68
 
@@ -118,6 +122,9 @@ oc-pilot review --engine openai --model deepseek-chat \
118
122
  # Local model through Ollama (no key needed on loopback)
119
123
  oc-pilot review --engine openai --model qwen2.5-coder \
120
124
  --base-url http://127.0.0.1:11434/v1
125
+
126
+ # The same, through a preset
127
+ oc-pilot review --engine-preset ollama --model qwen2.5-coder
121
128
  ```
122
129
 
123
130
  | Engine | Best for | Repository tools | Requires |
@@ -127,6 +134,70 @@ oc-pilot review --engine openai --model qwen2.5-coder \
127
134
 
128
135
  The key is read from `OC_PILOT_API_KEY`, or `OPENAI_API_KEY` when the default variable name is used. Set `apiKeyEnv` to use another variable. Logs and errors never print the key. With the `openai` engine, `repoContext` is forced off and `FULL_FILE_CONTEXT` is added automatically.
129
136
 
137
+ ### Engine presets
138
+
139
+ For the common OpenAI-compatible providers, `--engine-preset` fills in the base URL, the key variable and (where it is a sensible default) the model:
140
+
141
+ | Preset | Base URL | Key variable | Default model |
142
+ | --- | --- | --- | --- |
143
+ | `ollama` | `http://localhost:11434/v1` | `OLLAMA_API_KEY` (not needed on loopback) | `llama3.2` |
144
+ | `lmstudio` | `http://localhost:1234/v1` | `LMSTUDIO_API_KEY` (not needed on loopback) | — |
145
+ | `openrouter` | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` | — |
146
+
147
+ A preset switches the engine to `openai` unless you say otherwise, and explicit flags always win: `oc-pilot review --engine-preset ollama --model qwen2.5-coder` keeps the preset endpoint and overrides only the model.
148
+
149
+ ### Anthropic engine
150
+
151
+ `--engine anthropic` talks to the Claude Messages API directly, without an SDK:
152
+
153
+ ```bash
154
+ ANTHROPIC_API_KEY=sk-ant-... oc-pilot review --engine anthropic --model claude-sonnet-4-5
155
+ ```
156
+
157
+ The system prompt is sent as a `system` block with `cache_control: {"type": "ephemeral"}`, so repeated reviews reuse the cached prefix. Requests use `anthropic-version: 2023-06-01` and `x-api-key`. Token usage is mapped onto the same cost table as the OpenAI engine, so `--max-cost` and `oc-pilot stats` work unchanged.
158
+
159
+ Slicing, the two-phase router and the response cache are `openai`-only; the Anthropic engine always sends one request per run.
160
+
161
+ ### Smart slicing (`openai`)
162
+
163
+ When a diff is too large for a single prompt (300 000 characters), the `openai` engine does not truncate files away. Instead it groups the per-file diff blocks into slices that each fit the budget, runs one API call per slice with the same additional context, and merges the responses back into a single schema-compliant analysis (`meta.slices` shows how many calls were made). Up to three slices run in parallel and their answers are merged in the original file order. A single file whose diff exceeds the budget is sent as its own slice and only then truncated. Slicing applies to `review`, `test-fix`, `custom` and `commit --engine openai`; `fix` keeps a single prompt because it must return one patch.
164
+
165
+ ### Retries
166
+
167
+ Transient failures are retried instead of failing the run: HTTP 408, 425, 429, 500, 502, 503 and 504 are attempted up to three times with an exponential delay (500 ms, 1 s, 2 s, capped at 8 s). A `Retry-After` header wins over the computed delay. Authentication errors, other 4xx responses, network errors and timeouts still fail immediately, because retrying them only burns time. Slice calls share the same policy.
168
+
169
+ ### Two-phase routing (`openai`)
170
+
171
+ `review` can route through a cheap triage model before the main review call. The triage model sees the same input with a compact system prompt and answers with a single line: `TRIAGE: REVIEW` or `TRIAGE: LGTM`. Trivially safe diffs (docs, formatting, version bumps, renames) return `LGTM` and skip the main call entirely; everything else runs the full review. Any triage failure or unparsable answer falls back to the full review, so routing can only save money, never skip a real problem. Secret gating (`--fail-on-secrets`) is evaluated independently of the router.
172
+
173
+ ```bash
174
+ oc-pilot review --router --router-model gpt-4.1-mini --engine openai --model gpt-4.1
175
+ ```
176
+
177
+ Enable it per run with `--router`, or permanently with `routerEnabled: true` and `routerModel` in `.oc-pilot.json` (or `OC_PILOT_ROUTER` / `OC_PILOT_ROUTER_MODEL`). `--no-router` disables it for one run. The JSON report includes `meta.router` with the model, decision and duration.
178
+
179
+ ### Provider-cache-friendly prompts
180
+
181
+ oc-pilot orders every prompt so the stable parts come first and the volatile `GIT_DIFF` comes last: instruction, project type, recipe, repository flags, additional context and the secret report are identical across slices, fix rounds and repeated reviews of the same change. Providers with prefix caching (OpenAI, DeepSeek, Anthropic, OpenRouter) reuse that stable prefix automatically, so the second and later calls on the same context are cheaper and faster. No configuration is needed.
182
+
183
+ ### Response cache (`openai`)
184
+
185
+ The `openai` engine caches full model responses on disk. The cache key is the sha256 of the model, base URL, system prompt and input, so re-running the same command on the same diff (typical when wiring hooks, CI or scripts) costs zero tokens and returns instantly. Entries live in `.git/oc-pilot/cache/` — inside the repository metadata, never in the worktree or in commits.
186
+
187
+ The TTL defaults to 24 hours (`cacheTtlSeconds`, or `OC_PILOT_CACHE_TTL`; `0` disables caching). `--no-cache` bypasses the cache for one run, and `oc-pilot cache --stats | --prune | --clear` inspects and maintains it. `doctor --fix` prunes expired entries. The JSON report shows `meta.cache: "hit" | "miss"`. The cache only applies to the `openai` engine (triage, main and slice calls); OpenCode runs are never cached, and a cache error can never fail a review.
188
+
189
+ ### Second opinion (`openai`)
190
+
191
+ `review --second-opinion` re-checks up to 10 critical findings with a second, skeptical model before failing the build. Each verdict is a single line: `VERDICT: CONFIRM` keeps the finding, `VERDICT: REJECT — <reason>` downgrades it to style and annotates the bullet with `[second opinion: rejected — reason]`. If the verifier errors or is unreachable, findings are kept as-is — verification never weakens a review on failure. Configure the verifier with `--second-opinion-model` / `--second-opinion-base-url` / `--second-opinion-api-key-env` or the config keys `secondOpinionEnabled`, `secondOpinionModel`, `secondOpinionBaseUrl`, `secondOpinionApiKeyEnv`; the result is reported in `meta.secondOpinion`.
192
+
193
+ ### Usage limits and statistics
194
+
195
+ Every run and API call is recorded to `.git/oc-pilot/stats.jsonl` (findings, engine, exit code, cache hits). `oc-pilot stats` prints runs, API calls, cache hits and findings for today, the week and all time (`--clear` resets). A daily API-call budget can be enforced with `maxDailyApiCalls` or `OC_PILOT_MAX_DAILY_API_CALLS` (`0` disables it); once the limit of uncached calls is reached, the command exits 1 — cache hits never consume the budget. `oc-pilot day --since "1 day ago"` summarizes what the agent did: commits, churn per file, added/deleted lines and oc-pilot findings. A spend cap works the same way: `--max-cost 0.5` (or `maxCostUsd` / `OC_PILOT_MAX_COST`) adds up the estimated cost of today's uncached calls — tokens from the API response times the built-in price table — and refuses the next call once the cap is reached. Pricing is estimated, so treat it as a guard rail rather than an invoice; unknown models count as zero and cache hits are free.
196
+
197
+ ### Agent rules awareness
198
+
199
+ If the project has an `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.cursorrules` or `.github/copilot-instructions.md`, oc-pilot includes it in every prompt (first match, up to 10 000 characters, secrets masked). This keeps the reviewer aware of the same conventions the coding agent follows. Disable with `agentRules: false` in the config or `OC_PILOT_AGENT_RULES=0`.
200
+
130
201
  ## Commands
131
202
 
132
203
  ### `review`
@@ -152,6 +223,55 @@ git diff | oc-pilot review --stdin
152
223
  | `--context <text\|@file>` | Extra context for the model |
153
224
  | `--no-secret-scan` | Disable secret scanning and redaction |
154
225
  | `--fail-on-secrets` | Exit 3 on critical secrets |
226
+ | `--router` | Route through a cheap triage model first (`openai`) |
227
+ | `--no-router` | Disable routing enabled in the config |
228
+ | `--router-model <m>` | Triage model (default: `routerModel` from the config) |
229
+ | `--baseline` | Hide findings already recorded in `.oc-pilot/baseline.json` |
230
+ | `--update-baseline` | Record current findings as the baseline and exit |
231
+ | `--pr` | Post inline review comments to the PR (Actions REST or `gh`) |
232
+ | `--no-pr-inline` | Post a single summary comment instead of inline comments |
233
+ | `--pr-status` | Post a blocking commit status (`failure` on critical findings) |
234
+ | `--mr` | Post the review to a GitLab merge request (`CI_MERGE_REQUEST_IID`) |
235
+ | `--no-mr-inline` | Post a single summary note instead of inline discussions |
236
+ | `--mr-status` | Set the `oc-pilot` commit status in GitLab (`GITLAB_TOKEN`) |
237
+ | `--second-opinion` | Verify critical findings with a second model (`openai`) |
238
+ | `--second-opinion-model <m>` | Verifier model (default: `secondOpinionModel` or the main model) |
239
+ | `--require-tests` | Exit 3 when logic changed without tests |
240
+ | `--no-test-weakening` | Exit 3 when tests were weakened (skips, removed assertions) |
241
+ | `--interactive` | Ask per finding whether to hide it in the baseline (TTY) |
242
+
243
+ With `--baseline`, findings already recorded in `.oc-pilot/baseline.json` are hidden from the output and no longer affect the exit code, JSON, SARIF or PR comment — only new findings block. A finding fingerprint is `severity | file | normalized text`, without the line number, so known findings survive code shifts. `--update-baseline` rewrites the file from the current review and exits 0; `--interactive` hides findings one by one and appends them to the baseline. `--pr` posts the review to the current PR: inside GitHub Actions it uses the review API with `GITHUB_TOKEN` and attaches up to 25 findings as inline comments on their diff lines (grant `permissions: pull-requests: write`); findings that do not map to a changed line, and every run outside Actions, fall back to one summary comment (locally through `gh pr comment`, which must be installed and authenticated). `--no-pr-inline` forces that summary comment. `--pr-status` posts a commit status under the `oc-pilot` context - `failure` while critical findings remain, `success` otherwise - so a branch protection rule can require it (needs `statuses: write`). It works from text and SARIF runs alike.
244
+
245
+ Inside GitLab CI, `--mr` posts the review to the merge request: up to 25 findings become inline discussions anchored to their diff lines, and the summary always lands as a note. `CI_MERGE_REQUEST_IID`, `CI_PROJECT_ID` and `CI_API_V4_URL` identify the merge request, and `GITLAB_TOKEN` needs the `api` scope (`CI_JOB_TOKEN` cannot write notes). The discussion position needs `base_sha`/`start_sha`/`head_sha`: they are read from `CI_MERGE_REQUEST_DIFF_BASE_SHA`, `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` and `CI_COMMIT_SHA`, and when those are absent the tool asks the merge request for its `diff_refs` instead. `--no-mr-inline` keeps the summary note only. Add `--mr-status` to set the `oc-pilot` commit status as well. Every part degrades to a warning when the pipeline context, the token or a single position is missing, so a review never fails because of a missing integration.
246
+
247
+ A minimal job looks like this:
248
+
249
+ ```yaml
250
+ oc-pilot:
251
+ image: node:22
252
+ rules:
253
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
254
+ script:
255
+ - npm i -g oc-pilot
256
+ - oc-pilot review --base "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" --fail-on critical --mr --mr-status
257
+ variables:
258
+ OC_PILOT_API_KEY: $OC_PILOT_API_KEY
259
+ GITLAB_TOKEN: $GITLAB_TOKEN
260
+ ```
261
+
262
+ Before the model runs, `review` also inspects the diff locally: newly added dependencies are checked for typosquats, unpinned versions and VCS/URL specs across npm, PyPI, Go, Cargo, RubyGems, Packagist, Maven/Gradle and NuGet manifests (`DEPENDENCY_CHANGES` hint, `meta.dependencies`); a large logic change (20+ added code lines) with no test files produces a `TEST_COVERAGE_HINT` (`--require-tests` turns it into exit 3, `meta.testDelta`); weakened tests produce a `TEST_WEAKENING` hint (skipped or focused tests, removed assertions without replacements, commented-out assertions, raised timeouts; `--no-test-weakening` turns it into exit 3, `meta.testWeakening`); and added lines matching prompt-injection heuristics (instruction overrides, role hijacking, prompt exfiltration, base64 blobs, invisible Unicode) produce a `PROMPT_INJECTION_RISKS` warning (`meta.injection`).
263
+
264
+ See [Second opinion](#second-opinion-openai) for the verification flow.
265
+
266
+ ### Suppressing findings in code
267
+
268
+ An accepted trade-off should not come back as a finding on every review. Mark it at the source:
269
+
270
+ ```go
271
+ func pay() {} // oc-pilot:ignore: nil is impossible for a legacy wallet
272
+ ```
273
+
274
+ The marker suppresses a finding whose target is that line or the line right below it, so it also works as a comment on its own line above the code. `// oc-pilot:ignore-file: code generated by protoc` in the first ten lines silences the whole file. A reason is mandatory: a bare `oc-pilot:ignore` is ignored, the finding stays, and the run warns about it. Suppressed findings disappear from the text, JSON, SARIF and PR or MR comments and do not affect the exit code, while `meta.suppressed` lists each one with its file, line and reason. `oc-pilot:ignore-secret` belongs to the secret scanner and never hides a review finding.
155
275
 
156
276
  ### `test-fix`
157
277
 
@@ -166,6 +286,27 @@ npm test 2> failures.log; oc-pilot test-fix --log failures.log
166
286
 
167
287
  A passing test run is skipped unless `--force` is used.
168
288
 
289
+ ### `test-gen`
290
+
291
+ Turns a diff into tests that actually run. The model writes tests for the behavior changed in the diff, the patch is applied, the test command runs, and only the green result stays: a failing attempt is rolled back and the failure log goes back to the model as feedback.
292
+
293
+ ```bash
294
+ oc-pilot test-gen --staged --apply
295
+ oc-pilot test-gen --staged --coverage --apply
296
+ oc-pilot test-gen --from-slop --apply
297
+ oc-pilot test-gen src/billing.ts --apply --rounds 1
298
+ oc-pilot test-gen --base main --json
299
+ oc-pilot test-gen --diff changes.diff
300
+ ```
301
+
302
+ Without `--apply` the command only prints the patch. The patch may touch test files only (`*.test.ts`, `*.spec.ts`, `*_test.go`, `test_*.py`, or paths under `test/`); a production file in the patch rejects the whole round. Existing test files are protected unless `--force` is passed, so generated tests never overwrite hand-written ones. `--no-run` applies without executing the suite, `--keep-failing` keeps a red test file for debugging, and `--rounds <n>` (default 2) controls how many times a failure is sent back to the model.
303
+
304
+ `--coverage` narrows the request to changed lines that no test executed yet. The command reads an lcov, Istanbul JSON, Cobertura XML or Go cover profile report (`--coverage-file <path>` overrides the auto-detected `coverage/lcov.info`, `coverage/coverage-final.json`, `cobertura.xml`, `coverage.out` and similar files), intersects it with the lines added by the diff, and sends only those gaps to the model. When every changed line is already covered, it exits 0 without calling the model.
305
+
306
+ `--from-slop` ignores the diff and lets the slop scan choose the work: it looks for source files that have no test at all and ranks them by churn, duplicated blocks and unresolved TODOs. `--slop-files <n>` (default 3) caps how many files are sent. The command refuses to mix `--from-slop` with `--diff`, `--staged`, `--base`, `--coverage` or explicit paths, because those already define the input. When every source file has a test, it exits 0 without calling the model.
307
+
308
+ The JSON payload is `{applied, patch, files, status, tests, rounds, analysis, meta, coverage, targets}` with `status` equal to `pass`, `fail` or `not-run` and `coverage` holding `{report, format, uncoveredLines, gaps, missing}` (or `null` without `--coverage`); `targets` lists the slop-selected files with their scores and reasons (or `null` without `--from-slop`). The exit code is 1 while the tests stay red.
309
+
169
310
  ### `fix`
170
311
 
171
312
  Asks the model for a unified diff, checks that it applies, and optionally applies it in the project. This is the only command that can modify your working tree, and only with `--apply`.
@@ -180,6 +321,10 @@ oc-pilot fix --apply --command "npm test"
180
321
  # Apply but keep the patch even when tests fail
181
322
  oc-pilot fix --apply --command "go test ./..." --keep-on-test-failure
182
323
 
324
+ # Fix SAST findings in the changed files
325
+ oc-pilot fix --from-sast --staged --apply
326
+ oc-pilot fix --from-authz --staged --apply
327
+
183
328
  # Emit GitHub Actions annotations for the findings
184
329
  oc-pilot review --github
185
330
  ```
@@ -190,9 +335,16 @@ oc-pilot review --github
190
335
  | `--command <cmd>` | Test command after applying; defaults to `testCommand` or auto-detection |
191
336
  | `--no-tests` | Apply without running tests |
192
337
  | `--keep-on-test-failure` | Do not revert a patch when tests fail |
338
+ | `--no-snapshot` | Skip the safety snapshot before applying |
339
+ | `--from-sast` | Fix high-severity SAST findings in the changed files, then rescan |
340
+ | `--from-authz` | Fix high-severity authorization findings in the changed routes, then rescan |
193
341
 
194
342
  `fix` never commits or pushes. The patch is reverted when tests fail unless `--keep-on-test-failure` is used. Unrelated working-tree changes are not reverted.
195
343
 
344
+ Before applying, `fix` saves a snapshot under `.git/oc-pilot/snapshots/` (tracked changes as a binary patch plus copies of untracked files). Snapshots stay local: they are never committed or pushed. Restore the snapshot with `oc-pilot restore`; skip it with `--no-snapshot`.
345
+
346
+ With `--from-sast`, `fix` scans the changed files with the built-in SAST rules, asks the model for a patch that removes those findings, applies it, and rescans. The JSON payload gains `sast: {before, after, remaining}`; the exit code is 1 when high-severity findings remain. With `--from-authz`, the same loop targets route authorization findings (an unauthenticated route, an IDOR lookup, an admin path without a role) and the payload gains `authz: {before, after, remaining}`; `--from-sast` and `--from-authz` cannot be combined.
347
+
196
348
  ### `docgen`
197
349
 
198
350
  Generates GoDoc, RustDoc, JSDoc, Python docstrings, and similar comments without changing the code.
@@ -218,10 +370,343 @@ Offline secret scan. No model call.
218
370
  ```bash
219
371
  oc-pilot scan
220
372
  oc-pilot scan --staged --json
373
+ oc-pilot scan --all
221
374
  git diff main...HEAD | oc-pilot scan --stdin
375
+
376
+ # SARIF 2.1.0 for GitHub Code Scanning
377
+ oc-pilot scan --staged --format sarif -o oc-pilot.sarif
378
+ ```
379
+
380
+ Exit 1 means critical secrets were found. `--strict` also fails on warnings. `--format sarif` writes a SARIF 2.1.0 report (rules `oc-pilot/secret`, `oc-pilot/critical`, `oc-pilot/warning`) that GitHub Code Scanning can upload; secret previews stay redacted.
381
+
382
+ `--all` scans the whole tree instead of a diff. It respects `.gitignore` and `.oc-pilot/ignore`, skips binaries, `node_modules`, `dist` and files over 512 KB, and reports how many files were checked (`meta.filesScanned`).
383
+
384
+ ### `preflight`
385
+
386
+ One offline verdict before a commit. Six checks run without a model and without network: secrets in the diff, a deterministic SAST pass over the changed source files (command and SQL injection, path traversal, SSRF, code injection, XSS and more), route authorization over the changed source files (an unauthenticated route, an IDOR lookup, an admin path without a role), dependency health scoped to the files the diff touches, weakened tests (`it.skip`, removed assertions, raised timeouts), and the Slop Index delta against a base ref.
387
+
388
+ ```bash
389
+ oc-pilot preflight
390
+ oc-pilot preflight --staged
391
+ oc-pilot preflight --base main --max-delta 0
392
+ oc-pilot preflight --no-slop --fail-on-issue medium
393
+ oc-pilot preflight --staged --no-sast
394
+ ```
395
+
396
+ Dependency issues are scoped: preflight reports only problems in files the diff changes or packages named in added lines, so a legacy repository does not fail on the first run. Findings recorded in `.oc-pilot/baseline.json` are hidden by default, so the gate reports only new problems; `receipt` uses the same baseline. `--fail-on-issue` (default `high`) sets the gate for SAST, route authorization and dependency severity, and `--max-delta` (default 10 points) sets the Slop Index gate; each check can be disabled with `--no-secret-scan`, `--no-sast`, `--no-authz`, `--no-deps`, `--no-tests` or `--no-slop`. The exit code is 1 if any check fails and 0 otherwise, so one CI step replaces six.
397
+
398
+ The JSON report is `{status, checks, diff}`, where each check carries `{id, label, status, summary, details}` with `status` equal to `pass`, `fail` or `skip`.
399
+
400
+ ### `receipt`
401
+
402
+ Turns "it works on my machine" into a checked claim. A receipt runs the tests and the preflight (including SAST and authz), then records the result together with the commit SHA, branch, dirty state, diff stats, and the exact commands. It is written to `.git/oc-pilot/receipt.json`, so it never ends up in the working tree. Pass `--no-sast` or `--no-authz` to skip a check for a change where it does not apply.
403
+
404
+ ```bash
405
+ oc-pilot receipt --staged
406
+ oc-pilot receipt --staged --no-sast
407
+ oc-pilot receipt --json
408
+ oc-pilot receipt --verify
409
+ oc-pilot receipt --verify --receipt path/to/receipt.json
410
+ oc-pilot receipt --keygen receipt.key
411
+ oc-pilot receipt --sign --key receipt.key
412
+ oc-pilot receipt --verify --public-key receipt.key.pub
413
+ ```
414
+
415
+ The file is sealed with a SHA-256 hash of its body. `--verify` recomputes the hash and compares the commit with the current `HEAD`, so an edited file or a receipt from an older commit is reported as invalid: `{path, ok, hashOk, stale, commit, head, signature, reason?}`. The exit code is 1 when the tests fail, the preflight fails or the receipt does not verify.
416
+
417
+ A hash proves integrity, not authorship. `--sign` signs the hash with an Ed25519 key: `--keygen <file>` writes a private key (mode 0600) and `<file>.pub`; `--key` or `OC_PILOT_RECEIPT_KEY` points at the private key; the receipt gains a `signature` field. Verification checks the signature when a public key is available: `--public-key <file>`, `OC_PILOT_RECEIPT_PUBLIC_KEY`, or the committed `.oc-pilot/receipt.pub`. The `signature` status is `valid`, `invalid`, `missing` or `unchecked`; an unsigned receipt fails verification when a key is supplied. `ready` verifies the signature too when `.oc-pilot/receipt.pub` exists. Keep the private key outside the repository.
418
+
419
+ A receipt carries `{version, tool, toolVersion, createdAt, commit, branch, dirty, diff, files, tests, preflight, hash, signature?}`. `tests` is `{command, exitCode, timedOut, durationMs, passed, outputTail}` or `null` with `--no-run`; `preflight` is the same report `preflight` prints. Outside a repository the receipt can only be written to an explicit path with `--out`.
420
+
421
+ ### `ready`
422
+
423
+ Scores how ready a repository is for an AI agent. One offline number from 0 to 100 across nine checks: tests (15), lockfile (10), git hooks (15), Slop Index (15), a fresh receipt (10), agent instructions (10), CI (5), config (5) and security (15). Each check is `pass`, `warn` or `fail` and earns 100%, 50% or 0% of its weight. The security check runs the deterministic SAST and route-authorization rules over the repository and respects `.oc-pilot/baseline.json`. No model call, no network, but it does require a git repository.
424
+
425
+ ```bash
426
+ oc-pilot ready
427
+ oc-pilot ready --json
428
+ oc-pilot ready --badge
429
+ oc-pilot ready --fail-under 85
430
+ oc-pilot ready --fix
431
+ oc-pilot ready --fix --dry-run
432
+ oc-pilot ready --fix --receipt
433
+ ```
434
+
435
+ The score maps to a grade: 85 and above is `agent-ready`, 65 and above `almost`, 40 and above `risky`, anything lower `not-ready`. `--badge` prints a shields.io markdown badge for the README, and `--fail-under <n>` exits with code 1 below the threshold, so the score can gate a pull request.
436
+
437
+ `--fix` applies only safe, offline repairs: it installs the `pre-commit` hook when no hook exists, writes `.oc-pilot.json` with the detected test command, and creates `AGENTS.md` with the project commands. A foreign hook, an existing config or existing agent instructions are never overwritten; the report lists those as skipped together with checks that need a human, such as the receipt, the lockfile and the Slop Index. `--dry-run` prints the plan without touching the tree, and the score is recomputed after the fixes.
438
+
439
+ `--receipt` turns `--fix` into a full loop: after the repairs it runs the test command, runs the preflight and saves a receipt for `HEAD`, so the receipt check can pass in the same run. It requires `--fix`; without a test command the receipt is listed as skipped. A failed test run or a failed preflight still produces a receipt — the report says which part failed.
440
+
441
+ The JSON payload is `{score, grade, checks, commit}`; each check is `{id, label, status, points, max, detail}`, where `detail` names the file that earned or lost the points and the command that fixes it. With `--fix` the payload also carries `fix: {applied, dryRun, fixes, skipped, receipt}`, where `receipt` is `{path, testsPassed, preflight}` or omitted.
442
+
443
+ ### `mutate`
444
+
445
+ Mutation testing answers a harder question than coverage: do the tests actually catch bugs? The command flips comparison and boolean operators in the changed source files, runs the test suite against each mutant and counts how many mutants the tests killed. A surviving mutant is a line the suite executes but does not really check.
446
+
447
+ ```bash
448
+ oc-pilot mutate --staged
449
+ oc-pilot mutate --base main --max-mutants 10
450
+ oc-pilot mutate src/billing.ts --min-score 80
451
+ oc-pilot mutate --diff changes.diff --json
452
+ ```
453
+
454
+ Operators are `===`/`!==`, `==`/`!=`, `>=`/`<`, `<=`/`>`, `&&`/`||` and `true`/`false`; strings and comments are masked, so only real code is mutated. `--max-mutants <n>` caps the run (default 20, max 500) because every mutant executes the whole suite. `--min-score <n>` exits with code 1 when the mutation score is below the threshold, which makes it a CI gate. A timeout counts as a kill: the mutant changed observable behavior.
455
+
456
+ The JSON payload is `{command, files, sites, mutants, killed, survived, timedOut, score, truncated, durationMs}`; each mutant is `{file, line, column, operator, original, replacement, status, durationMs}` with `status` equal to `killed`, `survived` or `timeout`.
457
+
458
+ ### `flaky`
459
+
460
+ Runs the test command several times and compares per-test results. A test that passes in some runs and fails in others is flaky; one that fails every time is simply broken. The command never calls a model: it only runs the suite and parses the output.
461
+
462
+ ```bash
463
+ oc-pilot flaky
464
+ oc-pilot flaky --runs 5
465
+ oc-pilot flaky --shuffle --command "node --test {files}"
466
+ oc-pilot flaky --command "go test ./..." --json
467
+ oc-pilot flaky --fail-on-flaky
468
+ ```
469
+
470
+ `--runs <n>` defaults to 3 and caps at 10. `--command` overrides `testCommand` from `.oc-pilot.json` and the detected test command. `--fail-on-flaky` exits with code 1 when at least one test flipped, which makes it usable as a CI gate.
471
+
472
+ `--shuffle` turns the run into an order test. It requires `{files}` in `--command`, collects the test files from the repository, shuffles them with a seeded PRNG and substitutes the list into the command. Every run gets a fresh seed derived from the base one, so a test that fails only after a particular neighbour shows up as flaky. `--seed <value>` fixes the base seed when you need to reproduce a failure.
473
+
474
+ Per-test results are parsed from the Node test runner spec reporter, TAP, Go (`--- PASS`/`--- FAIL`), pytest (`PASSED`/`FAILED`) and Jest/Vitest (`✓`/`✕`). If the output carries no recognizable markers, `parsed` is false and the report only tells you that the exit codes were compared.
475
+
476
+ The JSON payload is `{command, runs, completed, parsed, tests, flaky, failing, shuffle}`; each test is `{name, passes, failures, status}` with `status` equal to `flaky`, `failing` or `passing`. With `--shuffle`, `shuffle` is `{seed, files}`.
477
+
478
+ ### `authz`
479
+
480
+ Authorization review of HTTP routes: a route that never checks the session, an object fetched by an id from the request without an ownership check (IDOR), and an admin path without a role guard. It reads the changed source files and never calls a model.
481
+
482
+ ```bash
483
+ oc-pilot authz --staged
484
+ oc-pilot authz --public /health,/login
485
+ oc-pilot authz --staged --fail-on-issue medium
486
+ oc-pilot authz --staged --format sarif -o authz.sarif
487
+ ```
488
+
489
+ Express, Fastify and Koa route calls (`app.get`, `router.post`, ...) are recognized, plus Next.js `app/**/route.ts` and `pages/api/**`. A handler passed by reference (`router.get("/x", controller.list)`) is skipped because the guard may live in the controller. Routes under `/health`, `/login`, `/signup`, `/public`, `/webhooks`, `/oauth` and similar are treated as public; `--public <prefixes>` extends that list.
490
+
491
+ Suppress a deliberate exception on the route line or the line above:
492
+
493
+ ```js
494
+ // oc-pilot:authz-ignore idor: the gateway scopes the query by tenant
495
+ router.get("/users/:id", handler);
496
+ ```
497
+
498
+ A suppression without a reason is ignored. `--ignore <rules>` skips whole rules and `--no-suppressions` disables inline suppressions.
499
+
500
+ `--fail-on-issue <high|medium>` sets the exit threshold (default `high`). `--format sarif` emits SARIF 2.1.0 with the rule id, method and route.
501
+
502
+ `--baseline <file>` hides findings already recorded in `.oc-pilot/baseline.json`, so only new findings fail the run; `--update-baseline` writes the current findings to that file and exits with code 0, which lets a legacy repository adopt the gate without fixing everything at once. The baseline stores SHA-256 fingerprints of the rule, file and snippet (or method and route for `authz`), so entries survive line moves. `preflight` and `receipt` read the same default baseline.
503
+
504
+ The JSON payload is `{files, routes, findings, suppressed, summary}`; each route is `{file, line, method, route, public}` and each finding adds `{rule, severity, method, route}` to the location. With `--baseline` the payload adds `baselineSuppressed`; with `--update-baseline` it adds `baseline: {path, updated, entries}`.
505
+
506
+ ### `sast`
507
+
508
+ Deterministic static analysis of the changed files: command and SQL injection, path traversal, SSRF, code execution, XSS, open redirects, disabled TLS verification, wildcard CORS, prototype pollution, weak hashes and JWT `none`. No model call, no network: the same diff always produces the same findings.
509
+
510
+ ```bash
511
+ oc-pilot sast --staged
512
+ oc-pilot sast --base main --fail-on-issue medium
513
+ oc-pilot sast src/server.ts --json
514
+ oc-pilot sast --staged --format sarif -o sast.sarif
222
515
  ```
223
516
 
224
- Exit 1 means critical secrets were found. `--strict` also fails on warnings.
517
+ The analysis is taint-aware but heuristic: a sink is reported when request data (`req.query`, `req.body`, `req.params`, headers, cookies, `userInput`, `untrusted*`) or a template interpolation reaches it within the same expression. Constants and parameterized queries are not reported. `path.resolve`, `path.normalize`, `path.basename` and `path.join` suppress path traversal findings.
518
+
519
+ Suppress a deliberate exception on the finding line or the line above:
520
+
521
+ ```js
522
+ exec(req.query.cmd); // oc-pilot:sast-ignore command-injection: admin-only endpoint
523
+ ```
524
+
525
+ A suppression without a reason is ignored. `--ignore <rules>` skips whole rules, `--no-suppressions` disables inline suppressions for an audit run.
526
+
527
+ `--fail-on-issue <high|medium|low>` sets the exit code threshold (default `high`). `--format sarif` emits SARIF 2.1.0 for GitHub code scanning and other SARIF consumers.
528
+
529
+ `--baseline <file>` hides findings already recorded in `.oc-pilot/baseline.json`, so only new findings fail the run; `--update-baseline` writes the current findings to that file and exits with code 0, which lets a legacy repository adopt the gate without fixing everything at once. The baseline stores SHA-256 fingerprints of the rule, file and snippet, so entries survive line moves. `preflight` and `receipt` read the same default baseline.
530
+
531
+ The JSON payload is `{files, findings, suppressed, summary}`; each finding is `{rule, severity, file, line, column, message, snippet}` and `summary` counts `high`, `medium`, `low`. With `--baseline` the payload adds `baselineSuppressed`; with `--update-baseline` it adds `baseline: {path, updated, entries}`.
532
+
533
+ ### `deps`
534
+
535
+ Offline health audit of npm, pnpm and yarn dependencies. No model call, no network.
536
+
537
+ ```bash
538
+ oc-pilot deps
539
+ oc-pilot deps --json
540
+ oc-pilot deps --fail-on-issue high
541
+ oc-pilot deps --ignore lint-staged
542
+ oc-pilot deps --fix --dry-run
543
+ oc-pilot deps --fix
544
+ oc-pilot deps --why qs
545
+ oc-pilot deps --audit --fail-on-vuln high
546
+ oc-pilot deps --audit --offline
547
+ oc-pilot deps --sbom sbom.json
548
+ ```
549
+
550
+ It compares `package.json` with what the code actually imports and with the lockfile the project uses: `package-lock.json`, `pnpm-lock.yaml` or `yarn.lock`.
551
+
552
+ - **phantom** (high) — a package is imported but not declared, so a clean install or CI fails;
553
+ - **unused** (medium) — declared but never imported or mentioned in scripts;
554
+ - **misplaced** (low) — used only by tests but listed in `dependencies`;
555
+ - **duplicate** (low) — two libraries from the same group solve one job (`moment` and `dayjs`);
556
+ - **unpinned** (medium) — `*`, `latest` or an empty spec;
557
+ - **vcs** (high for git/URL, low for `file:`) — a dependency from git, a URL or a local path;
558
+ - **lockfile** (high) — declared but missing from the project lockfile, so a clean install fails (`npm ci`, `pnpm install --frozen-lockfile` or `yarn install --immutable`).
559
+
560
+ `--fail-on-issue <high|medium|low>` turns the audit into a CI gate (exit 1). `--ignore <names>` accepts a comma-separated list for false positives, for example when a package is loaded dynamically. Workspace packages (`packages/*`) and `@types/*` are handled; peer dependencies are not reported as unused.
561
+
562
+ `--fix` repairs what can be decided offline: a package used only by tests moves to `devDependencies`, an imported-but-undeclared package is added with the version already resolved in the lockfile (to `devDependencies` when every importer is a test), and a floating `*`/`latest` spec is pinned to the lockfile version. Nothing is invented: a phantom import without a lockfile entry, an unused package, a duplicate or a vcs dependency is listed as skipped with the reason. `--dry-run` prints the plan without touching `package.json`. After writing, the audit re-runs, so the exit code reflects the fixed state.
563
+
564
+ `--why <name>` answers the question that comes before any cleanup: where does this package actually come from. It reads `package.json` and the lockfile locally and prints the declared field and spec, the files that import it, the packages that depend on it, every installed version and copy count, and the dependency chains from the root. A package that is neither declared, installed nor imported exits with code 1. `--why` cannot be combined with `--fix`.
565
+
566
+ `--audit` adds known vulnerabilities and licenses. It asks OSV (`api.osv.dev`) for every locked package, reports CVE/GHSA ids, severity and the first fixed version, and caches answers in `.oc-pilot/osv-cache.json` for 24 hours. `--offline` uses the cache without touching the network and fails when the cache is empty. `--osv-url` points the query at a mirror. `--fail-on-vuln <critical|high|medium|low>` turns the result into a security gate: the command exits with code 1 when any vulnerability reaches the threshold. Licenses are read from installed `node_modules` manifests; strong copyleft, proprietary and unknown licenses are listed, and packages without an installed manifest are counted as `missing` instead of guessed.
567
+
568
+ `--sbom <file>` writes a CycloneDX 1.5 software bill of materials for every locked package, with npm purls, relative to the project root. It needs no network.
569
+
570
+ The JSON payload is `{packageName, declared, imported, scannedFiles, truncated, lockfile, issues}`; with `--fix` it gains `fix: {applied, dryRun, fixes, skipped}`, with `--why` the command prints a single `DependencyWhy` object: `{name, versions, copies, declared, importedBy, dependents, chains}`, with `--audit` it gains `audit: {checked, unchecked, vulnerabilities, online, stale, cachePath, licenses}`, and with `--sbom` — `sbom: <path>`.
571
+
572
+ ### `commit`
573
+
574
+ Generates a Conventional Commits message from staged changes and optionally commits it. The model also sees the last 10 commit subjects, so the message matches the project's existing style.
575
+
576
+ ```bash
577
+ oc-pilot commit # print the message
578
+ oc-pilot commit --apply # commit staged with the generated message
579
+ oc-pilot commit --json # machine-readable output
580
+ ```
581
+
582
+ `--apply` works only with staged changes and is blocked (exit 3) when the diff contains critical secrets; pass `--allow-secrets` to override. With `--engine openai` an oversized diff is split into per-file slices (see Engines) and the message covers all of them.
583
+
584
+ ### `install-hook`
585
+
586
+ Installs native git hooks without husky or any dependency. The managed `pre-commit` hook runs `oc-pilot preflight --staged`: secrets, SAST, route authorization, dependency health scoped to the change, test weakening and the Slop Index delta. `--pre-push` also installs a `pre-push` hook that runs `oc-pilot review --fail-on critical --fail-on-secrets`. Pass `--scan` to keep a lighter pre-commit that only runs `oc-pilot scan --staged`.
587
+
588
+ ```bash
589
+ oc-pilot install-hook
590
+ oc-pilot install-hook --scan
591
+ oc-pilot install-hook --pre-push
592
+ oc-pilot install-hook --uninstall
593
+ ```
594
+
595
+ Hooks are opt-in and reversible. A hook that does not belong to oc-pilot is never overwritten unless `--force` is passed, and then the old file is kept as `<hook>.oc-pilot.bak`. Every managed hook is stamped with a marker; hooks written by older versions are reported as outdated by `oc-pilot doctor` and upgraded in place with `oc-pilot doctor --fix`. Set `OC_PILOT_SKIP=1` to bypass the hooks for a single command. `core.hooksPath` and worktrees are respected.
596
+
597
+ ### `restore`
598
+
599
+ Restores the working tree from the latest snapshot taken before `fix --apply`.
600
+
601
+ ```bash
602
+ oc-pilot restore # latest snapshot
603
+ oc-pilot restore --list # list saved snapshots
604
+ oc-pilot restore <id> # a specific snapshot
605
+ ```
606
+
607
+ Snapshots live under `.git/oc-pilot/snapshots/` and never enter commits. Restore refuses to run when the branch head has moved since the snapshot, so committed work is never lost. The staged/unstaged split is not restored, and untracked files created after the snapshot are not deleted.
608
+
609
+ ### `cache`
610
+
611
+ Inspects and maintains the `openai` response cache stored in `.git/oc-pilot/cache/`.
612
+
613
+ ```bash
614
+ oc-pilot cache # entries, size, expired count
615
+ oc-pilot cache --prune # delete expired entries
616
+ oc-pilot cache --clear # delete everything
617
+ ```
618
+
619
+ The cache only works inside a git repository and only applies to the `openai` engine.
620
+
621
+ ### `stats`
622
+
623
+ Prints usage statistics from `.git/oc-pilot/stats.jsonl`: runs, API calls, cache hits, average call duration and findings (critical/warning/style) for today, the last 7 days and all time.
624
+
625
+ ```bash
626
+ oc-pilot stats
627
+ oc-pilot stats --json
628
+ oc-pilot stats --clear
629
+ ```
630
+
631
+ ### `day`
632
+
633
+ A local activity digest: what was committed since a point in time, which files churned most, lines added/deleted, and the oc-pilot findings for that period.
634
+
635
+ ```bash
636
+ oc-pilot day
637
+ oc-pilot day --since "3 days ago" --json
638
+ ```
639
+
640
+ ### `impact`
641
+
642
+ Shows where else the symbols you changed are used. It extracts function, method, class, type and interface declarations from the diff (both added and removed lines, so renames show up under both names) and greps tracked and untracked files for them. This is a text search, not AST analysis, so it can over-report; it never fails the build.
643
+
644
+ ```bash
645
+ oc-pilot impact
646
+ oc-pilot impact --staged --json
647
+ ```
648
+
649
+ `--json` returns `symbols`, `references`, `scannedFiles` and `touchedFiles`, which is the shape agents want when they need to know what else a change might break.
650
+
651
+ ### `explain`
652
+
653
+ Explains the findings of the previous review for one place in the code. Every `review` run stores its findings in `.git/oc-pilot/last-review.json`; `explain` looks up the finding on the given line, or the nearest one within three lines, and prints it next to a snippet of the current file. Secrets in the snippet are redacted by the same scanner as everywhere else.
654
+
655
+ ```bash
656
+ oc-pilot explain src/wallet.ts:42
657
+ oc-pilot explain src/wallet.ts:42 --json
658
+ ```
659
+
660
+ Without a line number, or when nothing matches, it falls back to every finding in that file, so the command stays useful when the file shifted.
661
+
662
+ ### `reviewers`
663
+
664
+ Suggests who should review the current diff. For every changed file it walks the recent commits that touched it, counts the authors and picks the top candidates; bots are skipped. `CODEOWNERS` from `.github/CODEOWNERS`, `CODEOWNERS` or `docs/CODEOWNERS` is read and matched with the same last-rule-wins semantics as GitHub and GitLab.
665
+
666
+ ```bash
667
+ oc-pilot reviewers --base main
668
+ oc-pilot reviewers --staged --limit 5 --exclude renovate
669
+ oc-pilot reviewers --base main --json -o reviewers.json
670
+ ```
671
+
672
+ `--depth` looks further back than the default 50 commits. `--json` returns the files, the candidates (`name`, `email`, `commits`, `files`, `share`) and the matched `codeowners`, which is enough to fill `gh pr create --reviewer`.
673
+
674
+ ### `replay`
675
+
676
+ Re-renders the last saved review without calling a model. Every `review` writes `.git/oc-pilot/last-review.json`, so a SARIF report or a PR comment can be regenerated for free, or handed to a colleague.
677
+
678
+ ```bash
679
+ oc-pilot replay
680
+ oc-pilot replay --format sarif -o report.sarif
681
+ oc-pilot replay --as md -o pr-comment.md
682
+ oc-pilot replay --in saved.json --fail-on critical
683
+ ```
684
+
685
+ `--format` accepts text, json and sarif; `--as` adds md for the PR comment body. `--in` reads any file with a `findings` array. The exit code follows `--fail-on`, exactly as in `review`.
686
+
687
+ ### `slop`
688
+
689
+ Measures the entropy that fast, model-assisted coding leaves behind. `oc-pilot slop` scores a repository from 0 (clean) to 100 (rotten) without an API key, a model or network access: it needs only git and the working tree.
690
+
691
+ | Category | What it finds | Max |
692
+ | --- | --- | --- |
693
+ | Duplicates | Blocks of 72 or more tokens repeated across two different files | 30 |
694
+ | Dead code | Exports whose name appears in no other file (type-only exports and entry points are skipped) | 25 |
695
+ | Churn without tests | Files touched by five or more commits while tests changed in fewer than 40% of them | 25 |
696
+ | TODO graveyard | `TODO`, `FIXME`, `HACK` and `XXX` inside comments, weighted by age from `git blame` | 20 |
697
+
698
+ ```bash
699
+ oc-pilot slop
700
+ oc-pilot slop --since origin/main --max-delta 10
701
+ oc-pilot slop --json -o slop.json
702
+ oc-pilot slop --badge -o slop.svg
703
+ ```
704
+
705
+ `--since <ref>` compares the working tree with a revision and reports what this branch introduced and removed per category, plus `deltaPoints`. The delta matters more than the absolute number: a repository can sit at 50 and still be stable.
706
+
707
+ Other flags: `--limit` (items per category), `--since-days` (churn and blame window, default 30), `--exclude` (globs or substrings), `--no-blame` (skip `git blame`, much faster), `--max-index` and `--max-delta` (exit code 3 when exceeded), `--badge` (shields-style SVG), `--json`.
708
+
709
+ False positives go to `.oc-pilot/slopignore`: one glob or substring per line, `#` starts a comment. Comments and string literals are stripped before analysis, and test files, generated files and lock files are skipped. The score is a heuristic, not a verdict.
225
710
 
226
711
  ### `setup`
227
712
 
@@ -270,17 +755,26 @@ One command that explains why a setup is not working:
270
755
  oc-pilot doctor
271
756
  oc-pilot doctor --json
272
757
  oc-pilot doctor --engine openai --model deepseek-chat
758
+ oc-pilot doctor --fix
273
759
  ```
274
760
 
275
- It checks Node, the active engine, API credentials, git and repository state, the detected test command, project rules, and every MCP client config. Errors return exit code 1; `--strict` also fails on warnings. No secret values are printed.
761
+ It checks Node, the active engine, API credentials, git and repository state, the detected test command, project rules, oc-pilot git hooks and the response cache, and every MCP client config. `--fix` refreshes outdated oc-pilot hooks (a backup of the previous file is kept) and prunes expired cache entries; foreign hooks are never touched and are reported instead. Errors return exit code 1; `--strict` also fails on warnings. No secret values are printed.
276
762
 
277
763
  ### `init`
278
764
 
279
765
  ```bash
280
766
  oc-pilot init
767
+ oc-pilot init --preset node
768
+ oc-pilot init --hooks
281
769
  ```
282
770
 
283
- Creates `.oc-pilot.json`. It does not overwrite an existing config unless `--force` is passed.
771
+ Creates `.oc-pilot.json`. It does not overwrite an existing config unless `--force` is passed. With `--preset <a,b>` it also writes a curated `.oc-pilot/rules.md` for the stack. One or more presets can be combined, e.g. `--preset react,security`:
772
+
773
+ `base` (language-agnostic), `node`, `react`, `next`, `vue`, `svelte`, `angular`, `typescript`, `python`, `go`, `rust`, `java`, `kotlin`, `csharp`, `swift`, `php`, `ruby`, `flutter`, `elixir`, `zig`, `scala`, `security` (cross-stack hardening), `devops` (Docker, CI, infrastructure).
774
+
775
+ The rules file is a normal project rules file - edit it freely.
776
+
777
+ `--hooks` installs the git hooks from `install-hook`: `preflight --staged` on commit and `review` on push. Existing hooks are backed up, and a missing hook directory is only a warning, so `init` never fails because of it.
284
778
 
285
779
  ## Common options
286
780
 
@@ -289,6 +783,7 @@ Creates `.oc-pilot.json`. It does not overwrite an existing config unless `--for
289
783
  | `-C, --cwd <dir>` | current directory | Project directory |
290
784
  | `-m, --model <id>` | engine default | Model id (`provider/model` for OpenCode) |
291
785
  | `--engine <name>` | `opencode` | `opencode` or `openai` (any compatible API) |
786
+ | `--engine-preset <p>` | — | `ollama`, `lmstudio`, `openrouter` |
292
787
  | `--base-url <url>` | `https://api.openai.com/v1` | OpenAI-compatible endpoint |
293
788
  | `--api-key-env <name>` | `OC_PILOT_API_KEY` | Environment variable holding the API key |
294
789
  | `--agent <name>` | `oc-pilot` | Use another OpenCode agent |
@@ -298,12 +793,15 @@ Creates `.oc-pilot.json`. It does not overwrite an existing config unless `--for
298
793
  | `--fail-on <level>` | `none` | Exit 3 on `critical`, `warning`, or `style` findings |
299
794
  | `--max-diff <size>` | `120000` | Diff/context budget |
300
795
  | `--max-file <size>` | `60000` | Per-file budget for docgen |
796
+ | `--max-cost <usd>` | `0` | Daily spend cap for `openai` calls (`0` disables) |
301
797
  | `--timeout <duration>` | `300` | Model/test timeout; supports `5m` |
302
798
  | `-o, --output <path>` | stdout | Write the result to a file |
799
+ | `--format <f>` | `text` | Output format: `text`, `json`, `sarif` (`scan`, `review`) |
303
800
  | `--json` | off | Machine-readable output |
304
801
  | `--dry-run` | off | Print the prompt without calling a model |
305
802
  | `--strict` | off | Exit 3 when the output schema is invalid |
306
803
  | `--verbose` | off | Stream OpenCode diagnostics |
804
+ | `--no-cache` | off | Bypass the response cache (`openai`) |
307
805
 
308
806
  ## Project rules
309
807
 
@@ -325,7 +823,21 @@ The scanner looks at added diff lines and common source-file patterns:
325
823
  - private key blocks;
326
824
  - OpenAI, Anthropic, Hugging Face, Groq, Google, GitHub, GitLab, npm, PyPI, Slack, Discord, Telegram, Stripe, SendGrid, DigitalOcean, and AWS keys;
327
825
  - Azure storage account keys;
328
- - JWTs, credentials in URLs, and sensitive `.env` assignments.
826
+ - JWTs, credentials in URLs, and sensitive `.env` assignments;
827
+ - high-entropy assignments (Shannon entropy over 4 bits for values of 20+ characters) reported as critical.
828
+
829
+ To silence a false positive, add path globs to `.oc-pilot/ignore` (one per line, `#` starts a comment), or append the inline marker to the source line:
830
+
831
+ ```
832
+ # .oc-pilot/ignore
833
+ fixtures/**
834
+ docs/examples/**
835
+
836
+ # inline, any language
837
+ const TOKEN = "sk-proj-..."; // oc-pilot:ignore-secret
838
+ ```
839
+
840
+ Ignore rules are path- and line-scoped, apply to every scanning point (diff, rules, docgen, full-file context), and never disable scanning for the rest of the project.
329
841
 
330
842
  Detected values never appear in full in logs or JSON. The model receives `[REDACTED:<kind>]` and a masked preview. Review the provider's data policy before sending proprietary code.
331
843
 
@@ -339,7 +851,7 @@ Markdown response:
339
851
  ### Analysis: Short conclusion.
340
852
 
341
853
  #### Findings / Actions
342
- - **🔴 Critical `wallet.go:10`**: nil pointer dereference.
854
+ - **Critical `wallet.go:10`**: nil pointer dereference.
343
855
 
344
856
  #### Solution
345
857
  ```go
@@ -397,7 +909,10 @@ Markdown response:
397
909
  "maxFileChars": 60000,
398
910
  "timeoutSeconds": 300,
399
911
  "redactSecrets": true,
400
- "testCommand": "npm test"
912
+ "testCommand": "npm test",
913
+ "agentRules": true,
914
+ "secondOpinionEnabled": false,
915
+ "maxDailyApiCalls": 0
401
916
  }
402
917
  ```
403
918
 
@@ -409,7 +924,29 @@ Environment variables: `OC_PILOT_ENGINE`, `OC_PILOT_BASE_URL`, `OC_PILOT_API_KEY
409
924
 
410
925
  `oc-pilot-mcp` exposes the same recipes as MCP tools over stdio. This is the recommended way to embed oc-pilot in an agent: the agent calls a tool, and no prompt needs to be copied.
411
926
 
412
- Tools: `review`, `test_fix`, `fix`, `docgen`, `custom`, `scan`. The server is read-only by default; start it with `--allow-write` to permit `fix` with `apply: true` and `test_fix` with a command.
927
+ Besides tools, the server exposes read-only MCP resources: `oc-pilot://config` (resolved configuration), `oc-pilot://rules` (project rules with secrets redacted), `oc-pilot://prompt/en` and `oc-pilot://prompt/ru` (the built-in system prompts), and `oc-pilot://version`. `oc-pilot://slop` returns the Slop Index as JSON without calling a model. Resource templates cover live data: `oc-pilot://diff/{mode}` (staged/unstaged/head diff, secrets redacted), `oc-pilot://scan/{mode}` (offline secret scan as JSON) and `oc-pilot://file/{+path}` (any project file up to 2 MB with detected secrets redacted). Hosts can list them with `resources/list` and `resources/templates/list`.
928
+
929
+ Tools: `review`, `invariants`, `test_fix`, `fix`, `test_gen`, `docgen`, `custom`, `scan`, `commit`, `stats`, `slop`, `mutate`, `fuzz`, `bankruptcy`, `query_ast`, `sandbox`, `hivemind_status`, `flaky`, `impact`, `deps`, `coverage`, `reviewers`, `test_guard`, `env_sync`, `mock_leak`, `ecosystem_verify`, `pack`, `pack_hologram`, `zoom_symbol`, `contract_drift`, `anti_regression`, `boundaries`, `test_plan`, `compliance_verify`, `brag`, `battle`, `cleanup`, `arch_diff`, `roast`, `cinema`, `bounty`, `autopsy`, `echo`, `sast`, `authz`, `preflight`, `ready`, `receipt_verify`. `scan` accepts `all: true` to sweep the whole repository instead of the diff. `slop` runs the offline Slop Index and accepts `since`, `since_days`, `limit`, `exclude` and `no_blame`. `mutate` runs mutation testing on the changed code, accepts `command`, `max_mutants` and `min_score`, and returns the score plus every surviving mutant; a score below `min_score` is a normal result with exit code 1. `flaky` runs the test command several times and compares per-test results, accepting `runs`, `command`, `fail_on_flaky`, `shuffle` and `seed`; it never edits source files itself but does run the project's tests. `impact` lists the declarations the diff changed or removed and every untouched file that still mentions them, accepting the same source flags; the search is textual, so comments and strings can match. `deps` audits declared, imported and locked dependencies, explains a chain with `why`, adds OSV vulnerabilities and licenses with `audit`, and can rewrite `package.json` with `fix` or emit a CycloneDX SBOM; `fix` and `sbom` write files. `coverage` compares changed lines with a local lcov, Istanbul, Cobertura or Go coverage report and returns uncovered lines, missing files and a gate on `fail_under` or `max_uncovered`. `reviewers` ranks the authors who touched the changed files, lists CODEOWNERS owners and accepts `limit`, `depth` and `exclude`; it uses git history only. `test_guard` runs the offline anti-cheating check on the diff: skipped, focused or deleted tests, removed or commented assertions, raised timeouts, strict matchers replaced by existence checks, `@ts-ignore` and empty catch blocks; with `fail_on_weakening` a finding returns exit code 1. `env_sync` compares the environment variables added by the diff (`process.env`, `import.meta.env`, `Deno.env`, `Bun.env`) with the local `.env` files and `.env.example`/`.sample`/`.template`: missing definitions fail with `fail_on_missing`, and defined but undocumented variables warn. `mock_leak` scans added production code for forgotten stubs and hardcode: local paths, test tokens, `if (true)`/`if (false)`, mock/fake/stub identifiers, mock comments and localhost URLs; `fail_on_leak` returns exit code 1 at the chosen severity. `ecosystem_verify` checks that named imports from the changed files exist in the installed package entry or its `.d.ts`, and flags packages that are neither installed nor declared; with `online` it also checks the age and weekly downloads of newly added npm packages, and `fail_on_issue` returns exit code 1 at the chosen severity. `pack` builds a compact context bundle for a task: project rules, changed-file signatures, related references and an optional test log, capped by `budget` characters; it never edits files. `contract_drift` compares changed TypeScript interfaces, Zod objects, Prisma models, Pydantic models and JSON Schema with the previous version, flags removed or newly required fields and type changes, and reports untouched consumers that still use a removed field; `fail_on_drift` returns exit code 1 at the chosen severity. `anti_regression` compares the lines added by the diff with lines a human explicitly deleted in git history, cites the deleting commit and flags re-added code or imports; `fail_on_regression` returns exit code 1. `boundaries` checks added imports against `.oc-pilot/architecture.json` layer rules, reports the file, line, import and rule reason, and `fail_on_violation` returns exit code 1 when a layer boundary is crossed. `test_plan` selects the test files affected by the diff from a static import graph: changed tests, direct and transitive importers, naming matches and symbol references, with uncovered changed files reported and an optional `fail_on_uncovered` gate. `compliance_verify` checks a signed audit manifest for the current commit: SHA-256, commit, previous-manifest hash chain and optional Ed25519 signature; a missing, edited or broken manifest is a normal result with exit code 1. `brag` builds a self-contained HTML or SVG session report from stats, git history, the last review and the receipt; it uses inline assets only, never calls a model or the network, and `no_paths` hides file locations. `test_gen` asks the model for tests that cover the changed code; with `apply` it writes test files, runs the suite and reverts failing tests unless `keep_failing`, while `coverage` narrows generation to uncovered changed lines and `from_slop` picks untested files. `cleanup` builds an offline cleanup plan from dependency issues, the Slop Index, environment variables and weakened tests; `apply` performs only safe dependency fixes and needs `--allow-write`. `arch_diff` renders a Mermaid diagram of the internal imports the diff added and removed: green edges are new dependencies, red dashed edges are removed ones, and `fail_on_removed` returns exit code 1 when a dependency disappears. `roast` turns the diff into a technically grounded comedy roast in a chosen persona (linus, ramsay, corporate or default), cites file:line for every joke and ends with one real fix; it is model-backed and never edits files. `cinema` builds a terminal timelapse of the session from commits, runs, API calls, the last review and the receipt: a self-contained HTML player or an asciinema v2 cast, with no network, external assets or model call; `no_paths` hides subjects and finding locations. `bounty` scores every file from SAST findings, duplicated blocks, dead exports, aged TODOs and churn without tests, then renders an ASCII WANTED poster with the worst file and its reasons; deterministic and offline. `autopsy` performs a post-mortem on a commit: diff stats, SAST on added lines, weakened tests, mock leaks, ranked suspects and a before/after timeline; `fail_on_risk` returns exit 1 for a dangerous verdict. `echo` builds self-contained SVG badges from local stats, git history and the receipt (commits, agent runs, API calls, cache hits, findings and test status); `submit` sends only the nickname, window and aggregate numbers to an explicit `endpoint`, never paths, diffs or secrets, and there is no network by default. `sast` runs the deterministic static analysis over the change or over explicit `paths` and accepts `fail_on_issue`, `ignore` and `no_suppressions`; a finding at or above the gate is a normal result with exit code 1. `authz` checks route authorization over the change or explicit `paths` and accepts `fail_on_issue`, `ignore`, `public` and `no_suppressions`; a route finding at or above the gate is also a normal result with exit code 1. `preflight` returns one offline verdict over the planned change and accepts the same source flags (`diff`, `base`, `staged`, `unstaged`, `include_untracked`), plus `since`, `max_delta`, `fail_on_issue`, `no_secret_scan`, `no_sast`, `no_authz`, `no_deps`, `no_tests` and `no_slop`; a failed check is a normal result with `status: "fail"` and exit code 1, so the agent sees the failing check instead of a tool error. `ready` returns the offline agent readiness score 0-100 and accepts `fail_under`; a score below the threshold is also a normal result with exit code 1. `receipt_verify` recomputes the SHA-256 hash of the receipt, compares the recorded commit with HEAD and reports `ok`, `hashOk` and `stale`; a missing, edited or stale receipt is a normal result with `ok: false` and exit code 1, so an agent can see why the proof failed. The `fix` tool accepts `from_sast` and `from_authz` to repair security findings and rescan; the result then carries `sast` or `authz` with `{before, after, remaining}`. The server is read-only by default; start it with `--allow-write` to permit `fix` with `apply: true`, `commit` with `apply: true`, `test_fix` with a command, `stats` with `clear: true` and `mutate`, which rewrites source files temporarily. `test_gen` with `apply: true` also needs `--allow-write`. The `fix` result carries the unified `patch`, its `files` and `patchStats` (`additions`, `deletions`, `hunks`, `bytes`); patches over 200 000 characters are cut and marked with `patchTruncated` and `patchBytes`, so a host can show or apply the change without parsing the diff again.
930
+
931
+ `invariants` checks the diff against `.oc-pilot/invariants.md`
932
+
933
+ `hivemind` is an encrypted TCP mesh for sharing analysis work: `init` creates an ed25519 identity, `trust` pins a peer public key, `serve` accepts signed ECDH X25519 handshakes, and `send` transmits AES-256-GCM encrypted payloads. Only diffs, AST and LLM requests are allowed; executable payloads are rejected, and untrusted peers are dropped before any data is read.
934
+
935
+ `sandbox run <file>` executes one self-contained JavaScript or ESM file in `node:vm` with a separate context: static imports, `require`, `fetch`, `process.exit` and timers longer than a second are blocked, console output is captured, and execution is capped by `--timeout` and `--memory-limit`. TypeScript runs only from a built `dist/*.js`. This is not an OS sandbox for native code.
936
+
937
+ `query` runs a chainable AQL query against a lightweight code graph: `find(File|Function|Class|Import|CallExpression).where(field, value).sort(field).limit(n).select(fields...).count()`. It is not SQL and not a full TypeScript AST; it is a fast textual graph that the agent can probe before editing. MCP exposes it as `query_ast`.
938
+
939
+ `bankruptcy` forecasts technical debt from commit history: it scores each commit by weakened tests, SAST high/medium findings, added TODOs, churn and a slop proxy, smooths the daily series with a 14-day moving average, drops outliers and fits a linear regression. The output is the current score, slope, forecast date and a self-contained SVG chart; fewer than 10 commits returns `[INSUFFICIENT DATA: Need at least 10 commits to compute Slop Velocity]`. `--explain` prints the exact weights, where test weakening (0.35) and security (0.30) dominate.
940
+
941
+ `fix --shadow` and `battle --shadow` run the applied patch inside `.oc-pilot/shadow-tree`, a detached git worktree, so tests and external compilers see the modified files. The diff is extracted with `git diff` and applied to the real tree only when the run succeeds; a failed run leaves the working tree untouched. `--shadow` requires `--apply`.
942
+
943
+ `fuzz` discovers exported functions added by the diff and calls them with null, undefined, -1, 0, an empty string, a 10MB string, a `__proto__` object and empty containers. Every target runs in a separate Node process with a hard 2s timeout, a configurable heap cap and mocked fs/net/http/child_process; files that import side-effect modules are blocked before execution, and a crash or timeout is reported with the exact input. No model call.: each `## area` scopes its rules, `[CRITICAL]` marks a blocking rule, and rules whose area touches none of the changed files or their importers are skipped as `[SKIPPED: Out of scope]` without a model call. The model must answer strict JSON; verdicts are cached by the hash of diff and invariants, and violations are warnings by default. A CRITICAL violation or `--strict` / `--fail-on violation` returns exit code 3. `preflight --invariants` adds the same check to the gate.
944
+
945
+ `pack --hologram` builds a layered context instead of dumping the repository: LOD 0 keeps diff files complete, LOD 1 hides function bodies behind `body hidden` markers with `EFFECTS` hints and adds matching tests, LOD 2 lists export names only. Unbalanced braces fall back to full text with `[Hologram: parse bailout]`, and the final Markdown is secret-scanned. `zoom-symbol <file> <symbol>` returns one hidden body on demand; `pack_hologram` and `zoom_symbol` expose the same flow over MCP.
946
+
947
+ Every tool accepts `max_findings` (default 50; a capped result also carries `truncated` and `totalFindings`) and `verbosity` (`full` returns the JSON payload, `compact` returns a one-line summary and keeps the same data in `structuredContent`). When a client sends `_meta.progressToken`, the server emits `notifications/progress`; after `logging/setLevel` it also emits `notifications/message` per call. `--max-calls <n>` (or `OC_PILOT_MCP_MAX_CALLS`) caps model-backed calls per session, `0` disables the cap, and the offline tools `scan`, `fuzz`, `bankruptcy`, `query_ast`, `sandbox`, `hivemind_status`, `stats`, `slop`, `flaky`, `impact`, `deps`, `coverage`, `reviewers`, `test_guard`, `env_sync`, `mock_leak`, `ecosystem_verify`, `pack`, `pack_hologram`, `zoom_symbol`, `contract_drift`, `anti_regression`, `boundaries`, `test_plan`, `compliance_verify`, `brag`, `cleanup`, `arch_diff`, `roast`, `cinema`, `bounty`, `autopsy`, `echo`, `sast`, `authz`, `preflight`, `ready` and `receipt_verify` keep working after the budget is exhausted.
948
+
949
+ Ready-made prompts are advertised through `prompts/list` and rendered by `prompts/get`, so a host can offer them in its own menu: `review-my-diff` (optional `context`), `fix-failing-tests` (optional `log`), `pre-commit-gate` (scan, review and commit in one pass), `secure-change` (security-focused review), `slop-check` (entropy check for vibe-coded changes) and `preflight-before-commit` (run the offline gate, fix the cheapest failing check, rerun until the verdict is pass), `compile-hologram` (build a layered hologram, keep LOD 0 complete, treat hidden LOD 1 bodies as unknown and call `zoom_symbol` before reasoning about them), `check-invariants` (check the change against `.oc-pilot/invariants.md`, explain every violation with file:line and never hide a CRITICAL rule), `fuzz-the-diff` (call the new exports with hostile inputs in isolated processes, fix every crash and never count blocked targets as tested), `forecast-tech-debt` (read the tech-debt trend, explain the score and weights, and use the forecast date to plan repayment instead of gaming TODO counts), `query-the-ast` (locate calls, functions, classes and imports with AQL, verify each hit in the source and use the evidence to plan a refactor), `sandbox-script` (run a generated JS/ESM file in node:vm with imports and side effects blocked, then report its output and exact status), `join-the-swarm` (check the hivemind identity and trusted peers, then exchange only diffs, AST and LLM requests over the encrypted mesh; never send executable commands), `get-ready` (score the repository, fix the safe gaps, prove the result with a receipt and rerun until the score is at least 85), `fix-security` (repair SAST or route-authorization findings and rescan until no high findings remain), `stabilize-flaky` (reproduce a flaky test with repeated and shuffled runs and make the smallest deterministic fix), `check-impact` (find external references to changed declarations, update what the change broke and rerun until no stale references remain), `triage-deps` (audit dependencies and OSV vulnerabilities, fix the cheapest issue first and rerun without hiding findings), `cover-changed-lines` (add tests for the changed lines that no test executed and rerun the same gate), `generate-tests` (generate tests for the change or its uncovered lines, apply them, run the suite and iterate until green), `pick-reviewers` (choose the smallest set of reviewers who know the changed files, with evidence from history and CODEOWNERS), `guard-tests` (find tests the change skipped, weakened or deleted, restore their protection and rerun until the guard passes), `sync-env` (compare the environment variables added by the change with `.env` and `.env.example`, add the missing definitions and document them without inventing secrets), `remove-mocks` (find forgotten mocks, local paths, test tokens and always-true conditions in production code, replace them with the real implementation and rerun until the leak report is clean), `verify-ecosystem` (check that imports exist in installed packages and that new npm packages are not brand new with a handful of downloads, and do not install a suspicious package without a human decision), `pack-context` (build a compact task bundle of rules, signatures, references and test log and use it instead of dumping whole files), `check-contracts` (find removed or narrowed fields in changed types and schemas, update every remaining consumer without silently breaking callers), `guard-regressions` (compare added lines with code a human deleted, inspect the deleting commit and do not re-add it without an explicit decision), `respect-boundaries` (check added imports against the layer rules, fix the dependency direction and rerun until no boundary is crossed), `plan-tests` (run the affected-test plan, run only those tests, add a test for every uncovered changed file and rerun without widening the strategy), `prove-compliance` (verify the signed audit manifest for HEAD, check the hash chain and signature, and never claim completion while the manifest is invalid), `cleanup-plan` (build an offline cleanup plan and apply only safe dependency fixes), `show-arch-diff` (render a Mermaid diagram of the internal imports the change adds and removes), `roast-my-code` (roast the diff in a chosen persona, cite file:line and end with one real fix), `play-session` (build a terminal timelapse of the session as a self-contained HTML player or an asciinema cast), `wanted-poster` (post a WANTED poster for the worst file, explain its reasons and the highest-weight fix, and rerun after the fix), `autopsy-commit` (run a post-mortem on a commit, list its signals, rank the suspects, read the before/after timeline and propose the smallest fix and the test that would have caught it), `share-progress` (build local SVG badges and, only with explicit approval, submit the anonymous aggregate payload to the given endpoint), `prove-tests` (run mutation testing on the change and strengthen the tests until the mutation score is at least 80) and `prove-done` (verify the receipt for HEAD, create a fresh one when it is missing or stale, and refuse to claim completion until `ok` is true and the tests in the receipt passed).
413
950
 
414
951
  Configure a client automatically:
415
952
 
@@ -428,7 +965,7 @@ Start the server manually with an explicit project directory:
428
965
  ```bash
429
966
  oc-pilot-mcp --cwd /absolute/path/to/project
430
967
  # after the npm release, pinned for reproducibility:
431
- npx -y oc-pilot@0.5.1 mcp --cwd /absolute/path/to/project
968
+ npx -y oc-pilot@0.5.2 mcp --cwd /absolute/path/to/project
432
969
  ```
433
970
 
434
971
  The `diff` and `log` tool arguments are text, not file paths. `paths` and `files` are project-relative and cannot escape the project directory. `context` is literal text, not an `@file` reference. `base` must be a git ref. `lang`, `fail_on`, `repo_context`, and `no_secret_scan` are available where the corresponding CLI command supports them.
@@ -554,7 +1091,7 @@ oc-pilot-mcp --http --host 127.0.0.1 --port 8787 --path /mcp \
554
1091
  curl http://127.0.0.1:8787/health
555
1092
  ```
556
1093
 
557
- MCP is **read-only by default**, over both stdio and HTTP: `scan`, `review`, `docgen`, `custom`, and patch-proposal `fix` work; `fix` with `apply: true` and `test_fix` that runs a new command are rejected. Add `--allow-write` only for a trusted local client or an authenticated deployment. `oc-pilot setup --allow-write` adds the flag to generated client config.
1094
+ MCP is **read-only by default**, over both stdio and HTTP: `scan`, `review`, `docgen`, `custom`, and patch-proposal `fix` work; `fix` with `apply: true`, `test_fix` that runs a new command, and `deps` with `fix: true` or `sbom` are rejected. Add `--allow-write` only for a trusted local client or an authenticated deployment. `oc-pilot setup --allow-write` adds the flag to generated client config.
558
1095
 
559
1096
  The MCP endpoint sets `Mcp-Session-Id` on initialize. When `--token` is set, every request needs `Authorization: Bearer <token>` except `GET /health`. A non-loopback bind **refuses to start** without a token of at least 16 characters. Put a TLS-terminating reverse proxy in front of any public deployment.
560
1097
 
@@ -574,7 +1111,18 @@ Or use `docker-compose up`; the compose file requires `OC_PILOT_MCP_TOKEN`. The
574
1111
 
575
1112
  ### Agent skills
576
1113
 
577
- `skills/oc-pilot/SKILL.md` is an Agent Skill for Claude Code, Codex, and OpenCode. It teaches an agent when to call `scan`, `review`, `test-fix`, `fix`, and `docgen` through MCP or the CLI. Copy the `skills/oc-pilot` directory into your skills directory, or point the client at this repository.
1114
+ `skills/` contains portable Agent Skills for Claude Code, Codex, Cursor, Copilot and OpenCode. Each one teaches an agent a gate to clear before the work is called done:
1115
+
1116
+ | Skill | Purpose |
1117
+ | --- | --- |
1118
+ | `oc-pilot` | Entry point: when to call `scan`, `review`, `test-fix`, `fix` and `docgen`. |
1119
+ | `agent-self-review` | Pre-commit loop: scan, review, fix, re-verify, report the exit code. |
1120
+ | `test-failure-triage` | Capture the failing log, find the root cause, patch with a snapshot, re-run. |
1121
+ | `secure-diff-gate` | Secrets, security rules, prompt-injection signals and dependency risks, plus SARIF. |
1122
+ | `commit-discipline` | Conventional Commit message from the staged diff, with a secret gate. |
1123
+ | `stack-review-packs` | Combinable rules packs for 23 stacks. |
1124
+
1125
+ Copy the directory you need into your skills directory, or point the client at this repository.
578
1126
 
579
1127
  See [docs/integrations.md](docs/integrations.md) for protocols, result shapes, setup details, and shell-only fallbacks.
580
1128
 
@@ -592,7 +1140,7 @@ jobs:
592
1140
  - uses: actions/checkout@v4
593
1141
  with:
594
1142
  fetch-depth: 0
595
- - uses: MyNameIsNikki/oc-pilot@v0.5.1
1143
+ - uses: MyNameIsNikki/oc-pilot@v0.88.1
596
1144
  with:
597
1145
  command: scan
598
1146
  args: --base origin/${{ github.base_ref }} --fail-on-secrets
@@ -615,7 +1163,7 @@ jobs:
615
1163
  - uses: actions/setup-node@v4
616
1164
  with:
617
1165
  node-version: 22
618
- - run: npm install -g https://github.com/MyNameIsNikki/oc-pilot/archive/refs/tags/v0.5.1.tar.gz
1166
+ - run: npm install -g https://github.com/MyNameIsNikki/oc-pilot/archive/refs/tags/v0.88.1.tar.gz
619
1167
  - run: oc-pilot scan --base origin/${{ github.base_ref }}
620
1168
  - run: oc-pilot review --base origin/${{ github.base_ref }} --fail-on warning --fail-on-secrets --github
621
1169
  env:
@@ -624,6 +1172,17 @@ jobs:
624
1172
 
625
1173
  `--github` turns parsed findings into inline `::error file=...,line=...` and `::warning` annotations. `scan` needs no API key and is suitable for a fast first gate. Add `--json` instead when a later step needs the structured report.
626
1174
 
1175
+ To upload results to GitHub Code Scanning, write a SARIF report and upload it:
1176
+
1177
+ ```yaml
1178
+ - run: oc-pilot scan --base origin/${{ github.base_ref }} --format sarif -o oc-pilot.sarif
1179
+ - uses: github/codeql-action/upload-sarif@v3
1180
+ with:
1181
+ sarif_file: oc-pilot.sarif
1182
+ ```
1183
+
1184
+ Each release is published from a tag by the repository's own workflow (OIDC trusted publishing with a provenance attestation; set the `NPM_TOKEN` secret to fall back to a granular npm token), and the GitHub release body is taken from the matching `CHANGELOG.md` section. CI runs the suite on Node 22 and 24 and prints an informational coverage table from `node --test --experimental-test-coverage` — no threshold, so a noisy signal never blocks a merge.
1185
+
627
1186
  ## Security model
628
1187
 
629
1188
  - The built-in repo-context agent is read-only: `read`, `glob`, and `grep` only. `edit`, `shell`, `subagent`, web tools, and external directories are denied.
@@ -632,7 +1191,8 @@ jobs:
632
1191
  - Diff, rules, and full-file content are scanned before being sent to the model.
633
1192
  - Project rules and code comments are data, not instructions. The system prompt explicitly treats injected instructions as untrusted.
634
1193
  - The repo-context agent runs inside the analyzed project, so project-level OpenCode config and plugins may load. Use `--no-repo-context` for untrusted repositories.
635
- - Only `fix --apply` writes to the working tree. It rejects patches that touch files outside the reviewed diff, checks the patch with `git apply --check`, runs the configured tests, and reverts the patch when they fail. It never commits or pushes.
1194
+ - Only `fix --apply` writes to the working tree. It rejects patches that touch files outside the reviewed diff, checks the patch with `git apply --check`, saves a local snapshot under `.git/oc-pilot/snapshots/` before applying, runs the configured tests, and reverts the patch when they fail. It never commits or pushes.
1195
+ - `install-hook` writes only managed hook files inside the git hooks directory, marks them with a header comment, keeps backups when `--force` replaces a foreign hook, and is fully reversible with `--uninstall`.
636
1196
  - The HTTP transport uses the MCP session header and an optional bearer token. Plain HTTP has no transport encryption; use a TLS proxy for remote deployments, and do not expose it without a token.
637
1197
  - No file is modified by `review`, `test-fix`, `docgen`, or `custom`.
638
1198
 
@@ -661,6 +1221,14 @@ npm run prompt:export
661
1221
 
662
1222
  The project has no runtime dependencies. Dev dependencies are TypeScript and `@types/node`.
663
1223
 
1224
+ ### Regression tests
1225
+
1226
+ Three layers keep the contracts the model sees from drifting:
1227
+
1228
+ - `test/golden/` pins the built prompt input for fixed diffs, so section order, empty-value handling and secret redaction only change on purpose. Regenerate with `OC_PILOT_UPDATE_GOLDEN=1 npm test` and read the diff before committing.
1229
+ - `test/fixtures/model-output/` holds realistic model replies; `src/core/output-format.test.ts` asserts which severity, location and text the CLI reports for each one.
1230
+ - `normalizeLocation` collapses the location shapes models actually emit (`a.ts:10-14`, `a.ts:10,12`, `a.ts#L12`, `a.ts (line 10)` → `a.ts:10`), and bullets inside fenced code blocks are ignored when parsing.
1231
+
664
1232
  ## License
665
1233
 
666
1234
  MIT