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.
- package/LICENSE +110 -0
- package/README.md +133 -0
- package/dist/ai/runtime.js +535 -0
- package/dist/api/plugins/auth.js +169 -0
- package/dist/api/plugins/error-handler.js +114 -0
- package/dist/api/plugins/rate-limit.js +855 -0
- package/dist/api/plugins/security.js +321 -0
- package/dist/api/routes/activity.js +103 -0
- package/dist/api/routes/ai-runtime.js +67 -0
- package/dist/api/routes/auth.js +194 -0
- package/dist/api/routes/billing.js +209 -0
- package/dist/api/routes/bot-triage.js +1289 -0
- package/dist/api/routes/conflicts.js +730 -0
- package/dist/api/routes/contact.js +318 -0
- package/dist/api/routes/daily-brief.js +58 -0
- package/dist/api/routes/flow.js +71 -0
- package/dist/api/routes/health.js +7 -0
- package/dist/api/routes/insights.js +200 -0
- package/dist/api/routes/me.js +458 -0
- package/dist/api/routes/mentions.js +22 -0
- package/dist/api/routes/mergers.js +8 -0
- package/dist/api/routes/ml-labels.js +109 -0
- package/dist/api/routes/open-prs.js +35 -0
- package/dist/api/routes/prs.js +1633 -0
- package/dist/api/routes/reactions.js +217 -0
- package/dist/api/routes/repos.js +563 -0
- package/dist/api/routes/search.js +41 -0
- package/dist/api/routes/threads.js +118 -0
- package/dist/api/routes/timeline.js +150 -0
- package/dist/api/routes/users.js +67 -0
- package/dist/api/routes/webhooks.js +181 -0
- package/dist/api/routes/workspaces.js +322 -0
- package/dist/app.js +274 -0
- package/dist/ascii.js +19 -0
- package/dist/auth/account.js +482 -0
- package/dist/auth/crypto.js +46 -0
- package/dist/bot-triage/resolve.js +59 -0
- package/dist/cli.js +388 -0
- package/dist/coding/activity.js +80 -0
- package/dist/coding/agent.js +288 -0
- package/dist/coding/ai-fix/auto-fix.js +212 -0
- package/dist/coding/ai-fix/manager.js +370 -0
- package/dist/coding/ai-fix/persist.js +213 -0
- package/dist/coding/ai-fix/pr-context.js +58 -0
- package/dist/coding/ai-fix/prompts.js +138 -0
- package/dist/coding/ai-fix/review-seed.js +357 -0
- package/dist/coding/ai-fix/routes.js +425 -0
- package/dist/coding/git-ops.js +321 -0
- package/dist/coding/git.js +184 -0
- package/dist/coding/merge.js +262 -0
- package/dist/coding/safe-path.js +75 -0
- package/dist/coding/schema.js +43 -0
- package/dist/config.js +648 -0
- package/dist/conflict/diff.js +304 -0
- package/dist/conflict/git.js +118 -0
- package/dist/conflict/hash.js +73 -0
- package/dist/conflict/janitor.js +218 -0
- package/dist/conflict/land.js +675 -0
- package/dist/conflict/model-types.js +2 -0
- package/dist/conflict/model.js +1081 -0
- package/dist/conflict/parse.js +142 -0
- package/dist/conflict/seam.js +136 -0
- package/dist/conflict/session.js +532 -0
- package/dist/conflict/suggestion.js +398 -0
- package/dist/conflict/tokens.js +72 -0
- package/dist/data-dir.js +112 -0
- package/dist/db/actor-lanes.js +213 -0
- package/dist/db/automation-ids.js +97 -0
- package/dist/db/automation-output.js +349 -0
- package/dist/db/blast-radius-query.js +36 -0
- package/dist/db/blast-radius.js +226 -0
- package/dist/db/bot-dormancy.js +115 -0
- package/dist/db/bot-overlap.js +579 -0
- package/dist/db/bot-volume.js +722 -0
- package/dist/db/bot-window.js +35 -0
- package/dist/db/branch-queries.js +310 -0
- package/dist/db/change-shape.js +243 -0
- package/dist/db/changepoint.js +75 -0
- package/dist/db/cleanup.js +36 -0
- package/dist/db/client.js +102 -0
- package/dist/db/code-loc.js +388 -0
- package/dist/db/credits.js +79 -0
- package/dist/db/daily-brief.js +406 -0
- package/dist/db/dependency-cards.js +145 -0
- package/dist/db/erase-account.js +255 -0
- package/dist/db/export-account.js +189 -0
- package/dist/db/failing-checks.js +124 -0
- package/dist/db/file-coupling.js +197 -0
- package/dist/db/flow-detail.js +661 -0
- package/dist/db/flow-pointers.js +197 -0
- package/dist/db/flow-settings.js +67 -0
- package/dist/db/forecast.js +114 -0
- package/dist/db/line-overlap.js +81 -0
- package/dist/db/merge-queue.js +189 -0
- package/dist/db/migrate.js +17 -0
- package/dist/db/migrations/0000_purple_ben_grimm.sql +155 -0
- package/dist/db/migrations/0001_colorful_ozymandias.sql +31 -0
- package/dist/db/migrations/0002_famous_sersi.sql +9 -0
- package/dist/db/migrations/0003_clever_shinobi_shaw.sql +3 -0
- package/dist/db/migrations/0004_pale_scalphunter.sql +1 -0
- package/dist/db/migrations/0005_daffy_guardian.sql +2 -0
- package/dist/db/migrations/0006_cute_violations.sql +45 -0
- package/dist/db/migrations/0007_dark_anthem.sql +2 -0
- package/dist/db/migrations/0008_multitenant_accounts.sql +44 -0
- package/dist/db/migrations/0009_lean_bodies.sql +13 -0
- package/dist/db/migrations/0010_lean_bodies_nullable.sql +56 -0
- package/dist/db/migrations/0011_pr_diff_size.sql +10 -0
- package/dist/db/migrations/0012_findings_included_default.sql +7 -0
- package/dist/db/migrations/0013_review_routing.sql +9 -0
- package/dist/db/migrations/0014_account_last_active.sql +6 -0
- package/dist/db/migrations/0015_repos_inbox_watch.sql +9 -0
- package/dist/db/migrations/0016_repo_viewer_permission.sql +7 -0
- package/dist/db/migrations/0017_account_display_name.sql +7 -0
- package/dist/db/migrations/0018_finding_comment_kind.sql +7 -0
- package/dist/db/migrations/0019_finding_file_in_diff.sql +7 -0
- package/dist/db/migrations/0020_claude_review_cost_telemetry.sql +11 -0
- package/dist/db/migrations/0021_account_feed_last_seen.sql +7 -0
- package/dist/db/migrations/0022_ci_status_events.sql +21 -0
- package/dist/db/migrations/0023_ai_usage.sql +21 -0
- package/dist/db/migrations/0024_pr_head_ref_name.sql +6 -0
- package/dist/db/migrations/0025_account_billing.sql +7 -0
- package/dist/db/migrations/0026_ai_credit_allowance.sql +5 -0
- package/dist/db/migrations/0027_user_github_type.sql +6 -0
- package/dist/db/migrations/0028_bot_review_classification.sql +21 -0
- package/dist/db/migrations/0029_bot_mute_rules.sql +19 -0
- package/dist/db/migrations/0030_teams.sql +32 -0
- package/dist/db/migrations/0031_events_pr_idx.sql +7 -0
- package/dist/db/migrations/0032_benchmark_contributions.sql +29 -0
- package/dist/db/migrations/0033_thread_addressed_confidence.sql +9 -0
- package/dist/db/migrations/0034_thread_resolved_at.sql +6 -0
- package/dist/db/migrations/0035_pr_first_review_requested_at.sql +5 -0
- package/dist/db/migrations/0036_search_index.sql +25 -0
- package/dist/db/migrations/0037_author_indexes.sql +15 -0
- package/dist/db/migrations/0038_auto_merge_requests.sql +37 -0
- package/dist/db/migrations/0039_branch_status.sql +44 -0
- package/dist/db/migrations/0040_pr_review_decision.sql +10 -0
- package/dist/db/migrations/0041_branch_commit_detail.sql +38 -0
- package/dist/db/migrations/0042_repo_reviewers.sql +176 -0
- package/dist/db/migrations/0043_account_reviewers.sql +131 -0
- package/dist/db/migrations/0044_workspaces.sql +138 -0
- package/dist/db/migrations/0045_workspace_reviewers.sql +180 -0
- package/dist/db/migrations/0046_drop_inbox_watch.sql +25 -0
- package/dist/db/migrations/0047_ml_comment_labels.sql +40 -0
- package/dist/db/migrations/0048_ml_vendor_severity.sql +31 -0
- package/dist/db/migrations/0049_seat_pricing_repo_description.sql +21 -0
- package/dist/db/migrations/0050_users_app_slug.sql +12 -0
- package/dist/db/migrations/0051_auto_merge_queue.sql +17 -0
- package/dist/db/migrations/0052_trunk_ci_status_events.sql +43 -0
- package/dist/db/migrations/0053_automation_roles.sql +79 -0
- package/dist/db/migrations/0054_automation_vendor_kinds.sql +514 -0
- package/dist/db/migrations/0055_auto_merge_phase.sql +12 -0
- package/dist/db/migrations/0056_pr_mentions.sql +22 -0
- package/dist/db/migrations/0057_large_pr_threshold.sql +14 -0
- package/dist/db/migrations/0058_pending_mute.sql +53 -0
- package/dist/db/migrations/0059_merge_queue_state.sql +33 -0
- package/dist/db/migrations/0060_drop_my_turn_dismissals.sql +33 -0
- package/dist/db/migrations/0061_auto_merge_durable_anchors.sql +27 -0
- package/dist/db/migrations/0062_blast_radius_config.sql +21 -0
- package/dist/db/migrations/0063_repo_file_coupling.sql +33 -0
- package/dist/db/migrations/0064_pr_content_kind.sql +21 -0
- package/dist/db/migrations/0065_workspace_flow_settings.sql +13 -0
- package/dist/db/migrations/0066_review_request_events.sql +31 -0
- package/dist/db/migrations/0067_pr_security_signals.sql +86 -0
- package/dist/db/migrations/0068_my_turn_settings.sql +18 -0
- package/dist/db/migrations/0069_my_turn_dismissals.sql +41 -0
- package/dist/db/migrations/0070_claude_review_follow_up_ticket.sql +22 -0
- package/dist/db/migrations/0071_claude_review_trigger.sql +11 -0
- package/dist/db/migrations/0072_commit_message_headline.sql +13 -0
- package/dist/db/migrations/0073_claude_review_chat.sql +31 -0
- package/dist/db/migrations/0074_adopt_agentic_tables.sql +98 -0
- package/dist/db/migrations/0075_review_lens_drop_learnings.sql +15 -0
- package/dist/db/migrations/0076_claude_review_threads.sql +17 -0
- package/dist/db/migrations/0077_claude_review_ci_failures.sql +10 -0
- package/dist/db/migrations/0078_ai_fix_change_report.sql +17 -0
- package/dist/db/migrations/0079_claude_finding_story.sql +16 -0
- package/dist/db/migrations/meta/0000_snapshot.json +1116 -0
- package/dist/db/migrations/meta/0001_snapshot.json +1321 -0
- package/dist/db/migrations/meta/0002_snapshot.json +1375 -0
- package/dist/db/migrations/meta/0003_snapshot.json +1396 -0
- package/dist/db/migrations/meta/0004_snapshot.json +1416 -0
- package/dist/db/migrations/meta/0005_snapshot.json +1430 -0
- package/dist/db/migrations/meta/0006_snapshot.json +1745 -0
- package/dist/db/migrations/meta/0007_snapshot.json +1759 -0
- package/dist/db/migrations/meta/_journal.json +566 -0
- package/dist/db/migrations-pg/0000_tired_alex_power.sql +268 -0
- package/dist/db/migrations-pg/0001_dizzy_mockingbird.sql +3 -0
- package/dist/db/migrations-pg/0002_glorious_ikaris.sql +4 -0
- package/dist/db/migrations-pg/0003_melted_secret_warriors.sql +2 -0
- package/dist/db/migrations-pg/0004_numerous_triton.sql +1 -0
- package/dist/db/migrations-pg/0005_thick_blockbuster.sql +2 -0
- package/dist/db/migrations-pg/0006_majestic_dracula.sql +1 -0
- package/dist/db/migrations-pg/0007_same_human_torch.sql +2 -0
- package/dist/db/migrations-pg/0008_slimy_toxin.sql +1 -0
- package/dist/db/migrations-pg/0009_fuzzy_jubilee.sql +4 -0
- package/dist/db/migrations-pg/0010_late_hammerhead.sql +1 -0
- package/dist/db/migrations-pg/0011_happy_leech.sql +16 -0
- package/dist/db/migrations-pg/0012_woozy_captain_midlands.sql +16 -0
- package/dist/db/migrations-pg/0013_yielding_nicolaos.sql +1 -0
- package/dist/db/migrations-pg/0014_wakeful_bill_hollister.sql +2 -0
- package/dist/db/migrations-pg/0015_aspiring_black_tom.sql +1 -0
- package/dist/db/migrations-pg/0016_cute_ghost_rider.sql +30 -0
- package/dist/db/migrations-pg/0017_far_changeling.sql +24 -0
- package/dist/db/migrations-pg/0018_fancy_miss_america.sql +1 -0
- package/dist/db/migrations-pg/0019_benchmark_contributions.sql +22 -0
- package/dist/db/migrations-pg/0020_majestic_edwin_jarvis.sql +3 -0
- package/dist/db/migrations-pg/0021_green_salo.sql +1 -0
- package/dist/db/migrations-pg/0022_wise_baron_strucker.sql +1 -0
- package/dist/db/migrations-pg/0023_search_index.sql +19 -0
- package/dist/db/migrations-pg/0024_author_indexes.sql +9 -0
- package/dist/db/migrations-pg/0025_auto_merge_requests.sql +26 -0
- package/dist/db/migrations-pg/0026_branch_status.sql +32 -0
- package/dist/db/migrations-pg/0027_pr_review_decision.sql +5 -0
- package/dist/db/migrations-pg/0028_branch_commit_detail.sql +9 -0
- package/dist/db/migrations-pg/0029_repo_reviewers.sql +127 -0
- package/dist/db/migrations-pg/0030_account_reviewers.sql +77 -0
- package/dist/db/migrations-pg/0031_workspaces.sql +131 -0
- package/dist/db/migrations-pg/0032_workspace_reviewers.sql +165 -0
- package/dist/db/migrations-pg/0033_drop_inbox_watch.sql +14 -0
- package/dist/db/migrations-pg/0034_ml_comment_labels.sql +33 -0
- package/dist/db/migrations-pg/0035_ml_vendor_severity.sql +16 -0
- package/dist/db/migrations-pg/0036_seat_pricing_repo_description.sql +12 -0
- package/dist/db/migrations-pg/0037_users_app_slug.sql +9 -0
- package/dist/db/migrations-pg/0038_auto_merge_queue.sql +10 -0
- package/dist/db/migrations-pg/0039_trunk_ci_status_events.sql +27 -0
- package/dist/db/migrations-pg/0040_automation_roles.sql +63 -0
- package/dist/db/migrations-pg/0041_automation_vendor_kinds.sql +488 -0
- package/dist/db/migrations-pg/0042_auto_merge_phase.sql +8 -0
- package/dist/db/migrations-pg/0043_pr_mentions.sql +19 -0
- package/dist/db/migrations-pg/0044_large_pr_threshold.sql +6 -0
- package/dist/db/migrations-pg/0045_pending_mute.sql +26 -0
- package/dist/db/migrations-pg/0046_merge_queue_state.sql +18 -0
- package/dist/db/migrations-pg/0047_drop_my_turn_dismissals.sql +21 -0
- package/dist/db/migrations-pg/0048_auto_merge_durable_anchors.sql +27 -0
- package/dist/db/migrations-pg/0049_blast_radius_config.sql +8 -0
- package/dist/db/migrations-pg/0050_repo_file_coupling.sql +13 -0
- package/dist/db/migrations-pg/0051_pr_content_kind.sql +6 -0
- package/dist/db/migrations-pg/0052_workspace_flow_settings.sql +4 -0
- package/dist/db/migrations-pg/0053_review_request_events.sql +18 -0
- package/dist/db/migrations-pg/0054_pr_security_signals.sql +59 -0
- package/dist/db/migrations-pg/0055_my_turn_settings.sql +6 -0
- package/dist/db/migrations-pg/0056_my_turn_dismissals.sql +21 -0
- package/dist/db/migrations-pg/0057_claude_review_follow_up_ticket.sql +8 -0
- package/dist/db/migrations-pg/0058_claude_review_trigger.sql +4 -0
- package/dist/db/migrations-pg/0059_commit_message_headline.sql +3 -0
- package/dist/db/migrations-pg/0060_claude_review_chat.sql +24 -0
- package/dist/db/migrations-pg/0061_adopt_agentic_tables.sql +79 -0
- package/dist/db/migrations-pg/0062_review_lens_drop_learnings.sql +5 -0
- package/dist/db/migrations-pg/0063_claude_review_threads.sql +7 -0
- package/dist/db/migrations-pg/0064_claude_review_ci_failures.sql +4 -0
- package/dist/db/migrations-pg/0065_ai_fix_change_report.sql +9 -0
- package/dist/db/migrations-pg/0066_claude_finding_story.sql +7 -0
- package/dist/db/migrations-pg/meta/0000_snapshot.json +2137 -0
- package/dist/db/migrations-pg/meta/0001_snapshot.json +2143 -0
- package/dist/db/migrations-pg/meta/0002_snapshot.json +2170 -0
- package/dist/db/migrations-pg/meta/0003_snapshot.json +2182 -0
- package/dist/db/migrations-pg/meta/0004_snapshot.json +2188 -0
- package/dist/db/migrations-pg/meta/0005_snapshot.json +2201 -0
- package/dist/db/migrations-pg/meta/0006_snapshot.json +2207 -0
- package/dist/db/migrations-pg/meta/0007_snapshot.json +2219 -0
- package/dist/db/migrations-pg/meta/0008_snapshot.json +2226 -0
- package/dist/db/migrations-pg/meta/0009_snapshot.json +2250 -0
- package/dist/db/migrations-pg/meta/0010_snapshot.json +2256 -0
- package/dist/db/migrations-pg/meta/0011_snapshot.json +2412 -0
- package/dist/db/migrations-pg/meta/0012_snapshot.json +2528 -0
- package/dist/db/migrations-pg/meta/0013_snapshot.json +2534 -0
- package/dist/db/migrations-pg/meta/0014_snapshot.json +2547 -0
- package/dist/db/migrations-pg/meta/0015_snapshot.json +2553 -0
- package/dist/db/migrations-pg/meta/0016_snapshot.json +2774 -0
- package/dist/db/migrations-pg/meta/0017_snapshot.json +2999 -0
- package/dist/db/migrations-pg/meta/0018_snapshot.json +3020 -0
- package/dist/db/migrations-pg/meta/0020_snapshot.json +3228 -0
- package/dist/db/migrations-pg/meta/0021_snapshot.json +3234 -0
- package/dist/db/migrations-pg/meta/0022_snapshot.json +3240 -0
- package/dist/db/migrations-pg/meta/_journal.json +475 -0
- package/dist/db/ml-labels.js +1666 -0
- package/dist/db/my-turn-dismissals.js +343 -0
- package/dist/db/my-turn-settings.js +34 -0
- package/dist/db/pending-classify.js +46 -0
- package/dist/db/pending-mute.js +171 -0
- package/dist/db/pending-tabs.js +238 -0
- package/dist/db/period-metrics.js +820 -0
- package/dist/db/person-period.js +763 -0
- package/dist/db/pr-intervals.js +930 -0
- package/dist/db/pr-liveness.js +240 -0
- package/dist/db/pr-mentions.js +279 -0
- package/dist/db/pr-merge-queue-stamp.js +91 -0
- package/dist/db/queries.js +14631 -0
- package/dist/db/reaction-targets.js +94 -0
- package/dist/db/repo-activity.js +171 -0
- package/dist/db/retention.js +138 -0
- package/dist/db/review-threads-for-review.js +240 -0
- package/dist/db/run-migrations.js +24 -0
- package/dist/db/schema.pg.js +1220 -0
- package/dist/db/schema.sqlite.js +1801 -0
- package/dist/db/search.js +317 -0
- package/dist/db/security-alerts.js +218 -0
- package/dist/db/synthesis-input.js +565 -0
- package/dist/db/triage.js +320 -0
- package/dist/db/usage.js +63 -0
- package/dist/db/work-plan.js +1086 -0
- package/dist/db/working-hours.js +161 -0
- package/dist/feed/my-turn.js +122 -0
- package/dist/github/actions-logs.js +397 -0
- package/dist/github/auth.js +86 -0
- package/dist/github/branch-queries.js +229 -0
- package/dist/github/client.js +454 -0
- package/dist/github/codeowners.js +221 -0
- package/dist/github/commit-checks.js +105 -0
- package/dist/github/compare.js +110 -0
- package/dist/github/diff-anchor.js +227 -0
- package/dist/github/mutations.js +706 -0
- package/dist/github/pr-file-content.js +254 -0
- package/dist/github/pr-liveness.js +161 -0
- package/dist/github/queries.js +875 -0
- package/dist/github/rate-budget.js +129 -0
- package/dist/github/reactions.js +161 -0
- package/dist/github/reviewer-suggest.js +104 -0
- package/dist/github/team-reviewers.js +96 -0
- package/dist/index.js +184 -0
- package/dist/merge/auto-merge-runner.js +960 -0
- package/dist/ml/severity-client.js +218 -0
- package/dist/pr/detail-enricher.js +26 -0
- package/dist/pro/bind.js +302 -0
- package/dist/pro/contract.js +37 -0
- package/dist/pro/migrate.js +64 -0
- package/dist/review/agent-context.js +127 -0
- package/dist/review/agent.js +385 -0
- package/dist/review/agentic.js +59 -0
- package/dist/review/auth.js +94 -0
- package/dist/review/chat-agent.js +179 -0
- package/dist/review/claude-review/ai-ready.js +16 -0
- package/dist/review/claude-review/auto-settings.js +139 -0
- package/dist/review/claude-review/auto.js +244 -0
- package/dist/review/claude-review/chat.js +489 -0
- package/dist/review/claude-review/ci-failures.js +350 -0
- package/dist/review/claude-review/follow-up.js +310 -0
- package/dist/review/claude-review/manager.js +686 -0
- package/dist/review/claude-review/persist.js +996 -0
- package/dist/review/claude-review/prompts.js +357 -0
- package/dist/review/claude-review/routes.js +518 -0
- package/dist/review/claude-review/routing.js +61 -0
- package/dist/review/claude-review/specialists.js +190 -0
- package/dist/review/claude-review/threads.js +265 -0
- package/dist/review/claude-review/ticket.js +282 -0
- package/dist/review/clone-hygiene.js +380 -0
- package/dist/review/clone-manager.js +367 -0
- package/dist/review/llm.js +113 -0
- package/dist/review/model-options.js +48 -0
- package/dist/review/plugin-providers.js +12 -0
- package/dist/review/post-review.js +216 -0
- package/dist/review/post-seam.js +161 -0
- package/dist/review/prepare.js +168 -0
- package/dist/review/pricing.js +17 -0
- package/dist/review/schema.js +156 -0
- package/dist/review/submit-map.js +41 -0
- package/dist/review/usage.js +90 -0
- package/dist/shared/claude-review.js +335 -0
- package/dist/shared/conflict-fold.js +151 -0
- package/dist/shared/conflicts.js +30 -0
- package/dist/shared/flow-settings.js +191 -0
- package/dist/shared/index.js +8 -0
- package/dist/shared/my-turn-settings.js +238 -0
- package/dist/shared/pending-rules.js +281 -0
- package/dist/shared/product.js +54 -0
- package/dist/shared/types.js +896 -0
- package/dist/status.js +386 -0
- package/dist/sync/adaptive.js +185 -0
- package/dist/sync/app-attribution.js +66 -0
- package/dist/sync/auth-notices.js +36 -0
- package/dist/sync/backfill-ci-history.js +315 -0
- package/dist/sync/backfill-commit-headlines.js +136 -0
- package/dist/sync/backfill-pr-security.js +175 -0
- package/dist/sync/backfill-review-requests.js +97 -0
- package/dist/sync/benchmark-rollup.js +50 -0
- package/dist/sync/bot-detection.js +309 -0
- package/dist/sync/bot-resolution-markers.js +30 -0
- package/dist/sync/branch-status.js +634 -0
- package/dist/sync/classify-change-shape.js +150 -0
- package/dist/sync/commit-files.js +74 -0
- package/dist/sync/derive-thread-state.js +99 -0
- package/dist/sync/drain-review-threads.js +212 -0
- package/dist/sync/hydrate-detail.js +482 -0
- package/dist/sync/mention-scan.js +89 -0
- package/dist/sync/ml-enrichment.js +478 -0
- package/dist/sync/pr-change-signal.js +96 -0
- package/dist/sync/pr-liveness-sweep.js +139 -0
- package/dist/sync/pr-settle.js +381 -0
- package/dist/sync/refresh-pr.js +163 -0
- package/dist/sync/reparse-vendor-badges.js +495 -0
- package/dist/sync/resync-after-write.js +280 -0
- package/dist/sync/review-fingerprint.js +117 -0
- package/dist/sync/reviewer-behavior.js +103 -0
- package/dist/sync/reviewer-classify.js +503 -0
- package/dist/sync/routing-files.js +97 -0
- package/dist/sync/scheduled-jobs.js +12 -0
- package/dist/sync/scheduler.js +159 -0
- package/dist/sync/security-detect.js +521 -0
- package/dist/sync/sync-manager.js +750 -0
- package/dist/sync/sync-one-pr.js +205 -0
- package/dist/sync/sync-repo.js +400 -0
- package/dist/sync/unsettled-prs.js +506 -0
- package/dist/sync/upsert.js +1190 -0
- package/package.json +56 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/AiFixTab-CQP6PT5u.js +2 -0
- package/public/assets/ClaudeReviewTab-DQJ-58ZA.js +8 -0
- package/public/assets/PrBotBehaviourTab-CCnFrWbC.js +1 -0
- package/public/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
- package/public/assets/highlight-BEHUn5zE.css +10 -0
- package/public/assets/highlight-CIIZLa6Z.js +12 -0
- package/public/assets/index-BDnYs6zg.js +73 -0
- package/public/assets/index-mCuI--RC.css +1 -0
- package/public/assets/markdown-I_JLLVAZ.js +32 -0
- package/public/assets/perfProbe-Da13EhTE.js +1 -0
- package/public/assets/tanstack-5qsxxaLe.js +17 -0
- package/public/assets/vis-C3BCkP5U.js +1280 -0
- package/public/assets/vis-ziCVehMy.css +1 -0
- package/public/favicon-16.png +0 -0
- package/public/favicon-32.png +0 -0
- package/public/favicon-48.png +0 -0
- package/public/favicon.svg +5 -0
- package/public/index.html +41 -0
- package/public-landing/apple-touch-icon.png +0 -0
- package/public-landing/assets/archivo-latin-500-BWmvx2Mg.woff2 +0 -0
- package/public-landing/assets/archivo-latin-600-BN-JkNrl.woff2 +0 -0
- package/public-landing/assets/index-CA3eEGXO.js +40 -0
- package/public-landing/assets/index-naQrQ6Fe.css +1 -0
- package/public-landing/assets/jetbrains-mono-latin-400-RruCX7RW.woff2 +0 -0
- package/public-landing/assets/newsreader-latin-400-DExrRRrN.woff2 +0 -0
- package/public-landing/assets/newsreader-latin-400-italic-b0I-AwbV.woff2 +0 -0
- package/public-landing/bots/index.html +156 -0
- package/public-landing/contact/index.html +156 -0
- package/public-landing/cookies/index.html +156 -0
- package/public-landing/demo/limn-walkthrough-poster.jpg +0 -0
- package/public-landing/demo/limn-walkthrough.cues.json +44 -0
- package/public-landing/demo/limn-walkthrough.mp4 +0 -0
- package/public-landing/favicon-16.png +0 -0
- package/public-landing/favicon-32.png +0 -0
- package/public-landing/favicon-48.png +0 -0
- package/public-landing/favicon.svg +5 -0
- package/public-landing/features/index.html +156 -0
- package/public-landing/for-developers/index.html +156 -0
- package/public-landing/for-managers/index.html +156 -0
- package/public-landing/how-it-works/index.html +156 -0
- package/public-landing/how-we-measure/index.html +156 -0
- package/public-landing/icon-192.png +0 -0
- package/public-landing/icon-512.png +0 -0
- package/public-landing/index.html +156 -0
- package/public-landing/insights/index.html +156 -0
- package/public-landing/og-image.png +0 -0
- package/public-landing/pricing/index.html +156 -0
- package/public-landing/privacy/index.html +156 -0
- package/public-landing/pro/index.html +156 -0
- package/public-landing/reviews/index.html +156 -0
- package/public-landing/robots.txt +8 -0
- package/public-landing/shots/benchmark.png +0 -0
- package/public-landing/shots/bot-roi.png +0 -0
- package/public-landing/shots/bot-settings.png +0 -0
- package/public-landing/shots/chronology.png +0 -0
- package/public-landing/shots/feed.png +0 -0
- package/public-landing/shots/flow-metrics.png +0 -0
- package/public-landing/shots/free-reports.png +0 -0
- package/public-landing/shots/pending-board.png +0 -0
- package/public-landing/shots/pending-card.png +0 -0
- package/public-landing/shots/period-report.png +0 -0
- package/public-landing/shots/pr-detail.png +0 -0
- package/public-landing/shots/pr-threads.png +0 -0
- package/public-landing/shots/reach.png +0 -0
- package/public-landing/shots/repo-rows.png +0 -0
- package/public-landing/shots/severity-findings.png +0 -0
- package/public-landing/shots/severity-strip.png +0 -0
- package/public-landing/site.webmanifest +14 -0
- package/public-landing/sitemap.xml +51 -0
- package/public-landing/terms/index.html +156 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { inArray } from 'drizzle-orm';
|
|
2
|
+
import { db, schema } from './client.js';
|
|
3
|
+
import { getAccountUserId } from '../auth/account.js';
|
|
4
|
+
const { reviewRequests, prViews, events, reviews } = schema;
|
|
5
|
+
// The merge-state values that mean "GitHub would let this land right now". Mirrors the
|
|
6
|
+
// `canMerge` half of the frontend's `mergeVerdict()` resolver (lib/ui.ts) — the two must
|
|
7
|
+
// agree, or a PR reads "approved & ready" in the triage queue and "blocked" on the PR itself.
|
|
8
|
+
export const READY_MERGE_STATES = new Set([
|
|
9
|
+
'clean',
|
|
10
|
+
'has_hooks',
|
|
11
|
+
'unstable',
|
|
12
|
+
]);
|
|
13
|
+
function emptyNew() {
|
|
14
|
+
return { commits: 0, comments: 0, reviews: 0 };
|
|
15
|
+
}
|
|
16
|
+
/** Tier of a review state for the standing pick — LOWER WINS, regardless of timestamp.
|
|
17
|
+
*
|
|
18
|
+
* Tier 0 is the VERDICT tier and is the ONLY tier the approval count reads, which is what makes
|
|
19
|
+
* this rule a strict superset of its predecessor (below). Tiers 1 and 2 exist so a reviewer who
|
|
20
|
+
* never filed a verdict still appears on the card with an honest label instead of vanishing.
|
|
21
|
+
* A dismissal outranks a bare comment: it is a verdict-shaped fact about the review, and a
|
|
22
|
+
* reviewer who was dismissed and then commented has not gone back to saying nothing. */
|
|
23
|
+
function standingTier(state) {
|
|
24
|
+
if (state === 'approved' || state === 'changes_requested')
|
|
25
|
+
return 0;
|
|
26
|
+
if (state === 'dismissed')
|
|
27
|
+
return 1;
|
|
28
|
+
if (state === 'commented')
|
|
29
|
+
return 2;
|
|
30
|
+
// 'pending' — a review draft that was never submitted. Not a review; it says nothing about
|
|
31
|
+
// where the reviewer stands, and showing it as one would name someone who has not spoken.
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Per-PR reviewer standings: ONE indexed read of `reviews` over the given PR ids, grouped per
|
|
36
|
+
* (pr, author) in memory.
|
|
37
|
+
*
|
|
38
|
+
* THE RULE — a reviewer's standing is their latest VERDICT (approved / changes_requested) if they
|
|
39
|
+
* ever filed one; failing that their latest dismissal; failing that their latest comment.
|
|
40
|
+
*
|
|
41
|
+
* ⚠ THIS IS A STRICT SUPERSET OF THE OLD APPROVAL FOLD, BY CONSTRUCTION, AND HAS TO STAY ONE.
|
|
42
|
+
* `approvals` is hashed into stored Pro work plans (`db/work-plan.ts` → `payloadHashFor`), so a
|
|
43
|
+
* count that moves flips every stored plan on an affected workspace permanently `stale` and
|
|
44
|
+
* re-bills it. The predecessor picked the latest row whose state was `approved` or
|
|
45
|
+
* `changes_requested` and ignored every other row; tier 0 is that pick, unchanged, and tiers 1-2
|
|
46
|
+
* can only add reviewers the old rule dropped entirely — reviewers who count for nothing. Pinned
|
|
47
|
+
* in `triage-reviewers.test.ts`.
|
|
48
|
+
*
|
|
49
|
+
* ⚠ KNOWN DIVERGENCE FROM THE STANDING A HUMAN WOULD READ, KEPT DELIBERATELY: a reviewer who
|
|
50
|
+
* approved and was LATER DISMISSED (a fresh review node whose state is DISMISSED, which is how
|
|
51
|
+
* GitHub records a revoked approval) still reads `approved`, because tier 0 outranks tier 1
|
|
52
|
+
* whatever the clock says. Demoting them would be the honest label — and would also drop the
|
|
53
|
+
* approval, moving the hashed count. MEASURED on this repo's real data: 2 reviewer-PR pairs
|
|
54
|
+
* account-wide, 1 of them on an open PR (sourcery-ai). So it is a decision to take with the hash
|
|
55
|
+
* consequence in hand, not a patch: change the tier of `dismissed` to 0 and every stored plan on
|
|
56
|
+
* an affected workspace re-bills.
|
|
57
|
+
*
|
|
58
|
+
* ⚠ A NULL `authorId` IS ONE PSEUDO-REVIEWER PER PR, AND IT COUNTS. `reviews.authorId` is
|
|
59
|
+
* nullable (a deleted GitHub account), the old key was the string `${prId}:${authorId}`, so every
|
|
60
|
+
* ghost review on a PR collapsed into one slot and its verdict counted toward `approvals`. Both
|
|
61
|
+
* are preserved exactly. Zero such rows exist today, which is precisely why dropping them here
|
|
62
|
+
* would look safe and would silently move a count the day one appears.
|
|
63
|
+
*
|
|
64
|
+
* COST: one indexed scan (`rv_pr_idx` on `pr_id`), the same query the predecessor ran with `id`
|
|
65
|
+
* added to the select list — no second query, no per-PR fetch. Measured on real data: the 50
|
|
66
|
+
* most-recently-updated open PRs carry 147 review rows and 45 reviewers (max 4 on one PR); the 50
|
|
67
|
+
* BUSIEST open PRs carry 1,362 rows and 233 reviewers (max 10). The rows were always read; what
|
|
68
|
+
* changed is that they are no longer thrown away.
|
|
69
|
+
*/
|
|
70
|
+
export async function computeReviewStandingsByPr(prIds) {
|
|
71
|
+
const out = new Map();
|
|
72
|
+
if (prIds.length === 0)
|
|
73
|
+
return out;
|
|
74
|
+
const rows = await db
|
|
75
|
+
.select({
|
|
76
|
+
id: reviews.id,
|
|
77
|
+
prId: reviews.prId,
|
|
78
|
+
authorId: reviews.authorId,
|
|
79
|
+
state: reviews.state,
|
|
80
|
+
submittedAt: reviews.submittedAt,
|
|
81
|
+
})
|
|
82
|
+
.from(reviews)
|
|
83
|
+
.where(inArray(reviews.prId, prIds))
|
|
84
|
+
.execute();
|
|
85
|
+
// One winner per (pr, author). ⚠ The tiebreak is EXPLICIT — a later `submittedAt`, then the
|
|
86
|
+
// higher `id` — because rows arrive in heap order, which on Postgres flips after any UPDATE to
|
|
87
|
+
// the table. The predecessor's bare `>` handed a same-second tie to whichever row the heap
|
|
88
|
+
// offered first, so the same PR could resolve two ways between two identical reads. Measured:
|
|
89
|
+
// 35 (pr, author, submitted_at) ties exist in real data and NOT ONE disagrees on state, so
|
|
90
|
+
// making this deterministic moves no count today — it just stops the day one would.
|
|
91
|
+
const best = new Map();
|
|
92
|
+
for (const r of rows) {
|
|
93
|
+
const tier = standingTier(r.state);
|
|
94
|
+
if (tier == null)
|
|
95
|
+
continue;
|
|
96
|
+
const key = `${r.prId}:${r.authorId}`;
|
|
97
|
+
const prev = best.get(key);
|
|
98
|
+
if (prev != null) {
|
|
99
|
+
if (tier > prev.tier)
|
|
100
|
+
continue;
|
|
101
|
+
if (tier === prev.tier) {
|
|
102
|
+
const dt = r.submittedAt.getTime() - prev.at.getTime();
|
|
103
|
+
if (dt < 0 || (dt === 0 && r.id <= prev.id))
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
best.set(key, {
|
|
108
|
+
prId: r.prId,
|
|
109
|
+
userId: r.authorId,
|
|
110
|
+
tier,
|
|
111
|
+
state: r.state,
|
|
112
|
+
at: r.submittedAt,
|
|
113
|
+
id: r.id,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const byPr = new Map();
|
|
117
|
+
for (const v of best.values()) {
|
|
118
|
+
const list = byPr.get(v.prId) ?? [];
|
|
119
|
+
list.push({
|
|
120
|
+
userId: v.userId,
|
|
121
|
+
standing: v.state,
|
|
122
|
+
standingAt: v.at,
|
|
123
|
+
});
|
|
124
|
+
byPr.set(v.prId, list);
|
|
125
|
+
}
|
|
126
|
+
for (const [prId, list] of byPr) {
|
|
127
|
+
// Deterministic for the same reason the tiebreak is: a card's chips must not reshuffle
|
|
128
|
+
// because Postgres handed back the same rows in a different order. Newest standing first is
|
|
129
|
+
// also the order a caller capping to three wants.
|
|
130
|
+
list.sort((a, b) => b.standingAt.getTime() - a.standingAt.getTime() || (a.userId ?? 0) - (b.userId ?? 0));
|
|
131
|
+
out.set(prId, { reviewers: list, total: list.length });
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The approval standing of a PR, folded from its reviewer list — the ONE place the counts come
|
|
137
|
+
* from, so the chips on a card and the "approved by N" beside them cannot disagree. `dismissed`
|
|
138
|
+
* and `commented` count as nothing, exactly as the predecessor's skipped rows did.
|
|
139
|
+
*/
|
|
140
|
+
export function approvalInfoFromStandings(standings) {
|
|
141
|
+
let approvals = 0;
|
|
142
|
+
let blocks = 0;
|
|
143
|
+
let latestApprovalAt = null;
|
|
144
|
+
for (const r of standings.reviewers) {
|
|
145
|
+
if (r.standing === 'approved') {
|
|
146
|
+
approvals += 1;
|
|
147
|
+
if (!latestApprovalAt || r.standingAt.getTime() > latestApprovalAt.getTime()) {
|
|
148
|
+
latestApprovalAt = r.standingAt;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (r.standing === 'changes_requested') {
|
|
152
|
+
blocks += 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
approved: approvals > 0 && blocks === 0,
|
|
157
|
+
changesRequested: blocks > 0,
|
|
158
|
+
approvals,
|
|
159
|
+
latestApprovalAt,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Per-PR approval standing: at least one reviewer's standing decision is approved and none is
|
|
164
|
+
* changes_requested. Drives the `approved_ready` reason tag, the green/red review outline on
|
|
165
|
+
* timeline bars, the "your PR was approved" My Turn section and the Pro work plan's `approvals`.
|
|
166
|
+
*
|
|
167
|
+
* Now a projection of `computeReviewStandingsByPr` rather than a second fold over the same rows —
|
|
168
|
+
* a caller wanting the reviewers as well should call that once and pass the result through
|
|
169
|
+
* `approvalInfoFromStandings`, not run both.
|
|
170
|
+
*/
|
|
171
|
+
export async function computeApprovalInfoByPr(prIds) {
|
|
172
|
+
const out = new Map();
|
|
173
|
+
for (const [prId, standings] of await computeReviewStandingsByPr(prIds)) {
|
|
174
|
+
const info = approvalInfoFromStandings(standings);
|
|
175
|
+
// ⚠ THE EMISSION SET IS PRESERVED, not just the values. The predecessor built its map from
|
|
176
|
+
// the decisive picks alone, so a PR whose only reviews are comments had NO ENTRY — not a
|
|
177
|
+
// zeroed one. Every caller today reads `.get(id)?.field ?? default` and cannot tell, but a
|
|
178
|
+
// future `.has()` could, and this fold is the one whose output is hashed.
|
|
179
|
+
if (info.approvals === 0 && !info.changesRequested)
|
|
180
|
+
continue;
|
|
181
|
+
out.set(prId, info);
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Compute triage fields (reason tag, review-requested-from-me, new-since
|
|
187
|
+
* counts) for a batch of PRs. All supporting data is loaded in a handful of
|
|
188
|
+
* batched queries — safe to call on the hot timeline path.
|
|
189
|
+
*/
|
|
190
|
+
export async function computeTriage(prs, accountId) {
|
|
191
|
+
const out = new Map();
|
|
192
|
+
const prIds = prs.map((p) => p.id);
|
|
193
|
+
const localUserId = await getAccountUserId(accountId);
|
|
194
|
+
// ---- review requests (user-type) by PR ----
|
|
195
|
+
const reqByPr = new Map();
|
|
196
|
+
if (prIds.length > 0) {
|
|
197
|
+
const rows = await db
|
|
198
|
+
.select({ prId: reviewRequests.prId, userId: reviewRequests.userId })
|
|
199
|
+
.from(reviewRequests)
|
|
200
|
+
.where(inArray(reviewRequests.prId, prIds))
|
|
201
|
+
.execute();
|
|
202
|
+
for (const r of rows) {
|
|
203
|
+
// A null userId means the request went to a GITHUB team — a group of people on GitHub,
|
|
204
|
+
// NOT one of this app's workspaces — so it names no single person and can't be "me".
|
|
205
|
+
if (r.userId == null)
|
|
206
|
+
continue;
|
|
207
|
+
const entry = reqByPr.get(r.prId) ?? { mine: false, others: 0 };
|
|
208
|
+
if (localUserId != null && r.userId === localUserId)
|
|
209
|
+
entry.mine = true;
|
|
210
|
+
else
|
|
211
|
+
entry.others += 1;
|
|
212
|
+
reqByPr.set(r.prId, entry);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ---- last-viewed per PR ----
|
|
216
|
+
const viewedAtByPr = new Map();
|
|
217
|
+
if (prIds.length > 0) {
|
|
218
|
+
const rows = await db
|
|
219
|
+
.select({ prId: prViews.prId, lastViewedAt: prViews.lastViewedAt })
|
|
220
|
+
.from(prViews)
|
|
221
|
+
.where(inArray(prViews.prId, prIds))
|
|
222
|
+
.execute();
|
|
223
|
+
for (const r of rows)
|
|
224
|
+
viewedAtByPr.set(r.prId, r.lastViewedAt.getTime());
|
|
225
|
+
}
|
|
226
|
+
// ---- events per PR (for new-since counts) ----
|
|
227
|
+
const newByPr = new Map();
|
|
228
|
+
if (prIds.length > 0 && viewedAtByPr.size > 0) {
|
|
229
|
+
const rows = await db
|
|
230
|
+
.select({
|
|
231
|
+
prId: events.prId,
|
|
232
|
+
type: events.type,
|
|
233
|
+
occurredAt: events.occurredAt,
|
|
234
|
+
})
|
|
235
|
+
.from(events)
|
|
236
|
+
.where(inArray(events.prId, prIds))
|
|
237
|
+
.execute();
|
|
238
|
+
for (const r of rows) {
|
|
239
|
+
if (r.prId == null)
|
|
240
|
+
continue;
|
|
241
|
+
const threshold = viewedAtByPr.get(r.prId);
|
|
242
|
+
if (threshold == null)
|
|
243
|
+
continue;
|
|
244
|
+
if (r.occurredAt.getTime() <= threshold)
|
|
245
|
+
continue;
|
|
246
|
+
const n = newByPr.get(r.prId) ?? emptyNew();
|
|
247
|
+
if (r.type === 'commit_pushed')
|
|
248
|
+
n.commits += 1;
|
|
249
|
+
else if (r.type === 'pr_comment' || r.type === 'review_comment')
|
|
250
|
+
n.comments += 1;
|
|
251
|
+
else if (r.type === 'review_submitted')
|
|
252
|
+
n.reviews += 1;
|
|
253
|
+
newByPr.set(r.prId, n);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const approvalByPr = await computeApprovalInfoByPr(prIds);
|
|
257
|
+
for (const pr of prs) {
|
|
258
|
+
const req = reqByPr.get(pr.id);
|
|
259
|
+
const reviewRequestedFromMe = req?.mine ?? false;
|
|
260
|
+
const otherReviewersRequested = req?.others ?? 0;
|
|
261
|
+
const approval = approvalByPr.get(pr.id);
|
|
262
|
+
// No "new" badges on closed/merged PRs — done is done.
|
|
263
|
+
const newSinceLastViewed = pr.state === 'open' && viewedAtByPr.has(pr.id)
|
|
264
|
+
? (newByPr.get(pr.id) ?? emptyNew())
|
|
265
|
+
: null;
|
|
266
|
+
out.set(pr.id, {
|
|
267
|
+
reviewRequestedFromMe,
|
|
268
|
+
otherReviewersRequested,
|
|
269
|
+
isApproved: approval?.approved ?? false,
|
|
270
|
+
isChangesRequested: approval?.changesRequested ?? false,
|
|
271
|
+
newSinceLastViewed,
|
|
272
|
+
reasonTag: deriveReasonTag(pr, {
|
|
273
|
+
reviewRequestedFromMe,
|
|
274
|
+
localUserId,
|
|
275
|
+
newComments: newSinceLastViewed?.comments ?? 0,
|
|
276
|
+
approvedReady: approval?.approved ?? false,
|
|
277
|
+
}),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
function deriveReasonTag(pr, ctx) {
|
|
283
|
+
// The cascade only really applies to open PRs; closed/merged fall through to
|
|
284
|
+
// the default.
|
|
285
|
+
if (pr.state === 'open') {
|
|
286
|
+
// Actionable-by-you reasons win — "awaiting your review" beats CI failing
|
|
287
|
+
// because only you can clear it.
|
|
288
|
+
if (ctx.reviewRequestedFromMe)
|
|
289
|
+
return 'awaiting_your_review';
|
|
290
|
+
if (ctx.localUserId != null &&
|
|
291
|
+
pr.authorId === ctx.localUserId &&
|
|
292
|
+
ctx.newComments > 0)
|
|
293
|
+
return 'your_pr_new_comments';
|
|
294
|
+
if (pr.ciStatus === 'failure' || pr.ciStatus === 'error')
|
|
295
|
+
return 'ci_failing';
|
|
296
|
+
if (pr.mergeable === 'conflicting' || pr.mergeStateStatus === 'dirty')
|
|
297
|
+
return 'merge_conflicts';
|
|
298
|
+
// CI is known-not-failing here (failing CI returned above).
|
|
299
|
+
//
|
|
300
|
+
// `mergeable` ONLY reports merge-CONFLICT state (MERGEABLE / CONFLICTING / UNKNOWN) — it
|
|
301
|
+
// says nothing about branch protection. Testing it alone tagged PRs "approved & ready"
|
|
302
|
+
// while their REQUIRED checks were red or a second required review was outstanding, which
|
|
303
|
+
// is the same blindness the merge surfaces had. `mergeStateStatus` is the protection-aware
|
|
304
|
+
// field, so gate on it too:
|
|
305
|
+
// clean — mergeable and passing
|
|
306
|
+
// has_hooks — mergeable, just has a pre-receive hook to run
|
|
307
|
+
// unstable — NON-required checks are red; GitHub will still merge it, so it IS ready
|
|
308
|
+
// Everything else (blocked / behind / dirty / unknown) is not ready by definition.
|
|
309
|
+
if (ctx.approvedReady &&
|
|
310
|
+
pr.mergeable === 'mergeable' &&
|
|
311
|
+
READY_MERGE_STATES.has(pr.mergeStateStatus))
|
|
312
|
+
return 'approved_ready';
|
|
313
|
+
if (pr.isStalled)
|
|
314
|
+
return 'stalled';
|
|
315
|
+
if (pr.threadCounts.untouched > 0)
|
|
316
|
+
return 'untouched_threads';
|
|
317
|
+
}
|
|
318
|
+
return 'in_progress';
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=triage.js.map
|
package/dist/db/usage.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { and, eq, gte } from 'drizzle-orm';
|
|
2
|
+
import { db, schema } from './client.js';
|
|
3
|
+
const { aiUsage } = schema;
|
|
4
|
+
// Append one usage row. Best-effort + non-fatal: a ledger write must never break the
|
|
5
|
+
// feature that spent the money, so callers `.catch()` or await-in-try. Drops rows with
|
|
6
|
+
// no real cost (a $0 ambient-session completion isn't worth a row).
|
|
7
|
+
export async function recordAiUsage(row) {
|
|
8
|
+
if (!Number.isFinite(row.costUsd) || row.costUsd <= 0)
|
|
9
|
+
return;
|
|
10
|
+
await db
|
|
11
|
+
.insert(aiUsage)
|
|
12
|
+
.values({
|
|
13
|
+
accountId: row.accountId,
|
|
14
|
+
seam: row.seam,
|
|
15
|
+
feature: row.feature,
|
|
16
|
+
model: row.model,
|
|
17
|
+
costUsd: row.costUsd,
|
|
18
|
+
inputTokens: row.inputTokens ?? null,
|
|
19
|
+
outputTokens: row.outputTokens ?? null,
|
|
20
|
+
prId: row.prId ?? null,
|
|
21
|
+
repoId: row.repoId ?? null,
|
|
22
|
+
occurredAt: new Date(),
|
|
23
|
+
})
|
|
24
|
+
.execute();
|
|
25
|
+
}
|
|
26
|
+
// Sum recorded AI spend for an account since `sinceMs`, split by seam. Rows are small
|
|
27
|
+
// (per account, per month) so we fetch + reduce in JS — portable across both dialects.
|
|
28
|
+
export async function getAiUsageSummary(accountId, sinceMs) {
|
|
29
|
+
const rows = await db
|
|
30
|
+
.select({ seam: aiUsage.seam, costUsd: aiUsage.costUsd })
|
|
31
|
+
.from(aiUsage)
|
|
32
|
+
.where(and(eq(aiUsage.accountId, accountId), gte(aiUsage.occurredAt, new Date(sinceMs))))
|
|
33
|
+
.execute();
|
|
34
|
+
let summaryUsd = 0;
|
|
35
|
+
let agentUsd = 0;
|
|
36
|
+
for (const r of rows) {
|
|
37
|
+
if (r.seam === 'agent')
|
|
38
|
+
agentUsd += r.costUsd;
|
|
39
|
+
else
|
|
40
|
+
summaryUsd += r.costUsd;
|
|
41
|
+
}
|
|
42
|
+
return { summaryUsd, agentUsd, totalUsd: summaryUsd + agentUsd };
|
|
43
|
+
}
|
|
44
|
+
// Auxiliary summary sub-calls that belong to a SINGLE user-initiated turn and must NOT count as
|
|
45
|
+
// their own turn — e.g. the insights-chat CHART is a second Haiku call rendering the SAME
|
|
46
|
+
// question, so one charted question is still one turn. Their $ cost stays on the ledger (COGS
|
|
47
|
+
// visibility); only the turn TALLY excludes them.
|
|
48
|
+
const NON_TURN_SUMMARY_FEATURES = new Set(['sprint_chat_chart']);
|
|
49
|
+
// Count the SUMMARY-seam turns an account has spent since `sinceMs` (month-to-date). One
|
|
50
|
+
// recorded ledger row = one turn, EXCEPT auxiliary sub-calls (above); a $0 cache hit /
|
|
51
|
+
// ambient-session completion isn't recorded, so it's free and doesn't count (recordAiUsage
|
|
52
|
+
// drops non-positive-cost rows). A summary turn limit caps this. Portable across both dialects
|
|
53
|
+
// (fetch + reduce in JS; the row set is bounded by ~the monthly limit — same pattern as
|
|
54
|
+
// getAiUsageSummary).
|
|
55
|
+
export async function getSummaryTurnCount(accountId, sinceMs) {
|
|
56
|
+
const rows = await db
|
|
57
|
+
.select({ feature: aiUsage.feature })
|
|
58
|
+
.from(aiUsage)
|
|
59
|
+
.where(and(eq(aiUsage.accountId, accountId), eq(aiUsage.seam, 'summary'), gte(aiUsage.occurredAt, new Date(sinceMs))))
|
|
60
|
+
.execute();
|
|
61
|
+
return rows.filter((r) => !NON_TURN_SUMMARY_FEATURES.has(r.feature)).length;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=usage.js.map
|