@humanbased/crosscheck 1.2.0-beta.80 → 1.2.0

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 (301) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +388 -149
  3. package/README.zh.md +1 -1
  4. package/crosscheck.config.example.yml +2 -63
  5. package/dist/__tests__/codex.test.js +27 -14
  6. package/dist/__tests__/codex.test.js.map +1 -1
  7. package/dist/__tests__/comment-bodies.test.js +1 -49
  8. package/dist/__tests__/comment-bodies.test.js.map +1 -1
  9. package/dist/__tests__/conflict-resolve.test.js +1 -44
  10. package/dist/__tests__/conflict-resolve.test.js.map +1 -1
  11. package/dist/__tests__/fix.test.js +0 -33
  12. package/dist/__tests__/fix.test.js.map +1 -1
  13. package/dist/__tests__/onboard-preservation.test.js +0 -5
  14. package/dist/__tests__/onboard-preservation.test.js.map +1 -1
  15. package/dist/__tests__/optimize.test.js +0 -2
  16. package/dist/__tests__/optimize.test.js.map +1 -1
  17. package/dist/__tests__/repo-picker.test.js +1 -7
  18. package/dist/__tests__/repo-picker.test.js.map +1 -1
  19. package/dist/__tests__/review-comment-body.test.js +0 -35
  20. package/dist/__tests__/review-comment-body.test.js.map +1 -1
  21. package/dist/__tests__/review-models.test.js +3 -19
  22. package/dist/__tests__/review-models.test.js.map +1 -1
  23. package/dist/__tests__/runner.test.js +1 -29
  24. package/dist/__tests__/runner.test.js.map +1 -1
  25. package/dist/cli.js +0 -29
  26. package/dist/cli.js.map +1 -1
  27. package/dist/commands/onboard.d.ts +0 -11
  28. package/dist/commands/onboard.d.ts.map +1 -1
  29. package/dist/commands/onboard.js +2 -88
  30. package/dist/commands/onboard.js.map +1 -1
  31. package/dist/commands/review.d.ts.map +1 -1
  32. package/dist/commands/review.js +6 -65
  33. package/dist/commands/review.js.map +1 -1
  34. package/dist/commands/run.d.ts.map +1 -1
  35. package/dist/commands/run.js +3 -23
  36. package/dist/commands/run.js.map +1 -1
  37. package/dist/commands/status.d.ts.map +1 -1
  38. package/dist/commands/status.js +1 -53
  39. package/dist/commands/status.js.map +1 -1
  40. package/dist/commands/watch.d.ts.map +1 -1
  41. package/dist/commands/watch.js +6 -52
  42. package/dist/commands/watch.js.map +1 -1
  43. package/dist/config/loader.d.ts +1 -3
  44. package/dist/config/loader.d.ts.map +1 -1
  45. package/dist/config/loader.js +0 -13
  46. package/dist/config/loader.js.map +1 -1
  47. package/dist/config/review-model-tiers.json +3 -3
  48. package/dist/config/schema.d.ts +10 -254
  49. package/dist/config/schema.d.ts.map +1 -1
  50. package/dist/config/schema.js +2 -62
  51. package/dist/config/schema.js.map +1 -1
  52. package/dist/github/client.d.ts +1 -6
  53. package/dist/github/client.d.ts.map +1 -1
  54. package/dist/github/client.js +7 -15
  55. package/dist/github/client.js.map +1 -1
  56. package/dist/issues/ticket-ref.d.ts.map +1 -1
  57. package/dist/issues/ticket-ref.js +5 -6
  58. package/dist/issues/ticket-ref.js.map +1 -1
  59. package/dist/lib/clone.d.ts +0 -1
  60. package/dist/lib/clone.d.ts.map +1 -1
  61. package/dist/lib/clone.js +10 -32
  62. package/dist/lib/clone.js.map +1 -1
  63. package/dist/lib/comment-bodies.d.ts +0 -35
  64. package/dist/lib/comment-bodies.d.ts.map +1 -1
  65. package/dist/lib/comment-bodies.js +9 -43
  66. package/dist/lib/comment-bodies.js.map +1 -1
  67. package/dist/lib/repo-picker.d.ts +0 -3
  68. package/dist/lib/repo-picker.d.ts.map +1 -1
  69. package/dist/lib/repo-picker.js +9 -45
  70. package/dist/lib/repo-picker.js.map +1 -1
  71. package/dist/lib/review-models.d.ts +2 -15
  72. package/dist/lib/review-models.d.ts.map +1 -1
  73. package/dist/lib/review-models.js +6 -26
  74. package/dist/lib/review-models.js.map +1 -1
  75. package/dist/lib/runner.d.ts +0 -12
  76. package/dist/lib/runner.d.ts.map +1 -1
  77. package/dist/lib/runner.js +42 -186
  78. package/dist/lib/runner.js.map +1 -1
  79. package/dist/lib/workflow.d.ts +0 -9
  80. package/dist/lib/workflow.d.ts.map +1 -1
  81. package/dist/lib/workflow.js +0 -20
  82. package/dist/lib/workflow.js.map +1 -1
  83. package/dist/reviewers/claude.d.ts +1 -4
  84. package/dist/reviewers/claude.d.ts.map +1 -1
  85. package/dist/reviewers/claude.js +7 -39
  86. package/dist/reviewers/claude.js.map +1 -1
  87. package/dist/reviewers/codex.d.ts +1 -3
  88. package/dist/reviewers/codex.d.ts.map +1 -1
  89. package/dist/reviewers/codex.js +70 -76
  90. package/dist/reviewers/codex.js.map +1 -1
  91. package/dist/reviewers/conflict-resolve.d.ts +1 -3
  92. package/dist/reviewers/conflict-resolve.d.ts.map +1 -1
  93. package/dist/reviewers/conflict-resolve.js +6 -21
  94. package/dist/reviewers/conflict-resolve.js.map +1 -1
  95. package/dist/reviewers/fix.d.ts +2 -5
  96. package/dist/reviewers/fix.d.ts.map +1 -1
  97. package/dist/reviewers/fix.js +10 -26
  98. package/dist/reviewers/fix.js.map +1 -1
  99. package/get-started.md +4 -139
  100. package/package.json +3 -4
  101. package/assets/icon-256.png +0 -0
  102. package/assets/linear-comment.svg +0 -18
  103. package/assets/linear-onboard.svg +0 -30
  104. package/assets/linear-status.svg +0 -23
  105. package/assets/linear-test.svg +0 -34
  106. package/assets/skills/code-review/.crosscheck-skill.json +0 -9
  107. package/assets/skills/code-review/LICENSE +0 -21
  108. package/assets/skills/code-review/SKILL.md +0 -89
  109. package/assets/skills/code-review/agents/openai.yaml +0 -3
  110. package/assets/skills/code-review-skill/.crosscheck-skill.json +0 -9
  111. package/assets/skills/code-review-skill/LICENSE +0 -21
  112. package/assets/skills/code-review-skill/SKILL.md +0 -231
  113. package/assets/skills/code-review-skill/assets/pr-review-template.md +0 -137
  114. package/assets/skills/code-review-skill/assets/review-checklist.md +0 -123
  115. package/assets/skills/code-review-skill/reference/angular.md +0 -768
  116. package/assets/skills/code-review-skill/reference/architecture-review-guide.md +0 -472
  117. package/assets/skills/code-review-skill/reference/c.md +0 -890
  118. package/assets/skills/code-review-skill/reference/code-quality-universal.md +0 -488
  119. package/assets/skills/code-review-skill/reference/code-review-best-practices.md +0 -136
  120. package/assets/skills/code-review-skill/reference/common-bugs-checklist.md +0 -286
  121. package/assets/skills/code-review-skill/reference/cpp.md +0 -893
  122. package/assets/skills/code-review-skill/reference/cross-cutting/async-concurrency-patterns.md +0 -515
  123. package/assets/skills/code-review-skill/reference/cross-cutting/error-handling-principles.md +0 -492
  124. package/assets/skills/code-review-skill/reference/cross-cutting/n-plus-one-queries.md +0 -309
  125. package/assets/skills/code-review-skill/reference/cross-cutting/sql-injection-prevention.md +0 -308
  126. package/assets/skills/code-review-skill/reference/cross-cutting/xss-prevention.md +0 -264
  127. package/assets/skills/code-review-skill/reference/csharp.md +0 -525
  128. package/assets/skills/code-review-skill/reference/css-less-sass.md +0 -661
  129. package/assets/skills/code-review-skill/reference/django.md +0 -985
  130. package/assets/skills/code-review-skill/reference/fastapi.md +0 -580
  131. package/assets/skills/code-review-skill/reference/go.md +0 -993
  132. package/assets/skills/code-review-skill/reference/java.md +0 -409
  133. package/assets/skills/code-review-skill/reference/java8.md +0 -586
  134. package/assets/skills/code-review-skill/reference/kotlin.md +0 -1018
  135. package/assets/skills/code-review-skill/reference/nestjs.md +0 -593
  136. package/assets/skills/code-review-skill/reference/performance-review-guide.md +0 -816
  137. package/assets/skills/code-review-skill/reference/php.md +0 -684
  138. package/assets/skills/code-review-skill/reference/python.md +0 -1073
  139. package/assets/skills/code-review-skill/reference/qt.md +0 -757
  140. package/assets/skills/code-review-skill/reference/react.md +0 -871
  141. package/assets/skills/code-review-skill/reference/ruby.md +0 -964
  142. package/assets/skills/code-review-skill/reference/rust.md +0 -846
  143. package/assets/skills/code-review-skill/reference/security-review-guide.md +0 -494
  144. package/assets/skills/code-review-skill/reference/svelte.md +0 -1064
  145. package/assets/skills/code-review-skill/reference/swift.md +0 -936
  146. package/assets/skills/code-review-skill/reference/typescript.md +0 -1016
  147. package/assets/skills/code-review-skill/reference/vue.md +0 -924
  148. package/assets/skills/code-review-skill/reference/zig.md +0 -440
  149. package/assets/skills/code-review-skill/scripts/pr-analyzer.py +0 -435
  150. package/assets/skills/code-review-skill/scripts/test_pr_analyzer.py +0 -380
  151. package/assets/skills/codebase-design/.crosscheck-skill.json +0 -9
  152. package/assets/skills/codebase-design/DEEPENING.md +0 -37
  153. package/assets/skills/codebase-design/DESIGN-IT-TWICE.md +0 -44
  154. package/assets/skills/codebase-design/LICENSE +0 -21
  155. package/assets/skills/codebase-design/SKILL.md +0 -114
  156. package/assets/skills/codebase-design/agents/openai.yaml +0 -3
  157. package/assets/skills/diagnosing-bugs/.crosscheck-skill.json +0 -9
  158. package/assets/skills/diagnosing-bugs/LICENSE +0 -21
  159. package/assets/skills/diagnosing-bugs/SKILL.md +0 -134
  160. package/assets/skills/diagnosing-bugs/agents/openai.yaml +0 -3
  161. package/assets/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +0 -41
  162. package/dist/__tests__/can-write-verdict.test.d.ts +0 -2
  163. package/dist/__tests__/can-write-verdict.test.d.ts.map +0 -1
  164. package/dist/__tests__/can-write-verdict.test.js +0 -31
  165. package/dist/__tests__/can-write-verdict.test.js.map +0 -1
  166. package/dist/__tests__/linear-branding.test.d.ts +0 -2
  167. package/dist/__tests__/linear-branding.test.d.ts.map +0 -1
  168. package/dist/__tests__/linear-branding.test.js +0 -156
  169. package/dist/__tests__/linear-branding.test.js.map +0 -1
  170. package/dist/__tests__/linear-client.test.d.ts +0 -2
  171. package/dist/__tests__/linear-client.test.d.ts.map +0 -1
  172. package/dist/__tests__/linear-client.test.js +0 -120
  173. package/dist/__tests__/linear-client.test.js.map +0 -1
  174. package/dist/__tests__/linear-comment.test.d.ts +0 -2
  175. package/dist/__tests__/linear-comment.test.d.ts.map +0 -1
  176. package/dist/__tests__/linear-comment.test.js +0 -151
  177. package/dist/__tests__/linear-comment.test.js.map +0 -1
  178. package/dist/__tests__/linear-identity.test.d.ts +0 -2
  179. package/dist/__tests__/linear-identity.test.d.ts.map +0 -1
  180. package/dist/__tests__/linear-identity.test.js +0 -253
  181. package/dist/__tests__/linear-identity.test.js.map +0 -1
  182. package/dist/__tests__/linear-notify.test.d.ts +0 -2
  183. package/dist/__tests__/linear-notify.test.d.ts.map +0 -1
  184. package/dist/__tests__/linear-notify.test.js +0 -144
  185. package/dist/__tests__/linear-notify.test.js.map +0 -1
  186. package/dist/__tests__/linear-ref.test.d.ts +0 -2
  187. package/dist/__tests__/linear-ref.test.d.ts.map +0 -1
  188. package/dist/__tests__/linear-ref.test.js +0 -261
  189. package/dist/__tests__/linear-ref.test.js.map +0 -1
  190. package/dist/__tests__/linear-test-ref.test.d.ts +0 -2
  191. package/dist/__tests__/linear-test-ref.test.d.ts.map +0 -1
  192. package/dist/__tests__/linear-test-ref.test.js +0 -81
  193. package/dist/__tests__/linear-test-ref.test.js.map +0 -1
  194. package/dist/__tests__/linear-verify.test.d.ts +0 -2
  195. package/dist/__tests__/linear-verify.test.d.ts.map +0 -1
  196. package/dist/__tests__/linear-verify.test.js +0 -132
  197. package/dist/__tests__/linear-verify.test.js.map +0 -1
  198. package/dist/__tests__/linear-worker.test.d.ts +0 -2
  199. package/dist/__tests__/linear-worker.test.d.ts.map +0 -1
  200. package/dist/__tests__/linear-worker.test.js +0 -83
  201. package/dist/__tests__/linear-worker.test.js.map +0 -1
  202. package/dist/__tests__/linear-write-possible.test.d.ts +0 -2
  203. package/dist/__tests__/linear-write-possible.test.d.ts.map +0 -1
  204. package/dist/__tests__/linear-write-possible.test.js +0 -30
  205. package/dist/__tests__/linear-write-possible.test.js.map +0 -1
  206. package/dist/__tests__/repository-guidance.test.d.ts +0 -2
  207. package/dist/__tests__/repository-guidance.test.d.ts.map +0 -1
  208. package/dist/__tests__/repository-guidance.test.js +0 -107
  209. package/dist/__tests__/repository-guidance.test.js.map +0 -1
  210. package/dist/__tests__/review-strategy.test.d.ts +0 -2
  211. package/dist/__tests__/review-strategy.test.d.ts.map +0 -1
  212. package/dist/__tests__/review-strategy.test.js +0 -195
  213. package/dist/__tests__/review-strategy.test.js.map +0 -1
  214. package/dist/__tests__/skill-attribution.test.d.ts +0 -2
  215. package/dist/__tests__/skill-attribution.test.d.ts.map +0 -1
  216. package/dist/__tests__/skill-attribution.test.js +0 -53
  217. package/dist/__tests__/skill-attribution.test.js.map +0 -1
  218. package/dist/__tests__/skill-broker.test.d.ts +0 -2
  219. package/dist/__tests__/skill-broker.test.d.ts.map +0 -1
  220. package/dist/__tests__/skill-broker.test.js +0 -144
  221. package/dist/__tests__/skill-broker.test.js.map +0 -1
  222. package/dist/__tests__/skill-catalog.test.d.ts +0 -2
  223. package/dist/__tests__/skill-catalog.test.d.ts.map +0 -1
  224. package/dist/__tests__/skill-catalog.test.js +0 -40
  225. package/dist/__tests__/skill-catalog.test.js.map +0 -1
  226. package/dist/__tests__/skill-installer.test.d.ts +0 -2
  227. package/dist/__tests__/skill-installer.test.d.ts.map +0 -1
  228. package/dist/__tests__/skill-installer.test.js +0 -96
  229. package/dist/__tests__/skill-installer.test.js.map +0 -1
  230. package/dist/__tests__/skills-config.test.d.ts +0 -2
  231. package/dist/__tests__/skills-config.test.d.ts.map +0 -1
  232. package/dist/__tests__/skills-config.test.js +0 -12
  233. package/dist/__tests__/skills-config.test.js.map +0 -1
  234. package/dist/commands/linear-test.d.ts +0 -18
  235. package/dist/commands/linear-test.d.ts.map +0 -1
  236. package/dist/commands/linear-test.js +0 -130
  237. package/dist/commands/linear-test.js.map +0 -1
  238. package/dist/commands/skill.d.ts +0 -2
  239. package/dist/commands/skill.d.ts.map +0 -1
  240. package/dist/commands/skill.js +0 -16
  241. package/dist/commands/skill.js.map +0 -1
  242. package/dist/config/review-strategy.json +0 -202
  243. package/dist/lib/repository-guidance.d.ts +0 -2
  244. package/dist/lib/repository-guidance.d.ts.map +0 -1
  245. package/dist/lib/repository-guidance.js +0 -55
  246. package/dist/lib/repository-guidance.js.map +0 -1
  247. package/dist/lib/review-strategy.d.ts +0 -62
  248. package/dist/lib/review-strategy.d.ts.map +0 -1
  249. package/dist/lib/review-strategy.js +0 -235
  250. package/dist/lib/review-strategy.js.map +0 -1
  251. package/dist/linear/client.d.ts +0 -18
  252. package/dist/linear/client.d.ts.map +0 -1
  253. package/dist/linear/client.js +0 -67
  254. package/dist/linear/client.js.map +0 -1
  255. package/dist/linear/comment.d.ts +0 -20
  256. package/dist/linear/comment.d.ts.map +0 -1
  257. package/dist/linear/comment.js +0 -57
  258. package/dist/linear/comment.js.map +0 -1
  259. package/dist/linear/identity.d.ts +0 -59
  260. package/dist/linear/identity.d.ts.map +0 -1
  261. package/dist/linear/identity.js +0 -187
  262. package/dist/linear/identity.js.map +0 -1
  263. package/dist/linear/notify.d.ts +0 -35
  264. package/dist/linear/notify.d.ts.map +0 -1
  265. package/dist/linear/notify.js +0 -76
  266. package/dist/linear/notify.js.map +0 -1
  267. package/dist/linear/ref.d.ts +0 -13
  268. package/dist/linear/ref.d.ts.map +0 -1
  269. package/dist/linear/ref.js +0 -90
  270. package/dist/linear/ref.js.map +0 -1
  271. package/dist/linear/verify.d.ts +0 -26
  272. package/dist/linear/verify.d.ts.map +0 -1
  273. package/dist/linear/verify.js +0 -67
  274. package/dist/linear/verify.js.map +0 -1
  275. package/dist/skills/attribution.d.ts +0 -4
  276. package/dist/skills/attribution.d.ts.map +0 -1
  277. package/dist/skills/attribution.js +0 -14
  278. package/dist/skills/attribution.js.map +0 -1
  279. package/dist/skills/broker-server.d.ts +0 -2
  280. package/dist/skills/broker-server.d.ts.map +0 -1
  281. package/dist/skills/broker-server.js +0 -17
  282. package/dist/skills/broker-server.js.map +0 -1
  283. package/dist/skills/broker.d.ts +0 -42
  284. package/dist/skills/broker.d.ts.map +0 -1
  285. package/dist/skills/broker.js +0 -285
  286. package/dist/skills/broker.js.map +0 -1
  287. package/dist/skills/catalog.d.ts +0 -28
  288. package/dist/skills/catalog.d.ts.map +0 -1
  289. package/dist/skills/catalog.js +0 -104
  290. package/dist/skills/catalog.js.map +0 -1
  291. package/dist/skills/installer.d.ts +0 -10
  292. package/dist/skills/installer.d.ts.map +0 -1
  293. package/dist/skills/installer.js +0 -138
  294. package/dist/skills/installer.js.map +0 -1
  295. package/dist/skills/integrity.d.ts +0 -4
  296. package/dist/skills/integrity.d.ts.map +0 -1
  297. package/dist/skills/integrity.js +0 -36
  298. package/dist/skills/integrity.js.map +0 -1
  299. package/docs/dynamic-thoroughness.md +0 -286
  300. package/docs/linear-identity-contract.md +0 -139
  301. package/docs/linear-identity.md +0 -293
@@ -1,67 +0,0 @@
1
- // All Linear API traffic goes through this module — mirrors the rule that GitHub
2
- // calls go through src/github/client.ts. No raw fetch to api.linear.app elsewhere.
3
- const GRAPHQL_ENDPOINT = 'https://api.linear.app/graphql';
4
- const IDENTIFIER_PATTERN = /^([A-Za-z][A-Za-z0-9]{0,9})-(\d+)$/;
5
- function authHeader(auth) {
6
- return auth.bearer ? `Bearer ${auth.token}` : auth.token;
7
- }
8
- export async function linearGraphQL(auth, query, variables, opts = {}) {
9
- const doFetch = opts.fetchImpl ?? globalThis.fetch;
10
- const endpoint = opts.endpoint ?? GRAPHQL_ENDPOINT;
11
- // The token rides in a header, never in the URL or on argv.
12
- const response = await doFetch(endpoint, {
13
- method: 'POST',
14
- headers: { 'Content-Type': 'application/json', Authorization: authHeader(auth) },
15
- body: JSON.stringify({ query, variables }),
16
- });
17
- const text = await response.text();
18
- if (!response.ok) {
19
- throw new Error(`Linear API request failed (HTTP ${response.status})`);
20
- }
21
- let parsed;
22
- try {
23
- parsed = JSON.parse(text);
24
- }
25
- catch {
26
- throw new Error(`Linear API returned a non-JSON response (HTTP ${response.status})`);
27
- }
28
- if (parsed.errors && parsed.errors.length > 0) {
29
- const detail = parsed.errors.map(e => e.message ?? 'unknown error').join('; ');
30
- throw new Error(`Linear API error: ${detail}`);
31
- }
32
- if (parsed.data === undefined) {
33
- throw new Error('Linear API returned no data');
34
- }
35
- return parsed.data;
36
- }
37
- const FIND_ISSUE = `query($teamKey: String!, $number: Float!) {
38
- issues(first: 1, filter: { team: { key: { eq: $teamKey } }, number: { eq: $number } }) {
39
- nodes { id identifier url }
40
- }
41
- }`;
42
- export async function findIssueByIdentifier(auth, identifier, opts = {}) {
43
- const match = identifier.match(IDENTIFIER_PATTERN);
44
- if (!match) {
45
- throw new Error(`Malformed Linear issue identifier: "${identifier}" (expected e.g. IN-2269)`);
46
- }
47
- const data = await linearGraphQL(auth, FIND_ISSUE, { teamKey: match[1].toUpperCase(), number: Number.parseInt(match[2], 10) }, opts);
48
- return data.issues.nodes[0] ?? null;
49
- }
50
- const CREATE_COMMENT = `mutation($issueId: String!, $body: String!, $createAsUser: String) {
51
- commentCreate(input: { issueId: $issueId, body: $body, createAsUser: $createAsUser }) {
52
- success
53
- comment { id url }
54
- }
55
- }`;
56
- export async function postLinearComment(auth, issueId, body, opts = {}) {
57
- const variables = { issueId, body };
58
- // Only T1 can attribute the write to the app actor.
59
- if (auth.createAsUser)
60
- variables.createAsUser = auth.createAsUser;
61
- const data = await linearGraphQL(auth, CREATE_COMMENT, variables, opts);
62
- if (!data.commentCreate.success || !data.commentCreate.comment) {
63
- throw new Error('Linear rejected the comment mutation');
64
- }
65
- return data.commentCreate.comment;
66
- }
67
- //# sourceMappingURL=client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/linear/client.ts"],"names":[],"mappings":"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"}
@@ -1,20 +0,0 @@
1
- export interface LinearCommentInput {
2
- /** `🤖 {actor} · {product}` — must lead the comment (IN-2260 step 3). */
3
- signature: string;
4
- verdict: string | null;
5
- reviewer: string;
6
- origin: string;
7
- model: string;
8
- prUrl: string;
9
- prTitle: string;
10
- sha?: string;
11
- summary?: string;
12
- service?: string;
13
- /** Workflow step this verdict came from — a recheck must not read as a review. */
14
- stepType?: string;
15
- round?: number;
16
- }
17
- export declare function buildLinearCommentBody(input: LinearCommentInput): string;
18
- export declare function normalizeVerdict(verdict: string | null): string;
19
- export declare function shouldPostToLinear(verdict: string | null, commentOn: readonly string[]): boolean;
20
- //# sourceMappingURL=comment.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"comment.d.ts","sourceRoot":"","sources":["../../src/linear/comment.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAsBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CA6BxE;AAKD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE/D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAGhG"}
@@ -1,57 +0,0 @@
1
- // Body construction for the comment crosscheck posts back to a PR's Linear issue.
2
- import { buildAnnotation } from '../lib/annotation.js';
3
- const DEFAULT_SERVICE = 'crosscheck';
4
- // The visible line must agree with the annotation. Saying "review" on a recheck
5
- // made the human-readable text contradict the type=recheck sitting right below it.
6
- // Returns the whole connective phrase, including the preposition — "fix of" is
7
- // not English, so the label owns it rather than the template appending one.
8
- function stepLabel(stepType) {
9
- switch (stepType) {
10
- case 'recheck': return 'recheck of';
11
- case 'fix': return 'fix for';
12
- case 'conflict-resolve': return 'conflict resolution for';
13
- default: return 'review of';
14
- }
15
- }
16
- // A `]` in the PR title would terminate the markdown link label early.
17
- function escapeLinkLabel(text) {
18
- return text.replace(/([[\]])/g, '\\$1');
19
- }
20
- export function buildLinearCommentBody(input) {
21
- const verdict = input.verdict ?? 'UNKNOWN';
22
- const service = input.service ?? DEFAULT_SERVICE;
23
- const sections = [
24
- input.signature,
25
- '',
26
- // Both halves: the model when the vendor reported one — `default` means it
27
- // chose without saying which, and naming it would be a guess — and the step
28
- // label, so a recheck does not read as a review.
29
- `**${verdict}** — ${input.reviewer}${input.model && input.model !== 'default' ? ` (${input.model})` : ''}` +
30
- ` ${stepLabel(input.stepType)} [${escapeLinkLabel(input.prTitle)}](${input.prUrl})`,
31
- ];
32
- if (input.summary)
33
- sections.push('', input.summary);
34
- // Same annotation schema as the GitHub comment, so one parser reads both.
35
- sections.push('', buildAnnotation({
36
- origin: input.origin,
37
- reviewer: input.reviewer,
38
- model: input.model,
39
- type: input.stepType ?? 'review',
40
- round: input.round ?? 1,
41
- verdict,
42
- service,
43
- ...(input.sha && { sha: input.sha }),
44
- }));
45
- return sections.join('\n');
46
- }
47
- // parseVerdict yields the human spelling `NEEDS WORK`, while config and the
48
- // annotation schema both use `NEEDS_WORK`. Comparing them raw silently dropped
49
- // every NEEDS WORK verdict — the most common actionable one.
50
- export function normalizeVerdict(verdict) {
51
- return (verdict ?? 'UNKNOWN').trim().toUpperCase().replace(/\s+/g, '_');
52
- }
53
- export function shouldPostToLinear(verdict, commentOn) {
54
- const wanted = normalizeVerdict(verdict);
55
- return commentOn.some(v => normalizeVerdict(v) === wanted);
56
- }
57
- //# sourceMappingURL=comment.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"comment.js","sourceRoot":"","sources":["../../src/linear/comment.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAElF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAoBtD,MAAM,eAAe,GAAG,YAAY,CAAA;AAEpC,gFAAgF;AAChF,mFAAmF;AACnF,+EAA+E;AAC/E,4EAA4E;AAC5E,SAAS,SAAS,CAAC,QAA4B;IAC7C,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,SAAS,CAAC,CAAC,OAAO,YAAY,CAAA;QACnC,KAAK,KAAK,CAAC,CAAC,OAAO,SAAS,CAAA;QAC5B,KAAK,kBAAkB,CAAC,CAAC,OAAO,yBAAyB,CAAA;QACzD,OAAO,CAAC,CAAC,OAAO,WAAW,CAAA;IAC7B,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;AACzC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAyB;IAC9D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,SAAS,CAAA;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,eAAe,CAAA;IAEhD,MAAM,QAAQ,GAAG;QACf,KAAK,CAAC,SAAS;QACf,EAAE;QACF,2EAA2E;QAC3E,4EAA4E;QAC5E,iDAAiD;QACjD,KAAK,OAAO,QAAQ,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACxG,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,KAAK,GAAG;KACtF,CAAA;IAED,IAAI,KAAK,CAAC,OAAO;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAEnD,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,eAAe,CAAC;QAChC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,IAAI,EAAE,KAAK,CAAC,QAAQ,IAAI,QAAQ;QAChC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC;QACvB,OAAO;QACP,OAAO;QACP,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;KACrC,CAAC,CAAC,CAAA;IAEH,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC;AAED,4EAA4E;AAC5E,+EAA+E;AAC/E,6DAA6D;AAC7D,MAAM,UAAU,gBAAgB,CAAC,OAAsB;IACrD,OAAO,CAAC,OAAO,IAAI,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAsB,EAAE,SAA4B;IACrF,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;IACxC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAA;AAC5D,CAAC"}
@@ -1,59 +0,0 @@
1
- import type { LinearConfig } from '../config/schema.js';
2
- /** Injectable fetch, so tests never touch the network. */
3
- export type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
4
- export type LinearAuthMode = 'api_key' | 'client_credentials';
5
- /** Credentials read from the environment. Resolved in config/loader.ts — never here. */
6
- export interface LinearCredentials {
7
- apiKey?: string;
8
- clientId?: string;
9
- clientSecret?: string;
10
- }
11
- export interface ResolvedLinearAuth {
12
- mode: LinearAuthMode;
13
- /** Bare API key (T0) or minted app token (T1). Never log this. */
14
- token: string;
15
- /** T1 tokens need an `Authorization: Bearer` prefix; T0 keys are sent bare. */
16
- bearer: boolean;
17
- actor: string;
18
- /** Rendered `🤖 {actor} · {product}` line. Leads every write in both tiers. */
19
- signature: string;
20
- /** Set in T1 only — Linear attributes the write to this display name. */
21
- createAsUser?: string;
22
- /** Retained so withWorker() can re-render the signature for a derived actor. */
23
- signatureTemplate: string;
24
- product: string;
25
- }
26
- export interface MintOptions {
27
- fetchImpl?: FetchLike;
28
- }
29
- export interface ResolveOptions extends MintOptions {
30
- /** Product name used in the signature template. */
31
- product?: string;
32
- }
33
- export declare class LinearConfigError extends Error {
34
- readonly userError = true;
35
- constructor(message: string);
36
- }
37
- export declare function isLinearConfigError(err: unknown): boolean;
38
- export interface SignatureVars {
39
- actor: string;
40
- product: string;
41
- /** Reviewer model, e.g. `claude-opus-4.5`. Absent at auth-resolution time. */
42
- model?: string;
43
- /** Reviewing vendor, e.g. `codex`. */
44
- reviewer?: string;
45
- /** Pre-rendered markdown image, or '' when no icon is configured. */
46
- icon?: string;
47
- }
48
- export declare function renderSignature(template: string, vars: SignatureVars): string;
49
- /** Wraps a configured icon URL as inline markdown, or '' when unset. */
50
- export declare function renderIcon(iconUrl: string): string;
51
- export declare function normalizeScopes(scopes: string): string;
52
- export declare function mintAppToken(creds: {
53
- clientId: string;
54
- clientSecret: string;
55
- scopes: string;
56
- }, opts?: MintOptions): Promise<string>;
57
- export declare function resolveLinearAuth(config: LinearConfig, creds: LinearCredentials, opts?: ResolveOptions): Promise<ResolvedLinearAuth>;
58
- export declare function withWorker(auth: ResolvedLinearAuth, worker: string): ResolvedLinearAuth;
59
- //# sourceMappingURL=identity.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/linear/identity.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAIvD,0DAA0D;AAC1D,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAE7E,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,oBAAoB,CAAA;AAE7D,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,cAAc,CAAA;IACpB,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAA;IACjB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gFAAgF;IAChF,iBAAiB,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW;IACjD,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAOD,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,SAAS,QAAO;gBACb,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEzD;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAMD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAgB7E;AAmBD,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGlD;AAKD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACjE,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,MAAM,CAAC,CA8CjB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,iBAAiB,EACxB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,kBAAkB,CAAC,CAwD7B;AAWD,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAWvF"}
@@ -1,187 +0,0 @@
1
- // Tiered Linear identity (IN-2267 / IN-2269).
2
- //
3
- // T0 api_key — operator supplies a personal/workspace key. Writes carry a
4
- // `🤖 {actor} · {product}` signature line. Works everywhere,
5
- // weakest attribution.
6
- // T1 client_credentials — operator creates an OAuth app in their own workspace and
7
- // enables the Client credentials toggle. We mint a Bearer app
8
- // token per run and pass `createAsUser` on every write, so
9
- // Linear renders them as the app (botActor), not a human.
10
- //
11
- // Hard requirements this module enforces:
12
- // * Secrets travel in a POST body — never on argv, never in a URL, never logged.
13
- // * A configured-but-failing T1 mint ABORTS. It must never silently downgrade to
14
- // api_key, because that would re-attribute agent writes to a human account.
15
- const TOKEN_ENDPOINT = 'https://api.linear.app/oauth/token';
16
- const DEFAULT_PRODUCT = 'crosscheck';
17
- // Misconfiguration (missing env var, rejected credentials) is a user error, so it
18
- // must surface as exit 1 — the CLI contract reserves 2 for unexpected failures.
19
- // commands/review.ts already exits 1 here; the workflow path has to agree.
20
- export class LinearConfigError extends Error {
21
- userError = true;
22
- constructor(message) {
23
- super(message);
24
- this.name = 'LinearConfigError';
25
- }
26
- }
27
- export function isLinearConfigError(err) {
28
- return err instanceof LinearConfigError;
29
- }
30
- // Renders the signature template. Unknown placeholders are left alone; known ones
31
- // with no value resolve to empty, and the leftover punctuation is tidied so a
32
- // template like `🤖 {icon} {actor} · {product} · {model}` still reads correctly
33
- // when icon and model are unset.
34
- export function renderSignature(template, vars) {
35
- const values = {
36
- actor: vars.actor,
37
- product: vars.product,
38
- model: vars.model ?? '',
39
- reviewer: vars.reviewer ?? '',
40
- icon: vars.icon ?? '',
41
- };
42
- // Mark where an empty placeholder stood so tidying can target exactly those gaps
43
- // and leave separators the operator wrote deliberately alone.
44
- const HOLE = '\u0000';
45
- const substituted = template.replace(/\{(actor|product|model|reviewer|icon)\}/g, (_match, key) => values[key] === '' ? HOLE : values[key]);
46
- return tidyHoles(substituted, HOLE);
47
- }
48
- // Removes each empty-placeholder hole together with ONE adjacent separator, so
49
- // `{a} · {model} · {b}` with no model yields `a · b` rather than `a · · b`.
50
- // Separators the operator wrote between two present values are never touched.
51
- function tidyHoles(text, hole) {
52
- const SEP = '(?:\\s*[·—|]\\s*|\\s+)';
53
- return text
54
- // hole with a separator on either side — drop the hole and one separator
55
- .replace(new RegExp(`${SEP}${hole}(?=${SEP})`, 'g'), '')
56
- // hole at the end of the string, with its leading separator
57
- .replace(new RegExp(`${SEP}${hole}\\s*$`, 'g'), '')
58
- // hole at the start, with its trailing separator
59
- .replace(new RegExp(`^\\s*${hole}${SEP}`, 'g'), '')
60
- // any hole left over (no adjacent separator)
61
- .split(hole).join('')
62
- .trim();
63
- }
64
- /** Wraps a configured icon URL as inline markdown, or '' when unset. */
65
- export function renderIcon(iconUrl) {
66
- const trimmed = iconUrl.trim();
67
- return trimmed ? `![](${trimmed})` : '';
68
- }
69
- // Linear's token endpoint documents scope as a COMMA-separated list, but OAuth 2.0
70
- // itself specifies space-separated, so operators write it both ways. Accept either
71
- // and always send commas.
72
- export function normalizeScopes(scopes) {
73
- return scopes.split(/[\s,]+/).filter(Boolean).join(',');
74
- }
75
- export async function mintAppToken(creds, opts = {}) {
76
- const doFetch = opts.fetchImpl ?? globalThis.fetch;
77
- // URLSearchParams keeps the secret in the request body. Nothing here reaches argv.
78
- const body = new URLSearchParams({
79
- grant_type: 'client_credentials',
80
- client_id: creds.clientId,
81
- client_secret: creds.clientSecret,
82
- scope: normalizeScopes(creds.scopes),
83
- });
84
- let response;
85
- try {
86
- response = await doFetch(TOKEN_ENDPOINT, {
87
- method: 'POST',
88
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
89
- body: body.toString(),
90
- });
91
- }
92
- catch (err) {
93
- // Transport failure — Linear is unreachable. Not a configuration problem, so
94
- // this stays a plain Error and classifies as an unexpected failure (exit 2).
95
- throw new Error(`Linear token mint failed: ${err instanceof Error ? err.message : String(err)}`);
96
- }
97
- const text = await response.text();
98
- if (!response.ok) {
99
- // 4xx means Linear rejected these credentials or scopes — the operator can fix
100
- // that. 5xx is an outage, and 429 is rate limiting: both are transient and must
101
- // not be blamed on the config.
102
- const message = `Linear token mint rejected (HTTP ${response.status})`;
103
- const transient = response.status >= 500 || response.status === 429;
104
- throw transient ? new Error(message) : new LinearConfigError(message);
105
- }
106
- let parsed;
107
- try {
108
- parsed = JSON.parse(text);
109
- }
110
- catch {
111
- throw new Error(`Linear token mint returned a non-JSON response (HTTP ${response.status})`);
112
- }
113
- const token = parsed.access_token;
114
- if (typeof token !== 'string' || token.length === 0) {
115
- throw new Error('Linear token mint returned no access_token');
116
- }
117
- return token;
118
- }
119
- export async function resolveLinearAuth(config, creds, opts = {}) {
120
- const product = opts.product ?? DEFAULT_PRODUCT;
121
- const { actor, signature: template } = config.identity;
122
- const signature = renderSignature(template, { actor, product });
123
- if (config.auth.mode === 'client_credentials') {
124
- const missing = [];
125
- if (!creds.clientId)
126
- missing.push(config.auth.client_id_env);
127
- if (!creds.clientSecret)
128
- missing.push(config.auth.client_secret_env);
129
- if (missing.length > 0) {
130
- // Abort rather than fall back — see the module header.
131
- throw new LinearConfigError(`Linear auth mode is client_credentials but ${missing.join(' and ')} ` +
132
- `${missing.length > 1 ? 'are' : 'is'} not set. ` +
133
- 'Set them, or switch linear.auth.mode to api_key.');
134
- }
135
- let token;
136
- try {
137
- token = await mintAppToken({ clientId: creds.clientId, clientSecret: creds.clientSecret, scopes: config.auth.scopes }, opts);
138
- }
139
- catch (err) {
140
- // Preserve the classification the mint chose: a rejected credential stays a
141
- // config error (exit 1), an outage stays unexpected (exit 2). Both abort —
142
- // neither falls back to api_key.
143
- // Name the variables to check. A bare "HTTP 401" leaves an operator with
144
- // custom env names guessing which credential to rotate. Names only — never
145
- // the values.
146
- const message = `Linear client_credentials token mint failed — aborting rather than falling back ` +
147
- `to an API key (that would attribute agent writes to a human). ` +
148
- `Check ${config.auth.client_id_env} and ${config.auth.client_secret_env}. ` +
149
- `${err instanceof Error ? err.message : String(err)}`;
150
- throw isLinearConfigError(err) ? new LinearConfigError(message) : new Error(message);
151
- }
152
- return {
153
- mode: 'client_credentials', token, bearer: true, actor, signature,
154
- createAsUser: actor, signatureTemplate: template, product,
155
- };
156
- }
157
- if (!creds.apiKey) {
158
- throw new LinearConfigError(`Linear is enabled but ${config.auth.api_key_env} is not set. ` +
159
- `Set it, or disable the linear block in your config.`);
160
- }
161
- return {
162
- mode: 'api_key', token: creds.apiKey, bearer: false, actor, signature,
163
- signatureTemplate: template, product,
164
- };
165
- }
166
- // Derives a step-scoped identity: `crosscheck` → `crosscheck/review`.
167
- //
168
- // The epic calls for per-worker display names (its example is `symphony/worker-3`)
169
- // so a fleet of agents does not collapse into one indistinguishable bot. crosscheck's
170
- // natural axis is the workflow step that produced the write.
171
- //
172
- // T1 re-points createAsUser, which is what Linear actually renders. T0 has no
173
- // createAsUser, so the suffix only reaches the signature line — still an improvement,
174
- // since that is T0's whole attribution mechanism.
175
- export function withWorker(auth, worker) {
176
- const trimmed = worker.trim();
177
- if (!trimmed)
178
- return auth;
179
- const actor = `${auth.actor}/${trimmed}`;
180
- return {
181
- ...auth,
182
- actor,
183
- signature: renderSignature(auth.signatureTemplate, { actor, product: auth.product }),
184
- ...(auth.createAsUser !== undefined && { createAsUser: actor }),
185
- };
186
- }
187
- //# sourceMappingURL=identity.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/linear/identity.ts"],"names":[],"mappings":"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"}
@@ -1,35 +0,0 @@
1
- import { type LinearRequestOptions } from './client.js';
2
- import { type ResolvedLinearAuth } from './identity.js';
3
- import type { LinearConfig } from '../config/schema.js';
4
- export type LinearNotifyStatus = 'posted' | 'skipped' | 'failed';
5
- export interface LinearNotifyResult {
6
- status: LinearNotifyStatus;
7
- /** Machine-readable reason for skipped/failed. */
8
- reason?: string;
9
- identifier?: string;
10
- url?: string;
11
- }
12
- export interface LinearNotifyParams {
13
- auth: ResolvedLinearAuth;
14
- config: LinearConfig;
15
- pr: {
16
- branch?: string;
17
- title: string;
18
- body?: string;
19
- url: string;
20
- sha?: string;
21
- };
22
- verdict: string | null;
23
- reviewer: string;
24
- origin: string;
25
- model: string;
26
- summary?: string;
27
- service?: string;
28
- /** Workflow step that produced this verdict. Defaults to 'review'. */
29
- stepType?: string;
30
- /** 1-based workflow round. Defaults to 1. */
31
- round?: number;
32
- }
33
- export declare function issueBelongsToWorkspace(issueUrl: string, workspace: string): boolean;
34
- export declare function notifyLinear(params: LinearNotifyParams, opts?: LinearRequestOptions): Promise<LinearNotifyResult>;
35
- //# sourceMappingURL=notify.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/linear/notify.ts"],"names":[],"mappings":"AASA,OAAO,EAA4C,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAEjG,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAA;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAEvD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEhE,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,kBAAkB,CAAA;IAC1B,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,kBAAkB,CAAA;IACxB,MAAM,EAAE,YAAY,CAAA;IACpB,EAAE,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAChF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAGD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAQpF;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,EAC1B,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CAwD7B"}
@@ -1,76 +0,0 @@
1
- // Orchestrates the one Linear write crosscheck makes: mirroring a review verdict
2
- // onto the Linear issue the PR belongs to.
3
- //
4
- // This never throws. Auth is resolved up front (fail-fast, before the expensive
5
- // review runs), so anything that goes wrong here is a per-write problem on a review
6
- // that already succeeded and already posted to GitHub. The caller logs the returned
7
- // status — failures are reported, never swallowed.
8
- import { buildLinearCommentBody, shouldPostToLinear } from './comment.js';
9
- import { findIssueByIdentifier, postLinearComment } from './client.js';
10
- import { extractLinearRef } from './ref.js';
11
- import { renderIcon, renderSignature } from './identity.js';
12
- // Linear issue URLs are https://linear.app/<workspace>/issue/<ID>/<slug>.
13
- export function issueBelongsToWorkspace(issueUrl, workspace) {
14
- try {
15
- const segments = new URL(issueUrl).pathname.split('/').filter(Boolean);
16
- return segments[0]?.toLowerCase() === workspace.toLowerCase();
17
- }
18
- catch {
19
- // Unparseable URL from the API — fail closed rather than post to the wrong place.
20
- return false;
21
- }
22
- }
23
- export async function notifyLinear(params, opts = {}) {
24
- const { auth, config, pr } = params;
25
- if (!shouldPostToLinear(params.verdict, config.comment_on)) {
26
- return { status: 'skipped', reason: 'verdict-not-configured' };
27
- }
28
- const ref = extractLinearRef({ branch: pr.branch, title: pr.title, body: pr.body }, config.team_keys);
29
- if (!ref)
30
- return { status: 'skipped', reason: 'no-issue-ref' };
31
- try {
32
- const issue = await findIssueByIdentifier(auth, ref.id, opts);
33
- if (!issue)
34
- return { status: 'skipped', reason: 'issue-not-found', identifier: ref.id };
35
- // An explicit URL names a workspace, and identifiers repeat across workspaces.
36
- // If the credentials resolve a different workspace, this is a same-numbered
37
- // issue somewhere else — never the one the PR pointed at. Checked before any
38
- // body is built, so a mismatch costs nothing.
39
- if (ref.workspace && !issueBelongsToWorkspace(issue.url, ref.workspace)) {
40
- return { status: 'skipped', reason: 'workspace-mismatch', identifier: ref.id };
41
- }
42
- // Re-render here rather than reusing auth.signature: this is the first point
43
- // that knows the model and reviewer, which the template may reference.
44
- const signature = renderSignature(auth.signatureTemplate, {
45
- actor: auth.actor,
46
- product: auth.product,
47
- model: params.model === 'default' ? undefined : params.model,
48
- reviewer: params.reviewer,
49
- icon: renderIcon(config.identity.icon_url),
50
- });
51
- const body = buildLinearCommentBody({
52
- signature,
53
- verdict: params.verdict,
54
- reviewer: params.reviewer,
55
- origin: params.origin,
56
- model: params.model,
57
- prUrl: pr.url,
58
- prTitle: pr.title,
59
- ...(pr.sha && { sha: pr.sha }),
60
- ...(params.stepType && { stepType: params.stepType }),
61
- ...(params.round !== undefined && { round: params.round }),
62
- ...(params.summary && { summary: params.summary }),
63
- ...(params.service && { service: params.service }),
64
- });
65
- const comment = await postLinearComment(auth, issue.id, body, opts);
66
- return { status: 'posted', identifier: issue.identifier, url: comment.url };
67
- }
68
- catch (err) {
69
- return {
70
- status: 'failed',
71
- reason: err instanceof Error ? err.message : String(err),
72
- identifier: ref.id,
73
- };
74
- }
75
- }
76
- //# sourceMappingURL=notify.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/linear/notify.ts"],"names":[],"mappings":"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"}
@@ -1,13 +0,0 @@
1
- import { type TicketRef } from '../issues/ticket-ref.js';
2
- export interface LinearIssueRef extends TicketRef {
3
- source: 'branch' | 'title' | 'body';
4
- /** Workspace slug, when the ref came from an explicit URL. Bare refs have none. */
5
- workspace?: string;
6
- }
7
- export interface PRMetadata {
8
- branch?: string | null;
9
- title?: string | null;
10
- body?: string | null;
11
- }
12
- export declare function extractLinearRef(pr: PRMetadata, teamKeys: readonly string[]): LinearIssueRef | null;
13
- //# sourceMappingURL=ref.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ref.d.ts","sourceRoot":"","sources":["../../src/linear/ref.ts"],"names":[],"mappings":"AAYA,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAE1F,MAAM,WAAW,cAAe,SAAQ,SAAS;IAC/C,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAA;IACnC,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAiED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,GAAG,IAAI,CAanG"}