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,36 @@
|
|
|
1
|
+
import { and, eq, inArray, isNull, or, sql } from 'drizzle-orm';
|
|
2
|
+
import { db, schema } from './client.js';
|
|
3
|
+
import { config } from '../config.js';
|
|
4
|
+
// One-time / idempotent maintenance run at startup.
|
|
5
|
+
//
|
|
6
|
+
// GitHub wraps inline-only reviews in an empty "commented" review, which the
|
|
7
|
+
// sync used to record as a `review_submitted` event. Those wrappers duplicate
|
|
8
|
+
// the inline `review_comment` markers on the timeline. New syncs no longer emit
|
|
9
|
+
// them (see sync/upsert.ts), but pre-existing rows linger because upserts only
|
|
10
|
+
// update. This removes them; it also catches reviews whose summary body was
|
|
11
|
+
// later cleared on GitHub. Cheap and safe to run every boot.
|
|
12
|
+
//
|
|
13
|
+
// Written as portable drizzle (no raw better-sqlite3 / db.execute) so it runs on
|
|
14
|
+
// both dialects; the count comes from `.returning().length` (dialect-neutral).
|
|
15
|
+
export async function cleanupRedundantReviewEvents() {
|
|
16
|
+
// In cloud "lean storage" mode (config.persistBodies false) reviews.body is
|
|
17
|
+
// always null, so the body-emptiness signal this relies on is unavailable — and
|
|
18
|
+
// unnecessary: persistPr already only emits review_submitted for *substantive*
|
|
19
|
+
// reviews (it checks the in-memory GraphQL body, not the stored one), and cloud
|
|
20
|
+
// starts empty, so there are no wrapper events to remove. Skipping here avoids
|
|
21
|
+
// wrongly deleting markers for substantive "commented" reviews.
|
|
22
|
+
if (!config.persistBodies)
|
|
23
|
+
return 0;
|
|
24
|
+
const { events, reviews } = schema;
|
|
25
|
+
const emptyCommentedReviews = db
|
|
26
|
+
.select({ id: reviews.id })
|
|
27
|
+
.from(reviews)
|
|
28
|
+
.where(and(eq(reviews.state, 'commented'), or(isNull(reviews.body), eq(sql `trim(${reviews.body})`, ''))));
|
|
29
|
+
const deleted = await db
|
|
30
|
+
.delete(events)
|
|
31
|
+
.where(and(eq(events.type, 'review_submitted'), eq(events.refTable, 'reviews'), inArray(events.refId, emptyCommentedReviews)))
|
|
32
|
+
.returning({ id: events.id })
|
|
33
|
+
.execute();
|
|
34
|
+
return deleted.length;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=cleanup.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Mode-aware database client.
|
|
2
|
+
//
|
|
3
|
+
// local → better-sqlite3 (synchronous engine) + the sqlite-core schema
|
|
4
|
+
// cloud → node-postgres Pool + the pg-core schema
|
|
5
|
+
//
|
|
6
|
+
// The whole app talks to `db` through the PORTABLE async surface only
|
|
7
|
+
// (`await builder` / `.execute()` / `.returning().execute()` / runTransaction) —
|
|
8
|
+
// the empirically-verified subset that behaves identically on both drivers (see
|
|
9
|
+
// the probe in the cloud-refactor notes). `db` is TYPED as the Postgres drizzle
|
|
10
|
+
// instance so any stray `.get()/.all()/.run()` (better-sqlite3-only) is a compile
|
|
11
|
+
// error; at runtime the better-sqlite3 instance supports that same subset.
|
|
12
|
+
import { chmodSync, mkdirSync } from 'node:fs';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
import { config } from '../config.js';
|
|
15
|
+
import * as sqliteSchema from './schema.sqlite.js';
|
|
16
|
+
import * as pgSchema from './schema.pg.js';
|
|
17
|
+
export const isPg = config.dbDialect === 'postgres';
|
|
18
|
+
let dbInstance;
|
|
19
|
+
let schemaValue;
|
|
20
|
+
let closeFn;
|
|
21
|
+
// Kept for the sqlite-only manual transaction path; null in cloud mode.
|
|
22
|
+
let sqliteConn = null;
|
|
23
|
+
if (isPg) {
|
|
24
|
+
// Dynamic import so the unused native driver isn't loaded in the other mode.
|
|
25
|
+
const pg = await import('pg');
|
|
26
|
+
const Pool = pg.Pool ?? pg.default.Pool;
|
|
27
|
+
const { drizzle } = await import('drizzle-orm/node-postgres');
|
|
28
|
+
const pool = new Pool({ connectionString: config.databaseUrl });
|
|
29
|
+
schemaValue = pgSchema;
|
|
30
|
+
dbInstance = drizzle(pool, { schema: pgSchema });
|
|
31
|
+
closeFn = async () => {
|
|
32
|
+
await pool.end();
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const { default: Database } = await import('better-sqlite3');
|
|
37
|
+
const { drizzle } = await import('drizzle-orm/better-sqlite3');
|
|
38
|
+
// Owner-only on the DIRECTORY as well as the file: WAL mode creates two siblings
|
|
39
|
+
// (`-wal`, `-shm`) that hold the same data and are created by SQLite with the process
|
|
40
|
+
// umask, so locking down only the main file would leave the write-ahead log readable.
|
|
41
|
+
mkdirSync(dirname(config.dbPath), { recursive: true, mode: 0o700 });
|
|
42
|
+
sqliteConn = new Database(config.dbPath);
|
|
43
|
+
sqliteConn.pragma('journal_mode = WAL');
|
|
44
|
+
sqliteConn.pragma('foreign_keys = ON');
|
|
45
|
+
// The local database holds every synced PR body, review comment and GitHub login the user
|
|
46
|
+
// can see — on a shared or multi-user machine the default 0644 made all of that readable by
|
|
47
|
+
// any other local account. 0600 it (and the WAL siblings, which the pragma above has just
|
|
48
|
+
// created). Best-effort: chmod is not meaningful on Windows, and a failure here must never
|
|
49
|
+
// stop the app from starting.
|
|
50
|
+
for (const suffix of ['', '-wal', '-shm']) {
|
|
51
|
+
try {
|
|
52
|
+
chmodSync(`${config.dbPath}${suffix}`, 0o600);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* missing sibling (non-WAL platform) or an unsupported filesystem — not fatal */
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
schemaValue = sqliteSchema;
|
|
59
|
+
dbInstance = drizzle(sqliteConn, {
|
|
60
|
+
schema: sqliteSchema,
|
|
61
|
+
});
|
|
62
|
+
closeFn = async () => {
|
|
63
|
+
sqliteConn?.close();
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export const db = dbInstance;
|
|
67
|
+
export const schema = schemaValue;
|
|
68
|
+
// Close the underlying connection (pool.end() / better-sqlite3 .close()). Used by
|
|
69
|
+
// the one-shot migrate CLI; the long-running server never calls it.
|
|
70
|
+
export async function closeDb() {
|
|
71
|
+
await closeFn();
|
|
72
|
+
}
|
|
73
|
+
// Dialect-aware transaction. better-sqlite3 rejects async transaction callbacks
|
|
74
|
+
// ("Transaction function cannot return a promise"), so the sqlite path wraps a
|
|
75
|
+
// manual BEGIN/COMMIT around the awaited (synchronously-resolving) drizzle ops —
|
|
76
|
+
// no other macrotask interleaves between BEGIN and COMMIT because the callback
|
|
77
|
+
// only awaits in-process sqlite work (no real I/O). The pg path uses a real
|
|
78
|
+
// async transaction. Either way the callback uses the same portable `tx` surface.
|
|
79
|
+
export async function runTransaction(fn) {
|
|
80
|
+
if (isPg) {
|
|
81
|
+
return dbInstance.transaction(async (tx) => fn(tx));
|
|
82
|
+
}
|
|
83
|
+
const conn = sqliteConn;
|
|
84
|
+
if (!conn)
|
|
85
|
+
throw new Error('sqlite connection not initialised');
|
|
86
|
+
conn.exec('BEGIN');
|
|
87
|
+
try {
|
|
88
|
+
const result = await fn(dbInstance);
|
|
89
|
+
conn.exec('COMMIT');
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
try {
|
|
94
|
+
conn.exec('ROLLBACK');
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* rollback best-effort */
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Code-only lines-of-change for a pull request — the input to the LARGE-PR FLAG.
|
|
3
|
+
// ============================================================================
|
|
4
|
+
//
|
|
5
|
+
// "Large" is a claim about how much CODE a reviewer has to read, so a 2,000-line
|
|
6
|
+
// lockfile bump, a regenerated OpenAPI schema or a docs rewrite must not trip it.
|
|
7
|
+
// `codeLocFor` folds the PR's stored `files[]` (additions + deletions per file),
|
|
8
|
+
// dropping everything `isNonCodeFile` classifies as prose, structured config,
|
|
9
|
+
// generated/vendored output or binary payload.
|
|
10
|
+
//
|
|
11
|
+
// ---- RELATIONSHIP TO THE THREE OTHER CLASSIFIERS ----------------------------
|
|
12
|
+
//
|
|
13
|
+
// There are now FOUR path classifiers in this repo and they answer four
|
|
14
|
+
// different questions. They are NOT meant to agree, and none may be folded into
|
|
15
|
+
// another:
|
|
16
|
+
//
|
|
17
|
+
// 1. `NOISE_GLOBS` — apps/backend/src/review/prepare.ts
|
|
18
|
+
// "Which files should be stripped from the diff the Claude Review AGENT
|
|
19
|
+
// sees?" A BUDGET decision on a paid feature: lockfiles, minified bundles,
|
|
20
|
+
// dist/build output, snapshots. It deliberately keeps `.md`, `.yml`, `.json`
|
|
21
|
+
// and `docs/` IN the diff, because a review agent reading a config change is
|
|
22
|
+
// doing its job. ⚠ Widening it to satisfy THIS feature would silently change
|
|
23
|
+
// what a paid agent reviews, with no test to catch it.
|
|
24
|
+
//
|
|
25
|
+
// 2. `isLockFile` — apps/frontend/src/lib/diff.ts
|
|
26
|
+
// "Should this file's diff start COLLAPSED in the Changes tab?" Lockfiles
|
|
27
|
+
// only, by exact basename, case-sensitively (matching what git records).
|
|
28
|
+
// A narrow presentation heuristic; far too narrow to answer "is this code".
|
|
29
|
+
//
|
|
30
|
+
// 3. `isNonCodeFile` — HERE
|
|
31
|
+
// "Does this file's churn count toward how much CODE this PR asks a human to
|
|
32
|
+
// read?" The broadest of the three, and the only one that excludes prose and
|
|
33
|
+
// structured config. Case-INSENSITIVE (a `Readme.MD` is still prose), and
|
|
34
|
+
// the only one whose answer is a number a user compares against a threshold
|
|
35
|
+
// they configured.
|
|
36
|
+
//
|
|
37
|
+
// 4. `BLAST_SURFACES` + `isTestFile` — apps/backend/src/db/blast-radius.ts
|
|
38
|
+
// "Do this file's CONSUMERS LIVE OUTSIDE THE DIFF?" The only one asking
|
|
39
|
+
// about REACH rather than volume or presentation, which is why it is a list
|
|
40
|
+
// of contract shapes (migrations, IDL, `.d.ts`, OpenAPI, IaC) rather than a
|
|
41
|
+
// list of things to ignore. It sits ON TOP of this file rather than beside
|
|
42
|
+
// it: `isNonCodeFile` is reused verbatim to split code from prose/config,
|
|
43
|
+
// and blast-radius.ts adds only the two judgements this file has no opinion
|
|
44
|
+
// about — is it a test, and is it a contract.
|
|
45
|
+
//
|
|
46
|
+
// ---- THE THREE DATA TRAPS (all measured on the real corpus) -----------------
|
|
47
|
+
//
|
|
48
|
+
// See `codeLocFor` for how each is handled. In one line each:
|
|
49
|
+
// * 18.5% of PRs have `files IS NULL`, and 18.8% carry a never-observed size —
|
|
50
|
+
// `codeLoc` is NULL there, never 0. "Not large" about a PR nobody measured is
|
|
51
|
+
// a false claim (the same rule `lib/botVolume.ts`'s `formatLoc` states).
|
|
52
|
+
// * `files(first: 100)` truncates, and it truncates EXACTLY the largest PRs —
|
|
53
|
+
// so `codeLocIsLowerBound` rides alongside and the reading is ASYMMETRIC:
|
|
54
|
+
// over-threshold is safe to assert, under-threshold is not.
|
|
55
|
+
// * `files = []` is OVERLOADED: it is also `sync/routing-files.ts`'s "we tried
|
|
56
|
+
// and failed" sentinel. It never means "zero code lines".
|
|
57
|
+
/** The page size of the `files(first: 100)` connection in `github/queries.ts`. A PR whose
|
|
58
|
+
* stored `files[]` is at this length was (or may have been) truncated by that cap. */
|
|
59
|
+
export const PR_FILES_PAGE_CAP = 100;
|
|
60
|
+
// ---- the classifier ---------------------------------------------------------
|
|
61
|
+
function normalize(path) {
|
|
62
|
+
let p = path.replace(/\\/g, '/');
|
|
63
|
+
if (p.startsWith('./'))
|
|
64
|
+
p = p.slice(2);
|
|
65
|
+
if (p.startsWith('/'))
|
|
66
|
+
p = p.slice(1);
|
|
67
|
+
return p;
|
|
68
|
+
}
|
|
69
|
+
/** Prose. Extensions whose content is written for humans, not machines. */
|
|
70
|
+
const DOC_EXTENSIONS = new Set([
|
|
71
|
+
'md',
|
|
72
|
+
'mdx',
|
|
73
|
+
'markdown',
|
|
74
|
+
'mdown',
|
|
75
|
+
'rst',
|
|
76
|
+
'txt',
|
|
77
|
+
'adoc',
|
|
78
|
+
'asciidoc',
|
|
79
|
+
'org',
|
|
80
|
+
'tex',
|
|
81
|
+
'pdf',
|
|
82
|
+
]);
|
|
83
|
+
/** Structured configuration + data. NOT `.html`/`.css`/`.scss`/`.sql`, which are hand-written
|
|
84
|
+
* source a reviewer must actually read. `.xml` is here because in practice it is manifests,
|
|
85
|
+
* poms and layouts — data with a schema, not logic. */
|
|
86
|
+
const CONFIG_EXTENSIONS = new Set([
|
|
87
|
+
'json',
|
|
88
|
+
'json5',
|
|
89
|
+
'jsonc',
|
|
90
|
+
'yml',
|
|
91
|
+
'yaml',
|
|
92
|
+
'toml',
|
|
93
|
+
'ini',
|
|
94
|
+
'cfg',
|
|
95
|
+
'conf',
|
|
96
|
+
'properties',
|
|
97
|
+
'plist',
|
|
98
|
+
'xml',
|
|
99
|
+
'csv',
|
|
100
|
+
'tsv',
|
|
101
|
+
'lock',
|
|
102
|
+
'lockfile',
|
|
103
|
+
]);
|
|
104
|
+
/** Binary / media payloads. Their "additions" are a diff artefact, not lines anyone reads. */
|
|
105
|
+
const BINARY_EXTENSIONS = new Set([
|
|
106
|
+
'png',
|
|
107
|
+
'jpg',
|
|
108
|
+
'jpeg',
|
|
109
|
+
'gif',
|
|
110
|
+
'bmp',
|
|
111
|
+
'ico',
|
|
112
|
+
'icns',
|
|
113
|
+
'webp',
|
|
114
|
+
'avif',
|
|
115
|
+
'svg',
|
|
116
|
+
'tiff',
|
|
117
|
+
'psd',
|
|
118
|
+
'mp3',
|
|
119
|
+
'mp4',
|
|
120
|
+
'mov',
|
|
121
|
+
'webm',
|
|
122
|
+
'wav',
|
|
123
|
+
'ogg',
|
|
124
|
+
'woff',
|
|
125
|
+
'woff2',
|
|
126
|
+
'ttf',
|
|
127
|
+
'otf',
|
|
128
|
+
'eot',
|
|
129
|
+
'zip',
|
|
130
|
+
'gz',
|
|
131
|
+
'tgz',
|
|
132
|
+
'bz2',
|
|
133
|
+
'xz',
|
|
134
|
+
'7z',
|
|
135
|
+
'rar',
|
|
136
|
+
'jar',
|
|
137
|
+
'war',
|
|
138
|
+
'so',
|
|
139
|
+
'dylib',
|
|
140
|
+
'dll',
|
|
141
|
+
'exe',
|
|
142
|
+
'bin',
|
|
143
|
+
'wasm',
|
|
144
|
+
'pyc',
|
|
145
|
+
'class',
|
|
146
|
+
'pdb',
|
|
147
|
+
'db',
|
|
148
|
+
'sqlite',
|
|
149
|
+
'parquet',
|
|
150
|
+
]);
|
|
151
|
+
/** Machine-generated language lock files, by exact (lower-cased) basename. Deliberately a
|
|
152
|
+
* SUPERSET of the frontend's `isLockFile` list — this one also has the `*.lock` /
|
|
153
|
+
* `*.lockfile` extension arms above, which that one refuses on purpose. */
|
|
154
|
+
const LOCK_BASENAMES = new Set([
|
|
155
|
+
'package-lock.json',
|
|
156
|
+
'npm-shrinkwrap.json',
|
|
157
|
+
'pnpm-lock.yaml',
|
|
158
|
+
'yarn.lock',
|
|
159
|
+
'bun.lockb',
|
|
160
|
+
'bun.lock',
|
|
161
|
+
'cargo.lock',
|
|
162
|
+
'poetry.lock',
|
|
163
|
+
'uv.lock',
|
|
164
|
+
'pipfile.lock',
|
|
165
|
+
'gemfile.lock',
|
|
166
|
+
'composer.lock',
|
|
167
|
+
'go.sum',
|
|
168
|
+
'flake.lock',
|
|
169
|
+
'package.resolved',
|
|
170
|
+
'gradle.lockfile',
|
|
171
|
+
'mix.lock',
|
|
172
|
+
'pubspec.lock',
|
|
173
|
+
'packages.lock.json',
|
|
174
|
+
]);
|
|
175
|
+
/** Extensionless files that are prose or project metadata rather than source. Matched on the
|
|
176
|
+
* lower-cased basename with any doc extension already stripped. */
|
|
177
|
+
const DOC_BASENAMES = new Set([
|
|
178
|
+
'readme',
|
|
179
|
+
'license',
|
|
180
|
+
'licence',
|
|
181
|
+
'copying',
|
|
182
|
+
'notice',
|
|
183
|
+
'authors',
|
|
184
|
+
'contributors',
|
|
185
|
+
'changelog',
|
|
186
|
+
'changes',
|
|
187
|
+
'history',
|
|
188
|
+
'contributing',
|
|
189
|
+
'code_of_conduct',
|
|
190
|
+
'security',
|
|
191
|
+
'codeowners',
|
|
192
|
+
'owners',
|
|
193
|
+
'maintainers',
|
|
194
|
+
'patents',
|
|
195
|
+
'acknowledgements',
|
|
196
|
+
'acknowledgments',
|
|
197
|
+
]);
|
|
198
|
+
/** Directory names whose whole subtree is prose or generated/vendored output. Matched as a
|
|
199
|
+
* WHOLE path segment, anywhere in the path, so `apps/backend/docs/x.ts` counts too. */
|
|
200
|
+
const NON_CODE_DIRS = new Set([
|
|
201
|
+
'docs',
|
|
202
|
+
'doc',
|
|
203
|
+
'documentation',
|
|
204
|
+
'node_modules',
|
|
205
|
+
'vendor',
|
|
206
|
+
'vendored',
|
|
207
|
+
'third_party',
|
|
208
|
+
'thirdparty',
|
|
209
|
+
'3rdparty',
|
|
210
|
+
'dist',
|
|
211
|
+
'build',
|
|
212
|
+
'out',
|
|
213
|
+
'generated',
|
|
214
|
+
'gen',
|
|
215
|
+
'autogen',
|
|
216
|
+
'coverage',
|
|
217
|
+
'__snapshots__',
|
|
218
|
+
'__pycache__',
|
|
219
|
+
'fixtures',
|
|
220
|
+
'__fixtures__',
|
|
221
|
+
'testdata',
|
|
222
|
+
'golden',
|
|
223
|
+
'snapshots',
|
|
224
|
+
]);
|
|
225
|
+
function lastSegment(path) {
|
|
226
|
+
const idx = path.lastIndexOf('/');
|
|
227
|
+
return idx === -1 ? path : path.slice(idx + 1);
|
|
228
|
+
}
|
|
229
|
+
function extensionOf(basename) {
|
|
230
|
+
const idx = basename.lastIndexOf('.');
|
|
231
|
+
// A leading dot is a DOTFILE marker, not an extension (`.eslintrc` has no extension).
|
|
232
|
+
if (idx <= 0)
|
|
233
|
+
return '';
|
|
234
|
+
return basename.slice(idx + 1);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Does this path's churn count as CODE for the large-PR flag?
|
|
238
|
+
*
|
|
239
|
+
* Returns true for documentation, structured configuration, dotfiles, lockfiles, binary/media
|
|
240
|
+
* payloads and anything living under a generated / vendored / docs directory.
|
|
241
|
+
*
|
|
242
|
+
* ⚠ Read the "relationship to the two classifiers that already exist" block at the top of this
|
|
243
|
+
* file before widening it — and in particular do NOT reach for `review/prepare.ts`'s
|
|
244
|
+
* `NOISE_GLOBS` instead: that list is a paid agent's diff budget and answers a different
|
|
245
|
+
* question.
|
|
246
|
+
*/
|
|
247
|
+
export function isNonCodeFile(path) {
|
|
248
|
+
const p = normalize(path).toLowerCase();
|
|
249
|
+
if (p === '')
|
|
250
|
+
return true;
|
|
251
|
+
const segments = p.split('/');
|
|
252
|
+
const base = lastSegment(p);
|
|
253
|
+
// Dot-directories are tooling/config trees in their entirety (.github/, .vscode/, .circleci/,
|
|
254
|
+
// .idea/, .storybook/), and so is everything under a docs / generated / vendored directory.
|
|
255
|
+
//
|
|
256
|
+
// ⚠ DIRECTORY SEGMENTS ONLY — `segments.length - 1`, never the whole array. A file named
|
|
257
|
+
// `build` (as in `scripts/build`, a shell entrypoint) is source code; only a DIRECTORY called
|
|
258
|
+
// `build` is output. Testing the basename against the directory list is how a hand-written
|
|
259
|
+
// script gets silently uncounted.
|
|
260
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
261
|
+
const seg = segments[i];
|
|
262
|
+
if (seg.startsWith('.'))
|
|
263
|
+
return true;
|
|
264
|
+
if (NON_CODE_DIRS.has(seg))
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
// A dotfile basename (.env, .env.local, .gitignore, .eslintrc.js, .prettierrc) is
|
|
268
|
+
// configuration whatever extension it carries.
|
|
269
|
+
if (base.startsWith('.'))
|
|
270
|
+
return true;
|
|
271
|
+
if (LOCK_BASENAMES.has(base))
|
|
272
|
+
return true;
|
|
273
|
+
// Minified / sourcemap / snapshot artefacts, by compound suffix (a plain extension set can't
|
|
274
|
+
// express `.min.js`, whose extension is `js`).
|
|
275
|
+
if (base.endsWith('.min.js') ||
|
|
276
|
+
base.endsWith('.min.css') ||
|
|
277
|
+
base.endsWith('.min.mjs') ||
|
|
278
|
+
base.endsWith('.map') ||
|
|
279
|
+
base.endsWith('.snap')) {
|
|
280
|
+
return true;
|
|
281
|
+
}
|
|
282
|
+
// Conventional "this file was generated" markers, which no reviewer reads line by line.
|
|
283
|
+
if (base.includes('.generated.') ||
|
|
284
|
+
base.endsWith('.gen.go') ||
|
|
285
|
+
base.endsWith('.pb.go') ||
|
|
286
|
+
base.endsWith('_pb2.py') ||
|
|
287
|
+
base.endsWith('_pb2.pyi') ||
|
|
288
|
+
base.endsWith('.pb.cc') ||
|
|
289
|
+
base.endsWith('.pb.h') ||
|
|
290
|
+
base.endsWith('.g.dart') ||
|
|
291
|
+
base.endsWith('.freezed.dart')) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
const ext = extensionOf(base);
|
|
295
|
+
if (ext === '') {
|
|
296
|
+
// No extension at all: prose/metadata by name (README, LICENSE, CODEOWNERS), otherwise a
|
|
297
|
+
// script or a Dockerfile/Makefile — hand-written logic, which COUNTS.
|
|
298
|
+
return DOC_BASENAMES.has(base);
|
|
299
|
+
}
|
|
300
|
+
if (DOC_EXTENSIONS.has(ext))
|
|
301
|
+
return true;
|
|
302
|
+
if (CONFIG_EXTENSIONS.has(ext))
|
|
303
|
+
return true;
|
|
304
|
+
if (BINARY_EXTENSIONS.has(ext))
|
|
305
|
+
return true;
|
|
306
|
+
// A prose basename that carries a doc-ish extension is already caught above; one carrying a
|
|
307
|
+
// code extension (e.g. `changelog.ts`) is real source and must not be excluded — hence no
|
|
308
|
+
// DOC_BASENAMES check down here.
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
/** The one honest "we don't know". Never `{codeLoc: 0}` — see the trap notes above. */
|
|
312
|
+
const UNKNOWN = { codeLoc: null, codeLocIsLowerBound: false };
|
|
313
|
+
/**
|
|
314
|
+
* Fold a stored PR row into its code-only line count.
|
|
315
|
+
*
|
|
316
|
+
* The three data traps, each handled explicitly:
|
|
317
|
+
*
|
|
318
|
+
* 1. **`files IS NULL`** (18.5% of the real corpus) — the per-file breakdown was never stored
|
|
319
|
+
* for this PR. Nothing to fold → `null`.
|
|
320
|
+
*
|
|
321
|
+
* 2. **A never-observed size** (18.8%) — `additions`, `deletions` and `changedFiles` are all 0.
|
|
322
|
+
* ⚠ NARROWED: this now refuses only when the FILE LIST agrees (sums to zero too). See the
|
|
323
|
+
* inline note — a stored `files[]` carrying real per-file numbers is a positive observation of
|
|
324
|
+
* size, and refusing on the columns alone was discarding it for 9.8% of open pull requests.
|
|
325
|
+
* Those columns are NOT NULL with a 0 default, so "we never saw this PR's size" and "this PR
|
|
326
|
+
* genuinely changed nothing" are the SAME row. `lib/botVolume.ts` states the rule for the
|
|
327
|
+
* total-LoC label and it holds identically here: send null, never a fabricated 0, because
|
|
328
|
+
* rendering "not large" about a PR nobody measured is a false claim.
|
|
329
|
+
*
|
|
330
|
+
* 3. **`files = []` is OVERLOADED** — `sync/routing-files.ts` persists `[]` as a "we tried and
|
|
331
|
+
* failed" sentinel, which is byte-identical to a PR that really touched nothing. The
|
|
332
|
+
* cross-check is `changedFiles`: a PR with `changedFiles > 0` and an empty `files` array is
|
|
333
|
+
* the sentinel, and treating it as zero code lines would mark real PRs as trivially small.
|
|
334
|
+
* We go further and return null for EVERY empty array, because the only remaining case —
|
|
335
|
+
* `changedFiles === 0` — has already been answered by trap 2.
|
|
336
|
+
*
|
|
337
|
+
* 4. (Not a trap so much as a bound.) `files(first: 100)` truncates. `codeLocIsLowerBound` is
|
|
338
|
+
* set when the stored list hit the page cap OR when `changedFiles` exceeds what we hold —
|
|
339
|
+
* both mean files are missing, and a missing file can only ADD lines.
|
|
340
|
+
*/
|
|
341
|
+
export function codeLocFor(pr) {
|
|
342
|
+
// Trap 1.
|
|
343
|
+
if (pr.files == null)
|
|
344
|
+
return UNKNOWN;
|
|
345
|
+
// ⚠ TRAP 2, NARROWED — read this before widening it back.
|
|
346
|
+
//
|
|
347
|
+
// The ambiguity is real: `additions`/`deletions`/`changedFiles` are NOT NULL with a 0 default,
|
|
348
|
+
// so "we never saw this pull request's size" and "it genuinely changed nothing" are the same
|
|
349
|
+
// row. But that is only unresolvable when the FILE LIST cannot answer it either. A non-empty
|
|
350
|
+
// `files[]` whose per-file numbers sum above zero IS a positive observation of size, from the
|
|
351
|
+
// same GitHub payload the columns should have carried — so refusing on the columns alone
|
|
352
|
+
// discards evidence we hold.
|
|
353
|
+
//
|
|
354
|
+
// Measured after `backfillMissingPrFiles` landed: 154 of 1,564 open pull requests (9.8%) had a
|
|
355
|
+
// real file list and three zeroed columns, and were reported UNKNOWN — no large-PR flag and no
|
|
356
|
+
// blast-radius chip — purely because this test ran first and read only the columns.
|
|
357
|
+
//
|
|
358
|
+
// ⚠ THE REFUSAL STILL STANDS WHEN THE FILES AGREE. All-zero columns AND a file list that sums
|
|
359
|
+
// to zero is still UNKNOWN, so a genuinely empty pull request never acquires a fabricated size.
|
|
360
|
+
const filesSum = pr.files.reduce((n, f) => n + ((f?.additions ?? 0) + (f?.deletions ?? 0)), 0);
|
|
361
|
+
if (pr.additions === 0 && pr.deletions === 0 && pr.changedFiles === 0 && filesSum === 0) {
|
|
362
|
+
return UNKNOWN;
|
|
363
|
+
}
|
|
364
|
+
// Trap 3: an empty array with a non-zero size is the routing-files sentinel (or a partial
|
|
365
|
+
// write); either way we hold no per-file breakdown to classify.
|
|
366
|
+
if (pr.files.length === 0)
|
|
367
|
+
return UNKNOWN;
|
|
368
|
+
let codeLoc = 0;
|
|
369
|
+
for (const f of pr.files) {
|
|
370
|
+
if (typeof f?.path !== 'string')
|
|
371
|
+
continue;
|
|
372
|
+
if (isNonCodeFile(f.path))
|
|
373
|
+
continue;
|
|
374
|
+
codeLoc += (f.additions ?? 0) + (f.deletions ?? 0);
|
|
375
|
+
}
|
|
376
|
+
const codeLocIsLowerBound = pr.files.length >= PR_FILES_PAGE_CAP || pr.changedFiles > pr.files.length;
|
|
377
|
+
return { codeLoc, codeLocIsLowerBound };
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* The product default when an account has stored no threshold of its own. Mirrors
|
|
381
|
+
* `LARGE_PR_CODE_LOC_DEFAULT` in `@pierre-review/shared` — one number, spelled in the two
|
|
382
|
+
* places that need it without the backend importing a value from a types-only package at
|
|
383
|
+
* runtime (`shared` is `import type` only on this side; see PACKAGING).
|
|
384
|
+
*/
|
|
385
|
+
export function resolveLargePrThreshold(stored) {
|
|
386
|
+
return stored != null && Number.isInteger(stored) && stored > 0 ? stored : 1500;
|
|
387
|
+
}
|
|
388
|
+
//# sourceMappingURL=code-loc.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { getAiUsageSummary, getSummaryTurnCount } from './usage.js';
|
|
2
|
+
// USD → credits conversion, MIRRORED from @pierre-review/shared AI_CREDITS_PER_USD. Inlined
|
|
3
|
+
// here (not imported) because the release guard forbids a real runtime import of the
|
|
4
|
+
// types-only shared package into release/dist. TWO sites, kept in lockstep: shared
|
|
5
|
+
// (types.ts) + this one. (The pro plugin's third copy in insights/routes.ts is gone — it
|
|
6
|
+
// reads ctx.aiCredits now.) $1 of model cost = 1250 credits (1 credit ≈ $0.0008).
|
|
7
|
+
export const AI_CREDITS_PER_USD = 1250;
|
|
8
|
+
// The two AI seams are metered DIFFERENTLY:
|
|
9
|
+
// - SUMMARY (cheap one-shot Haiku completions — digests, sprint report, insights chat, PR
|
|
10
|
+
// summary, CI analysis, themes, …) is metered by a monthly TURN COUNT. Each real (billed)
|
|
11
|
+
// completion is one turn; a $0 cache hit / ambient-session call is free and doesn't count.
|
|
12
|
+
// - AGENT (Agent-SDK runs — Claude Review, AI Fix) is metered by CREDITS ($ cost), because a
|
|
13
|
+
// single agentic run's cost varies wildly and a flat turn count would be unfair.
|
|
14
|
+
// Defaults for a paid cloud account (both reset at the UTC month boundary):
|
|
15
|
+
export const DEFAULT_SUMMARY_TURN_LIMIT = 500; // ~$5/mo of Haiku at a blended ~$0.01/turn
|
|
16
|
+
export const DEFAULT_AGENT_CREDIT_ALLOWANCE = 15 * AI_CREDITS_PER_USD; // 18,750 cr = $15/mo
|
|
17
|
+
const toCredits = (usd) => Math.round(usd * AI_CREDITS_PER_USD);
|
|
18
|
+
// UTC first-of-month epoch ms — the month-to-date window start (matches the /api/pro/ai-usage
|
|
19
|
+
// route). Monthly reset is automatic: the window rolls at the UTC month boundary.
|
|
20
|
+
export function monthStartMs(nowMs) {
|
|
21
|
+
const d = new Date(nowMs);
|
|
22
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1);
|
|
23
|
+
}
|
|
24
|
+
// The account's SUMMARY monthly turn limit, or null when unmetered.
|
|
25
|
+
// - local (isLocal): unmetered — null (never blocked; today's behavior).
|
|
26
|
+
// - paid cloud (plan !== 'free'): the plan default turn limit.
|
|
27
|
+
// - free cloud: a zero limit (belt-and-braces — the /api/pro/* 402 gate blocks these first).
|
|
28
|
+
function summaryTurnLimitFor(account) {
|
|
29
|
+
if (account.isLocal)
|
|
30
|
+
return null;
|
|
31
|
+
if (account.plan !== 'free')
|
|
32
|
+
return DEFAULT_SUMMARY_TURN_LIMIT;
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
// The account's AGENT credit allowance, or null when unmetered. The per-account override
|
|
36
|
+
// column (accounts.aiCreditAllowance) now governs the AGENT budget (a forward hook for
|
|
37
|
+
// top-ups / alternate plans); summary is a flat turn count with no per-account override yet.
|
|
38
|
+
function agentAllowanceFor(account) {
|
|
39
|
+
if (account.isLocal)
|
|
40
|
+
return null;
|
|
41
|
+
if (account.plan !== 'free')
|
|
42
|
+
return account.aiCreditAllowance ?? DEFAULT_AGENT_CREDIT_ALLOWANCE;
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
// The PURE metering computation (no I/O) — month-to-date usage vs. the per-seam budgets. Split
|
|
46
|
+
// out from aiCreditStatus so the budget math is unit-testable without a DB.
|
|
47
|
+
export function computeAiCreditStatus(account, usage) {
|
|
48
|
+
const summaryTurnLimit = summaryTurnLimitFor(account);
|
|
49
|
+
const summaryTurnsUsed = usage.summaryTurns;
|
|
50
|
+
const summaryTurnsRemaining = summaryTurnLimit == null ? null : Math.max(0, summaryTurnLimit - summaryTurnsUsed);
|
|
51
|
+
const summaryBlocked = summaryTurnsRemaining != null && summaryTurnsRemaining <= 0;
|
|
52
|
+
const agentAllowanceCredits = agentAllowanceFor(account);
|
|
53
|
+
const agentCreditsUsed = toCredits(usage.agentUsd);
|
|
54
|
+
const agentCreditsRemaining = agentAllowanceCredits == null ? null : Math.max(0, agentAllowanceCredits - agentCreditsUsed);
|
|
55
|
+
const agentBlocked = agentCreditsRemaining != null && agentCreditsRemaining <= 0;
|
|
56
|
+
return {
|
|
57
|
+
summaryTurnsUsed,
|
|
58
|
+
summaryTurnLimit,
|
|
59
|
+
summaryTurnsRemaining,
|
|
60
|
+
summaryBlocked,
|
|
61
|
+
agentCreditsUsed,
|
|
62
|
+
agentAllowanceCredits,
|
|
63
|
+
agentCreditsRemaining,
|
|
64
|
+
agentBlocked,
|
|
65
|
+
blocked: summaryBlocked,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// The per-account metering status: reads the month-to-date ledger (summary turn count + agent
|
|
69
|
+
// $ spend) and applies the pure computation above. This is what ctx.aiCredits.check calls per
|
|
70
|
+
// generation, and what /api/me + /api/pro/ai-usage surface.
|
|
71
|
+
export async function aiCreditStatus(account, nowMs) {
|
|
72
|
+
const since = monthStartMs(nowMs);
|
|
73
|
+
const [summaryTurns, usage] = await Promise.all([
|
|
74
|
+
getSummaryTurnCount(account.id, since),
|
|
75
|
+
getAiUsageSummary(account.id, since),
|
|
76
|
+
]);
|
|
77
|
+
return computeAiCreditStatus(account, { summaryTurns, agentUsd: usage.agentUsd });
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=credits.js.map
|