@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
@@ -0,0 +1,144 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { notifyLinear, issueBelongsToWorkspace } from '../linear/notify.js';
3
+ import { LinearConfigSchema } from '../config/schema.js';
4
+ const AUTH = {
5
+ mode: 'client_credentials',
6
+ token: 'minted',
7
+ bearer: true,
8
+ actor: 'crosscheck',
9
+ signature: '🤖 crosscheck · crosscheck',
10
+ signatureTemplate: '🤖 {actor} · {product}',
11
+ product: 'crosscheck',
12
+ createAsUser: 'crosscheck',
13
+ };
14
+ const PR = {
15
+ branch: 'feat/in-2269-identity',
16
+ title: 'feat: tiered Linear identity',
17
+ body: '',
18
+ url: 'https://github.com/acme/app/pull/12',
19
+ sha: 'abc1234',
20
+ };
21
+ const BASE = { auth: AUTH, pr: PR, verdict: 'NEEDS_WORK', reviewer: 'codex', origin: 'claude', model: 'gpt-5' };
22
+ function cfg(overrides = {}) {
23
+ return LinearConfigSchema.parse({ enabled: true, team_keys: ['IN'], ...overrides });
24
+ }
25
+ /** Replies to the issue lookup, then the comment mutation. */
26
+ function scriptedFetch(issueNodes, mutation) {
27
+ let call = 0;
28
+ return vi.fn(async () => {
29
+ call++;
30
+ if (call === 1)
31
+ return new Response(JSON.stringify({ data: { issues: { nodes: issueNodes } } }), { status: 200 });
32
+ return new Response(JSON.stringify({
33
+ data: mutation ?? { commentCreate: { success: true, comment: { id: 'c1', url: 'https://linear.app/x/c1' } } },
34
+ }), { status: 200 });
35
+ });
36
+ }
37
+ const FOUND = [{ id: 'uuid-1', identifier: 'IN-2269', url: 'https://linear.app/x/issue/IN-2269' }];
38
+ describe('notifyLinear', () => {
39
+ it('posts to the issue referenced by the branch', async () => {
40
+ const fetchImpl = scriptedFetch(FOUND);
41
+ const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
42
+ expect(result).toEqual({ status: 'posted', identifier: 'IN-2269', url: 'https://linear.app/x/c1' });
43
+ });
44
+ it('sends a body that leads with the signature and carries createAsUser', async () => {
45
+ const fetchImpl = scriptedFetch(FOUND);
46
+ await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
47
+ const calls = fetchImpl.mock.calls;
48
+ const mutation = JSON.parse(calls[1][1].body);
49
+ expect(mutation.variables.createAsUser).toBe('crosscheck');
50
+ expect(mutation.variables.body.split('\n')[0]).toBe('🤖 crosscheck · crosscheck');
51
+ });
52
+ it('skips when the verdict is not in comment_on', async () => {
53
+ const fetchImpl = scriptedFetch(FOUND);
54
+ const result = await notifyLinear({ ...BASE, config: cfg({ comment_on: ['BLOCK'] }) }, { fetchImpl });
55
+ expect(result).toEqual({ status: 'skipped', reason: 'verdict-not-configured' });
56
+ expect(fetchImpl).not.toHaveBeenCalled();
57
+ });
58
+ it('skips when the PR references no Linear issue', async () => {
59
+ const fetchImpl = scriptedFetch(FOUND);
60
+ const pr = { ...PR, branch: 'feat/no-ref', title: 'chore: bump', body: '' };
61
+ const result = await notifyLinear({ ...BASE, pr, config: cfg() }, { fetchImpl });
62
+ expect(result).toEqual({ status: 'skipped', reason: 'no-issue-ref' });
63
+ expect(fetchImpl).not.toHaveBeenCalled();
64
+ });
65
+ it('skips when the referenced issue does not exist', async () => {
66
+ const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl: scriptedFetch([]) });
67
+ expect(result).toEqual({ status: 'skipped', reason: 'issue-not-found', identifier: 'IN-2269' });
68
+ });
69
+ it('reports failure instead of throwing when the API errors', async () => {
70
+ const fetchImpl = vi.fn(async () => new Response('{}', { status: 500 }));
71
+ const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
72
+ expect(result.status).toBe('failed');
73
+ expect(result.reason).toContain('500');
74
+ });
75
+ it('reports failure when the mutation is rejected', async () => {
76
+ const fetchImpl = scriptedFetch(FOUND, { commentCreate: { success: false, comment: null } });
77
+ const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
78
+ expect(result.status).toBe('failed');
79
+ expect(result.reason).toMatch(/rejected/i);
80
+ });
81
+ it('never leaks the token in a failure reason', async () => {
82
+ const fetchImpl = vi.fn(async () => new Response('{}', { status: 401 }));
83
+ const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
84
+ expect(result.reason).not.toContain('minted');
85
+ });
86
+ it('follows a linear.app URL in the body when no team_keys are configured', async () => {
87
+ const pr = { ...PR, branch: 'feat/nope', title: 'chore', body: 'https://linear.app/x/issue/IN-2269/z' };
88
+ const result = await notifyLinear({ ...BASE, pr, config: cfg({ team_keys: [] }) }, { fetchImpl: scriptedFetch(FOUND) });
89
+ expect(result.status).toBe('posted');
90
+ });
91
+ });
92
+ describe('workspace mismatch', () => {
93
+ const scoped = (issueUrl) => {
94
+ let call = 0;
95
+ return vi.fn(async () => {
96
+ call++;
97
+ if (call === 1) {
98
+ return new Response(JSON.stringify({
99
+ data: { issues: { nodes: [{ id: 'u1', identifier: 'IN-2269', url: issueUrl }] } },
100
+ }), { status: 200 });
101
+ }
102
+ return new Response(JSON.stringify({
103
+ data: { commentCreate: { success: true, comment: { id: 'c1', url: 'https://linear.app/x/c1' } } },
104
+ }), { status: 200 });
105
+ });
106
+ };
107
+ const prWithUrl = (url) => ({ ...PR, branch: 'feat/none', title: 'chore', body: url });
108
+ it('posts when the resolved issue is in the URL-named workspace', async () => {
109
+ const fetchImpl = scoped('https://linear.app/acme/issue/IN-2269/slug');
110
+ const result = await notifyLinear({ ...BASE, pr: prWithUrl('https://linear.app/acme/issue/IN-2269'), config: cfg({ team_keys: [] }) }, { fetchImpl });
111
+ expect(result.status).toBe('posted');
112
+ });
113
+ it('skips when the credentials resolve a different workspace', async () => {
114
+ // Same identifier, different workspace — a real issue, but not the one meant.
115
+ const fetchImpl = scoped('https://linear.app/other-corp/issue/IN-2269/slug');
116
+ const result = await notifyLinear({ ...BASE, pr: prWithUrl('https://linear.app/acme/issue/IN-2269'), config: cfg({ team_keys: [] }) }, { fetchImpl });
117
+ expect(result).toEqual({ status: 'skipped', reason: 'workspace-mismatch', identifier: 'IN-2269' });
118
+ });
119
+ it('does not post the comment when the workspace mismatches', async () => {
120
+ const fetchImpl = scoped('https://linear.app/other-corp/issue/IN-2269/slug');
121
+ await notifyLinear({ ...BASE, pr: prWithUrl('https://linear.app/acme/issue/IN-2269'), config: cfg({ team_keys: [] }) }, { fetchImpl });
122
+ expect(fetchImpl.mock.calls).toHaveLength(1);
123
+ });
124
+ it('does not constrain a bare ref, which names no workspace', async () => {
125
+ const fetchImpl = scoped('https://linear.app/whatever/issue/IN-2269/slug');
126
+ const result = await notifyLinear({ ...BASE, config: cfg() }, { fetchImpl });
127
+ expect(result.status).toBe('posted');
128
+ });
129
+ });
130
+ describe('issueBelongsToWorkspace', () => {
131
+ it('matches the first path segment', () => {
132
+ expect(issueBelongsToWorkspace('https://linear.app/acme/issue/IN-1', 'acme')).toBe(true);
133
+ });
134
+ it('is case insensitive', () => {
135
+ expect(issueBelongsToWorkspace('https://linear.app/ACME/issue/IN-1', 'acme')).toBe(true);
136
+ });
137
+ it('rejects a different workspace', () => {
138
+ expect(issueBelongsToWorkspace('https://linear.app/other/issue/IN-1', 'acme')).toBe(false);
139
+ });
140
+ it('fails closed on an unparseable URL', () => {
141
+ expect(issueBelongsToWorkspace('not a url', 'acme')).toBe(false);
142
+ });
143
+ });
144
+ //# sourceMappingURL=linear-notify.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear-notify.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-notify.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=linear-ref.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear-ref.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-ref.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,261 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { extractLinearRef } from '../linear/ref.js';
3
+ describe('extractLinearRef', () => {
4
+ describe('Linear URLs (no team_keys needed)', () => {
5
+ it('finds an issue URL in the PR body', () => {
6
+ const ref = extractLinearRef({ body: 'Closes https://linear.app/inductive-network/issue/IN-2269/some-slug' }, []);
7
+ expect(ref).toEqual({ id: 'IN-2269', key: 'IN', number: 2269, source: 'body', workspace: 'inductive-network' });
8
+ });
9
+ it('finds an issue URL in the PR title', () => {
10
+ const ref = extractLinearRef({ title: 'fix: see linear.app/acme/issue/ENG-7/thing' }, []);
11
+ expect(ref?.id).toBe('ENG-7');
12
+ expect(ref?.source).toBe('title');
13
+ });
14
+ it('accepts a URL with no trailing slug', () => {
15
+ const ref = extractLinearRef({ body: 'https://linear.app/acme/issue/AB-12' }, []);
16
+ expect(ref?.id).toBe('AB-12');
17
+ });
18
+ });
19
+ describe('bare identifiers require configured team_keys', () => {
20
+ it('ignores bare identifiers when team_keys is empty', () => {
21
+ expect(extractLinearRef({ branch: 'feat/in-2269-thing', title: 'IN-2269 fix' }, [])).toBeNull();
22
+ });
23
+ it('matches a configured key in a branch name, case-insensitively', () => {
24
+ const ref = extractLinearRef({ branch: 'feat/in-2269-tiered-identity' }, ['IN']);
25
+ expect(ref).toEqual({ id: 'IN-2269', key: 'IN', number: 2269, source: 'branch' });
26
+ });
27
+ it('matches an uppercase key in a title', () => {
28
+ const ref = extractLinearRef({ title: 'IN-543: unified resolver' }, ['IN']);
29
+ expect(ref?.id).toBe('IN-543');
30
+ expect(ref?.source).toBe('title');
31
+ });
32
+ it('does not match a key that is not configured', () => {
33
+ expect(extractLinearRef({ branch: 'feat/eng-1-thing' }, ['IN'])).toBeNull();
34
+ });
35
+ });
36
+ describe('false-positive resistance', () => {
37
+ // The reason bare-identifier matching is gated behind team_keys: these all
38
+ // look exactly like Linear identifiers to a naive regex.
39
+ const traps = ['UTF-8', 'SHA-256', 'ISO-8601', 'GPT-5', 'RFC-2119'];
40
+ for (const trap of traps) {
41
+ it(`does not treat ${trap} as an issue ref`, () => {
42
+ expect(extractLinearRef({ title: `chore: switch to ${trap}` }, [])).toBeNull();
43
+ });
44
+ }
45
+ it('still ignores traps when unrelated team_keys are configured', () => {
46
+ expect(extractLinearRef({ title: 'chore: SHA-256 hashing' }, ['IN'])).toBeNull();
47
+ });
48
+ it('requires a word boundary so SUBIN-1 does not match key IN', () => {
49
+ expect(extractLinearRef({ title: 'SUBIN-1 nope' }, ['IN'])).toBeNull();
50
+ });
51
+ });
52
+ describe('precedence', () => {
53
+ it('prefers branch over title and body', () => {
54
+ const ref = extractLinearRef({ branch: 'feat/in-1-a', title: 'IN-2 b', body: 'IN-3 c' }, ['IN']);
55
+ expect(ref?.id).toBe('IN-1');
56
+ expect(ref?.source).toBe('branch');
57
+ });
58
+ it('prefers title over body', () => {
59
+ const ref = extractLinearRef({ title: 'IN-2 b', body: 'IN-3 c' }, ['IN']);
60
+ expect(ref?.id).toBe('IN-2');
61
+ });
62
+ it('prefers an unambiguous URL over a bare key in the same field', () => {
63
+ const ref = extractLinearRef({ body: 'IN-99 mentioned, real one: https://linear.app/x/issue/IN-2269/z' }, ['IN']);
64
+ expect(ref?.id).toBe('IN-2269');
65
+ });
66
+ });
67
+ it('returns null when nothing matches', () => {
68
+ expect(extractLinearRef({ branch: 'main', title: 'chore: bump', body: '' }, ['IN'])).toBeNull();
69
+ });
70
+ it('handles absent fields', () => {
71
+ expect(extractLinearRef({}, ['IN'])).toBeNull();
72
+ });
73
+ });
74
+ describe('hostname anchoring', () => {
75
+ // Regression: an unanchored `linear.app/` fragment let any domain ending in
76
+ // "linear.app" bypass the team_keys gate and target an arbitrary issue.
77
+ const impostors = [
78
+ 'https://notlinear.app/acme/issue/IN-2269',
79
+ 'https://evil-linear.app/acme/issue/IN-2269',
80
+ 'https://mylinear.app/x/issue/IN-1/slug',
81
+ ];
82
+ for (const url of impostors) {
83
+ it(`does not trust ${url.replace('https://', '').split('/')[0]}`, () => {
84
+ expect(extractLinearRef({ body: url }, [])).toBeNull();
85
+ });
86
+ }
87
+ it('still trusts the real host', () => {
88
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-2269' }, [])?.id).toBe('IN-2269');
89
+ });
90
+ it('trusts it bare, without a scheme', () => {
91
+ expect(extractLinearRef({ body: 'see linear.app/acme/issue/IN-7' }, [])?.id).toBe('IN-7');
92
+ });
93
+ it('trusts it at the very start of a field', () => {
94
+ expect(extractLinearRef({ body: 'linear.app/acme/issue/IN-8' }, [])?.id).toBe('IN-8');
95
+ });
96
+ it('falls through to team_keys when an impostor URL is present', () => {
97
+ // The impostor must not shadow a legitimate bare ref elsewhere in the text.
98
+ const ref = extractLinearRef({ body: 'https://notlinear.app/x/issue/IN-999 but really IN-42' }, ['IN']);
99
+ expect(ref?.id).toBe('IN-999');
100
+ });
101
+ });
102
+ describe('hostname is parsed, not pattern-matched', () => {
103
+ // A path segment named linear.app is not the linear.app host. A boundary check
104
+ // on the surrounding character passes here; only real parsing rejects it.
105
+ const impostors = [
106
+ 'https://evil.example/linear.app/acme/issue/IN-2269',
107
+ 'https://evil.example/?u=https://linear.app/acme/issue/IN-2269',
108
+ 'https://linear.app.evil.example/acme/issue/IN-2269',
109
+ 'https://user@evil.example/linear.app/acme/issue/IN-1',
110
+ ];
111
+ for (const url of impostors) {
112
+ it(`rejects ${url.slice(8, 40)}...`, () => {
113
+ expect(extractLinearRef({ body: url }, [])).toBeNull();
114
+ });
115
+ }
116
+ it('accepts the real host over https', () => {
117
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-2269/slug' }, [])?.id).toBe('IN-2269');
118
+ });
119
+ it('accepts the real host case-insensitively', () => {
120
+ expect(extractLinearRef({ body: 'https://LINEAR.APP/acme/issue/IN-3' }, [])?.id).toBe('IN-3');
121
+ });
122
+ it('ignores a malformed URL without throwing', () => {
123
+ expect(extractLinearRef({ body: 'https://[not-a-url/issue/IN-9' }, [])).toBeNull();
124
+ });
125
+ it('finds a real URL after an impostor one', () => {
126
+ const body = 'https://evil.example/linear.app/x/issue/IN-1 and https://linear.app/acme/issue/IN-42';
127
+ expect(extractLinearRef({ body }, [])?.id).toBe('IN-42');
128
+ });
129
+ });
130
+ describe('workspace and issue-number boundaries', () => {
131
+ // Identifiers are unique only within a workspace, so an explicit URL must carry
132
+ // its slug — otherwise a URL for workspace A resolves against credentials for
133
+ // workspace B and comments on B's same-numbered issue.
134
+ it('carries the workspace slug from an absolute URL', () => {
135
+ const ref = extractLinearRef({ body: 'https://linear.app/acme/issue/IN-42' }, []);
136
+ expect(ref?.workspace).toBe('acme');
137
+ });
138
+ it('carries it from a scheme-less URL', () => {
139
+ expect(extractLinearRef({ body: 'see linear.app/beta-corp/issue/IN-42' }, [])?.workspace).toBe('beta-corp');
140
+ });
141
+ it('lower-cases the slug for comparison', () => {
142
+ expect(extractLinearRef({ body: 'https://linear.app/ACME/issue/IN-42' }, [])?.workspace).toBe('acme');
143
+ });
144
+ it('leaves a bare ref with no workspace, since it names none', () => {
145
+ const ref = extractLinearRef({ branch: 'feat/in-42-x' }, ['IN']);
146
+ expect(ref?.id).toBe('IN-42');
147
+ expect(ref?.workspace).toBeUndefined();
148
+ });
149
+ // Without a boundary after the digits, /issue/IN-123abc matched the IN-123
150
+ // prefix and could post to an unrelated issue while bypassing team_keys.
151
+ it('rejects a malformed identifier with a trailing suffix', () => {
152
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
153
+ });
154
+ it('accepts an identifier followed by a slug', () => {
155
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123/some-slug' }, [])?.id).toBe('IN-123');
156
+ });
157
+ it('accepts an identifier at the end of the path', () => {
158
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123' }, [])?.id).toBe('IN-123');
159
+ });
160
+ it('rejects a suffixed identifier in the scheme-less form too', () => {
161
+ expect(extractLinearRef({ body: 'see linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
162
+ });
163
+ });
164
+ describe('punctuation after a URL', () => {
165
+ // A ref at the end of a sentence is still a ref. Requiring a slash or the exact
166
+ // end of the path silently missed these.
167
+ const cases = [
168
+ ['Fixes https://linear.app/acme/issue/IN-42.', 'IN-42'],
169
+ ['See https://linear.app/acme/issue/IN-42, then merge', 'IN-42'],
170
+ ['(https://linear.app/acme/issue/IN-42)', 'IN-42'],
171
+ ['see linear.app/acme/issue/IN-42.', 'IN-42'],
172
+ ['see linear.app/acme/issue/IN-42, ok', 'IN-42'],
173
+ ];
174
+ for (const [body, expected] of cases) {
175
+ it(`resolves ${expected} from ${JSON.stringify(body.slice(0, 46))}`, () => {
176
+ expect(extractLinearRef({ body }, [])?.id).toBe(expected);
177
+ });
178
+ }
179
+ it('still rejects an alphanumeric suffix', () => {
180
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
181
+ expect(extractLinearRef({ body: 'see linear.app/acme/issue/IN-123abc' }, [])).toBeNull();
182
+ });
183
+ it('still rejects extra digits', () => {
184
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/IN-12/x' }, [])?.id).toBe('IN-12');
185
+ });
186
+ });
187
+ describe('the identifier must be a whole path segment', () => {
188
+ // Two lookahead attempts got this wrong in opposite directions: requiring a
189
+ // slash missed "IN-42." at the end of a sentence; allowing any non-alphanumeric
190
+ // accepted "IN-123-typo" as IN-123. The segment is now parsed whole.
191
+ const rejected = [
192
+ 'https://linear.app/acme/issue/IN-123-typo',
193
+ 'https://linear.app/acme/issue/IN-123abc',
194
+ 'https://linear.app/acme/issue/IN-123_x',
195
+ 'see linear.app/acme/issue/IN-123-typo',
196
+ ];
197
+ for (const url of rejected) {
198
+ it(`rejects ${url.split('/issue/')[1]}`, () => {
199
+ expect(extractLinearRef({ body: url }, [])).toBeNull();
200
+ });
201
+ }
202
+ const accepted = [
203
+ ['https://linear.app/acme/issue/IN-42', 'IN-42'],
204
+ ['https://linear.app/acme/issue/IN-42/some-slug', 'IN-42'],
205
+ ['Fixes https://linear.app/acme/issue/IN-42.', 'IN-42'],
206
+ ['See https://linear.app/acme/issue/IN-42, then merge', 'IN-42'],
207
+ ['(https://linear.app/acme/issue/IN-42)', 'IN-42'],
208
+ ['see linear.app/acme/issue/IN-42.', 'IN-42'],
209
+ ];
210
+ for (const [body, expected] of accepted) {
211
+ it(`accepts ${JSON.stringify(body.slice(0, 44))}`, () => {
212
+ expect(extractLinearRef({ body }, [])?.id).toBe(expected);
213
+ });
214
+ }
215
+ });
216
+ describe('a malformed candidate does not hide a valid one', () => {
217
+ it('skips a malformed scheme-less URL and finds the later valid one', () => {
218
+ const body = 'linear.app/acme/issue/IN-bad and then linear.app/acme/issue/IN-42';
219
+ expect(extractLinearRef({ body }, [])?.id).toBe('IN-42');
220
+ });
221
+ it('skips a malformed absolute URL and finds the later valid one', () => {
222
+ const body = 'https://linear.app/acme/issue/IN-bad https://linear.app/acme/issue/IN-42';
223
+ expect(extractLinearRef({ body }, [])?.id).toBe('IN-42');
224
+ });
225
+ it('returns null when every candidate is malformed', () => {
226
+ expect(extractLinearRef({ body: 'linear.app/a/issue/IN-bad linear.app/b/issue/X' }, [])).toBeNull();
227
+ });
228
+ it('keeps the workspace of the candidate that actually resolved', () => {
229
+ const body = 'linear.app/wrong/issue/IN-bad linear.app/right/issue/IN-42';
230
+ expect(extractLinearRef({ body }, [])?.workspace).toBe('right');
231
+ });
232
+ });
233
+ describe('punctuated scheme-less URLs keep their workspace', () => {
234
+ // Regression: `(linear.app/...)` missed the URL matcher, fell through to the
235
+ // bare-ID extractor, and came back without a workspace — silently losing the
236
+ // workspace-mismatch protection that an explicit URL is supposed to carry.
237
+ const wrapped = ['(linear.app/acme/issue/IN-42)', '[linear.app/acme/issue/IN-42]', '"linear.app/acme/issue/IN-42"', 'see: linear.app/acme/issue/IN-42'];
238
+ for (const body of wrapped) {
239
+ it(`keeps the workspace for ${JSON.stringify(body.slice(0, 32))}`, () => {
240
+ const ref = extractLinearRef({ body }, ['IN']);
241
+ expect(ref?.id).toBe('IN-42');
242
+ expect(ref?.workspace).toBe('acme');
243
+ });
244
+ }
245
+ it('still refuses a lookalike host after punctuation', () => {
246
+ expect(extractLinearRef({ body: '(notlinear.app/acme/issue/IN-42)' }, [])).toBeNull();
247
+ });
248
+ });
249
+ describe('one-character team keys', () => {
250
+ // Linear permits them; requiring two silently skipped those workspaces.
251
+ it('resolves a one-character key from a URL', () => {
252
+ expect(extractLinearRef({ body: 'https://linear.app/acme/issue/X-42' }, [])?.id).toBe('X-42');
253
+ });
254
+ it('resolves a one-character key as a configured bare ref', () => {
255
+ expect(extractLinearRef({ branch: 'feat/x-42-thing' }, ['X'])?.id).toBe('X-42');
256
+ });
257
+ it('still rejects a bare ref whose key is not configured', () => {
258
+ expect(extractLinearRef({ branch: 'feat/y-42-thing' }, ['X'])).toBeNull();
259
+ });
260
+ });
261
+ //# sourceMappingURL=linear-ref.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear-ref.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-ref.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=linear-test-ref.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear-test-ref.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-test-ref.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { resolveTestRef, softWrap } from '../commands/linear-test.js';
3
+ describe('resolveTestRef', () => {
4
+ it('takes a named issue verbatim, upper-cased', () => {
5
+ expect(resolveTestRef('eng-42', {}, [])).toEqual({ id: 'ENG-42', source: 'argument' });
6
+ });
7
+ it('lets a named issue bypass the team_keys gate', () => {
8
+ // Typing the ref is unambiguous in a way that finding it in a branch is not,
9
+ // so `crosscheck linear-test ENG-42` works before team_keys is configured.
10
+ expect(resolveTestRef('ENG-42', {}, [])).toEqual({ id: 'ENG-42', source: 'argument' });
11
+ });
12
+ it('prefers the named issue over anything inferable', () => {
13
+ expect(resolveTestRef('ENG-1', { branch: 'feat/in-2269-x' }, ['IN']))
14
+ .toEqual({ id: 'ENG-1', source: 'argument' });
15
+ });
16
+ it('falls back to the branch when no issue is named', () => {
17
+ expect(resolveTestRef(undefined, { branch: 'feat/in-2269-x' }, ['IN']))
18
+ .toEqual({ id: 'IN-2269', source: 'branch' });
19
+ });
20
+ it('falls back to the title', () => {
21
+ expect(resolveTestRef(undefined, { title: 'IN-543: resolver' }, ['IN']))
22
+ .toEqual({ id: 'IN-543', source: 'title' });
23
+ });
24
+ it('still honours the team_keys gate for inferred refs', () => {
25
+ expect(resolveTestRef(undefined, { branch: 'feat/in-2269-x' }, [])).toBeNull();
26
+ });
27
+ it('returns null with nothing to go on', () => {
28
+ expect(resolveTestRef(undefined, {}, ['IN'])).toBeNull();
29
+ });
30
+ it('treats a blank argument as absent', () => {
31
+ expect(resolveTestRef(' ', { branch: 'feat/in-7-x' }, ['IN']))
32
+ .toEqual({ id: 'IN-7', source: 'branch' });
33
+ });
34
+ });
35
+ describe('softWrap', () => {
36
+ it('leaves a short line alone', () => {
37
+ expect(softWrap('short', 20)).toEqual(['short']);
38
+ });
39
+ it('wraps on whitespace at the boundary', () => {
40
+ expect(softWrap('aaa bbb ccc ddd', 7)).toEqual(['aaa bbb', 'ccc ddd']);
41
+ });
42
+ it('never breaks a token, even one longer than the width', () => {
43
+ const url = 'https://github.com/acme/app/pull/42';
44
+ expect(softWrap(`see ${url} ok`, 10)).toEqual(['see', url, 'ok']);
45
+ });
46
+ it('wraps the annotation without splitting a field', () => {
47
+ const line = '<!-- crosscheck: origin=claude reviewer=codex model=gpt-5.6-terra type=review round=1 verdict=NEEDS_WORK -->';
48
+ for (const chunk of softWrap(line, 80))
49
+ expect(chunk.length).toBeLessThanOrEqual(80);
50
+ expect(softWrap(line, 80).join(' ')).toBe(line);
51
+ });
52
+ it('preserves content exactly when rejoined', () => {
53
+ const line = 'the quick brown fox jumps over the lazy dog';
54
+ expect(softWrap(line, 12).join(' ')).toBe(line);
55
+ });
56
+ });
57
+ describe('onboard Linear decision — regression guards', () => {
58
+ // These encode the two onboarding defects the recheck found. The prompt itself
59
+ // needs a TTY, so these pin the decision rules the prompt implements.
60
+ it('Enter must map to the current mode, not to off', () => {
61
+ const decide = (answer, currentMode) => answer === '1' ? 'off'
62
+ : answer === '2' ? 'api_key'
63
+ : answer === '3' ? 'client_credentials'
64
+ : currentMode;
65
+ expect(decide('', 'client_credentials')).toBe('client_credentials');
66
+ expect(decide('', 'api_key')).toBe('api_key');
67
+ expect(decide('', 'off')).toBe('off');
68
+ expect(decide('1', 'client_credentials')).toBe('off');
69
+ expect(decide('2', 'off')).toBe('api_key');
70
+ expect(decide('3', 'off')).toBe('client_credentials');
71
+ });
72
+ it('an explicit clear must be distinguishable from Enter', () => {
73
+ const resolveKeys = (answer, current) => answer === '-' ? []
74
+ : answer ? answer.split(',').map(k => k.trim().toUpperCase()).filter(Boolean)
75
+ : current;
76
+ expect(resolveKeys('', ['IN'])).toEqual(['IN']);
77
+ expect(resolveKeys('-', ['IN'])).toEqual([]);
78
+ expect(resolveKeys('eng,ops', ['IN'])).toEqual(['ENG', 'OPS']);
79
+ });
80
+ });
81
+ //# sourceMappingURL=linear-test-ref.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear-test-ref.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-test-ref.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=linear-verify.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear-verify.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-verify.test.ts"],"names":[],"mappings":""}