@humanbased/crosscheck 1.2.0 → 1.3.0-beta.100

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 (438) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +183 -375
  3. package/README.zh.md +1 -1
  4. package/assets/icon-256.png +0 -0
  5. package/assets/linear-comment.svg +18 -0
  6. package/assets/linear-onboard.svg +30 -0
  7. package/assets/linear-status.svg +23 -0
  8. package/assets/linear-test.svg +34 -0
  9. package/assets/skills/code-review/.crosscheck-skill.json +9 -0
  10. package/assets/skills/code-review/LICENSE +21 -0
  11. package/assets/skills/code-review/SKILL.md +89 -0
  12. package/assets/skills/code-review/agents/openai.yaml +3 -0
  13. package/assets/skills/code-review-skill/.crosscheck-skill.json +9 -0
  14. package/assets/skills/code-review-skill/LICENSE +21 -0
  15. package/assets/skills/code-review-skill/SKILL.md +231 -0
  16. package/assets/skills/code-review-skill/assets/pr-review-template.md +137 -0
  17. package/assets/skills/code-review-skill/assets/review-checklist.md +123 -0
  18. package/assets/skills/code-review-skill/reference/angular.md +768 -0
  19. package/assets/skills/code-review-skill/reference/architecture-review-guide.md +472 -0
  20. package/assets/skills/code-review-skill/reference/c.md +890 -0
  21. package/assets/skills/code-review-skill/reference/code-quality-universal.md +488 -0
  22. package/assets/skills/code-review-skill/reference/code-review-best-practices.md +136 -0
  23. package/assets/skills/code-review-skill/reference/common-bugs-checklist.md +286 -0
  24. package/assets/skills/code-review-skill/reference/cpp.md +893 -0
  25. package/assets/skills/code-review-skill/reference/cross-cutting/async-concurrency-patterns.md +515 -0
  26. package/assets/skills/code-review-skill/reference/cross-cutting/error-handling-principles.md +492 -0
  27. package/assets/skills/code-review-skill/reference/cross-cutting/n-plus-one-queries.md +309 -0
  28. package/assets/skills/code-review-skill/reference/cross-cutting/sql-injection-prevention.md +308 -0
  29. package/assets/skills/code-review-skill/reference/cross-cutting/xss-prevention.md +264 -0
  30. package/assets/skills/code-review-skill/reference/csharp.md +525 -0
  31. package/assets/skills/code-review-skill/reference/css-less-sass.md +661 -0
  32. package/assets/skills/code-review-skill/reference/django.md +985 -0
  33. package/assets/skills/code-review-skill/reference/fastapi.md +580 -0
  34. package/assets/skills/code-review-skill/reference/go.md +993 -0
  35. package/assets/skills/code-review-skill/reference/java.md +409 -0
  36. package/assets/skills/code-review-skill/reference/java8.md +586 -0
  37. package/assets/skills/code-review-skill/reference/kotlin.md +1018 -0
  38. package/assets/skills/code-review-skill/reference/nestjs.md +593 -0
  39. package/assets/skills/code-review-skill/reference/performance-review-guide.md +816 -0
  40. package/assets/skills/code-review-skill/reference/php.md +684 -0
  41. package/assets/skills/code-review-skill/reference/python.md +1073 -0
  42. package/assets/skills/code-review-skill/reference/qt.md +757 -0
  43. package/assets/skills/code-review-skill/reference/react.md +871 -0
  44. package/assets/skills/code-review-skill/reference/ruby.md +964 -0
  45. package/assets/skills/code-review-skill/reference/rust.md +846 -0
  46. package/assets/skills/code-review-skill/reference/security-review-guide.md +494 -0
  47. package/assets/skills/code-review-skill/reference/svelte.md +1064 -0
  48. package/assets/skills/code-review-skill/reference/swift.md +936 -0
  49. package/assets/skills/code-review-skill/reference/typescript.md +1016 -0
  50. package/assets/skills/code-review-skill/reference/vue.md +924 -0
  51. package/assets/skills/code-review-skill/reference/zig.md +440 -0
  52. package/assets/skills/code-review-skill/scripts/pr-analyzer.py +435 -0
  53. package/assets/skills/code-review-skill/scripts/test_pr_analyzer.py +380 -0
  54. package/assets/skills/codebase-design/.crosscheck-skill.json +9 -0
  55. package/assets/skills/codebase-design/DEEPENING.md +37 -0
  56. package/assets/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  57. package/assets/skills/codebase-design/LICENSE +21 -0
  58. package/assets/skills/codebase-design/SKILL.md +114 -0
  59. package/assets/skills/codebase-design/agents/openai.yaml +3 -0
  60. package/assets/skills/diagnosing-bugs/.crosscheck-skill.json +9 -0
  61. package/assets/skills/diagnosing-bugs/LICENSE +21 -0
  62. package/assets/skills/diagnosing-bugs/SKILL.md +134 -0
  63. package/assets/skills/diagnosing-bugs/agents/openai.yaml +3 -0
  64. package/assets/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
  65. package/crosscheck.config.example.yml +124 -11
  66. package/dist/__tests__/adoption.test.d.ts +2 -0
  67. package/dist/__tests__/adoption.test.d.ts.map +1 -0
  68. package/dist/__tests__/adoption.test.js +175 -0
  69. package/dist/__tests__/adoption.test.js.map +1 -0
  70. package/dist/__tests__/auto-fix-branch.test.d.ts +2 -0
  71. package/dist/__tests__/auto-fix-branch.test.d.ts.map +1 -0
  72. package/dist/__tests__/auto-fix-branch.test.js +143 -0
  73. package/dist/__tests__/auto-fix-branch.test.js.map +1 -0
  74. package/dist/__tests__/board.test.js +13 -0
  75. package/dist/__tests__/board.test.js.map +1 -1
  76. package/dist/__tests__/can-write-verdict.test.d.ts +2 -0
  77. package/dist/__tests__/can-write-verdict.test.d.ts.map +1 -0
  78. package/dist/__tests__/can-write-verdict.test.js +31 -0
  79. package/dist/__tests__/can-write-verdict.test.js.map +1 -0
  80. package/dist/__tests__/codex-env.test.d.ts +2 -0
  81. package/dist/__tests__/codex-env.test.d.ts.map +1 -0
  82. package/dist/__tests__/codex-env.test.js +70 -0
  83. package/dist/__tests__/codex-env.test.js.map +1 -0
  84. package/dist/__tests__/codex.test.js +100 -25
  85. package/dist/__tests__/codex.test.js.map +1 -1
  86. package/dist/__tests__/comment-bodies.test.js +49 -1
  87. package/dist/__tests__/comment-bodies.test.js.map +1 -1
  88. package/dist/__tests__/commit-subject.test.d.ts +2 -0
  89. package/dist/__tests__/commit-subject.test.d.ts.map +1 -0
  90. package/dist/__tests__/commit-subject.test.js +72 -0
  91. package/dist/__tests__/commit-subject.test.js.map +1 -0
  92. package/dist/__tests__/conflict-resolve.test.js +44 -1
  93. package/dist/__tests__/conflict-resolve.test.js.map +1 -1
  94. package/dist/__tests__/credential-free-origin.test.d.ts +2 -0
  95. package/dist/__tests__/credential-free-origin.test.d.ts.map +1 -0
  96. package/dist/__tests__/credential-free-origin.test.js +59 -0
  97. package/dist/__tests__/credential-free-origin.test.js.map +1 -0
  98. package/dist/__tests__/detector.test.js +87 -1
  99. package/dist/__tests__/detector.test.js.map +1 -1
  100. package/dist/__tests__/error-classification.test.js +34 -0
  101. package/dist/__tests__/error-classification.test.js.map +1 -1
  102. package/dist/__tests__/fix.test.js +69 -0
  103. package/dist/__tests__/fix.test.js.map +1 -1
  104. package/dist/__tests__/harness-section.test.d.ts +2 -0
  105. package/dist/__tests__/harness-section.test.d.ts.map +1 -0
  106. package/dist/__tests__/harness-section.test.js +46 -0
  107. package/dist/__tests__/harness-section.test.js.map +1 -0
  108. package/dist/__tests__/kickass.test.js +31 -0
  109. package/dist/__tests__/kickass.test.js.map +1 -1
  110. package/dist/__tests__/linear-branding.test.d.ts +2 -0
  111. package/dist/__tests__/linear-branding.test.d.ts.map +1 -0
  112. package/dist/__tests__/linear-branding.test.js +156 -0
  113. package/dist/__tests__/linear-branding.test.js.map +1 -0
  114. package/dist/__tests__/linear-client.test.d.ts +2 -0
  115. package/dist/__tests__/linear-client.test.d.ts.map +1 -0
  116. package/dist/__tests__/linear-client.test.js +120 -0
  117. package/dist/__tests__/linear-client.test.js.map +1 -0
  118. package/dist/__tests__/linear-comment.test.d.ts +2 -0
  119. package/dist/__tests__/linear-comment.test.d.ts.map +1 -0
  120. package/dist/__tests__/linear-comment.test.js +151 -0
  121. package/dist/__tests__/linear-comment.test.js.map +1 -0
  122. package/dist/__tests__/linear-identity.test.d.ts +2 -0
  123. package/dist/__tests__/linear-identity.test.d.ts.map +1 -0
  124. package/dist/__tests__/linear-identity.test.js +253 -0
  125. package/dist/__tests__/linear-identity.test.js.map +1 -0
  126. package/dist/__tests__/linear-notify.test.d.ts +2 -0
  127. package/dist/__tests__/linear-notify.test.d.ts.map +1 -0
  128. package/dist/__tests__/linear-notify.test.js +144 -0
  129. package/dist/__tests__/linear-notify.test.js.map +1 -0
  130. package/dist/__tests__/linear-ref.test.d.ts +2 -0
  131. package/dist/__tests__/linear-ref.test.d.ts.map +1 -0
  132. package/dist/__tests__/linear-ref.test.js +261 -0
  133. package/dist/__tests__/linear-ref.test.js.map +1 -0
  134. package/dist/__tests__/linear-test-ref.test.d.ts +2 -0
  135. package/dist/__tests__/linear-test-ref.test.d.ts.map +1 -0
  136. package/dist/__tests__/linear-test-ref.test.js +81 -0
  137. package/dist/__tests__/linear-test-ref.test.js.map +1 -0
  138. package/dist/__tests__/linear-verify.test.d.ts +2 -0
  139. package/dist/__tests__/linear-verify.test.d.ts.map +1 -0
  140. package/dist/__tests__/linear-verify.test.js +132 -0
  141. package/dist/__tests__/linear-verify.test.js.map +1 -0
  142. package/dist/__tests__/linear-worker.test.d.ts +2 -0
  143. package/dist/__tests__/linear-worker.test.d.ts.map +1 -0
  144. package/dist/__tests__/linear-worker.test.js +83 -0
  145. package/dist/__tests__/linear-worker.test.js.map +1 -0
  146. package/dist/__tests__/linear-write-possible.test.d.ts +2 -0
  147. package/dist/__tests__/linear-write-possible.test.d.ts.map +1 -0
  148. package/dist/__tests__/linear-write-possible.test.js +30 -0
  149. package/dist/__tests__/linear-write-possible.test.js.map +1 -0
  150. package/dist/__tests__/loader.test.js +2 -2
  151. package/dist/__tests__/loader.test.js.map +1 -1
  152. package/dist/__tests__/onboard-preservation.test.js +59 -4
  153. package/dist/__tests__/onboard-preservation.test.js.map +1 -1
  154. package/dist/__tests__/optimize.test.js +2 -0
  155. package/dist/__tests__/optimize.test.js.map +1 -1
  156. package/dist/__tests__/pr-status.test.js +213 -3
  157. package/dist/__tests__/pr-status.test.js.map +1 -1
  158. package/dist/__tests__/pr-workflow-state.test.js +247 -7
  159. package/dist/__tests__/pr-workflow-state.test.js.map +1 -1
  160. package/dist/__tests__/repo-picker.test.js +7 -1
  161. package/dist/__tests__/repo-picker.test.js.map +1 -1
  162. package/dist/__tests__/repository-guidance.test.d.ts +2 -0
  163. package/dist/__tests__/repository-guidance.test.d.ts.map +1 -0
  164. package/dist/__tests__/repository-guidance.test.js +107 -0
  165. package/dist/__tests__/repository-guidance.test.js.map +1 -0
  166. package/dist/__tests__/review-comment-body.test.js +35 -0
  167. package/dist/__tests__/review-comment-body.test.js.map +1 -1
  168. package/dist/__tests__/review-models.test.js +19 -3
  169. package/dist/__tests__/review-models.test.js.map +1 -1
  170. package/dist/__tests__/review-strategy.test.d.ts +2 -0
  171. package/dist/__tests__/review-strategy.test.d.ts.map +1 -0
  172. package/dist/__tests__/review-strategy.test.js +397 -0
  173. package/dist/__tests__/review-strategy.test.js.map +1 -0
  174. package/dist/__tests__/reviewed-sha.test.d.ts +2 -0
  175. package/dist/__tests__/reviewed-sha.test.d.ts.map +1 -0
  176. package/dist/__tests__/reviewed-sha.test.js +116 -0
  177. package/dist/__tests__/reviewed-sha.test.js.map +1 -0
  178. package/dist/__tests__/runner.test.js +206 -2
  179. package/dist/__tests__/runner.test.js.map +1 -1
  180. package/dist/__tests__/skill-attribution.test.d.ts +2 -0
  181. package/dist/__tests__/skill-attribution.test.d.ts.map +1 -0
  182. package/dist/__tests__/skill-attribution.test.js +53 -0
  183. package/dist/__tests__/skill-attribution.test.js.map +1 -0
  184. package/dist/__tests__/skill-broker.test.d.ts +2 -0
  185. package/dist/__tests__/skill-broker.test.d.ts.map +1 -0
  186. package/dist/__tests__/skill-broker.test.js +186 -0
  187. package/dist/__tests__/skill-broker.test.js.map +1 -0
  188. package/dist/__tests__/skill-catalog.test.d.ts +2 -0
  189. package/dist/__tests__/skill-catalog.test.d.ts.map +1 -0
  190. package/dist/__tests__/skill-catalog.test.js +40 -0
  191. package/dist/__tests__/skill-catalog.test.js.map +1 -0
  192. package/dist/__tests__/skill-installer.test.d.ts +2 -0
  193. package/dist/__tests__/skill-installer.test.d.ts.map +1 -0
  194. package/dist/__tests__/skill-installer.test.js +96 -0
  195. package/dist/__tests__/skill-installer.test.js.map +1 -0
  196. package/dist/__tests__/skills-config.test.d.ts +2 -0
  197. package/dist/__tests__/skills-config.test.d.ts.map +1 -0
  198. package/dist/__tests__/skills-config.test.js +12 -0
  199. package/dist/__tests__/skills-config.test.js.map +1 -0
  200. package/dist/__tests__/superseded-fix-pr.test.d.ts +2 -0
  201. package/dist/__tests__/superseded-fix-pr.test.d.ts.map +1 -0
  202. package/dist/__tests__/superseded-fix-pr.test.js +225 -0
  203. package/dist/__tests__/superseded-fix-pr.test.js.map +1 -0
  204. package/dist/__tests__/webhook.test.js +60 -0
  205. package/dist/__tests__/webhook.test.js.map +1 -1
  206. package/dist/cli.js +37 -0
  207. package/dist/cli.js.map +1 -1
  208. package/dist/commands/adoption.d.ts +6 -0
  209. package/dist/commands/adoption.d.ts.map +1 -0
  210. package/dist/commands/adoption.js +132 -0
  211. package/dist/commands/adoption.js.map +1 -0
  212. package/dist/commands/detect-step.d.ts.map +1 -1
  213. package/dist/commands/detect-step.js +5 -1
  214. package/dist/commands/detect-step.js.map +1 -1
  215. package/dist/commands/diagnose.d.ts.map +1 -1
  216. package/dist/commands/diagnose.js +2 -2
  217. package/dist/commands/diagnose.js.map +1 -1
  218. package/dist/commands/init.d.ts.map +1 -1
  219. package/dist/commands/init.js +1 -2
  220. package/dist/commands/init.js.map +1 -1
  221. package/dist/commands/issue.js +1 -1
  222. package/dist/commands/issue.js.map +1 -1
  223. package/dist/commands/kickass.d.ts +1 -1
  224. package/dist/commands/kickass.d.ts.map +1 -1
  225. package/dist/commands/kickass.js +36 -22
  226. package/dist/commands/kickass.js.map +1 -1
  227. package/dist/commands/linear-test.d.ts +18 -0
  228. package/dist/commands/linear-test.d.ts.map +1 -0
  229. package/dist/commands/linear-test.js +130 -0
  230. package/dist/commands/linear-test.js.map +1 -0
  231. package/dist/commands/onboard.d.ts +36 -3
  232. package/dist/commands/onboard.d.ts.map +1 -1
  233. package/dist/commands/onboard.js +263 -45
  234. package/dist/commands/onboard.js.map +1 -1
  235. package/dist/commands/optimize.d.ts.map +1 -1
  236. package/dist/commands/optimize.js +1 -0
  237. package/dist/commands/optimize.js.map +1 -1
  238. package/dist/commands/review.d.ts.map +1 -1
  239. package/dist/commands/review.js +65 -6
  240. package/dist/commands/review.js.map +1 -1
  241. package/dist/commands/run.d.ts.map +1 -1
  242. package/dist/commands/run.js +85 -11
  243. package/dist/commands/run.js.map +1 -1
  244. package/dist/commands/scan.d.ts.map +1 -1
  245. package/dist/commands/scan.js +6 -0
  246. package/dist/commands/scan.js.map +1 -1
  247. package/dist/commands/skill.d.ts +2 -0
  248. package/dist/commands/skill.d.ts.map +1 -0
  249. package/dist/commands/skill.js +16 -0
  250. package/dist/commands/skill.js.map +1 -0
  251. package/dist/commands/status.d.ts.map +1 -1
  252. package/dist/commands/status.js +53 -1
  253. package/dist/commands/status.js.map +1 -1
  254. package/dist/commands/watch.d.ts.map +1 -1
  255. package/dist/commands/watch.js +215 -66
  256. package/dist/commands/watch.js.map +1 -1
  257. package/dist/config/loader.d.ts +3 -1
  258. package/dist/config/loader.d.ts.map +1 -1
  259. package/dist/config/loader.js +19 -6
  260. package/dist/config/loader.js.map +1 -1
  261. package/dist/config/review-model-tiers.json +3 -3
  262. package/dist/config/review-strategy.json +204 -0
  263. package/dist/config/schema.d.ts +269 -15
  264. package/dist/config/schema.d.ts.map +1 -1
  265. package/dist/config/schema.js +109 -10
  266. package/dist/config/schema.js.map +1 -1
  267. package/dist/github/client.d.ts +21 -1
  268. package/dist/github/client.d.ts.map +1 -1
  269. package/dist/github/client.js +46 -7
  270. package/dist/github/client.js.map +1 -1
  271. package/dist/github/detector.d.ts.map +1 -1
  272. package/dist/github/detector.js +28 -1
  273. package/dist/github/detector.js.map +1 -1
  274. package/dist/github/merge.d.ts +0 -5
  275. package/dist/github/merge.d.ts.map +1 -1
  276. package/dist/github/merge.js +0 -9
  277. package/dist/github/merge.js.map +1 -1
  278. package/dist/github/reviewed-sha.d.ts +13 -0
  279. package/dist/github/reviewed-sha.d.ts.map +1 -0
  280. package/dist/github/reviewed-sha.js +62 -0
  281. package/dist/github/reviewed-sha.js.map +1 -0
  282. package/dist/github/superseded-fix-pr.d.ts +19 -0
  283. package/dist/github/superseded-fix-pr.d.ts.map +1 -0
  284. package/dist/github/superseded-fix-pr.js +97 -0
  285. package/dist/github/superseded-fix-pr.js.map +1 -0
  286. package/dist/github/webhook.d.ts +8 -1
  287. package/dist/github/webhook.d.ts.map +1 -1
  288. package/dist/github/webhook.js +8 -1
  289. package/dist/github/webhook.js.map +1 -1
  290. package/dist/issues/ticket-ref.d.ts.map +1 -1
  291. package/dist/issues/ticket-ref.js +6 -5
  292. package/dist/issues/ticket-ref.js.map +1 -1
  293. package/dist/lib/adoption.d.ts +64 -0
  294. package/dist/lib/adoption.d.ts.map +1 -0
  295. package/dist/lib/adoption.js +165 -0
  296. package/dist/lib/adoption.js.map +1 -0
  297. package/dist/lib/annotation.d.ts +7 -0
  298. package/dist/lib/annotation.d.ts.map +1 -1
  299. package/dist/lib/annotation.js +11 -8
  300. package/dist/lib/annotation.js.map +1 -1
  301. package/dist/lib/auto-fix-branch.d.ts +35 -0
  302. package/dist/lib/auto-fix-branch.d.ts.map +1 -0
  303. package/dist/lib/auto-fix-branch.js +88 -0
  304. package/dist/lib/auto-fix-branch.js.map +1 -0
  305. package/dist/lib/board.d.ts +3 -0
  306. package/dist/lib/board.d.ts.map +1 -1
  307. package/dist/lib/board.js +4 -2
  308. package/dist/lib/board.js.map +1 -1
  309. package/dist/lib/clone.d.ts +2 -0
  310. package/dist/lib/clone.d.ts.map +1 -1
  311. package/dist/lib/clone.js +69 -10
  312. package/dist/lib/clone.js.map +1 -1
  313. package/dist/lib/comment-bodies.d.ts +37 -0
  314. package/dist/lib/comment-bodies.d.ts.map +1 -1
  315. package/dist/lib/comment-bodies.js +50 -9
  316. package/dist/lib/comment-bodies.js.map +1 -1
  317. package/dist/lib/logger.d.ts.map +1 -1
  318. package/dist/lib/logger.js +17 -1
  319. package/dist/lib/logger.js.map +1 -1
  320. package/dist/lib/pr-picker.d.ts +1 -1
  321. package/dist/lib/pr-picker.d.ts.map +1 -1
  322. package/dist/lib/pr-picker.js +10 -5
  323. package/dist/lib/pr-picker.js.map +1 -1
  324. package/dist/lib/pr-status.d.ts +2 -1
  325. package/dist/lib/pr-status.d.ts.map +1 -1
  326. package/dist/lib/pr-status.js +70 -6
  327. package/dist/lib/pr-status.js.map +1 -1
  328. package/dist/lib/pr-workflow-state.d.ts +17 -1
  329. package/dist/lib/pr-workflow-state.d.ts.map +1 -1
  330. package/dist/lib/pr-workflow-state.js +110 -5
  331. package/dist/lib/pr-workflow-state.js.map +1 -1
  332. package/dist/lib/repo-picker.d.ts +3 -0
  333. package/dist/lib/repo-picker.d.ts.map +1 -1
  334. package/dist/lib/repo-picker.js +45 -10
  335. package/dist/lib/repo-picker.js.map +1 -1
  336. package/dist/lib/repository-guidance.d.ts +2 -0
  337. package/dist/lib/repository-guidance.d.ts.map +1 -0
  338. package/dist/lib/repository-guidance.js +55 -0
  339. package/dist/lib/repository-guidance.js.map +1 -0
  340. package/dist/lib/review-models.d.ts +15 -2
  341. package/dist/lib/review-models.d.ts.map +1 -1
  342. package/dist/lib/review-models.js +26 -6
  343. package/dist/lib/review-models.js.map +1 -1
  344. package/dist/lib/review-strategy.d.ts +92 -0
  345. package/dist/lib/review-strategy.d.ts.map +1 -0
  346. package/dist/lib/review-strategy.js +282 -0
  347. package/dist/lib/review-strategy.js.map +1 -0
  348. package/dist/lib/runner.d.ts +123 -1
  349. package/dist/lib/runner.d.ts.map +1 -1
  350. package/dist/lib/runner.js +919 -101
  351. package/dist/lib/runner.js.map +1 -1
  352. package/dist/lib/vendor.d.ts +1 -0
  353. package/dist/lib/vendor.d.ts.map +1 -1
  354. package/dist/lib/vendor.js +6 -0
  355. package/dist/lib/vendor.js.map +1 -1
  356. package/dist/lib/workflow.d.ts +13 -0
  357. package/dist/lib/workflow.d.ts.map +1 -1
  358. package/dist/lib/workflow.js +24 -1
  359. package/dist/lib/workflow.js.map +1 -1
  360. package/dist/linear/client.d.ts +18 -0
  361. package/dist/linear/client.d.ts.map +1 -0
  362. package/dist/linear/client.js +67 -0
  363. package/dist/linear/client.js.map +1 -0
  364. package/dist/linear/comment.d.ts +20 -0
  365. package/dist/linear/comment.d.ts.map +1 -0
  366. package/dist/linear/comment.js +57 -0
  367. package/dist/linear/comment.js.map +1 -0
  368. package/dist/linear/identity.d.ts +59 -0
  369. package/dist/linear/identity.d.ts.map +1 -0
  370. package/dist/linear/identity.js +187 -0
  371. package/dist/linear/identity.js.map +1 -0
  372. package/dist/linear/notify.d.ts +35 -0
  373. package/dist/linear/notify.d.ts.map +1 -0
  374. package/dist/linear/notify.js +76 -0
  375. package/dist/linear/notify.js.map +1 -0
  376. package/dist/linear/ref.d.ts +13 -0
  377. package/dist/linear/ref.d.ts.map +1 -0
  378. package/dist/linear/ref.js +90 -0
  379. package/dist/linear/ref.js.map +1 -0
  380. package/dist/linear/verify.d.ts +26 -0
  381. package/dist/linear/verify.d.ts.map +1 -0
  382. package/dist/linear/verify.js +67 -0
  383. package/dist/linear/verify.js.map +1 -0
  384. package/dist/reviewers/address.d.ts +1 -1
  385. package/dist/reviewers/address.d.ts.map +1 -1
  386. package/dist/reviewers/address.js +1 -1
  387. package/dist/reviewers/address.js.map +1 -1
  388. package/dist/reviewers/claude.d.ts +4 -1
  389. package/dist/reviewers/claude.d.ts.map +1 -1
  390. package/dist/reviewers/claude.js +39 -7
  391. package/dist/reviewers/claude.js.map +1 -1
  392. package/dist/reviewers/codex-env.d.ts +12 -0
  393. package/dist/reviewers/codex-env.d.ts.map +1 -0
  394. package/dist/reviewers/codex-env.js +54 -0
  395. package/dist/reviewers/codex-env.js.map +1 -0
  396. package/dist/reviewers/codex.d.ts +18 -1
  397. package/dist/reviewers/codex.d.ts.map +1 -1
  398. package/dist/reviewers/codex.js +164 -81
  399. package/dist/reviewers/codex.js.map +1 -1
  400. package/dist/reviewers/conflict-resolve.d.ts +3 -1
  401. package/dist/reviewers/conflict-resolve.d.ts.map +1 -1
  402. package/dist/reviewers/conflict-resolve.js +21 -6
  403. package/dist/reviewers/conflict-resolve.js.map +1 -1
  404. package/dist/reviewers/fix.d.ts +5 -2
  405. package/dist/reviewers/fix.d.ts.map +1 -1
  406. package/dist/reviewers/fix.js +38 -12
  407. package/dist/reviewers/fix.js.map +1 -1
  408. package/dist/skills/attribution.d.ts +4 -0
  409. package/dist/skills/attribution.d.ts.map +1 -0
  410. package/dist/skills/attribution.js +14 -0
  411. package/dist/skills/attribution.js.map +1 -0
  412. package/dist/skills/broker-server.d.ts +2 -0
  413. package/dist/skills/broker-server.d.ts.map +1 -0
  414. package/dist/skills/broker-server.js +17 -0
  415. package/dist/skills/broker-server.js.map +1 -0
  416. package/dist/skills/broker.d.ts +43 -0
  417. package/dist/skills/broker.d.ts.map +1 -0
  418. package/dist/skills/broker.js +311 -0
  419. package/dist/skills/broker.js.map +1 -0
  420. package/dist/skills/catalog.d.ts +28 -0
  421. package/dist/skills/catalog.d.ts.map +1 -0
  422. package/dist/skills/catalog.js +104 -0
  423. package/dist/skills/catalog.js.map +1 -0
  424. package/dist/skills/installer.d.ts +10 -0
  425. package/dist/skills/installer.d.ts.map +1 -0
  426. package/dist/skills/installer.js +138 -0
  427. package/dist/skills/installer.js.map +1 -0
  428. package/dist/skills/integrity.d.ts +4 -0
  429. package/dist/skills/integrity.d.ts.map +1 -0
  430. package/dist/skills/integrity.js +36 -0
  431. package/dist/skills/integrity.js.map +1 -0
  432. package/docs/dynamic-thoroughness.md +738 -0
  433. package/docs/linear-identity-contract.md +139 -0
  434. package/docs/linear-identity.md +293 -0
  435. package/docs/metrics.md +115 -0
  436. package/docs/trust.md +159 -0
  437. package/get-started.md +322 -16
  438. package/package.json +6 -3
@@ -12,21 +12,28 @@ import { execSync } from 'child_process';
12
12
  import { promptRepoPicker, promptSinglePicker } from '../lib/repo-picker.js';
13
13
  import { DEFAULT_REVIEW_INSTRUCTIONS, DEFAULT_FIX_INSTRUCTIONS, DEFAULT_RECHECK_INSTRUCTIONS, DEFAULT_CONFLICT_RESOLVE_INSTRUCTIONS } from '../lib/workflow.js';
14
14
  import { formatRepoWorkflowSteps, readRepoWorkflowStepTypes } from '../lib/repo-workflow.js';
15
- // Model and effort settings for each quality tier.
16
- // These are written directly to vendors.claude / vendors.codex in the config.
15
+ import { initLogger, log as fileLog } from '../lib/logger.js';
16
+ import { LogsConfigSchema } from '../config/schema.js';
17
+ import { BUNDLED_SKILL_RECOMMENDATIONS, findCompetingSkill, loadSkillCatalog, RECOMMENDED_SKILL_NAMES, } from '../skills/catalog.js';
18
+ // Effort settings and display hints per quality tier.
19
+ //
20
+ // Cost is output-token cost at 48k output tokens, the measured median for one
21
+ // review. A review is an agentic session, so it takes 10-16 minutes of wall
22
+ // clock (median 643s over 43 logged runs) — the tier changes depth and the
23
+ // subprocess timeout, not seconds-scale latency.
17
24
  const QUALITY_TIERS = {
18
25
  fast: {
19
- description: 'quick scan, top issues only (~10s, lowest cost)',
26
+ description: 'quick scan, top issues only (~$0.24 per review)',
20
27
  claude: { model: 'haiku', effort: 'low' },
21
28
  codex: { model: 'gpt-5.6-luna', effort: 'low' },
22
29
  },
23
30
  balanced: {
24
- description: 'full review, all issues with explanations (~30s)',
31
+ description: 'full review, all issues with explanations (~$0.72 per review)',
25
32
  claude: { model: 'sonnet', effort: 'medium' },
26
33
  codex: { model: 'gpt-5.6-terra', effort: 'medium' },
27
34
  },
28
35
  thorough: {
29
- description: 'deep multi-pass, security + architecture (~60s+, higher cost)',
36
+ description: 'deep multi-pass, security + architecture (~$1.20 per review)',
30
37
  claude: { model: 'opus', effort: 'max' },
31
38
  codex: { model: 'gpt-5.6-sol', effort: 'high' },
32
39
  },
@@ -168,11 +175,36 @@ async function promptAuthorVendor(login, existingAuthorRoutes, opts) {
168
175
  console.log();
169
176
  return idx === 1 ? 'codex' : idx === 2 ? 'both' : 'claude';
170
177
  }
171
- async function promptQualityTier(claudeEnabled, codexEnabled, currentTier, opts) {
178
+ /**
179
+ * What a re-run of onboard proposes, from the `quality` block as it is WRITTEN.
180
+ *
181
+ * Both fields must come from the raw yaml, never the parsed config: the schema
182
+ * defaults `mode` to smart and `tier` to balanced, so a parsed value can never
183
+ * distinguish "the user chose this" from "this config predates the field".
184
+ * Reading the parsed tier made every existing config look hand-set, so any
185
+ * re-onboard — silently, under `--yes` — opted the user out of the new default.
186
+ *
187
+ * A config that named a tier before smart existed keeps it, on the reasoning
188
+ * that it was a deliberate choice; one that never mentioned quality does not.
189
+ */
190
+ export function thoroughnessDefaults(rawQuality) {
191
+ return {
192
+ tier: (rawQuality.tier ?? 'balanced'),
193
+ mode: rawQuality.mode === 'fixed' ? 'fixed'
194
+ : rawQuality.mode === 'smart' ? 'smart'
195
+ : rawQuality.tier ? 'fixed' : 'smart',
196
+ };
197
+ }
198
+ /**
199
+ * One question covering both knobs. `smart` leads because it is the default and
200
+ * the better answer for most repos; the three fixed tiers stay available for
201
+ * anyone who wants one model on everything.
202
+ */
203
+ async function promptQualityTier(claudeEnabled, codexEnabled, rawQuality, opts) {
204
+ const { tier: fallbackTier, mode: fallbackMode } = thoroughnessDefaults(rawQuality);
172
205
  if (opts.yes) {
173
- const tier = (currentTier ?? 'balanced');
174
- console.log(` Quality: ${chalk.cyan(tier)}`);
175
- return tier;
206
+ console.log(` Thoroughness: ${chalk.cyan(fallbackMode === 'smart' ? 'smart' : fallbackTier)}`);
207
+ return { tier: fallbackTier, mode: fallbackMode };
176
208
  }
177
209
  function modelHint(tier) {
178
210
  const t = QUALITY_TIERS[tier];
@@ -184,18 +216,30 @@ async function promptQualityTier(claudeEnabled, codexEnabled, currentTier, opts)
184
216
  return parts.join(' · ');
185
217
  }
186
218
  const tiers = ['fast', 'balanced', 'thorough'];
187
- const items = tiers.map(tier => ({
188
- label: tier,
189
- description: QUALITY_TIERS[tier].description,
190
- hint: modelHint(tier),
191
- }));
192
- const defaultIdx = tiers.indexOf((currentTier ?? 'balanced'));
219
+ const items = [
220
+ {
221
+ label: 'smart',
222
+ description: 'adjust model + effort per PR (recommended)',
223
+ hint: 'skips lockfiles · docs get review only · auth and migrations get the strongest model · escalates when a round does not resolve',
224
+ },
225
+ ...tiers.map(tier => ({
226
+ label: tier,
227
+ description: `${QUALITY_TIERS[tier].description} — same model on every PR`,
228
+ hint: modelHint(tier),
229
+ })),
230
+ ];
231
+ // smart is index 0; the three fixed tiers follow, so this is always >= 0.
232
+ const defaultIdx = fallbackMode === 'smart' ? 0 : tiers.indexOf(fallbackTier) + 1;
193
233
  const idx = await promptSinglePicker(items, {
194
- title: 'Review quality — how deep should the analysis go?',
195
- defaultIndex: defaultIdx >= 0 ? defaultIdx : 1,
234
+ title: 'Review thoroughness — how should crosscheck spend its budget?',
235
+ defaultIndex: defaultIdx,
196
236
  });
197
237
  console.log();
198
- return tiers[idx];
238
+ // Under smart the tier still matters: it is the fallback whenever a PR's file
239
+ // list can't be read (one-shot commands, API failures).
240
+ return idx === 0
241
+ ? { tier: fallbackTier, mode: 'smart' }
242
+ : { tier: tiers[idx - 1], mode: 'fixed' };
199
243
  }
200
244
  // Exported for tests; `workflowDir` defaults to the user's ~/.crosscheck for runtime callsites.
201
245
  export function detectCurrentPreset(workflowDir = join(homedir(), '.crosscheck')) {
@@ -290,6 +334,29 @@ async function promptWorkflowPipeline(opts) {
290
334
  return 'review-fix-recheck';
291
335
  return 'review-fix';
292
336
  }
337
+ // Linear write-back is in beta and not selectable from onboarding yet. The whole
338
+ // attribution ladder is still printed — each rung buys stronger attribution for
339
+ // more setup — because this is where people look for what crosscheck can do, and
340
+ // a single greyed-out "[1] off" advertises nothing. Nothing here is a prompt:
341
+ // whatever the config already has is carried through untouched, so a re-run can
342
+ // never disable an integration this step can no longer configure.
343
+ export async function promptLinear(current, opts) {
344
+ const currentMode = !current?.enabled
345
+ ? 'off'
346
+ : current.mode === 'client_credentials' ? 'client_credentials' : 'api_key';
347
+ const decision = { mode: currentMode, teamKeys: current?.teamKeys ?? [] };
348
+ if (opts.yes)
349
+ return decision;
350
+ console.log(chalk.dim(' Post the review verdict onto the Linear issue a PR belongs to.'));
351
+ console.log(` ${chalk.yellow('(beta)')} ${chalk.dim('— preview of what is coming; not selectable here yet.')}\n`);
352
+ console.log(chalk.dim(' [1] off — leave Linear alone'));
353
+ console.log(chalk.dim(' [2] api key — works immediately. Comments post under your own Linear account'));
354
+ console.log(chalk.dim(' [3] workspace app — comments post as crosscheck itself, with its own icon'));
355
+ console.log(chalk.dim(' one app per workspace, ~5 min, needs Linear settings access'));
356
+ console.log(chalk.dim(`\n Current: ${currentMode} (unchanged — edit \`linear:\` in crosscheck.config.yml to set it by hand)`));
357
+ console.log();
358
+ return decision;
359
+ }
293
360
  async function promptConnectionType(currentTunnel, opts) {
294
361
  if (opts.yes) {
295
362
  const backend = currentTunnel ?? 'localhost.run';
@@ -394,7 +461,7 @@ function buildWorkflowYaml(preset, maxRounds = 1, conflictResolve = false) {
394
461
  // Writes all onboard decisions to configPath and manages the global workflow.yml.
395
462
  // On re-runs, only the fields onboard owns are updated; everything else is preserved.
396
463
  export function applyOnboardConfig(configPath, decisions, workflowDir = join(homedir(), '.crosscheck')) {
397
- const { deployment, login, selectedRepos, selectedOrgs, vendorConfig, qualityTier, pipelinePreset, maxRounds, conflictResolve, tunnelBackend, smeeChannel, cloneProtocol } = decisions;
464
+ const { deployment, login, selectedRepos, selectedOrgs, vendorConfig, qualityTier, qualityMode, enabledSkills, pipelinePreset, maxRounds, conflictResolve, tunnelBackend, smeeChannel, cloneProtocol, linear } = decisions;
398
465
  mkdirSync(dirname(configPath), { recursive: true });
399
466
  // Load existing config (preserves all custom fields) or start fresh
400
467
  const raw = existsSync(configPath)
@@ -405,6 +472,9 @@ export function applyOnboardConfig(configPath, decisions, workflowDir = join(hom
405
472
  raw.orgs = selectedOrgs;
406
473
  raw.mode = vendorConfig.mode;
407
474
  raw.clone_protocol = cloneProtocol;
475
+ if (!raw.skills || typeof raw.skills !== 'object')
476
+ raw.skills = {};
477
+ raw.skills.enabled = enabledSkills;
408
478
  // Repos. Per-repo workflow depth is NOT stored here — it lives in standalone
409
479
  // files under ~/.crosscheck/workflows/ (written by `crosscheck alter`).
410
480
  raw.repos = selectedRepos.map(r => {
@@ -469,16 +539,67 @@ export function applyOnboardConfig(configPath, decisions, workflowDir = join(hom
469
539
  tunnelObj.backend = tunnelBackend;
470
540
  if (tunnelBackend === 'smee' && smeeChannel)
471
541
  tunnelObj.smee_channel = smeeChannel;
542
+ // ── Linear write-back ───────────────────────────────────────────────────────
543
+ // Only touched when onboard actually asked. Auth env-var names and the signature
544
+ // template are left alone so hand-edits survive a re-run.
545
+ if (linear) {
546
+ if (!raw.linear || typeof raw.linear !== 'object')
547
+ raw.linear = {};
548
+ const linearObj = raw.linear;
549
+ linearObj.enabled = linear.mode !== 'off';
550
+ if (linear.mode !== 'off') {
551
+ if (!linearObj.auth || typeof linearObj.auth !== 'object')
552
+ linearObj.auth = {};
553
+ linearObj.auth.mode = linear.mode;
554
+ // Write the selection even when empty — otherwise the `-` clear silently
555
+ // leaves the previous keys in the file and nothing appears to happen.
556
+ linearObj.team_keys = linear.teamKeys;
557
+ }
558
+ }
472
559
  // ── Quality tier + per-vendor effort ────────────────────────────────────────
473
- // claude.ts derives the model from quality.tier at runtime (vendor.model is ignored).
474
- // vendor.model is written for codex onlyapi-key auth uses it as an override.
560
+ // An explicit vendors.*.model outranks everything in resolveClaudeModel /
561
+ // resolveCodexModel. Under `fixed` that is exactly right one model on every
562
+ // PR is what the user asked for. Under `smart` it is fatal: a pinned model
563
+ // makes per-PR tier selection a silent no-op, so the pin is cleared and the
564
+ // model resolves from the strategy instead.
565
+ //
566
+ // `tier` is written in both modes. Under smart it is the fallback for PRs
567
+ // whose file list can't be read (one-shot commands, API failures).
475
568
  if (!raw.quality || typeof raw.quality !== 'object')
476
569
  raw.quality = {};
477
- raw.quality.tier = qualityTier;
570
+ const qualityRaw = raw.quality;
571
+ qualityRaw.tier = qualityTier;
572
+ qualityRaw.mode = qualityMode;
478
573
  const tierCfg = QUALITY_TIERS[qualityTier];
479
574
  vendors.claude.effort = tierCfg.claude.effort;
480
- vendors.codex.model = tierCfg.codex.model;
481
575
  vendors.codex.effort = tierCfg.codex.effort;
576
+ if (qualityMode === 'smart') {
577
+ // A pinned model outranks the strategy, so smart mode cannot work while one
578
+ // is set. Report every removal — silently discarding user-authored config
579
+ // (especially under `--yes`) is worse than leaving smart mode inert.
580
+ for (const vendor of ['codex', 'claude']) {
581
+ const pinned = vendors[vendor].model;
582
+ if (pinned) {
583
+ console.log(chalk.yellow(` cleared vendors.${vendor}.model (${String(pinned)}) — a pinned model overrides smart mode`));
584
+ delete vendors[vendor].model;
585
+ }
586
+ }
587
+ // Codex needs an explicit per-tier map to vary by tier at all: with no
588
+ // `model` and no `model_tiers`, resolveCodexModel returns the CLI's own
589
+ // 'default' under subscription auth, so fast/balanced/thorough would be the
590
+ // same run. `model_tiers` is honored ahead of the auth check, so writing it
591
+ // is what makes smart mode mean anything for codex. Claude needs no
592
+ // equivalent — its tier map applies under either auth.
593
+ ;
594
+ vendors.codex.model_tiers = {
595
+ fast: QUALITY_TIERS.fast.codex.model,
596
+ balanced: QUALITY_TIERS.balanced.codex.model,
597
+ thorough: QUALITY_TIERS.thorough.codex.model,
598
+ };
599
+ }
600
+ else {
601
+ vendors.codex.model = tierCfg.codex.model;
602
+ }
482
603
  // ── Fix delivery mechanism (operational config, not pipeline logic) ──────────
483
604
  // Pipeline steps and trigger conditions live in workflow.yml.
484
605
  // config.yml only retains how fixes land on the PR (commit / pull_request / comment).
@@ -567,17 +688,52 @@ export async function runOnboard(opts = {}) {
567
688
  console.error(chalk.dim('Run crosscheck init and edit crosscheck.config.yml manually.'));
568
689
  process.exit(1);
569
690
  }
691
+ // Onboard runs before a config exists on a fresh install, so it initialises
692
+ // logging from the schema defaults (enabled, local files only) unless an
693
+ // existing config already opted out or set a custom retention — otherwise
694
+ // re-running onboard on an existing `logs.enabled: false` install would start
695
+ // writing logs again, and could delete logs older than the default retention.
696
+ const preOnboardConfigPath = opts.config ?? resolveConfigPath() ?? join(homedir(), '.crosscheck', 'config.yml');
697
+ const preOnboardConfig = existsSync(preOnboardConfigPath) ? loadConfig(preOnboardConfigPath) : null;
698
+ initLogger(preOnboardConfig?.logs ?? LogsConfigSchema.parse({}));
699
+ fileLog({ level: 'info', event: 'onboard_started', command: 'onboard' });
700
+ // Every path out of onboard reports an outcome, so `started` minus `completed`
701
+ // is the abandonment count rather than a number that also absorbs crashes.
702
+ const onboardAbandoned = (stage) => {
703
+ fileLog({ level: 'info', event: 'onboard_completed', command: 'onboard', outcome: 'abandoned', stage });
704
+ };
570
705
  console.log(chalk.bold('\ncrosscheck onboard\n'));
571
706
  // ── Step 1: Auth check ─────────────────────────────────────────────────────
572
707
  console.log(chalk.bold('Step 1 — environment check'));
708
+ console.log(chalk.dim(' Confirms the CLIs and auth crosscheck needs before it writes anything.'));
573
709
  const env = await checkEnv();
574
- if (!env.ok)
710
+ if (!env.ok) {
711
+ onboardAbandoned('environment_check');
575
712
  process.exit(1);
713
+ }
576
714
  console.log();
577
715
  // ── Step 2: Deployment mode ────────────────────────────────────────────────
578
- console.log(chalk.bold('Step 2 — deployment mode'));
579
- const configPath = opts.config ?? resolveConfigPath() ?? join(homedir(), '.crosscheck', 'config.yml');
580
- const existingConfig = existsSync(configPath) ? loadConfig(configPath) : null;
716
+ console.log(chalk.bold('Step 2 — who you review for'));
717
+ console.log(chalk.dim(' Personal reviews only PRs you author. Team reviews every author in the org.'));
718
+ const configPath = preOnboardConfigPath;
719
+ const existingConfig = preOnboardConfig;
720
+ // Parsed config always carries a `tier` and a `mode` (schema defaults), so
721
+ // read the raw file to tell an explicit choice from a pre-`mode` config.
722
+ const rawQuality = (() => {
723
+ if (!existsSync(configPath))
724
+ return {};
725
+ try {
726
+ const raw = yaml.load(readFileSync(configPath, 'utf8'));
727
+ const quality = raw?.quality;
728
+ return {
729
+ ...(typeof quality?.tier === 'string' && { tier: quality.tier }),
730
+ ...(typeof quality?.mode === 'string' && { mode: quality.mode }),
731
+ };
732
+ }
733
+ catch {
734
+ return {};
735
+ }
736
+ })();
581
737
  const currentDeployment = existingConfig?.deployment;
582
738
  let deployment;
583
739
  if (opts.personal) {
@@ -606,13 +762,15 @@ export async function runOnboard(opts = {}) {
606
762
  }
607
763
  console.log();
608
764
  // ── Step 3: Repo selection (hierarchical: namespace → repos) ───────────────
609
- console.log(chalk.bold('Step 3 — select repos to monitor'));
765
+ console.log(chalk.bold('Step 3 — repos to watch'));
766
+ console.log(chalk.dim(' Only these get a webhook, and only these get reviewed.'));
610
767
  let token;
611
768
  try {
612
769
  token = getGithubToken();
613
770
  }
614
771
  catch (err) {
615
772
  console.error(chalk.red(err instanceof Error ? err.message : String(err)));
773
+ onboardAbandoned('github_token');
616
774
  process.exit(1);
617
775
  }
618
776
  const login = detectGitHubLogin() ?? '';
@@ -738,33 +896,70 @@ export async function runOnboard(opts = {}) {
738
896
  }
739
897
  console.log();
740
898
  // ── Step 4: Review mode (cross-vendor vs single-vendor) ───────────────────
741
- console.log(chalk.bold('Step 4 — review mode'));
899
+ console.log(chalk.bold('Step 4 — who reviews'));
900
+ console.log(chalk.dim(' Cross-vendor keeps the reviewer independent of the author — self-review is where early victory hides.'));
742
901
  const vendorConfig = await promptVendorMode(env.claudeOk, env.codexOk, existingConfig?.mode, existingConfig?.vendors?.claude?.enabled ?? true, existingConfig?.vendors?.codex?.enabled ?? true, opts);
743
902
  console.log();
744
903
  // ── Step 5: Primary author (cross-vendor + personal only) ───────────────────
745
- console.log(chalk.bold('Step 5 primary author'));
904
+ // Header and description live inside the branch: printing them unconditionally
905
+ // and then skipping the prompt left a heading describing a question never asked.
746
906
  let authorVendor = 'both';
747
907
  if (vendorConfig.mode === 'cross-vendor' && deployment === 'personal') {
908
+ console.log(chalk.bold('Step 5 — your coding agent'));
909
+ console.log(chalk.dim(' Routes your PRs to the other vendor even when the attribution footer is missing.'));
748
910
  const existingRoutes = existingConfig?.routing?.author_routes ?? null;
749
911
  authorVendor = await promptAuthorVendor(login, existingRoutes, opts);
750
912
  }
751
913
  else {
752
914
  const reason = vendorConfig.mode === 'single-vendor' ? 'single-vendor mode' : 'team mode';
753
- console.log(chalk.dim(` Skipped — not applicable in ${reason}.`));
915
+ console.log(chalk.bold('Step 5 — your coding agent') + chalk.dim(` — skipped, not applicable in ${reason}.`));
754
916
  console.log();
755
917
  }
756
- // ── Step 6: Review quality ────────────────────────────────────────────────
757
- console.log(chalk.bold('Step 6 — review quality'));
758
- const qualityTier = await promptQualityTier(vendorConfig.claudeEnabled, vendorConfig.codexEnabled, existingConfig?.quality?.tier, opts);
918
+ // ── Step 6: Review thoroughness ───────────────────────────────────────────
919
+ console.log(chalk.bold('Step 6 — review thoroughness'));
920
+ console.log(chalk.dim(' Decides how much model budget each PR earns.'));
921
+ const thoroughness = await promptQualityTier(vendorConfig.claudeEnabled, vendorConfig.codexEnabled, rawQuality, opts);
922
+ const qualityTier = thoroughness.tier;
923
+ const qualityMode = thoroughness.mode;
924
+ console.log();
925
+ // ── Step 6.5: Agent skills ────────────────────────────────────────────────
926
+ console.log(chalk.bold('Step 7 — agent skills'));
927
+ console.log(chalk.dim(' Extra review practices the agent can draw on. It picks what fits each step.'));
928
+ const bundledSkills = loadSkillCatalog();
929
+ const initialSkills = existingConfig
930
+ ? existingConfig.skills.enabled
931
+ : [...RECOMMENDED_SKILL_NAMES];
932
+ console.log(chalk.dim(' Base-branch AGENTS.md and CLAUDE.md practices take precedence over Crosscheck skill advice.'));
933
+ const enabledSkills = opts.yes
934
+ ? initialSkills
935
+ : await promptRepoPicker(bundledSkills.map(skill => skill.name), {
936
+ title: 'Enable skills for coding agents:',
937
+ initialSelected: initialSkills,
938
+ getDescription: name => {
939
+ const skill = bundledSkills.find(candidate => candidate.name === name);
940
+ return skill ? `(by @${skill.author}, ${skill.license})` : '';
941
+ },
942
+ getHint: (name, selected) => {
943
+ const skill = bundledSkills.find(candidate => candidate.name === name);
944
+ const recommendation = BUNDLED_SKILL_RECOMMENDATIONS[name] ?? skill?.description ?? '';
945
+ const competitor = findCompetingSkill(name, selected);
946
+ return competitor ? `${recommendation} Competes with selected ${competitor}.` : recommendation;
947
+ },
948
+ getSelectionWarning: (name, selected) => {
949
+ const competitor = findCompetingSkill(name, selected);
950
+ return competitor ? `${name} competes with selected ${competitor}; deselect it first.` : undefined;
951
+ },
952
+ });
953
+ console.log(` Enabled: ${enabledSkills.length > 0 ? enabledSkills.map(name => chalk.cyan(name)).join(', ') : chalk.dim('none')}`);
759
954
  console.log();
760
955
  // ── Step 7: Workflow pipeline ──────────────────────────────────────────────
761
- console.log(chalk.bold('Step 7workflow pipeline'));
956
+ console.log(chalk.bold('Step 8what happens after a review'));
957
+ console.log(chalk.dim(' Review alone posts findings. Adding fix and re-check closes the loop before merge.'));
762
958
  const pipelinePreset = await promptWorkflowPipeline(opts);
763
959
  console.log();
764
- // ── Step 7.5: Max rounds (review-fix-recheck only) ────────────────────────
960
+ // Sub-prompt of step 8 only meaningful when a recheck step exists.
765
961
  let maxRounds;
766
962
  if (pipelinePreset === 'review-fix-recheck') {
767
- console.log(chalk.bold('Step 7.5 — fix → re-check rounds'));
768
963
  const globalWorkflowPath = join(homedir(), '.crosscheck', 'workflow.yml');
769
964
  let currentMaxRounds;
770
965
  if (existsSync(globalWorkflowPath)) {
@@ -778,13 +973,14 @@ export async function runOnboard(opts = {}) {
778
973
  maxRounds = await promptMaxRounds(currentMaxRounds, opts);
779
974
  console.log();
780
975
  }
781
- // ── Step 7.7: Auto-conflict-resolve (opt-in) ──────────────────────────────
782
- console.log(chalk.bold('Step 7.7 auto conflict-resolve'));
976
+ // Sub-prompt of step 8 orthogonal to depth, so it is asked for any preset
977
+ // that permits code modification.
783
978
  const currentConflictResolve = detectConflictResolveEnabled();
784
979
  const conflictResolve = await promptConflictResolve(currentConflictResolve, opts);
785
980
  console.log();
786
- // ── Step 8: Connection type ────────────────────────────────────────────────
787
- console.log(chalk.bold('Step 8 — connection type'));
981
+ // ── Step 9: Connection + clone protocol ───────────────────────────────────
982
+ console.log(chalk.bold('Step 9 — connection'));
983
+ console.log(chalk.dim(' How GitHub reaches this machine with PR events.'));
788
984
  const currentTunnel = existingConfig?.tunnel?.backend;
789
985
  let tunnelBackend = await promptConnectionType(currentTunnel, opts);
790
986
  let smeeChannel = existingConfig?.tunnel?.smee_channel ?? '';
@@ -810,20 +1006,28 @@ export async function runOnboard(opts = {}) {
810
1006
  }
811
1007
  }
812
1008
  console.log();
813
- // ── Step 9: Clone protocol ─────────────────────────────────────────────────
814
- console.log(chalk.bold('Step 9 — clone protocol'));
1009
+ // Sub-prompt of step 9 same plumbing concern as the tunnel.
815
1010
  const cloneProtocol = await promptCloneProtocol(existingConfig?.clone_protocol, opts);
1011
+ console.log();
1012
+ // ── Step 10: Linear write-back (beta, opt-in) ─────────────────────────────
1013
+ console.log(chalk.bold(`Step 10 — Linear write-back ${chalk.yellow('(beta)')}`));
1014
+ const linear = await promptLinear({
1015
+ enabled: existingConfig?.linear?.enabled,
1016
+ mode: existingConfig?.linear?.auth?.mode,
1017
+ teamKeys: existingConfig?.linear?.team_keys,
1018
+ }, opts);
816
1019
  // ── Step 10: Confirm and write ─────────────────────────────────────────────
817
1020
  const selectedRepoWorkflowOverrides = selectedRepos.flatMap(repoKey => {
818
1021
  const [owner, name] = repoKey.split('/');
819
1022
  const steps = readRepoWorkflowStepTypes(owner, name);
820
1023
  return steps ? [`${repoKey}: ${formatRepoWorkflowSteps(steps)}`] : [];
821
1024
  });
822
- console.log(chalk.bold('Step 10 — review and write config'));
1025
+ console.log(chalk.bold('Step 11 — review and write config'));
823
1026
  console.log();
824
1027
  console.log(` deployment ${chalk.cyan(deployment)}`);
825
1028
  console.log(` connection ${chalk.cyan(tunnelBackend)}${tunnelBackend === 'smee' && smeeChannel ? chalk.dim(` (${smeeChannel})`) : ''}`);
826
1029
  console.log(` clone ${chalk.cyan(cloneProtocol)}`);
1030
+ console.log(` linear ${chalk.cyan(linear.mode === 'off' ? 'off' : linear.mode)}${linear.mode !== 'off' && linear.teamKeys.length > 0 ? chalk.dim(` (${linear.teamKeys.join(', ')})`) : ''} ${chalk.yellow('(beta)')}`);
827
1031
  console.log(` mode ${chalk.cyan(vendorConfig.mode)}`);
828
1032
  if (vendorConfig.mode === 'single-vendor') {
829
1033
  const activeVendor = vendorConfig.claudeEnabled ? 'claude' : 'codex';
@@ -835,7 +1039,10 @@ export async function runOnboard(opts = {}) {
835
1039
  : `${authorVendor} → reviewed by ${authorVendor === 'claude' ? 'codex' : 'claude'}`;
836
1040
  console.log(` routing ${chalk.cyan(routingLabel)}`);
837
1041
  }
838
- console.log(` quality ${chalk.cyan(qualityTier)}${chalk.dim(` — ${QUALITY_TIERS[qualityTier].description.split(' ')[0]}`)}`);
1042
+ console.log(qualityMode === 'smart'
1043
+ ? ` quality ${chalk.cyan('smart')}${chalk.dim(` — model + effort adjusted per PR (${qualityTier} fallback)`)}`
1044
+ : ` quality ${chalk.cyan(qualityTier)}${chalk.dim(` — ${QUALITY_TIERS[qualityTier].description.split(' ')[0]}`)}`);
1045
+ console.log(` skills ${enabledSkills.length > 0 ? enabledSkills.map(name => chalk.cyan(name)).join(', ') : chalk.dim('none')}`);
839
1046
  console.log(` pipeline ${chalk.cyan(pipelinePreset)}`);
840
1047
  if (pipelinePreset === 'review-fix-recheck') {
841
1048
  console.log(` max rounds ${chalk.cyan(String(maxRounds ?? 1))}`);
@@ -860,6 +1067,7 @@ export async function runOnboard(opts = {}) {
860
1067
  const confirm = await ask(` Write to config? [Y/n]: `);
861
1068
  if (confirm.toLowerCase() === 'n') {
862
1069
  console.log(chalk.dim(' Aborted — no changes written.'));
1070
+ onboardAbandoned('config_write_declined');
863
1071
  return;
864
1072
  }
865
1073
  }
@@ -873,12 +1081,15 @@ export async function runOnboard(opts = {}) {
873
1081
  vendorConfig,
874
1082
  authorVendor,
875
1083
  qualityTier,
1084
+ qualityMode,
1085
+ enabledSkills,
876
1086
  pipelinePreset,
877
1087
  maxRounds,
878
1088
  conflictResolve,
879
1089
  tunnelBackend,
880
1090
  smeeChannel,
881
1091
  cloneProtocol,
1092
+ linear,
882
1093
  });
883
1094
  console.log(chalk.green(` ✓ config written to ${configPath}`));
884
1095
  if (!hadWorkflow) {
@@ -888,6 +1099,13 @@ export async function runOnboard(opts = {}) {
888
1099
  console.log(chalk.dim(` keeping existing workflow at ${globalWorkflowPath}`));
889
1100
  }
890
1101
  console.log();
1102
+ fileLog({
1103
+ level: 'info', event: 'onboard_completed', command: 'onboard', outcome: 'completed',
1104
+ // Shape of the setup, never its contents: counts, not repo or org names.
1105
+ deployment, repos: selectedRepos.length, orgs: selectedOrgs.length,
1106
+ quality_tier: qualityTier, quality_mode: qualityMode, pipeline: pipelinePreset,
1107
+ tunnel: tunnelBackend, linear: linear !== null,
1108
+ });
891
1109
  // ── Next step hint ─────────────────────────────────────────────────────────
892
1110
  console.log(chalk.dim(' Run crosscheck watch to start monitoring.'));
893
1111
  const alterExample = selectedRepos[0] ?? 'owner/repo';