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,535 @@
1
+ // THE AI RUNTIME LOADER — the ONE module allowed to import an AI SDK.
2
+ //
3
+ // Claude Review, the review chat and AI Fix are free and ship in the public npm package, but the
4
+ // SDKs they run on do NOT: `@anthropic-ai/claude-agent-sdk` pulls a native `claude` binary of
5
+ // ~110 MB to download (~240 MB unpacked) per platform, against a ~18 MB package. Putting it in
6
+ // `dependencies` (or `optionalDependencies`, which npm installs by default and a dependent package
7
+ // cannot switch off under `npx`) would make every `npx limn-review` fifteen times heavier for
8
+ // people who never press a Review button. So the four packages are INSTALLED ON FIRST USE:
9
+ //
10
+ // @anthropic-ai/claude-agent-sdk · @anthropic-ai/sdk · @modelcontextprotocol/sdk · zod
11
+ //
12
+ // at EXACT pinned versions into `<dataDir>/ai-runtime` (`~/.limn/ai-runtime`), by
13
+ // `npm install --prefix`, after the reader presses "Set up AI" or runs `limn ai install`. The pins
14
+ // ride the release manifest as a NON-dependency field (`limnAiRuntime`), generated by
15
+ // scripts/build-release.mjs from what the workspace actually installed — so a user downloads the
16
+ // versions the test suite ran against.
17
+ //
18
+ // ⚠ HOW A MODULE OUTSIDE OUR OWN node_modules IS LOADED. A bare specifier in ESM cannot see another
19
+ // prefix and NODE_PATH does not apply to ESM, so each package is located by walking
20
+ // `<dir>/node_modules` the way ESM resolution does, and then imported by FILE URL. The entry
21
+ // is picked with the ESM conditions (`node`, `import`, `default`), NEVER `require.resolve`: zod
22
+ // publishes `index.cjs` under `require` and `index.js` under `import`, and the SDK's own
23
+ // `import 'zod'` takes the ESM one. Loading the CJS twin would give our tool schemas a SECOND zod
24
+ // instance, which silently breaks the SDK's schema conversion. Same install, same conditions,
25
+ // same instance — the test pins it.
26
+ //
27
+ // ⚠ DEV IS UNCHANGED. When the workspace's own node_modules resolve the Agent SDK (`pnpm dev`, the
28
+ // test suite), everything loads by plain bare specifier exactly as the static imports did, and no
29
+ // runtime directory is consulted first. The cloud image is the third case: it ships
30
+ // `@anthropic-ai/sdk` as a real dependency (`--with-pro`, for the metered summary path) and no
31
+ // Agent SDK, so a package missing from one source falls through to the other.
32
+ //
33
+ // ⚠ NOTHING ELSE MAY IMPORT THESE PACKAGES. Type-only imports are fine (erased at emit); a value
34
+ // import anywhere else fails with ERR_MODULE_NOT_FOUND on an npm install — and only when someone
35
+ // clicks the feature. scripts/build-release.mjs greps the emitted JS and fails the build on one.
36
+ import { spawn } from 'node:child_process';
37
+ import { existsSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from 'node:fs';
38
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
39
+ import { fileURLToPath, pathToFileURL } from 'node:url';
40
+ import { resolveDataDir } from '../data-dir.js';
41
+ export const AI_RUNTIME_PACKAGES = [
42
+ '@anthropic-ai/claude-agent-sdk',
43
+ '@anthropic-ai/sdk',
44
+ '@modelcontextprotocol/sdk',
45
+ 'zod',
46
+ ];
47
+ /** The release manifest's field carrying the pins. Spelled once; build-release.mjs mirrors it. */
48
+ export const AI_RUNTIME_MANIFEST_FIELD = 'limnAiRuntime';
49
+ /** An exact semver version — no range, no tag. Anything else is refused before npm sees it. */
50
+ const EXACT_VERSION = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
51
+ const AGENT_SDK = '@anthropic-ai/claude-agent-sdk';
52
+ /**
53
+ * The entry each package is loaded through. ⚠ The MCP SDK's ROOT export names a file it does not
54
+ * ship (`dist/esm/index.js` is absent in 1.29) — its real entries are subpaths — so it is checked and
55
+ * loaded through `./server`. Measured on the real download, not assumed.
56
+ */
57
+ const ENTRY_SUBPATH = {
58
+ '@anthropic-ai/claude-agent-sdk': '.',
59
+ '@anthropic-ai/sdk': '.',
60
+ '@modelcontextprotocol/sdk': './server',
61
+ zod: '.',
62
+ };
63
+ const specifierFor = (name) => ENTRY_SUBPATH[name] === '.' ? name : `${name}${ENTRY_SUBPATH[name].slice(1)}`;
64
+ export const AI_RUNTIME_MISSING_MESSAGE = 'AI is not set up yet. Press "Set up AI" in Limn, or run: limn ai install';
65
+ /** Thrown by a loader when no source carries the package. Its message is safe to show. */
66
+ export class AiRuntimeMissingError extends Error {
67
+ pkg;
68
+ code = 'AI_RUNTIME_MISSING';
69
+ constructor(pkg) {
70
+ super(AI_RUNTIME_MISSING_MESSAGE);
71
+ this.pkg = pkg;
72
+ this.name = 'AiRuntimeMissingError';
73
+ }
74
+ }
75
+ // ---- package location (no exports-map surprises) ----------------------------------------------
76
+ function readJson(path) {
77
+ try {
78
+ return JSON.parse(readFileSync(path, 'utf8'));
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /**
85
+ * The package's directory as an ESM `import '<spec>'` from `fromDir` would find it: the nearest
86
+ * `node_modules/<spec>` walking up the ancestors. ⚠ NOT `createRequire(...).resolve.paths()` — that
87
+ * list also carries NODE_PATH and the global folders (`~/.node_modules`, `<prefix>/lib/node`),
88
+ * which ESM ignores, so a package found there would be one the real import cannot see. (Under
89
+ * pnpm/vitest NODE_PATH points into the workspace store, which is how this was found.)
90
+ */
91
+ function findPackageDir(spec, fromDir) {
92
+ let dir = resolve(fromDir);
93
+ for (;;) {
94
+ const candidate = join(dir, 'node_modules', spec);
95
+ if (existsSync(join(candidate, 'package.json'))) {
96
+ try {
97
+ return realpathSync(candidate);
98
+ }
99
+ catch {
100
+ return candidate;
101
+ }
102
+ }
103
+ const parent = dirname(dir);
104
+ if (parent === dir)
105
+ return null;
106
+ dir = parent;
107
+ }
108
+ }
109
+ const ESM_CONDITIONS = new Set(['node', 'import', 'default']);
110
+ /** Walk one exports target with the ESM conditions, in the package's own key order. */
111
+ function pickTarget(target) {
112
+ if (typeof target === 'string')
113
+ return target;
114
+ if (Array.isArray(target)) {
115
+ for (const t of target) {
116
+ const hit = pickTarget(t);
117
+ if (hit)
118
+ return hit;
119
+ }
120
+ return null;
121
+ }
122
+ if (target && typeof target === 'object') {
123
+ for (const [key, value] of Object.entries(target)) {
124
+ if (!ESM_CONDITIONS.has(key))
125
+ continue;
126
+ const hit = pickTarget(value);
127
+ if (hit)
128
+ return hit;
129
+ }
130
+ }
131
+ return null;
132
+ }
133
+ /**
134
+ * The file an ESM `import '<pkg>'` (or `'<pkg>/<subpath>'`) resolves to, given the package
135
+ * directory. `subpath` is an exports key: '.' for the root, './server' for a subpath.
136
+ */
137
+ export function esmEntryOf(pkgDir, subpath = '.') {
138
+ const pkg = readJson(join(pkgDir, 'package.json'));
139
+ if (!pkg)
140
+ return null;
141
+ const exp = pkg.exports;
142
+ let rel = null;
143
+ if (exp !== undefined) {
144
+ const isSubpathMap = !!exp && typeof exp === 'object' && !Array.isArray(exp) &&
145
+ Object.keys(exp).some((k) => k.startsWith('.'));
146
+ if (isSubpathMap)
147
+ rel = pickTarget(exp[subpath]);
148
+ else if (subpath === '.')
149
+ rel = pickTarget(exp);
150
+ }
151
+ else if (subpath !== '.') {
152
+ rel = subpath;
153
+ }
154
+ if (subpath === '.') {
155
+ if (!rel && typeof pkg.module === 'string' && pkg.type === 'module')
156
+ rel = pkg.module;
157
+ if (!rel && typeof pkg.main === 'string')
158
+ rel = pkg.main;
159
+ if (!rel)
160
+ rel = 'index.js';
161
+ }
162
+ if (!rel)
163
+ return null;
164
+ const file = resolve(pkgDir, rel);
165
+ return existsSync(file) ? file : null;
166
+ }
167
+ function installedVersion(pkgDir) {
168
+ if (!pkgDir)
169
+ return null;
170
+ const v = readJson(join(pkgDir, 'package.json'))?.version;
171
+ return typeof v === 'string' ? v : null;
172
+ }
173
+ const HERE = dirname(fileURLToPath(import.meta.url));
174
+ /**
175
+ * Build a runtime over one data directory. The app uses the module-level default below; tests
176
+ * build their own over temp directories.
177
+ */
178
+ export function createAiRuntime(opts = {}) {
179
+ const env = opts.env ?? process.env;
180
+ const dataDir = opts.dataDir ?? resolveDataDir(env);
181
+ const runtimeDir = join(dataDir, 'ai-runtime');
182
+ const workspaceFrom = opts.workspaceFrom ?? HERE;
183
+ // src/ai/runtime.ts → apps/backend; dist/ai/runtime.js → the release root. Both hold the
184
+ // package.json the pins are read from.
185
+ const packageRoot = opts.packageRoot ?? resolve(HERE, '..', '..');
186
+ let workspaceHasSdk = null;
187
+ const workspaceReady = () => {
188
+ if (workspaceHasSdk === null) {
189
+ workspaceHasSdk =
190
+ findPackageDir(AGENT_SDK, workspaceFrom) !== null &&
191
+ findPackageDir('zod', workspaceFrom) !== null;
192
+ }
193
+ return workspaceHasSdk;
194
+ };
195
+ // ---- pins ----
196
+ function pins() {
197
+ const fromManifest = readJson(join(packageRoot, 'package.json'))?.[AI_RUNTIME_MANIFEST_FIELD];
198
+ const out = {};
199
+ for (const name of AI_RUNTIME_PACKAGES) {
200
+ let v = fromManifest && typeof fromManifest === 'object'
201
+ ? fromManifest[name]
202
+ : undefined;
203
+ // A dev checkout has no manifest field: pin to what the workspace itself installed.
204
+ if (v === undefined)
205
+ v = installedVersion(findPackageDir(name, workspaceFrom));
206
+ if (typeof v !== 'string' || !EXACT_VERSION.test(v))
207
+ return null;
208
+ out[name] = v;
209
+ }
210
+ return out;
211
+ }
212
+ /**
213
+ * 'ready' when every package is installed at its pin; 'stale' when installed at another;
214
+ * 'no_binary' when the JS is there but the Agent SDK's native `claude` program is not (and no
215
+ * LIMN_CLAUDE_PATH stands in for it).
216
+ */
217
+ function runtimeDirState() {
218
+ if (!existsSync(join(runtimeDir, 'package.json')))
219
+ return 'absent';
220
+ const want = pins();
221
+ let anyMissing = false;
222
+ let anyMismatch = false;
223
+ for (const name of AI_RUNTIME_PACKAGES) {
224
+ const dir = findPackageDir(name, runtimeDir);
225
+ const have = installedVersion(dir);
226
+ if (!dir || !have || !esmEntryOf(dir, ENTRY_SUBPATH[name]))
227
+ anyMissing = true;
228
+ else if (want && want[name] !== have)
229
+ anyMismatch = true;
230
+ }
231
+ if (anyMissing)
232
+ return 'absent';
233
+ if (!binaryOk(runtimeDir))
234
+ return 'no_binary';
235
+ return anyMismatch ? 'stale' : 'ready';
236
+ }
237
+ // ⚠ THE AGENT SDK'S NATIVE PROGRAM IS AN OPTIONAL DEPENDENCY (one package per platform), and npm
238
+ // treats a failed optional dependency as NON-FATAL: a fetch error or a libc/cpu mismatch still
239
+ // exits 0. An install made with LIMN_CLAUDE_PATH set skips it on purpose (`--omit=optional`), and
240
+ // that variable can be unset later. Either way the JS loads and every run then fails with
241
+ // "Claude Code executable not found" — so 'ready' requires the binary, unless the user's own
242
+ // `claude` is configured right now.
243
+ function binaryOk(fromDir) {
244
+ if (env.LIMN_CLAUDE_PATH?.trim())
245
+ return true;
246
+ const sdkDir = findPackageDir(AGENT_SDK, fromDir);
247
+ if (!sdkDir)
248
+ return false;
249
+ const exe = process.platform === 'win32' ? 'claude.exe' : 'claude';
250
+ // The same candidates, in any order, the SDK itself tries (linux: glibc or musl).
251
+ const base = `${AGENT_SDK}-${process.platform}-${process.arch}`;
252
+ const names = process.platform === 'linux' ? [base, `${base}-musl`] : [base];
253
+ return names.some((name) => {
254
+ const dir = findPackageDir(name, sdkDir);
255
+ return dir !== null && existsSync(join(dir, exe));
256
+ });
257
+ }
258
+ // ---- status ----
259
+ let inFlight = null;
260
+ const listeners = new Set();
261
+ let lastFailure = null;
262
+ function status() {
263
+ if (inFlight)
264
+ return { runtime: 'installing', runtimeMessage: null };
265
+ if (workspaceReady())
266
+ return { runtime: 'ready', runtimeMessage: null };
267
+ const state = runtimeDirState();
268
+ if (state === 'ready')
269
+ return { runtime: 'ready', runtimeMessage: null };
270
+ if (lastFailure)
271
+ return { runtime: 'failed', runtimeMessage: lastFailure };
272
+ if (state === 'no_binary') {
273
+ return {
274
+ runtime: 'absent',
275
+ runtimeMessage: 'The Claude Code program is missing from the AI download. Set up AI again, or set LIMN_CLAUDE_PATH to your own claude.',
276
+ };
277
+ }
278
+ if (state === 'stale') {
279
+ return {
280
+ runtime: 'absent',
281
+ runtimeMessage: 'Limn was updated. Set up AI again to get the matching version.',
282
+ };
283
+ }
284
+ return { runtime: 'absent', runtimeMessage: null };
285
+ }
286
+ // ---- loading ----
287
+ const loaded = new Map();
288
+ function load(spec) {
289
+ const hit = loaded.get(spec);
290
+ if (hit)
291
+ return hit;
292
+ const p = (async () => {
293
+ const order = workspaceReady()
294
+ ? ['workspace', 'runtime']
295
+ : ['runtime', 'workspace'];
296
+ for (const source of order) {
297
+ if (source === 'workspace') {
298
+ // Plain bare specifier: identical to the static imports this module replaced.
299
+ const dir = findPackageDir(spec, workspaceFrom);
300
+ if (dir && workspaceFrom === HERE)
301
+ return (await import(specifierFor(spec)));
302
+ const entry = dir ? esmEntryOf(dir, ENTRY_SUBPATH[spec]) : null;
303
+ if (entry)
304
+ return (await import(pathToFileURL(entry).href));
305
+ }
306
+ else if (['ready', 'stale'].includes(runtimeDirState())) {
307
+ const dir = findPackageDir(spec, runtimeDir);
308
+ const entry = dir ? esmEntryOf(dir, ENTRY_SUBPATH[spec]) : null;
309
+ if (entry)
310
+ return (await import(pathToFileURL(entry).href));
311
+ }
312
+ }
313
+ throw new AiRuntimeMissingError(spec);
314
+ })();
315
+ loaded.set(spec, p);
316
+ // A miss must not be remembered: the reader may set AI up a minute later.
317
+ p.catch(() => loaded.delete(spec));
318
+ return p;
319
+ }
320
+ // ---- install ----
321
+ function emit(p) {
322
+ for (const l of listeners) {
323
+ try {
324
+ l(p);
325
+ }
326
+ catch {
327
+ /* a listener's failure is its own */
328
+ }
329
+ }
330
+ }
331
+ function resolveNpm() {
332
+ // Run npm's own JS entry with THIS node, so there is no shell and no PATH guessing on Windows
333
+ // (where `npm` is a .cmd that cannot be spawned without one).
334
+ const viaEnv = env.npm_execpath;
335
+ if (viaEnv && /npm-cli\.c?js$/.test(viaEnv) && existsSync(viaEnv)) {
336
+ return { command: process.execPath, args: [viaEnv] };
337
+ }
338
+ const nodeDir = dirname(process.execPath);
339
+ for (const candidate of [
340
+ join(nodeDir, '..', 'lib', 'node_modules', 'npm', 'bin', 'npm-cli.js'),
341
+ join(nodeDir, 'node_modules', 'npm', 'bin', 'npm-cli.js'),
342
+ ]) {
343
+ if (existsSync(candidate))
344
+ return { command: process.execPath, args: [candidate] };
345
+ }
346
+ if (process.platform !== 'win32')
347
+ return { command: 'npm', args: [] };
348
+ return null;
349
+ }
350
+ function install(onProgress, installOpts = {}) {
351
+ if (onProgress)
352
+ listeners.add(onProgress);
353
+ const detach = () => {
354
+ if (onProgress)
355
+ listeners.delete(onProgress);
356
+ };
357
+ // Single-flight: a second press (or a second tab) joins the running download.
358
+ if (inFlight)
359
+ return inFlight.finally(detach);
360
+ inFlight = runInstall(installOpts).finally(() => {
361
+ inFlight = null;
362
+ });
363
+ return inFlight.finally(detach);
364
+ }
365
+ async function runInstall(installOpts) {
366
+ const failWith = (message) => {
367
+ lastFailure = message;
368
+ emit({ phase: 'failed', message });
369
+ return { ok: false, message };
370
+ };
371
+ const want = pins();
372
+ if (!want) {
373
+ return failWith('This copy of Limn does not list which AI runtime versions to download. Update Limn and try again.');
374
+ }
375
+ const npm = installOpts.npm ?? resolveNpm();
376
+ if (!npm)
377
+ return failWith('npm was not found. Install Node.js with npm, then try again.');
378
+ lastFailure = null;
379
+ emit({ phase: 'starting', message: 'Starting the one-time download (about 110 MB).' });
380
+ const staging = `${runtimeDir}.staging-${process.pid}-${Date.now()}`;
381
+ try {
382
+ mkdirSync(staging, { recursive: true });
383
+ writeFileSync(join(staging, 'package.json'), JSON.stringify({
384
+ name: 'limn-ai-runtime',
385
+ private: true,
386
+ description: 'The AI runtime Limn downloads on first use. Safe to delete; Limn can download it again.',
387
+ dependencies: want,
388
+ }, null, 2) + '\n');
389
+ }
390
+ catch (err) {
391
+ rmSync(staging, { recursive: true, force: true });
392
+ return failWith(`Could not write to ${dataDir}: ${err.message}`);
393
+ }
394
+ const args = [
395
+ ...npm.args,
396
+ 'install',
397
+ '--prefix',
398
+ staging,
399
+ '--no-audit',
400
+ '--no-fund',
401
+ '--no-update-notifier',
402
+ // None of the four needs an install script, so none gets to run one.
403
+ '--ignore-scripts',
404
+ '--no-package-lock',
405
+ ];
406
+ // Using your own `claude`: skip the SDK's bundled ~110 MB binary (its optional dependencies).
407
+ if (env.LIMN_CLAUDE_PATH?.trim())
408
+ args.push('--omit=optional');
409
+ const timeoutMs = installOpts.timeoutMs ?? 10 * 60_000;
410
+ const tail = [];
411
+ const exit = await new Promise((done) => {
412
+ let child;
413
+ try {
414
+ child = spawn(npm.command, args, {
415
+ cwd: staging,
416
+ env: { ...env, npm_config_loglevel: 'http' },
417
+ stdio: ['ignore', 'pipe', 'pipe'],
418
+ shell: false,
419
+ windowsHide: true,
420
+ });
421
+ }
422
+ catch (err) {
423
+ done({ code: null, error: err.message, timedOut: false });
424
+ return;
425
+ }
426
+ let timedOut = false;
427
+ const timer = setTimeout(() => {
428
+ timedOut = true;
429
+ child.kill('SIGTERM');
430
+ }, timeoutMs);
431
+ const onData = (buf) => {
432
+ for (const raw of buf.toString('utf8').split(/\r?\n/)) {
433
+ const line = raw.trim();
434
+ if (!line)
435
+ continue;
436
+ tail.push(line);
437
+ if (tail.length > 8)
438
+ tail.shift();
439
+ emit({ phase: 'downloading', message: line.slice(0, 200) });
440
+ }
441
+ };
442
+ child.stdout?.on('data', onData);
443
+ child.stderr?.on('data', onData);
444
+ child.on('error', (err) => {
445
+ clearTimeout(timer);
446
+ done({
447
+ code: null,
448
+ error: err.code === 'ENOENT'
449
+ ? 'npm was not found. Install Node.js with npm, then try again.'
450
+ : err.message,
451
+ timedOut: false,
452
+ });
453
+ });
454
+ child.on('close', (code) => {
455
+ clearTimeout(timer);
456
+ done({ code, error: null, timedOut });
457
+ });
458
+ });
459
+ if (exit.error || exit.timedOut || exit.code !== 0) {
460
+ rmSync(staging, { recursive: true, force: true });
461
+ if (exit.error)
462
+ return failWith(exit.error);
463
+ if (exit.timedOut) {
464
+ return failWith(`The download took longer than ${Math.round(timeoutMs / 60_000)} minutes and was stopped. Check your network and try again: limn ai install`);
465
+ }
466
+ const last = tail.filter((l) => /err/i.test(l)).slice(-2).join(' ') || tail.slice(-1)[0] || '';
467
+ return failWith(`The download failed (npm exit code ${exit.code}). Check your network or proxy and try again: limn ai install${last ? `. npm said: ${last.slice(0, 300)}` : ''}`);
468
+ }
469
+ emit({ phase: 'verifying', message: 'Checking the download.' });
470
+ for (const name of AI_RUNTIME_PACKAGES) {
471
+ const dir = findPackageDir(name, staging);
472
+ const have = installedVersion(dir);
473
+ if (!dir || have !== want[name] || !esmEntryOf(dir, ENTRY_SUBPATH[name])) {
474
+ rmSync(staging, { recursive: true, force: true });
475
+ return failWith(`The download finished but ${name} ${want[name]} is missing from it. Try again: limn ai install`);
476
+ }
477
+ }
478
+ if (!binaryOk(staging)) {
479
+ rmSync(staging, { recursive: true, force: true });
480
+ return failWith(`The download finished but the Claude Code program for ${process.platform}-${process.arch} is missing from it. Try again: limn ai install. Or set LIMN_CLAUDE_PATH to your own claude.`);
481
+ }
482
+ // Swap in place: the previous runtime (if any) moves aside first, so a failure here leaves
483
+ // either the old one or the new one — never half of each.
484
+ const old = `${runtimeDir}.old-${process.pid}-${Date.now()}`;
485
+ try {
486
+ if (existsSync(runtimeDir))
487
+ renameSync(runtimeDir, old);
488
+ renameSync(staging, runtimeDir);
489
+ }
490
+ catch (err) {
491
+ if (!existsSync(runtimeDir) && existsSync(old))
492
+ renameSync(old, runtimeDir);
493
+ rmSync(staging, { recursive: true, force: true });
494
+ return failWith(`Could not finish setting up AI in ${runtimeDir}: ${err.message}`);
495
+ }
496
+ rmSync(old, { recursive: true, force: true });
497
+ lastFailure = null;
498
+ emit({ phase: 'done', message: 'AI is set up.' });
499
+ return { ok: true };
500
+ }
501
+ return { runtimeDir, pins, status, load, install };
502
+ }
503
+ // ---- the app's runtime ----------------------------------------------------------------------
504
+ const runtime = createAiRuntime();
505
+ /** The AI runtime's state, for `/api/me` (`ai.runtime` / `ai.runtimeMessage`). Cheap and sync. */
506
+ export function getAiRuntimeStatus() {
507
+ return runtime.status();
508
+ }
509
+ /** Download the pinned SDKs into `<dataDir>/ai-runtime`. Single-flight; never throws. */
510
+ export function installAiRuntime(onProgress, opts) {
511
+ return runtime.install(onProgress, opts);
512
+ }
513
+ /** The exact versions this copy of Limn downloads, or null when it cannot tell. */
514
+ export function aiRuntimePins() {
515
+ return runtime.pins();
516
+ }
517
+ export const loadAgentSdk = () => runtime.load('@anthropic-ai/claude-agent-sdk');
518
+ export const loadAnthropicSdk = () => runtime.load('@anthropic-ai/sdk');
519
+ export const loadMcpSdk = () => runtime.load('@modelcontextprotocol/sdk');
520
+ export const loadZodModule = () => runtime.load('zod');
521
+ /** zod's `z` namespace, from the SAME install the Agent SDK uses. */
522
+ export const loadZod = async () => (await loadZodModule()).z;
523
+ /**
524
+ * `LIMN_CLAUDE_PATH` → the Agent SDK's `pathToClaudeCodeExecutable`: run the user's own installed
525
+ * `claude` instead of the SDK's bundled binary (and `limn ai install` then skips downloading it).
526
+ * Opt-in only — the bundled binary is version-locked to the SDK; a different `claude` may not be.
527
+ * Spread into every `query()` options object.
528
+ */
529
+ export function claudeExecutableOptions(env = process.env) {
530
+ const raw = env.LIMN_CLAUDE_PATH?.trim();
531
+ if (!raw)
532
+ return {};
533
+ return { pathToClaudeCodeExecutable: isAbsolute(raw) ? raw : resolve(raw) };
534
+ }
535
+ //# sourceMappingURL=runtime.js.map