limn-review 0.1.123

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 (474) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +133 -0
  3. package/dist/ai/runtime.js +535 -0
  4. package/dist/api/plugins/auth.js +169 -0
  5. package/dist/api/plugins/error-handler.js +114 -0
  6. package/dist/api/plugins/rate-limit.js +855 -0
  7. package/dist/api/plugins/security.js +321 -0
  8. package/dist/api/routes/activity.js +103 -0
  9. package/dist/api/routes/ai-runtime.js +67 -0
  10. package/dist/api/routes/auth.js +194 -0
  11. package/dist/api/routes/billing.js +209 -0
  12. package/dist/api/routes/bot-triage.js +1289 -0
  13. package/dist/api/routes/conflicts.js +730 -0
  14. package/dist/api/routes/contact.js +318 -0
  15. package/dist/api/routes/daily-brief.js +58 -0
  16. package/dist/api/routes/flow.js +71 -0
  17. package/dist/api/routes/health.js +7 -0
  18. package/dist/api/routes/insights.js +200 -0
  19. package/dist/api/routes/me.js +458 -0
  20. package/dist/api/routes/mentions.js +22 -0
  21. package/dist/api/routes/mergers.js +8 -0
  22. package/dist/api/routes/ml-labels.js +109 -0
  23. package/dist/api/routes/open-prs.js +35 -0
  24. package/dist/api/routes/prs.js +1633 -0
  25. package/dist/api/routes/reactions.js +217 -0
  26. package/dist/api/routes/repos.js +563 -0
  27. package/dist/api/routes/search.js +41 -0
  28. package/dist/api/routes/threads.js +118 -0
  29. package/dist/api/routes/timeline.js +150 -0
  30. package/dist/api/routes/users.js +67 -0
  31. package/dist/api/routes/webhooks.js +181 -0
  32. package/dist/api/routes/workspaces.js +322 -0
  33. package/dist/app.js +274 -0
  34. package/dist/ascii.js +19 -0
  35. package/dist/auth/account.js +482 -0
  36. package/dist/auth/crypto.js +46 -0
  37. package/dist/bot-triage/resolve.js +59 -0
  38. package/dist/cli.js +388 -0
  39. package/dist/coding/activity.js +80 -0
  40. package/dist/coding/agent.js +288 -0
  41. package/dist/coding/ai-fix/auto-fix.js +212 -0
  42. package/dist/coding/ai-fix/manager.js +370 -0
  43. package/dist/coding/ai-fix/persist.js +213 -0
  44. package/dist/coding/ai-fix/pr-context.js +58 -0
  45. package/dist/coding/ai-fix/prompts.js +138 -0
  46. package/dist/coding/ai-fix/review-seed.js +357 -0
  47. package/dist/coding/ai-fix/routes.js +425 -0
  48. package/dist/coding/git-ops.js +321 -0
  49. package/dist/coding/git.js +184 -0
  50. package/dist/coding/merge.js +262 -0
  51. package/dist/coding/safe-path.js +75 -0
  52. package/dist/coding/schema.js +43 -0
  53. package/dist/config.js +648 -0
  54. package/dist/conflict/diff.js +304 -0
  55. package/dist/conflict/git.js +118 -0
  56. package/dist/conflict/hash.js +73 -0
  57. package/dist/conflict/janitor.js +218 -0
  58. package/dist/conflict/land.js +675 -0
  59. package/dist/conflict/model-types.js +2 -0
  60. package/dist/conflict/model.js +1081 -0
  61. package/dist/conflict/parse.js +142 -0
  62. package/dist/conflict/seam.js +136 -0
  63. package/dist/conflict/session.js +532 -0
  64. package/dist/conflict/suggestion.js +398 -0
  65. package/dist/conflict/tokens.js +72 -0
  66. package/dist/data-dir.js +112 -0
  67. package/dist/db/actor-lanes.js +213 -0
  68. package/dist/db/automation-ids.js +97 -0
  69. package/dist/db/automation-output.js +349 -0
  70. package/dist/db/blast-radius-query.js +36 -0
  71. package/dist/db/blast-radius.js +226 -0
  72. package/dist/db/bot-dormancy.js +115 -0
  73. package/dist/db/bot-overlap.js +579 -0
  74. package/dist/db/bot-volume.js +722 -0
  75. package/dist/db/bot-window.js +35 -0
  76. package/dist/db/branch-queries.js +310 -0
  77. package/dist/db/change-shape.js +243 -0
  78. package/dist/db/changepoint.js +75 -0
  79. package/dist/db/cleanup.js +36 -0
  80. package/dist/db/client.js +102 -0
  81. package/dist/db/code-loc.js +388 -0
  82. package/dist/db/credits.js +79 -0
  83. package/dist/db/daily-brief.js +406 -0
  84. package/dist/db/dependency-cards.js +145 -0
  85. package/dist/db/erase-account.js +255 -0
  86. package/dist/db/export-account.js +189 -0
  87. package/dist/db/failing-checks.js +124 -0
  88. package/dist/db/file-coupling.js +197 -0
  89. package/dist/db/flow-detail.js +661 -0
  90. package/dist/db/flow-pointers.js +197 -0
  91. package/dist/db/flow-settings.js +67 -0
  92. package/dist/db/forecast.js +114 -0
  93. package/dist/db/line-overlap.js +81 -0
  94. package/dist/db/merge-queue.js +189 -0
  95. package/dist/db/migrate.js +17 -0
  96. package/dist/db/migrations/0000_purple_ben_grimm.sql +155 -0
  97. package/dist/db/migrations/0001_colorful_ozymandias.sql +31 -0
  98. package/dist/db/migrations/0002_famous_sersi.sql +9 -0
  99. package/dist/db/migrations/0003_clever_shinobi_shaw.sql +3 -0
  100. package/dist/db/migrations/0004_pale_scalphunter.sql +1 -0
  101. package/dist/db/migrations/0005_daffy_guardian.sql +2 -0
  102. package/dist/db/migrations/0006_cute_violations.sql +45 -0
  103. package/dist/db/migrations/0007_dark_anthem.sql +2 -0
  104. package/dist/db/migrations/0008_multitenant_accounts.sql +44 -0
  105. package/dist/db/migrations/0009_lean_bodies.sql +13 -0
  106. package/dist/db/migrations/0010_lean_bodies_nullable.sql +56 -0
  107. package/dist/db/migrations/0011_pr_diff_size.sql +10 -0
  108. package/dist/db/migrations/0012_findings_included_default.sql +7 -0
  109. package/dist/db/migrations/0013_review_routing.sql +9 -0
  110. package/dist/db/migrations/0014_account_last_active.sql +6 -0
  111. package/dist/db/migrations/0015_repos_inbox_watch.sql +9 -0
  112. package/dist/db/migrations/0016_repo_viewer_permission.sql +7 -0
  113. package/dist/db/migrations/0017_account_display_name.sql +7 -0
  114. package/dist/db/migrations/0018_finding_comment_kind.sql +7 -0
  115. package/dist/db/migrations/0019_finding_file_in_diff.sql +7 -0
  116. package/dist/db/migrations/0020_claude_review_cost_telemetry.sql +11 -0
  117. package/dist/db/migrations/0021_account_feed_last_seen.sql +7 -0
  118. package/dist/db/migrations/0022_ci_status_events.sql +21 -0
  119. package/dist/db/migrations/0023_ai_usage.sql +21 -0
  120. package/dist/db/migrations/0024_pr_head_ref_name.sql +6 -0
  121. package/dist/db/migrations/0025_account_billing.sql +7 -0
  122. package/dist/db/migrations/0026_ai_credit_allowance.sql +5 -0
  123. package/dist/db/migrations/0027_user_github_type.sql +6 -0
  124. package/dist/db/migrations/0028_bot_review_classification.sql +21 -0
  125. package/dist/db/migrations/0029_bot_mute_rules.sql +19 -0
  126. package/dist/db/migrations/0030_teams.sql +32 -0
  127. package/dist/db/migrations/0031_events_pr_idx.sql +7 -0
  128. package/dist/db/migrations/0032_benchmark_contributions.sql +29 -0
  129. package/dist/db/migrations/0033_thread_addressed_confidence.sql +9 -0
  130. package/dist/db/migrations/0034_thread_resolved_at.sql +6 -0
  131. package/dist/db/migrations/0035_pr_first_review_requested_at.sql +5 -0
  132. package/dist/db/migrations/0036_search_index.sql +25 -0
  133. package/dist/db/migrations/0037_author_indexes.sql +15 -0
  134. package/dist/db/migrations/0038_auto_merge_requests.sql +37 -0
  135. package/dist/db/migrations/0039_branch_status.sql +44 -0
  136. package/dist/db/migrations/0040_pr_review_decision.sql +10 -0
  137. package/dist/db/migrations/0041_branch_commit_detail.sql +38 -0
  138. package/dist/db/migrations/0042_repo_reviewers.sql +176 -0
  139. package/dist/db/migrations/0043_account_reviewers.sql +131 -0
  140. package/dist/db/migrations/0044_workspaces.sql +138 -0
  141. package/dist/db/migrations/0045_workspace_reviewers.sql +180 -0
  142. package/dist/db/migrations/0046_drop_inbox_watch.sql +25 -0
  143. package/dist/db/migrations/0047_ml_comment_labels.sql +40 -0
  144. package/dist/db/migrations/0048_ml_vendor_severity.sql +31 -0
  145. package/dist/db/migrations/0049_seat_pricing_repo_description.sql +21 -0
  146. package/dist/db/migrations/0050_users_app_slug.sql +12 -0
  147. package/dist/db/migrations/0051_auto_merge_queue.sql +17 -0
  148. package/dist/db/migrations/0052_trunk_ci_status_events.sql +43 -0
  149. package/dist/db/migrations/0053_automation_roles.sql +79 -0
  150. package/dist/db/migrations/0054_automation_vendor_kinds.sql +514 -0
  151. package/dist/db/migrations/0055_auto_merge_phase.sql +12 -0
  152. package/dist/db/migrations/0056_pr_mentions.sql +22 -0
  153. package/dist/db/migrations/0057_large_pr_threshold.sql +14 -0
  154. package/dist/db/migrations/0058_pending_mute.sql +53 -0
  155. package/dist/db/migrations/0059_merge_queue_state.sql +33 -0
  156. package/dist/db/migrations/0060_drop_my_turn_dismissals.sql +33 -0
  157. package/dist/db/migrations/0061_auto_merge_durable_anchors.sql +27 -0
  158. package/dist/db/migrations/0062_blast_radius_config.sql +21 -0
  159. package/dist/db/migrations/0063_repo_file_coupling.sql +33 -0
  160. package/dist/db/migrations/0064_pr_content_kind.sql +21 -0
  161. package/dist/db/migrations/0065_workspace_flow_settings.sql +13 -0
  162. package/dist/db/migrations/0066_review_request_events.sql +31 -0
  163. package/dist/db/migrations/0067_pr_security_signals.sql +86 -0
  164. package/dist/db/migrations/0068_my_turn_settings.sql +18 -0
  165. package/dist/db/migrations/0069_my_turn_dismissals.sql +41 -0
  166. package/dist/db/migrations/0070_claude_review_follow_up_ticket.sql +22 -0
  167. package/dist/db/migrations/0071_claude_review_trigger.sql +11 -0
  168. package/dist/db/migrations/0072_commit_message_headline.sql +13 -0
  169. package/dist/db/migrations/0073_claude_review_chat.sql +31 -0
  170. package/dist/db/migrations/0074_adopt_agentic_tables.sql +98 -0
  171. package/dist/db/migrations/0075_review_lens_drop_learnings.sql +15 -0
  172. package/dist/db/migrations/0076_claude_review_threads.sql +17 -0
  173. package/dist/db/migrations/0077_claude_review_ci_failures.sql +10 -0
  174. package/dist/db/migrations/0078_ai_fix_change_report.sql +17 -0
  175. package/dist/db/migrations/0079_claude_finding_story.sql +16 -0
  176. package/dist/db/migrations/meta/0000_snapshot.json +1116 -0
  177. package/dist/db/migrations/meta/0001_snapshot.json +1321 -0
  178. package/dist/db/migrations/meta/0002_snapshot.json +1375 -0
  179. package/dist/db/migrations/meta/0003_snapshot.json +1396 -0
  180. package/dist/db/migrations/meta/0004_snapshot.json +1416 -0
  181. package/dist/db/migrations/meta/0005_snapshot.json +1430 -0
  182. package/dist/db/migrations/meta/0006_snapshot.json +1745 -0
  183. package/dist/db/migrations/meta/0007_snapshot.json +1759 -0
  184. package/dist/db/migrations/meta/_journal.json +566 -0
  185. package/dist/db/migrations-pg/0000_tired_alex_power.sql +268 -0
  186. package/dist/db/migrations-pg/0001_dizzy_mockingbird.sql +3 -0
  187. package/dist/db/migrations-pg/0002_glorious_ikaris.sql +4 -0
  188. package/dist/db/migrations-pg/0003_melted_secret_warriors.sql +2 -0
  189. package/dist/db/migrations-pg/0004_numerous_triton.sql +1 -0
  190. package/dist/db/migrations-pg/0005_thick_blockbuster.sql +2 -0
  191. package/dist/db/migrations-pg/0006_majestic_dracula.sql +1 -0
  192. package/dist/db/migrations-pg/0007_same_human_torch.sql +2 -0
  193. package/dist/db/migrations-pg/0008_slimy_toxin.sql +1 -0
  194. package/dist/db/migrations-pg/0009_fuzzy_jubilee.sql +4 -0
  195. package/dist/db/migrations-pg/0010_late_hammerhead.sql +1 -0
  196. package/dist/db/migrations-pg/0011_happy_leech.sql +16 -0
  197. package/dist/db/migrations-pg/0012_woozy_captain_midlands.sql +16 -0
  198. package/dist/db/migrations-pg/0013_yielding_nicolaos.sql +1 -0
  199. package/dist/db/migrations-pg/0014_wakeful_bill_hollister.sql +2 -0
  200. package/dist/db/migrations-pg/0015_aspiring_black_tom.sql +1 -0
  201. package/dist/db/migrations-pg/0016_cute_ghost_rider.sql +30 -0
  202. package/dist/db/migrations-pg/0017_far_changeling.sql +24 -0
  203. package/dist/db/migrations-pg/0018_fancy_miss_america.sql +1 -0
  204. package/dist/db/migrations-pg/0019_benchmark_contributions.sql +22 -0
  205. package/dist/db/migrations-pg/0020_majestic_edwin_jarvis.sql +3 -0
  206. package/dist/db/migrations-pg/0021_green_salo.sql +1 -0
  207. package/dist/db/migrations-pg/0022_wise_baron_strucker.sql +1 -0
  208. package/dist/db/migrations-pg/0023_search_index.sql +19 -0
  209. package/dist/db/migrations-pg/0024_author_indexes.sql +9 -0
  210. package/dist/db/migrations-pg/0025_auto_merge_requests.sql +26 -0
  211. package/dist/db/migrations-pg/0026_branch_status.sql +32 -0
  212. package/dist/db/migrations-pg/0027_pr_review_decision.sql +5 -0
  213. package/dist/db/migrations-pg/0028_branch_commit_detail.sql +9 -0
  214. package/dist/db/migrations-pg/0029_repo_reviewers.sql +127 -0
  215. package/dist/db/migrations-pg/0030_account_reviewers.sql +77 -0
  216. package/dist/db/migrations-pg/0031_workspaces.sql +131 -0
  217. package/dist/db/migrations-pg/0032_workspace_reviewers.sql +165 -0
  218. package/dist/db/migrations-pg/0033_drop_inbox_watch.sql +14 -0
  219. package/dist/db/migrations-pg/0034_ml_comment_labels.sql +33 -0
  220. package/dist/db/migrations-pg/0035_ml_vendor_severity.sql +16 -0
  221. package/dist/db/migrations-pg/0036_seat_pricing_repo_description.sql +12 -0
  222. package/dist/db/migrations-pg/0037_users_app_slug.sql +9 -0
  223. package/dist/db/migrations-pg/0038_auto_merge_queue.sql +10 -0
  224. package/dist/db/migrations-pg/0039_trunk_ci_status_events.sql +27 -0
  225. package/dist/db/migrations-pg/0040_automation_roles.sql +63 -0
  226. package/dist/db/migrations-pg/0041_automation_vendor_kinds.sql +488 -0
  227. package/dist/db/migrations-pg/0042_auto_merge_phase.sql +8 -0
  228. package/dist/db/migrations-pg/0043_pr_mentions.sql +19 -0
  229. package/dist/db/migrations-pg/0044_large_pr_threshold.sql +6 -0
  230. package/dist/db/migrations-pg/0045_pending_mute.sql +26 -0
  231. package/dist/db/migrations-pg/0046_merge_queue_state.sql +18 -0
  232. package/dist/db/migrations-pg/0047_drop_my_turn_dismissals.sql +21 -0
  233. package/dist/db/migrations-pg/0048_auto_merge_durable_anchors.sql +27 -0
  234. package/dist/db/migrations-pg/0049_blast_radius_config.sql +8 -0
  235. package/dist/db/migrations-pg/0050_repo_file_coupling.sql +13 -0
  236. package/dist/db/migrations-pg/0051_pr_content_kind.sql +6 -0
  237. package/dist/db/migrations-pg/0052_workspace_flow_settings.sql +4 -0
  238. package/dist/db/migrations-pg/0053_review_request_events.sql +18 -0
  239. package/dist/db/migrations-pg/0054_pr_security_signals.sql +59 -0
  240. package/dist/db/migrations-pg/0055_my_turn_settings.sql +6 -0
  241. package/dist/db/migrations-pg/0056_my_turn_dismissals.sql +21 -0
  242. package/dist/db/migrations-pg/0057_claude_review_follow_up_ticket.sql +8 -0
  243. package/dist/db/migrations-pg/0058_claude_review_trigger.sql +4 -0
  244. package/dist/db/migrations-pg/0059_commit_message_headline.sql +3 -0
  245. package/dist/db/migrations-pg/0060_claude_review_chat.sql +24 -0
  246. package/dist/db/migrations-pg/0061_adopt_agentic_tables.sql +79 -0
  247. package/dist/db/migrations-pg/0062_review_lens_drop_learnings.sql +5 -0
  248. package/dist/db/migrations-pg/0063_claude_review_threads.sql +7 -0
  249. package/dist/db/migrations-pg/0064_claude_review_ci_failures.sql +4 -0
  250. package/dist/db/migrations-pg/0065_ai_fix_change_report.sql +9 -0
  251. package/dist/db/migrations-pg/0066_claude_finding_story.sql +7 -0
  252. package/dist/db/migrations-pg/meta/0000_snapshot.json +2137 -0
  253. package/dist/db/migrations-pg/meta/0001_snapshot.json +2143 -0
  254. package/dist/db/migrations-pg/meta/0002_snapshot.json +2170 -0
  255. package/dist/db/migrations-pg/meta/0003_snapshot.json +2182 -0
  256. package/dist/db/migrations-pg/meta/0004_snapshot.json +2188 -0
  257. package/dist/db/migrations-pg/meta/0005_snapshot.json +2201 -0
  258. package/dist/db/migrations-pg/meta/0006_snapshot.json +2207 -0
  259. package/dist/db/migrations-pg/meta/0007_snapshot.json +2219 -0
  260. package/dist/db/migrations-pg/meta/0008_snapshot.json +2226 -0
  261. package/dist/db/migrations-pg/meta/0009_snapshot.json +2250 -0
  262. package/dist/db/migrations-pg/meta/0010_snapshot.json +2256 -0
  263. package/dist/db/migrations-pg/meta/0011_snapshot.json +2412 -0
  264. package/dist/db/migrations-pg/meta/0012_snapshot.json +2528 -0
  265. package/dist/db/migrations-pg/meta/0013_snapshot.json +2534 -0
  266. package/dist/db/migrations-pg/meta/0014_snapshot.json +2547 -0
  267. package/dist/db/migrations-pg/meta/0015_snapshot.json +2553 -0
  268. package/dist/db/migrations-pg/meta/0016_snapshot.json +2774 -0
  269. package/dist/db/migrations-pg/meta/0017_snapshot.json +2999 -0
  270. package/dist/db/migrations-pg/meta/0018_snapshot.json +3020 -0
  271. package/dist/db/migrations-pg/meta/0020_snapshot.json +3228 -0
  272. package/dist/db/migrations-pg/meta/0021_snapshot.json +3234 -0
  273. package/dist/db/migrations-pg/meta/0022_snapshot.json +3240 -0
  274. package/dist/db/migrations-pg/meta/_journal.json +475 -0
  275. package/dist/db/ml-labels.js +1666 -0
  276. package/dist/db/my-turn-dismissals.js +343 -0
  277. package/dist/db/my-turn-settings.js +34 -0
  278. package/dist/db/pending-classify.js +46 -0
  279. package/dist/db/pending-mute.js +171 -0
  280. package/dist/db/pending-tabs.js +238 -0
  281. package/dist/db/period-metrics.js +820 -0
  282. package/dist/db/person-period.js +763 -0
  283. package/dist/db/pr-intervals.js +930 -0
  284. package/dist/db/pr-liveness.js +240 -0
  285. package/dist/db/pr-mentions.js +279 -0
  286. package/dist/db/pr-merge-queue-stamp.js +91 -0
  287. package/dist/db/queries.js +14631 -0
  288. package/dist/db/reaction-targets.js +94 -0
  289. package/dist/db/repo-activity.js +171 -0
  290. package/dist/db/retention.js +138 -0
  291. package/dist/db/review-threads-for-review.js +240 -0
  292. package/dist/db/run-migrations.js +24 -0
  293. package/dist/db/schema.pg.js +1220 -0
  294. package/dist/db/schema.sqlite.js +1801 -0
  295. package/dist/db/search.js +317 -0
  296. package/dist/db/security-alerts.js +218 -0
  297. package/dist/db/synthesis-input.js +565 -0
  298. package/dist/db/triage.js +320 -0
  299. package/dist/db/usage.js +63 -0
  300. package/dist/db/work-plan.js +1086 -0
  301. package/dist/db/working-hours.js +161 -0
  302. package/dist/feed/my-turn.js +122 -0
  303. package/dist/github/actions-logs.js +397 -0
  304. package/dist/github/auth.js +86 -0
  305. package/dist/github/branch-queries.js +229 -0
  306. package/dist/github/client.js +454 -0
  307. package/dist/github/codeowners.js +221 -0
  308. package/dist/github/commit-checks.js +105 -0
  309. package/dist/github/compare.js +110 -0
  310. package/dist/github/diff-anchor.js +227 -0
  311. package/dist/github/mutations.js +706 -0
  312. package/dist/github/pr-file-content.js +254 -0
  313. package/dist/github/pr-liveness.js +161 -0
  314. package/dist/github/queries.js +875 -0
  315. package/dist/github/rate-budget.js +129 -0
  316. package/dist/github/reactions.js +161 -0
  317. package/dist/github/reviewer-suggest.js +104 -0
  318. package/dist/github/team-reviewers.js +96 -0
  319. package/dist/index.js +184 -0
  320. package/dist/merge/auto-merge-runner.js +960 -0
  321. package/dist/ml/severity-client.js +218 -0
  322. package/dist/pr/detail-enricher.js +26 -0
  323. package/dist/pro/bind.js +302 -0
  324. package/dist/pro/contract.js +37 -0
  325. package/dist/pro/migrate.js +64 -0
  326. package/dist/review/agent-context.js +127 -0
  327. package/dist/review/agent.js +385 -0
  328. package/dist/review/agentic.js +59 -0
  329. package/dist/review/auth.js +94 -0
  330. package/dist/review/chat-agent.js +179 -0
  331. package/dist/review/claude-review/ai-ready.js +16 -0
  332. package/dist/review/claude-review/auto-settings.js +139 -0
  333. package/dist/review/claude-review/auto.js +244 -0
  334. package/dist/review/claude-review/chat.js +489 -0
  335. package/dist/review/claude-review/ci-failures.js +350 -0
  336. package/dist/review/claude-review/follow-up.js +310 -0
  337. package/dist/review/claude-review/manager.js +686 -0
  338. package/dist/review/claude-review/persist.js +996 -0
  339. package/dist/review/claude-review/prompts.js +357 -0
  340. package/dist/review/claude-review/routes.js +518 -0
  341. package/dist/review/claude-review/routing.js +61 -0
  342. package/dist/review/claude-review/specialists.js +190 -0
  343. package/dist/review/claude-review/threads.js +265 -0
  344. package/dist/review/claude-review/ticket.js +282 -0
  345. package/dist/review/clone-hygiene.js +380 -0
  346. package/dist/review/clone-manager.js +367 -0
  347. package/dist/review/llm.js +113 -0
  348. package/dist/review/model-options.js +48 -0
  349. package/dist/review/plugin-providers.js +12 -0
  350. package/dist/review/post-review.js +216 -0
  351. package/dist/review/post-seam.js +161 -0
  352. package/dist/review/prepare.js +168 -0
  353. package/dist/review/pricing.js +17 -0
  354. package/dist/review/schema.js +156 -0
  355. package/dist/review/submit-map.js +41 -0
  356. package/dist/review/usage.js +90 -0
  357. package/dist/shared/claude-review.js +335 -0
  358. package/dist/shared/conflict-fold.js +151 -0
  359. package/dist/shared/conflicts.js +30 -0
  360. package/dist/shared/flow-settings.js +191 -0
  361. package/dist/shared/index.js +8 -0
  362. package/dist/shared/my-turn-settings.js +238 -0
  363. package/dist/shared/pending-rules.js +281 -0
  364. package/dist/shared/product.js +54 -0
  365. package/dist/shared/types.js +896 -0
  366. package/dist/status.js +386 -0
  367. package/dist/sync/adaptive.js +185 -0
  368. package/dist/sync/app-attribution.js +66 -0
  369. package/dist/sync/auth-notices.js +36 -0
  370. package/dist/sync/backfill-ci-history.js +315 -0
  371. package/dist/sync/backfill-commit-headlines.js +136 -0
  372. package/dist/sync/backfill-pr-security.js +175 -0
  373. package/dist/sync/backfill-review-requests.js +97 -0
  374. package/dist/sync/benchmark-rollup.js +50 -0
  375. package/dist/sync/bot-detection.js +309 -0
  376. package/dist/sync/bot-resolution-markers.js +30 -0
  377. package/dist/sync/branch-status.js +634 -0
  378. package/dist/sync/classify-change-shape.js +150 -0
  379. package/dist/sync/commit-files.js +74 -0
  380. package/dist/sync/derive-thread-state.js +99 -0
  381. package/dist/sync/drain-review-threads.js +212 -0
  382. package/dist/sync/hydrate-detail.js +482 -0
  383. package/dist/sync/mention-scan.js +89 -0
  384. package/dist/sync/ml-enrichment.js +478 -0
  385. package/dist/sync/pr-change-signal.js +96 -0
  386. package/dist/sync/pr-liveness-sweep.js +139 -0
  387. package/dist/sync/pr-settle.js +381 -0
  388. package/dist/sync/refresh-pr.js +163 -0
  389. package/dist/sync/reparse-vendor-badges.js +495 -0
  390. package/dist/sync/resync-after-write.js +280 -0
  391. package/dist/sync/review-fingerprint.js +117 -0
  392. package/dist/sync/reviewer-behavior.js +103 -0
  393. package/dist/sync/reviewer-classify.js +503 -0
  394. package/dist/sync/routing-files.js +97 -0
  395. package/dist/sync/scheduled-jobs.js +12 -0
  396. package/dist/sync/scheduler.js +159 -0
  397. package/dist/sync/security-detect.js +521 -0
  398. package/dist/sync/sync-manager.js +750 -0
  399. package/dist/sync/sync-one-pr.js +205 -0
  400. package/dist/sync/sync-repo.js +400 -0
  401. package/dist/sync/unsettled-prs.js +506 -0
  402. package/dist/sync/upsert.js +1190 -0
  403. package/package.json +56 -0
  404. package/public/apple-touch-icon.png +0 -0
  405. package/public/assets/AiFixTab-CQP6PT5u.js +2 -0
  406. package/public/assets/ClaudeReviewTab-DQJ-58ZA.js +8 -0
  407. package/public/assets/PrBotBehaviourTab-CCnFrWbC.js +1 -0
  408. package/public/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
  409. package/public/assets/highlight-BEHUn5zE.css +10 -0
  410. package/public/assets/highlight-CIIZLa6Z.js +12 -0
  411. package/public/assets/index-BDnYs6zg.js +73 -0
  412. package/public/assets/index-mCuI--RC.css +1 -0
  413. package/public/assets/markdown-I_JLLVAZ.js +32 -0
  414. package/public/assets/perfProbe-Da13EhTE.js +1 -0
  415. package/public/assets/tanstack-5qsxxaLe.js +17 -0
  416. package/public/assets/vis-C3BCkP5U.js +1280 -0
  417. package/public/assets/vis-ziCVehMy.css +1 -0
  418. package/public/favicon-16.png +0 -0
  419. package/public/favicon-32.png +0 -0
  420. package/public/favicon-48.png +0 -0
  421. package/public/favicon.svg +5 -0
  422. package/public/index.html +41 -0
  423. package/public-landing/apple-touch-icon.png +0 -0
  424. package/public-landing/assets/archivo-latin-500-BWmvx2Mg.woff2 +0 -0
  425. package/public-landing/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
  426. package/public-landing/assets/index-CA3eEGXO.js +40 -0
  427. package/public-landing/assets/index-naQrQ6Fe.css +1 -0
  428. package/public-landing/assets/jetbrains-mono-latin-400-RruCX7RW.woff2 +0 -0
  429. package/public-landing/assets/newsreader-latin-400-DExrRRrN.woff2 +0 -0
  430. package/public-landing/assets/newsreader-latin-400-italic-b0I-AwbV.woff2 +0 -0
  431. package/public-landing/bots/index.html +156 -0
  432. package/public-landing/contact/index.html +156 -0
  433. package/public-landing/cookies/index.html +156 -0
  434. package/public-landing/demo/limn-walkthrough-poster.jpg +0 -0
  435. package/public-landing/demo/limn-walkthrough.cues.json +44 -0
  436. package/public-landing/demo/limn-walkthrough.mp4 +0 -0
  437. package/public-landing/favicon-16.png +0 -0
  438. package/public-landing/favicon-32.png +0 -0
  439. package/public-landing/favicon-48.png +0 -0
  440. package/public-landing/favicon.svg +5 -0
  441. package/public-landing/features/index.html +156 -0
  442. package/public-landing/for-developers/index.html +156 -0
  443. package/public-landing/for-managers/index.html +156 -0
  444. package/public-landing/how-it-works/index.html +156 -0
  445. package/public-landing/how-we-measure/index.html +156 -0
  446. package/public-landing/icon-192.png +0 -0
  447. package/public-landing/icon-512.png +0 -0
  448. package/public-landing/index.html +156 -0
  449. package/public-landing/insights/index.html +156 -0
  450. package/public-landing/og-image.png +0 -0
  451. package/public-landing/pricing/index.html +156 -0
  452. package/public-landing/privacy/index.html +156 -0
  453. package/public-landing/pro/index.html +156 -0
  454. package/public-landing/reviews/index.html +156 -0
  455. package/public-landing/robots.txt +8 -0
  456. package/public-landing/shots/benchmark.png +0 -0
  457. package/public-landing/shots/bot-roi.png +0 -0
  458. package/public-landing/shots/bot-settings.png +0 -0
  459. package/public-landing/shots/chronology.png +0 -0
  460. package/public-landing/shots/feed.png +0 -0
  461. package/public-landing/shots/flow-metrics.png +0 -0
  462. package/public-landing/shots/free-reports.png +0 -0
  463. package/public-landing/shots/pending-board.png +0 -0
  464. package/public-landing/shots/pending-card.png +0 -0
  465. package/public-landing/shots/period-report.png +0 -0
  466. package/public-landing/shots/pr-detail.png +0 -0
  467. package/public-landing/shots/pr-threads.png +0 -0
  468. package/public-landing/shots/reach.png +0 -0
  469. package/public-landing/shots/repo-rows.png +0 -0
  470. package/public-landing/shots/severity-findings.png +0 -0
  471. package/public-landing/shots/severity-strip.png +0 -0
  472. package/public-landing/site.webmanifest +14 -0
  473. package/public-landing/sitemap.xml +51 -0
  474. package/public-landing/terms/index.html +156 -0
@@ -0,0 +1,36 @@
1
+ import { and, eq, inArray, isNull, or, sql } from 'drizzle-orm';
2
+ import { db, schema } from './client.js';
3
+ import { config } from '../config.js';
4
+ // One-time / idempotent maintenance run at startup.
5
+ //
6
+ // GitHub wraps inline-only reviews in an empty "commented" review, which the
7
+ // sync used to record as a `review_submitted` event. Those wrappers duplicate
8
+ // the inline `review_comment` markers on the timeline. New syncs no longer emit
9
+ // them (see sync/upsert.ts), but pre-existing rows linger because upserts only
10
+ // update. This removes them; it also catches reviews whose summary body was
11
+ // later cleared on GitHub. Cheap and safe to run every boot.
12
+ //
13
+ // Written as portable drizzle (no raw better-sqlite3 / db.execute) so it runs on
14
+ // both dialects; the count comes from `.returning().length` (dialect-neutral).
15
+ export async function cleanupRedundantReviewEvents() {
16
+ // In cloud "lean storage" mode (config.persistBodies false) reviews.body is
17
+ // always null, so the body-emptiness signal this relies on is unavailable — and
18
+ // unnecessary: persistPr already only emits review_submitted for *substantive*
19
+ // reviews (it checks the in-memory GraphQL body, not the stored one), and cloud
20
+ // starts empty, so there are no wrapper events to remove. Skipping here avoids
21
+ // wrongly deleting markers for substantive "commented" reviews.
22
+ if (!config.persistBodies)
23
+ return 0;
24
+ const { events, reviews } = schema;
25
+ const emptyCommentedReviews = db
26
+ .select({ id: reviews.id })
27
+ .from(reviews)
28
+ .where(and(eq(reviews.state, 'commented'), or(isNull(reviews.body), eq(sql `trim(${reviews.body})`, ''))));
29
+ const deleted = await db
30
+ .delete(events)
31
+ .where(and(eq(events.type, 'review_submitted'), eq(events.refTable, 'reviews'), inArray(events.refId, emptyCommentedReviews)))
32
+ .returning({ id: events.id })
33
+ .execute();
34
+ return deleted.length;
35
+ }
36
+ //# sourceMappingURL=cleanup.js.map
@@ -0,0 +1,102 @@
1
+ // Mode-aware database client.
2
+ //
3
+ // local → better-sqlite3 (synchronous engine) + the sqlite-core schema
4
+ // cloud → node-postgres Pool + the pg-core schema
5
+ //
6
+ // The whole app talks to `db` through the PORTABLE async surface only
7
+ // (`await builder` / `.execute()` / `.returning().execute()` / runTransaction) —
8
+ // the empirically-verified subset that behaves identically on both drivers (see
9
+ // the probe in the cloud-refactor notes). `db` is TYPED as the Postgres drizzle
10
+ // instance so any stray `.get()/.all()/.run()` (better-sqlite3-only) is a compile
11
+ // error; at runtime the better-sqlite3 instance supports that same subset.
12
+ import { chmodSync, mkdirSync } from 'node:fs';
13
+ import { dirname } from 'node:path';
14
+ import { config } from '../config.js';
15
+ import * as sqliteSchema from './schema.sqlite.js';
16
+ import * as pgSchema from './schema.pg.js';
17
+ export const isPg = config.dbDialect === 'postgres';
18
+ let dbInstance;
19
+ let schemaValue;
20
+ let closeFn;
21
+ // Kept for the sqlite-only manual transaction path; null in cloud mode.
22
+ let sqliteConn = null;
23
+ if (isPg) {
24
+ // Dynamic import so the unused native driver isn't loaded in the other mode.
25
+ const pg = await import('pg');
26
+ const Pool = pg.Pool ?? pg.default.Pool;
27
+ const { drizzle } = await import('drizzle-orm/node-postgres');
28
+ const pool = new Pool({ connectionString: config.databaseUrl });
29
+ schemaValue = pgSchema;
30
+ dbInstance = drizzle(pool, { schema: pgSchema });
31
+ closeFn = async () => {
32
+ await pool.end();
33
+ };
34
+ }
35
+ else {
36
+ const { default: Database } = await import('better-sqlite3');
37
+ const { drizzle } = await import('drizzle-orm/better-sqlite3');
38
+ // Owner-only on the DIRECTORY as well as the file: WAL mode creates two siblings
39
+ // (`-wal`, `-shm`) that hold the same data and are created by SQLite with the process
40
+ // umask, so locking down only the main file would leave the write-ahead log readable.
41
+ mkdirSync(dirname(config.dbPath), { recursive: true, mode: 0o700 });
42
+ sqliteConn = new Database(config.dbPath);
43
+ sqliteConn.pragma('journal_mode = WAL');
44
+ sqliteConn.pragma('foreign_keys = ON');
45
+ // The local database holds every synced PR body, review comment and GitHub login the user
46
+ // can see — on a shared or multi-user machine the default 0644 made all of that readable by
47
+ // any other local account. 0600 it (and the WAL siblings, which the pragma above has just
48
+ // created). Best-effort: chmod is not meaningful on Windows, and a failure here must never
49
+ // stop the app from starting.
50
+ for (const suffix of ['', '-wal', '-shm']) {
51
+ try {
52
+ chmodSync(`${config.dbPath}${suffix}`, 0o600);
53
+ }
54
+ catch {
55
+ /* missing sibling (non-WAL platform) or an unsupported filesystem — not fatal */
56
+ }
57
+ }
58
+ schemaValue = sqliteSchema;
59
+ dbInstance = drizzle(sqliteConn, {
60
+ schema: sqliteSchema,
61
+ });
62
+ closeFn = async () => {
63
+ sqliteConn?.close();
64
+ };
65
+ }
66
+ export const db = dbInstance;
67
+ export const schema = schemaValue;
68
+ // Close the underlying connection (pool.end() / better-sqlite3 .close()). Used by
69
+ // the one-shot migrate CLI; the long-running server never calls it.
70
+ export async function closeDb() {
71
+ await closeFn();
72
+ }
73
+ // Dialect-aware transaction. better-sqlite3 rejects async transaction callbacks
74
+ // ("Transaction function cannot return a promise"), so the sqlite path wraps a
75
+ // manual BEGIN/COMMIT around the awaited (synchronously-resolving) drizzle ops —
76
+ // no other macrotask interleaves between BEGIN and COMMIT because the callback
77
+ // only awaits in-process sqlite work (no real I/O). The pg path uses a real
78
+ // async transaction. Either way the callback uses the same portable `tx` surface.
79
+ export async function runTransaction(fn) {
80
+ if (isPg) {
81
+ return dbInstance.transaction(async (tx) => fn(tx));
82
+ }
83
+ const conn = sqliteConn;
84
+ if (!conn)
85
+ throw new Error('sqlite connection not initialised');
86
+ conn.exec('BEGIN');
87
+ try {
88
+ const result = await fn(dbInstance);
89
+ conn.exec('COMMIT');
90
+ return result;
91
+ }
92
+ catch (err) {
93
+ try {
94
+ conn.exec('ROLLBACK');
95
+ }
96
+ catch {
97
+ /* rollback best-effort */
98
+ }
99
+ throw err;
100
+ }
101
+ }
102
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,388 @@
1
+ // ============================================================================
2
+ // Code-only lines-of-change for a pull request — the input to the LARGE-PR FLAG.
3
+ // ============================================================================
4
+ //
5
+ // "Large" is a claim about how much CODE a reviewer has to read, so a 2,000-line
6
+ // lockfile bump, a regenerated OpenAPI schema or a docs rewrite must not trip it.
7
+ // `codeLocFor` folds the PR's stored `files[]` (additions + deletions per file),
8
+ // dropping everything `isNonCodeFile` classifies as prose, structured config,
9
+ // generated/vendored output or binary payload.
10
+ //
11
+ // ---- RELATIONSHIP TO THE THREE OTHER CLASSIFIERS ----------------------------
12
+ //
13
+ // There are now FOUR path classifiers in this repo and they answer four
14
+ // different questions. They are NOT meant to agree, and none may be folded into
15
+ // another:
16
+ //
17
+ // 1. `NOISE_GLOBS` — apps/backend/src/review/prepare.ts
18
+ // "Which files should be stripped from the diff the Claude Review AGENT
19
+ // sees?" A BUDGET decision on a paid feature: lockfiles, minified bundles,
20
+ // dist/build output, snapshots. It deliberately keeps `.md`, `.yml`, `.json`
21
+ // and `docs/` IN the diff, because a review agent reading a config change is
22
+ // doing its job. ⚠ Widening it to satisfy THIS feature would silently change
23
+ // what a paid agent reviews, with no test to catch it.
24
+ //
25
+ // 2. `isLockFile` — apps/frontend/src/lib/diff.ts
26
+ // "Should this file's diff start COLLAPSED in the Changes tab?" Lockfiles
27
+ // only, by exact basename, case-sensitively (matching what git records).
28
+ // A narrow presentation heuristic; far too narrow to answer "is this code".
29
+ //
30
+ // 3. `isNonCodeFile` — HERE
31
+ // "Does this file's churn count toward how much CODE this PR asks a human to
32
+ // read?" The broadest of the three, and the only one that excludes prose and
33
+ // structured config. Case-INSENSITIVE (a `Readme.MD` is still prose), and
34
+ // the only one whose answer is a number a user compares against a threshold
35
+ // they configured.
36
+ //
37
+ // 4. `BLAST_SURFACES` + `isTestFile` — apps/backend/src/db/blast-radius.ts
38
+ // "Do this file's CONSUMERS LIVE OUTSIDE THE DIFF?" The only one asking
39
+ // about REACH rather than volume or presentation, which is why it is a list
40
+ // of contract shapes (migrations, IDL, `.d.ts`, OpenAPI, IaC) rather than a
41
+ // list of things to ignore. It sits ON TOP of this file rather than beside
42
+ // it: `isNonCodeFile` is reused verbatim to split code from prose/config,
43
+ // and blast-radius.ts adds only the two judgements this file has no opinion
44
+ // about — is it a test, and is it a contract.
45
+ //
46
+ // ---- THE THREE DATA TRAPS (all measured on the real corpus) -----------------
47
+ //
48
+ // See `codeLocFor` for how each is handled. In one line each:
49
+ // * 18.5% of PRs have `files IS NULL`, and 18.8% carry a never-observed size —
50
+ // `codeLoc` is NULL there, never 0. "Not large" about a PR nobody measured is
51
+ // a false claim (the same rule `lib/botVolume.ts`'s `formatLoc` states).
52
+ // * `files(first: 100)` truncates, and it truncates EXACTLY the largest PRs —
53
+ // so `codeLocIsLowerBound` rides alongside and the reading is ASYMMETRIC:
54
+ // over-threshold is safe to assert, under-threshold is not.
55
+ // * `files = []` is OVERLOADED: it is also `sync/routing-files.ts`'s "we tried
56
+ // and failed" sentinel. It never means "zero code lines".
57
+ /** The page size of the `files(first: 100)` connection in `github/queries.ts`. A PR whose
58
+ * stored `files[]` is at this length was (or may have been) truncated by that cap. */
59
+ export const PR_FILES_PAGE_CAP = 100;
60
+ // ---- the classifier ---------------------------------------------------------
61
+ function normalize(path) {
62
+ let p = path.replace(/\\/g, '/');
63
+ if (p.startsWith('./'))
64
+ p = p.slice(2);
65
+ if (p.startsWith('/'))
66
+ p = p.slice(1);
67
+ return p;
68
+ }
69
+ /** Prose. Extensions whose content is written for humans, not machines. */
70
+ const DOC_EXTENSIONS = new Set([
71
+ 'md',
72
+ 'mdx',
73
+ 'markdown',
74
+ 'mdown',
75
+ 'rst',
76
+ 'txt',
77
+ 'adoc',
78
+ 'asciidoc',
79
+ 'org',
80
+ 'tex',
81
+ 'pdf',
82
+ ]);
83
+ /** Structured configuration + data. NOT `.html`/`.css`/`.scss`/`.sql`, which are hand-written
84
+ * source a reviewer must actually read. `.xml` is here because in practice it is manifests,
85
+ * poms and layouts — data with a schema, not logic. */
86
+ const CONFIG_EXTENSIONS = new Set([
87
+ 'json',
88
+ 'json5',
89
+ 'jsonc',
90
+ 'yml',
91
+ 'yaml',
92
+ 'toml',
93
+ 'ini',
94
+ 'cfg',
95
+ 'conf',
96
+ 'properties',
97
+ 'plist',
98
+ 'xml',
99
+ 'csv',
100
+ 'tsv',
101
+ 'lock',
102
+ 'lockfile',
103
+ ]);
104
+ /** Binary / media payloads. Their "additions" are a diff artefact, not lines anyone reads. */
105
+ const BINARY_EXTENSIONS = new Set([
106
+ 'png',
107
+ 'jpg',
108
+ 'jpeg',
109
+ 'gif',
110
+ 'bmp',
111
+ 'ico',
112
+ 'icns',
113
+ 'webp',
114
+ 'avif',
115
+ 'svg',
116
+ 'tiff',
117
+ 'psd',
118
+ 'mp3',
119
+ 'mp4',
120
+ 'mov',
121
+ 'webm',
122
+ 'wav',
123
+ 'ogg',
124
+ 'woff',
125
+ 'woff2',
126
+ 'ttf',
127
+ 'otf',
128
+ 'eot',
129
+ 'zip',
130
+ 'gz',
131
+ 'tgz',
132
+ 'bz2',
133
+ 'xz',
134
+ '7z',
135
+ 'rar',
136
+ 'jar',
137
+ 'war',
138
+ 'so',
139
+ 'dylib',
140
+ 'dll',
141
+ 'exe',
142
+ 'bin',
143
+ 'wasm',
144
+ 'pyc',
145
+ 'class',
146
+ 'pdb',
147
+ 'db',
148
+ 'sqlite',
149
+ 'parquet',
150
+ ]);
151
+ /** Machine-generated language lock files, by exact (lower-cased) basename. Deliberately a
152
+ * SUPERSET of the frontend's `isLockFile` list — this one also has the `*.lock` /
153
+ * `*.lockfile` extension arms above, which that one refuses on purpose. */
154
+ const LOCK_BASENAMES = new Set([
155
+ 'package-lock.json',
156
+ 'npm-shrinkwrap.json',
157
+ 'pnpm-lock.yaml',
158
+ 'yarn.lock',
159
+ 'bun.lockb',
160
+ 'bun.lock',
161
+ 'cargo.lock',
162
+ 'poetry.lock',
163
+ 'uv.lock',
164
+ 'pipfile.lock',
165
+ 'gemfile.lock',
166
+ 'composer.lock',
167
+ 'go.sum',
168
+ 'flake.lock',
169
+ 'package.resolved',
170
+ 'gradle.lockfile',
171
+ 'mix.lock',
172
+ 'pubspec.lock',
173
+ 'packages.lock.json',
174
+ ]);
175
+ /** Extensionless files that are prose or project metadata rather than source. Matched on the
176
+ * lower-cased basename with any doc extension already stripped. */
177
+ const DOC_BASENAMES = new Set([
178
+ 'readme',
179
+ 'license',
180
+ 'licence',
181
+ 'copying',
182
+ 'notice',
183
+ 'authors',
184
+ 'contributors',
185
+ 'changelog',
186
+ 'changes',
187
+ 'history',
188
+ 'contributing',
189
+ 'code_of_conduct',
190
+ 'security',
191
+ 'codeowners',
192
+ 'owners',
193
+ 'maintainers',
194
+ 'patents',
195
+ 'acknowledgements',
196
+ 'acknowledgments',
197
+ ]);
198
+ /** Directory names whose whole subtree is prose or generated/vendored output. Matched as a
199
+ * WHOLE path segment, anywhere in the path, so `apps/backend/docs/x.ts` counts too. */
200
+ const NON_CODE_DIRS = new Set([
201
+ 'docs',
202
+ 'doc',
203
+ 'documentation',
204
+ 'node_modules',
205
+ 'vendor',
206
+ 'vendored',
207
+ 'third_party',
208
+ 'thirdparty',
209
+ '3rdparty',
210
+ 'dist',
211
+ 'build',
212
+ 'out',
213
+ 'generated',
214
+ 'gen',
215
+ 'autogen',
216
+ 'coverage',
217
+ '__snapshots__',
218
+ '__pycache__',
219
+ 'fixtures',
220
+ '__fixtures__',
221
+ 'testdata',
222
+ 'golden',
223
+ 'snapshots',
224
+ ]);
225
+ function lastSegment(path) {
226
+ const idx = path.lastIndexOf('/');
227
+ return idx === -1 ? path : path.slice(idx + 1);
228
+ }
229
+ function extensionOf(basename) {
230
+ const idx = basename.lastIndexOf('.');
231
+ // A leading dot is a DOTFILE marker, not an extension (`.eslintrc` has no extension).
232
+ if (idx <= 0)
233
+ return '';
234
+ return basename.slice(idx + 1);
235
+ }
236
+ /**
237
+ * Does this path's churn count as CODE for the large-PR flag?
238
+ *
239
+ * Returns true for documentation, structured configuration, dotfiles, lockfiles, binary/media
240
+ * payloads and anything living under a generated / vendored / docs directory.
241
+ *
242
+ * ⚠ Read the "relationship to the two classifiers that already exist" block at the top of this
243
+ * file before widening it — and in particular do NOT reach for `review/prepare.ts`'s
244
+ * `NOISE_GLOBS` instead: that list is a paid agent's diff budget and answers a different
245
+ * question.
246
+ */
247
+ export function isNonCodeFile(path) {
248
+ const p = normalize(path).toLowerCase();
249
+ if (p === '')
250
+ return true;
251
+ const segments = p.split('/');
252
+ const base = lastSegment(p);
253
+ // Dot-directories are tooling/config trees in their entirety (.github/, .vscode/, .circleci/,
254
+ // .idea/, .storybook/), and so is everything under a docs / generated / vendored directory.
255
+ //
256
+ // ⚠ DIRECTORY SEGMENTS ONLY — `segments.length - 1`, never the whole array. A file named
257
+ // `build` (as in `scripts/build`, a shell entrypoint) is source code; only a DIRECTORY called
258
+ // `build` is output. Testing the basename against the directory list is how a hand-written
259
+ // script gets silently uncounted.
260
+ for (let i = 0; i < segments.length - 1; i++) {
261
+ const seg = segments[i];
262
+ if (seg.startsWith('.'))
263
+ return true;
264
+ if (NON_CODE_DIRS.has(seg))
265
+ return true;
266
+ }
267
+ // A dotfile basename (.env, .env.local, .gitignore, .eslintrc.js, .prettierrc) is
268
+ // configuration whatever extension it carries.
269
+ if (base.startsWith('.'))
270
+ return true;
271
+ if (LOCK_BASENAMES.has(base))
272
+ return true;
273
+ // Minified / sourcemap / snapshot artefacts, by compound suffix (a plain extension set can't
274
+ // express `.min.js`, whose extension is `js`).
275
+ if (base.endsWith('.min.js') ||
276
+ base.endsWith('.min.css') ||
277
+ base.endsWith('.min.mjs') ||
278
+ base.endsWith('.map') ||
279
+ base.endsWith('.snap')) {
280
+ return true;
281
+ }
282
+ // Conventional "this file was generated" markers, which no reviewer reads line by line.
283
+ if (base.includes('.generated.') ||
284
+ base.endsWith('.gen.go') ||
285
+ base.endsWith('.pb.go') ||
286
+ base.endsWith('_pb2.py') ||
287
+ base.endsWith('_pb2.pyi') ||
288
+ base.endsWith('.pb.cc') ||
289
+ base.endsWith('.pb.h') ||
290
+ base.endsWith('.g.dart') ||
291
+ base.endsWith('.freezed.dart')) {
292
+ return true;
293
+ }
294
+ const ext = extensionOf(base);
295
+ if (ext === '') {
296
+ // No extension at all: prose/metadata by name (README, LICENSE, CODEOWNERS), otherwise a
297
+ // script or a Dockerfile/Makefile — hand-written logic, which COUNTS.
298
+ return DOC_BASENAMES.has(base);
299
+ }
300
+ if (DOC_EXTENSIONS.has(ext))
301
+ return true;
302
+ if (CONFIG_EXTENSIONS.has(ext))
303
+ return true;
304
+ if (BINARY_EXTENSIONS.has(ext))
305
+ return true;
306
+ // A prose basename that carries a doc-ish extension is already caught above; one carrying a
307
+ // code extension (e.g. `changelog.ts`) is real source and must not be excluded — hence no
308
+ // DOC_BASENAMES check down here.
309
+ return false;
310
+ }
311
+ /** The one honest "we don't know". Never `{codeLoc: 0}` — see the trap notes above. */
312
+ const UNKNOWN = { codeLoc: null, codeLocIsLowerBound: false };
313
+ /**
314
+ * Fold a stored PR row into its code-only line count.
315
+ *
316
+ * The three data traps, each handled explicitly:
317
+ *
318
+ * 1. **`files IS NULL`** (18.5% of the real corpus) — the per-file breakdown was never stored
319
+ * for this PR. Nothing to fold → `null`.
320
+ *
321
+ * 2. **A never-observed size** (18.8%) — `additions`, `deletions` and `changedFiles` are all 0.
322
+ * ⚠ NARROWED: this now refuses only when the FILE LIST agrees (sums to zero too). See the
323
+ * inline note — a stored `files[]` carrying real per-file numbers is a positive observation of
324
+ * size, and refusing on the columns alone was discarding it for 9.8% of open pull requests.
325
+ * Those columns are NOT NULL with a 0 default, so "we never saw this PR's size" and "this PR
326
+ * genuinely changed nothing" are the SAME row. `lib/botVolume.ts` states the rule for the
327
+ * total-LoC label and it holds identically here: send null, never a fabricated 0, because
328
+ * rendering "not large" about a PR nobody measured is a false claim.
329
+ *
330
+ * 3. **`files = []` is OVERLOADED** — `sync/routing-files.ts` persists `[]` as a "we tried and
331
+ * failed" sentinel, which is byte-identical to a PR that really touched nothing. The
332
+ * cross-check is `changedFiles`: a PR with `changedFiles > 0` and an empty `files` array is
333
+ * the sentinel, and treating it as zero code lines would mark real PRs as trivially small.
334
+ * We go further and return null for EVERY empty array, because the only remaining case —
335
+ * `changedFiles === 0` — has already been answered by trap 2.
336
+ *
337
+ * 4. (Not a trap so much as a bound.) `files(first: 100)` truncates. `codeLocIsLowerBound` is
338
+ * set when the stored list hit the page cap OR when `changedFiles` exceeds what we hold —
339
+ * both mean files are missing, and a missing file can only ADD lines.
340
+ */
341
+ export function codeLocFor(pr) {
342
+ // Trap 1.
343
+ if (pr.files == null)
344
+ return UNKNOWN;
345
+ // ⚠ TRAP 2, NARROWED — read this before widening it back.
346
+ //
347
+ // The ambiguity is real: `additions`/`deletions`/`changedFiles` are NOT NULL with a 0 default,
348
+ // so "we never saw this pull request's size" and "it genuinely changed nothing" are the same
349
+ // row. But that is only unresolvable when the FILE LIST cannot answer it either. A non-empty
350
+ // `files[]` whose per-file numbers sum above zero IS a positive observation of size, from the
351
+ // same GitHub payload the columns should have carried — so refusing on the columns alone
352
+ // discards evidence we hold.
353
+ //
354
+ // Measured after `backfillMissingPrFiles` landed: 154 of 1,564 open pull requests (9.8%) had a
355
+ // real file list and three zeroed columns, and were reported UNKNOWN — no large-PR flag and no
356
+ // blast-radius chip — purely because this test ran first and read only the columns.
357
+ //
358
+ // ⚠ THE REFUSAL STILL STANDS WHEN THE FILES AGREE. All-zero columns AND a file list that sums
359
+ // to zero is still UNKNOWN, so a genuinely empty pull request never acquires a fabricated size.
360
+ const filesSum = pr.files.reduce((n, f) => n + ((f?.additions ?? 0) + (f?.deletions ?? 0)), 0);
361
+ if (pr.additions === 0 && pr.deletions === 0 && pr.changedFiles === 0 && filesSum === 0) {
362
+ return UNKNOWN;
363
+ }
364
+ // Trap 3: an empty array with a non-zero size is the routing-files sentinel (or a partial
365
+ // write); either way we hold no per-file breakdown to classify.
366
+ if (pr.files.length === 0)
367
+ return UNKNOWN;
368
+ let codeLoc = 0;
369
+ for (const f of pr.files) {
370
+ if (typeof f?.path !== 'string')
371
+ continue;
372
+ if (isNonCodeFile(f.path))
373
+ continue;
374
+ codeLoc += (f.additions ?? 0) + (f.deletions ?? 0);
375
+ }
376
+ const codeLocIsLowerBound = pr.files.length >= PR_FILES_PAGE_CAP || pr.changedFiles > pr.files.length;
377
+ return { codeLoc, codeLocIsLowerBound };
378
+ }
379
+ /**
380
+ * The product default when an account has stored no threshold of its own. Mirrors
381
+ * `LARGE_PR_CODE_LOC_DEFAULT` in `@pierre-review/shared` — one number, spelled in the two
382
+ * places that need it without the backend importing a value from a types-only package at
383
+ * runtime (`shared` is `import type` only on this side; see PACKAGING).
384
+ */
385
+ export function resolveLargePrThreshold(stored) {
386
+ return stored != null && Number.isInteger(stored) && stored > 0 ? stored : 1500;
387
+ }
388
+ //# sourceMappingURL=code-loc.js.map
@@ -0,0 +1,79 @@
1
+ import { getAiUsageSummary, getSummaryTurnCount } from './usage.js';
2
+ // USD → credits conversion, MIRRORED from @pierre-review/shared AI_CREDITS_PER_USD. Inlined
3
+ // here (not imported) because the release guard forbids a real runtime import of the
4
+ // types-only shared package into release/dist. TWO sites, kept in lockstep: shared
5
+ // (types.ts) + this one. (The pro plugin's third copy in insights/routes.ts is gone — it
6
+ // reads ctx.aiCredits now.) $1 of model cost = 1250 credits (1 credit ≈ $0.0008).
7
+ export const AI_CREDITS_PER_USD = 1250;
8
+ // The two AI seams are metered DIFFERENTLY:
9
+ // - SUMMARY (cheap one-shot Haiku completions — digests, sprint report, insights chat, PR
10
+ // summary, CI analysis, themes, …) is metered by a monthly TURN COUNT. Each real (billed)
11
+ // completion is one turn; a $0 cache hit / ambient-session call is free and doesn't count.
12
+ // - AGENT (Agent-SDK runs — Claude Review, AI Fix) is metered by CREDITS ($ cost), because a
13
+ // single agentic run's cost varies wildly and a flat turn count would be unfair.
14
+ // Defaults for a paid cloud account (both reset at the UTC month boundary):
15
+ export const DEFAULT_SUMMARY_TURN_LIMIT = 500; // ~$5/mo of Haiku at a blended ~$0.01/turn
16
+ export const DEFAULT_AGENT_CREDIT_ALLOWANCE = 15 * AI_CREDITS_PER_USD; // 18,750 cr = $15/mo
17
+ const toCredits = (usd) => Math.round(usd * AI_CREDITS_PER_USD);
18
+ // UTC first-of-month epoch ms — the month-to-date window start (matches the /api/pro/ai-usage
19
+ // route). Monthly reset is automatic: the window rolls at the UTC month boundary.
20
+ export function monthStartMs(nowMs) {
21
+ const d = new Date(nowMs);
22
+ return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1);
23
+ }
24
+ // The account's SUMMARY monthly turn limit, or null when unmetered.
25
+ // - local (isLocal): unmetered — null (never blocked; today's behavior).
26
+ // - paid cloud (plan !== 'free'): the plan default turn limit.
27
+ // - free cloud: a zero limit (belt-and-braces — the /api/pro/* 402 gate blocks these first).
28
+ function summaryTurnLimitFor(account) {
29
+ if (account.isLocal)
30
+ return null;
31
+ if (account.plan !== 'free')
32
+ return DEFAULT_SUMMARY_TURN_LIMIT;
33
+ return 0;
34
+ }
35
+ // The account's AGENT credit allowance, or null when unmetered. The per-account override
36
+ // column (accounts.aiCreditAllowance) now governs the AGENT budget (a forward hook for
37
+ // top-ups / alternate plans); summary is a flat turn count with no per-account override yet.
38
+ function agentAllowanceFor(account) {
39
+ if (account.isLocal)
40
+ return null;
41
+ if (account.plan !== 'free')
42
+ return account.aiCreditAllowance ?? DEFAULT_AGENT_CREDIT_ALLOWANCE;
43
+ return 0;
44
+ }
45
+ // The PURE metering computation (no I/O) — month-to-date usage vs. the per-seam budgets. Split
46
+ // out from aiCreditStatus so the budget math is unit-testable without a DB.
47
+ export function computeAiCreditStatus(account, usage) {
48
+ const summaryTurnLimit = summaryTurnLimitFor(account);
49
+ const summaryTurnsUsed = usage.summaryTurns;
50
+ const summaryTurnsRemaining = summaryTurnLimit == null ? null : Math.max(0, summaryTurnLimit - summaryTurnsUsed);
51
+ const summaryBlocked = summaryTurnsRemaining != null && summaryTurnsRemaining <= 0;
52
+ const agentAllowanceCredits = agentAllowanceFor(account);
53
+ const agentCreditsUsed = toCredits(usage.agentUsd);
54
+ const agentCreditsRemaining = agentAllowanceCredits == null ? null : Math.max(0, agentAllowanceCredits - agentCreditsUsed);
55
+ const agentBlocked = agentCreditsRemaining != null && agentCreditsRemaining <= 0;
56
+ return {
57
+ summaryTurnsUsed,
58
+ summaryTurnLimit,
59
+ summaryTurnsRemaining,
60
+ summaryBlocked,
61
+ agentCreditsUsed,
62
+ agentAllowanceCredits,
63
+ agentCreditsRemaining,
64
+ agentBlocked,
65
+ blocked: summaryBlocked,
66
+ };
67
+ }
68
+ // The per-account metering status: reads the month-to-date ledger (summary turn count + agent
69
+ // $ spend) and applies the pure computation above. This is what ctx.aiCredits.check calls per
70
+ // generation, and what /api/me + /api/pro/ai-usage surface.
71
+ export async function aiCreditStatus(account, nowMs) {
72
+ const since = monthStartMs(nowMs);
73
+ const [summaryTurns, usage] = await Promise.all([
74
+ getSummaryTurnCount(account.id, since),
75
+ getAiUsageSummary(account.id, since),
76
+ ]);
77
+ return computeAiCreditStatus(account, { summaryTurns, agentUsd: usage.agentUsd });
78
+ }
79
+ //# sourceMappingURL=credits.js.map