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,216 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { ghRestGet, ghRestPost } from '../github/client.js';
|
|
4
|
+
import { buildAnchorIndex, fallbackAnchor, isFindingAnchored, splitDiffByFile, } from '../github/diff-anchor.js';
|
|
5
|
+
const execFileAsync = promisify(execFile);
|
|
6
|
+
// The pure unified-diff anchoring helpers now live in github/diff-anchor.ts (so
|
|
7
|
+
// they're reusable by the on-demand PR write-action paths). Re-export them here
|
|
8
|
+
// so existing importers (agent.ts, routing.ts, claude-review.ts, the review
|
|
9
|
+
// tests) keep their `./post-review.js` import path unchanged.
|
|
10
|
+
export { buildAnchorIndex, buildFileAnchors, extractHunk, fallbackAnchor, HUNK_RE, isFindingAnchored, splitDiffByFile, } from '../github/diff-anchor.js';
|
|
11
|
+
// Drop files matched by `isNoise` (lockfiles/generated) from the diff, returning
|
|
12
|
+
// the stripped diff + the excluded paths (recorded on the run).
|
|
13
|
+
export function stripNoiseFromDiff(diff, isNoise) {
|
|
14
|
+
const kept = [];
|
|
15
|
+
const excluded = [];
|
|
16
|
+
for (const seg of splitDiffByFile(diff)) {
|
|
17
|
+
if (isNoise(seg.path))
|
|
18
|
+
excluded.push(seg.path);
|
|
19
|
+
else
|
|
20
|
+
kept.push(seg.text);
|
|
21
|
+
}
|
|
22
|
+
return { diff: kept.join('\n'), excluded };
|
|
23
|
+
}
|
|
24
|
+
// Truncate a (noise-stripped) unified diff to roughly `maxChars`, cutting only at
|
|
25
|
+
// whole-FILE boundaries (never mid-hunk), for the PROMPT only — routing + anchoring
|
|
26
|
+
// keep the full diff. Whole file segments are kept in order until the next one would
|
|
27
|
+
// blow the budget; the rest are reported as omitted. The first file is always kept
|
|
28
|
+
// (even if it alone exceeds the budget), so a single huge file still gets reviewed.
|
|
29
|
+
export function capDiff(diff, maxChars) {
|
|
30
|
+
const totalBytes = diff.length;
|
|
31
|
+
if (totalBytes <= maxChars) {
|
|
32
|
+
return { diff, capped: false, omittedFiles: [], totalBytes };
|
|
33
|
+
}
|
|
34
|
+
const segs = splitDiffByFile(diff);
|
|
35
|
+
const kept = [];
|
|
36
|
+
const omittedFiles = [];
|
|
37
|
+
let used = 0;
|
|
38
|
+
for (const seg of segs) {
|
|
39
|
+
const cost = seg.text.length + 1; // +1 for the '\n' join
|
|
40
|
+
if (kept.length === 0 || used + cost <= maxChars) {
|
|
41
|
+
kept.push(seg.text);
|
|
42
|
+
used += cost;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
omittedFiles.push(seg.path);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
diff: kept.join('\n'),
|
|
50
|
+
capped: omittedFiles.length > 0,
|
|
51
|
+
omittedFiles,
|
|
52
|
+
totalBytes,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
// Appended to a comment placed on a fallback line, so PR readers know the inline
|
|
56
|
+
// position is approximate (the finding's real line isn't part of this diff).
|
|
57
|
+
export const FALLBACK_ANCHOR_NOTE = '_The line this refers to isn’t part of the PR’s diff, so it’s anchored to the file’s first change._';
|
|
58
|
+
// Build the GitHub review payload from the user's authored body/verdict plus the
|
|
59
|
+
// findings they ticked. Each ticked finding is routed:
|
|
60
|
+
// • anchorable on its own line → inline comment on that line
|
|
61
|
+
// • unanchored but its file IS in the diff → inline comment on the file's first
|
|
62
|
+
// change (with a note)
|
|
63
|
+
// • its file is NOT in the diff → a standalone PR-level comment (so a
|
|
64
|
+
// deep review's finding on an unchanged
|
|
65
|
+
// file still posts, marked accordingly)
|
|
66
|
+
export function buildReview(input) {
|
|
67
|
+
const index = buildAnchorIndex(input.diff);
|
|
68
|
+
const comments = [];
|
|
69
|
+
const prComments = [];
|
|
70
|
+
const inlineFindingIds = [];
|
|
71
|
+
for (const f of input.includedFindings) {
|
|
72
|
+
if (f.line != null && isFindingAnchored(index, f.path, f.line, f.side)) {
|
|
73
|
+
comments.push({
|
|
74
|
+
path: f.path,
|
|
75
|
+
line: f.line,
|
|
76
|
+
side: f.side,
|
|
77
|
+
body: findingCommentBody(f),
|
|
78
|
+
});
|
|
79
|
+
inlineFindingIds.push(f.id);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
// The finding's own line isn't an addable diff position — anchor it to the
|
|
83
|
+
// file's first change (added preferred) so it can still post inline. When the
|
|
84
|
+
// file itself isn't in the diff there's no diff line to attach to, so it posts
|
|
85
|
+
// as a standalone PR-level comment marked as outside the PR's diff.
|
|
86
|
+
const fb = fallbackAnchor(index, f.path);
|
|
87
|
+
if (fb) {
|
|
88
|
+
comments.push({
|
|
89
|
+
path: f.path,
|
|
90
|
+
line: fb.line,
|
|
91
|
+
side: fb.side,
|
|
92
|
+
body: findingCommentBody(f, { fallbackNote: true }),
|
|
93
|
+
});
|
|
94
|
+
inlineFindingIds.push(f.id);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
prComments.push({ findingId: f.id, path: f.path, body: prLevelFindingBody(f) });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
preview: {
|
|
102
|
+
commitId: input.commitId,
|
|
103
|
+
body: input.body,
|
|
104
|
+
event: input.event,
|
|
105
|
+
comments,
|
|
106
|
+
prComments,
|
|
107
|
+
},
|
|
108
|
+
inlineFindingIds,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// The hidden provenance stamp on EVERY finding comment Limn posts (inline or PR-level). It is how
|
|
112
|
+
// a later review tells its own comments from other reviewers' (db/review-threads-for-review.ts
|
|
113
|
+
// `isLimnPostedComment`, together with the author being the account's own login — Limn posts as
|
|
114
|
+
// the reader, so neither fact alone is enough). It survives an edit on GitHub, which the old
|
|
115
|
+
// text-prefix match did not. The `pierre:claude-review` prefix is deliberate: it is also the
|
|
116
|
+
// 'pierre' fingerprint (sync/review-fingerprint.ts). ⚠ The spelling is permanent once posted.
|
|
117
|
+
export const FINDING_COMMENT_MARKER = '<!-- pierre:claude-review-finding v=1 -->';
|
|
118
|
+
// A finding's inline comment body: the user's reworded text if they wrote one,
|
|
119
|
+
// else Claude's wording — with an optional fenced ```suggestion block appended
|
|
120
|
+
// (GitHub renders it as an applyable suggestion). Takes a minimal shape so it
|
|
121
|
+
// works for both the wire ClaudeFinding and a raw DB row.
|
|
122
|
+
//
|
|
123
|
+
// `fallbackNote` is set when the comment is posted on a fallback line (the
|
|
124
|
+
// finding's real line isn't in the diff). Then we (a) append FALLBACK_ANCHOR_NOTE
|
|
125
|
+
// and (b) render any suggestion as a PLAIN code block rather than ```suggestion —
|
|
126
|
+
// an applyable suggestion on the wrong line would offer to corrupt that line.
|
|
127
|
+
//
|
|
128
|
+
// `storyLead` is a USER-STORY finding's first line ("BMD-1040 · AC2 (partly met): <criterion>",
|
|
129
|
+
// shared `storyCommentLead`). It is not stored in the body — on screen the card sits under its
|
|
130
|
+
// story — so it is added HERE, the one place every story comment is built (single post and Submit
|
|
131
|
+
// review alike), and GitHub still names the story.
|
|
132
|
+
export function findingCommentBody(f, opts) {
|
|
133
|
+
const body = f.editedBody && f.editedBody.trim() ? f.editedBody : f.body;
|
|
134
|
+
const parts = [f.storyLead, body].filter((p) => p != null && p.trim() !== '');
|
|
135
|
+
if (f.suggestion && f.suggestion.trim()) {
|
|
136
|
+
parts.push(opts?.fallbackNote
|
|
137
|
+
? `\`\`\`\n${f.suggestion}\n\`\`\``
|
|
138
|
+
: `\`\`\`suggestion\n${f.suggestion}\n\`\`\``);
|
|
139
|
+
}
|
|
140
|
+
if (opts?.fallbackNote)
|
|
141
|
+
parts.push(FALLBACK_ANCHOR_NOTE);
|
|
142
|
+
parts.push(FINDING_COMMENT_MARKER);
|
|
143
|
+
return parts.join('\n\n');
|
|
144
|
+
}
|
|
145
|
+
// Note appended to a PR-level comment, so readers know the referenced code is NOT
|
|
146
|
+
// part of this PR's changes (it can't be an inline comment — there's no diff line).
|
|
147
|
+
export const OUTSIDE_DIFF_NOTE = '_This refers to a file that isn’t part of this PR’s diff, so it’s posted as a PR-level comment rather than inline._';
|
|
148
|
+
// Body for a finding posted as a standalone PR-level (issue) comment. Since it
|
|
149
|
+
// isn't attached to a diff line, prefix the file (and line, if known) for context,
|
|
150
|
+
// then the finding text (the user's reword if any, else Claude's), and append a note
|
|
151
|
+
// that it's outside the PR's diff. Any suggestion renders as a PLAIN code block — an
|
|
152
|
+
// applyable ```suggestion only makes sense on a real diff line.
|
|
153
|
+
// A finding with NO file (`path` blank — a user-story finding about the whole change, see
|
|
154
|
+
// claude-review/ticket.ts `storyFindingsFrom`) is about the PR, not about a file outside it: it
|
|
155
|
+
// carries neither the file line nor the outside-the-diff note.
|
|
156
|
+
export function prLevelFindingBody(f) {
|
|
157
|
+
const body = f.editedBody && f.editedBody.trim() ? f.editedBody : f.body;
|
|
158
|
+
const hasPath = f.path.trim() !== '';
|
|
159
|
+
const ref = f.line != null ? `${f.path}:${f.line}` : f.path;
|
|
160
|
+
// A story finding's lead comes FIRST, so the comment opens on the story it is about.
|
|
161
|
+
const parts = [f.storyLead, hasPath ? `**\`${ref}\`**` : null, body].filter((p) => p != null && p.trim() !== '');
|
|
162
|
+
if (f.suggestion && f.suggestion.trim()) {
|
|
163
|
+
parts.push(`\`\`\`\n${f.suggestion}\n\`\`\``);
|
|
164
|
+
}
|
|
165
|
+
if (hasPath)
|
|
166
|
+
parts.push(OUTSIDE_DIFF_NOTE);
|
|
167
|
+
parts.push(FINDING_COMMENT_MARKER);
|
|
168
|
+
return parts.join('\n\n');
|
|
169
|
+
}
|
|
170
|
+
// The PR's current head SHA (to detect head-moved before posting).
|
|
171
|
+
export async function fetchCurrentHeadSha(owner, name, prNumber) {
|
|
172
|
+
const pull = await ghRestGet(`/repos/${owner}/${name}/pulls/${prNumber}`);
|
|
173
|
+
return pull.head.sha;
|
|
174
|
+
}
|
|
175
|
+
// The PR's unified diff, via the gh CLI (matches GitHub's 3-dot view and reuses
|
|
176
|
+
// the gh auth/SSO already required by the app). Bounded so a huge PR can't hang.
|
|
177
|
+
export async function fetchPrDiff(owner, name, prNumber) {
|
|
178
|
+
const { stdout } = await execFileAsync('gh', ['pr', 'diff', String(prNumber), '--repo', `${owner}/${name}`], { maxBuffer: 64 * 1024 * 1024, timeout: 60_000 });
|
|
179
|
+
return stdout;
|
|
180
|
+
}
|
|
181
|
+
// POST a single PR review with inline comments + body + verdict. Inline comments
|
|
182
|
+
// REQUIRE this REST endpoint. `commit_id` pins the review to the head SHA.
|
|
183
|
+
export async function submitGithubReview(input) {
|
|
184
|
+
const res = await ghRestPost(`/repos/${input.owner}/${input.name}/pulls/${input.prNumber}/reviews`, {
|
|
185
|
+
commit_id: input.commitId,
|
|
186
|
+
body: input.body,
|
|
187
|
+
event: input.event,
|
|
188
|
+
comments: input.comments.map((c) => ({
|
|
189
|
+
path: c.path,
|
|
190
|
+
line: c.line,
|
|
191
|
+
side: c.side,
|
|
192
|
+
body: c.body,
|
|
193
|
+
})),
|
|
194
|
+
});
|
|
195
|
+
return { reviewId: String(res.id) };
|
|
196
|
+
}
|
|
197
|
+
// Post a SINGLE standalone inline review comment (not part of a review draft).
|
|
198
|
+
// `commit_id` pins it to the head SHA; the line must be addable on that side.
|
|
199
|
+
export async function submitGithubComment(input) {
|
|
200
|
+
const res = await ghRestPost(`/repos/${input.owner}/${input.name}/pulls/${input.prNumber}/comments`, {
|
|
201
|
+
body: input.body,
|
|
202
|
+
commit_id: input.commitId,
|
|
203
|
+
path: input.path,
|
|
204
|
+
line: input.line,
|
|
205
|
+
side: input.side,
|
|
206
|
+
});
|
|
207
|
+
return { commentId: String(res.id) };
|
|
208
|
+
}
|
|
209
|
+
// Post a SINGLE standalone PR-level (issue) comment. Used for an unanchored finding
|
|
210
|
+
// the user chooses to post individually rather than forcing it onto a diff line.
|
|
211
|
+
// Issue comments aren't pinned to a commit or a line, so there's no commit_id/side.
|
|
212
|
+
export async function submitGithubIssueComment(input) {
|
|
213
|
+
const res = await ghRestPost(`/repos/${input.owner}/${input.name}/issues/${input.prNumber}/comments`, { body: input.body });
|
|
214
|
+
return { commentId: String(res.id) };
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=post-review.js.map
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { isNoiseFile } from './prepare.js';
|
|
2
|
+
import { buildAnchorIndex, fallbackAnchor, fetchCurrentHeadSha, fetchPrDiff, findingCommentBody, isFindingAnchored, prLevelFindingBody, stripNoiseFromDiff, submitGithubComment, submitGithubIssueComment, submitGithubReview, } from './post-review.js';
|
|
3
|
+
// The GitHub-write half of the ctx.review seam. Security-sensitive (per-account gh token +
|
|
4
|
+
// line-anchoring), so it stays CORE — the plugin hands over the user's ticked findings (read
|
|
5
|
+
// from the core tables) + body/verdict, and core re-checks the head, re-anchors against the
|
|
6
|
+
// live diff, and submits. GitHub errors THROW (the plugin's route maps them to 502); a moved
|
|
7
|
+
// head returns { headMoved: true } (→ 409). Findings' `body` is pre-resolved (editedBody ??
|
|
8
|
+
// body) by the plugin, so findingCommentBody sees editedBody=null and uses `body` as-is.
|
|
9
|
+
// ctx.review.postReview — submit ONE GitHub review (inline comments + body + verdict), pinned
|
|
10
|
+
// to reviewHeadSha. dryRun returns the preview instead of posting.
|
|
11
|
+
export async function postReview(args) {
|
|
12
|
+
const currentHead = await fetchCurrentHeadSha(args.owner, args.name, args.prNumber);
|
|
13
|
+
if (currentHead !== args.reviewHeadSha)
|
|
14
|
+
return { headMoved: true };
|
|
15
|
+
const { diff } = stripNoiseFromDiff(await fetchPrDiff(args.owner, args.name, args.prNumber), isNoiseFile);
|
|
16
|
+
const index = buildAnchorIndex(diff);
|
|
17
|
+
const comments = [];
|
|
18
|
+
const prComments = [];
|
|
19
|
+
const inlineFindingIds = [];
|
|
20
|
+
for (const f of args.includedFindings) {
|
|
21
|
+
const shape = { body: f.body, editedBody: null, suggestion: f.suggestion, storyLead: f.storyLead };
|
|
22
|
+
// Anchorable on its own line → inline comment there.
|
|
23
|
+
if (f.line != null && isFindingAnchored(index, f.path, f.line, f.side)) {
|
|
24
|
+
comments.push({ path: f.path, line: f.line, side: f.side, body: findingCommentBody(shape) });
|
|
25
|
+
inlineFindingIds.push(f.id);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
// Its own line isn't addable → anchor to the file's first change if the file is in the
|
|
29
|
+
// diff (inline, with a note); else post as a standalone PR-level comment.
|
|
30
|
+
const fb = fallbackAnchor(index, f.path);
|
|
31
|
+
if (fb) {
|
|
32
|
+
comments.push({
|
|
33
|
+
path: f.path,
|
|
34
|
+
line: fb.line,
|
|
35
|
+
side: fb.side,
|
|
36
|
+
body: findingCommentBody(shape, { fallbackNote: true }),
|
|
37
|
+
});
|
|
38
|
+
inlineFindingIds.push(f.id);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
prComments.push({
|
|
42
|
+
findingId: f.id,
|
|
43
|
+
path: f.path,
|
|
44
|
+
body: prLevelFindingBody({
|
|
45
|
+
path: f.path,
|
|
46
|
+
line: f.line,
|
|
47
|
+
body: f.body,
|
|
48
|
+
editedBody: null,
|
|
49
|
+
suggestion: f.suggestion,
|
|
50
|
+
storyLead: f.storyLead,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Bot-Triage WS2c — the Limn provenance stamp. UNCONDITIONAL, and that is the point: this hidden,
|
|
56
|
+
// invisible, idempotent HTML marker is the ONLY producer of the 'pierre' AutomatedReviewerKind
|
|
57
|
+
// (`sync/review-fingerprint.ts` matches exactly this string). Everything downstream of that
|
|
58
|
+
// sentinel — the Bot-ROI "Limn · Claude" row, the ai_verbatim-vs-human_curated provenance,
|
|
59
|
+
// BotPrsDetail's 'pierre' tab, the bot_only_review risk flag — goes dark without it, so the
|
|
60
|
+
// account setting that used to gate it was a switch for silently deleting an analytics lane.
|
|
61
|
+
// DO NOT re-gate this. `PostReviewArgs.pierreMarker?`/`pierreFooter?` remain DECLARED on the
|
|
62
|
+
// contract (both sides, back-compat) but nothing passes them any more; the visible
|
|
63
|
+
// "🤖 Reviewed with Limn + Claude" footer appeared in no detector and is gone.
|
|
64
|
+
const finalBody = args.body + '\n\n<!-- pierre:claude-review v=1 -->';
|
|
65
|
+
const preview = {
|
|
66
|
+
commitId: args.reviewHeadSha,
|
|
67
|
+
body: finalBody,
|
|
68
|
+
event: args.verdict,
|
|
69
|
+
comments,
|
|
70
|
+
prComments,
|
|
71
|
+
};
|
|
72
|
+
if (args.dryRun)
|
|
73
|
+
return { preview };
|
|
74
|
+
const { reviewId: ghReviewId } = await submitGithubReview({
|
|
75
|
+
owner: args.owner,
|
|
76
|
+
name: args.name,
|
|
77
|
+
prNumber: args.prNumber,
|
|
78
|
+
commitId: args.reviewHeadSha,
|
|
79
|
+
body: finalBody,
|
|
80
|
+
event: args.verdict,
|
|
81
|
+
comments,
|
|
82
|
+
});
|
|
83
|
+
// Off-diff findings post as standalone PR-level comments alongside the review. Each is
|
|
84
|
+
// best-effort: a single failed comment must NOT strand the already-posted (irreversible)
|
|
85
|
+
// review — collect what lands; the caller stamps only those.
|
|
86
|
+
const prCommentResults = [];
|
|
87
|
+
for (const pc of prComments) {
|
|
88
|
+
try {
|
|
89
|
+
const { commentId } = await submitGithubIssueComment({
|
|
90
|
+
owner: args.owner,
|
|
91
|
+
name: args.name,
|
|
92
|
+
prNumber: args.prNumber,
|
|
93
|
+
body: pc.body,
|
|
94
|
+
});
|
|
95
|
+
prCommentResults.push({ findingId: pc.findingId, commentId });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* best-effort — leave the finding un-posted rather than strand the posted review */
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
postedReviewId: ghReviewId,
|
|
103
|
+
inlineFindingIds,
|
|
104
|
+
prComments: prCommentResults,
|
|
105
|
+
commentCount: comments.length,
|
|
106
|
+
prCommentCount: prCommentResults.length,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// ctx.review.postFinding — post ONE finding as a standalone inline / PR-level comment. Uses
|
|
110
|
+
// the stored `anchored` to short-circuit to an on-line inline comment; else consults the live
|
|
111
|
+
// diff to re-anchor (inline) or fall back to a PR-level comment.
|
|
112
|
+
export async function postFinding(args) {
|
|
113
|
+
const { owner, name, prNumber, reviewHeadSha, finding: f } = args;
|
|
114
|
+
const currentHead = await fetchCurrentHeadSha(owner, name, prNumber);
|
|
115
|
+
if (currentHead !== reviewHeadSha)
|
|
116
|
+
return { headMoved: true };
|
|
117
|
+
const shape = { body: f.body, editedBody: null, suggestion: f.suggestion, storyLead: f.storyLead };
|
|
118
|
+
if (f.line != null && f.anchored) {
|
|
119
|
+
const { commentId } = await submitGithubComment({
|
|
120
|
+
owner,
|
|
121
|
+
name,
|
|
122
|
+
prNumber,
|
|
123
|
+
commitId: reviewHeadSha,
|
|
124
|
+
path: f.path,
|
|
125
|
+
line: f.line,
|
|
126
|
+
side: f.side,
|
|
127
|
+
body: findingCommentBody(shape),
|
|
128
|
+
});
|
|
129
|
+
return { commentId, postedCommentKind: 'inline' };
|
|
130
|
+
}
|
|
131
|
+
const { diff } = stripNoiseFromDiff(await fetchPrDiff(owner, name, prNumber), isNoiseFile);
|
|
132
|
+
const fb = fallbackAnchor(buildAnchorIndex(diff), f.path);
|
|
133
|
+
if (fb) {
|
|
134
|
+
const { commentId } = await submitGithubComment({
|
|
135
|
+
owner,
|
|
136
|
+
name,
|
|
137
|
+
prNumber,
|
|
138
|
+
commitId: reviewHeadSha,
|
|
139
|
+
path: f.path,
|
|
140
|
+
line: fb.line,
|
|
141
|
+
side: fb.side,
|
|
142
|
+
body: findingCommentBody(shape, { fallbackNote: true }),
|
|
143
|
+
});
|
|
144
|
+
return { commentId, postedCommentKind: 'inline' };
|
|
145
|
+
}
|
|
146
|
+
const { commentId } = await submitGithubIssueComment({
|
|
147
|
+
owner,
|
|
148
|
+
name,
|
|
149
|
+
prNumber,
|
|
150
|
+
body: prLevelFindingBody({
|
|
151
|
+
path: f.path,
|
|
152
|
+
line: f.line,
|
|
153
|
+
body: f.body,
|
|
154
|
+
editedBody: null,
|
|
155
|
+
suggestion: f.suggestion,
|
|
156
|
+
storyLead: f.storyLead,
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
return { commentId, postedCommentKind: 'pr_comment' };
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=post-seam.js.map
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { config } from '../config.js';
|
|
2
|
+
import { capDiff, fetchPrDiff, splitDiffByFile, stripNoiseFromDiff, } from './post-review.js';
|
|
3
|
+
// Core diff-prep for the Claude Review seam (ctx.review.prepareReview). Everything that
|
|
4
|
+
// touches the diff lives in CORE so the mode-routing metrics the plugin consumes and the
|
|
5
|
+
// run-time/post-time line-anchoring all derive from the SAME noise-stripped diff (anchoring
|
|
6
|
+
// drift would corrupt inline comments). The plugin gets DATA back (a PreparedReview) and
|
|
7
|
+
// only runs the pure routing decision + prompt formatting. (The noise matcher moved here
|
|
8
|
+
// from prompt.ts and analyzeDiff from routing.ts — both were CORE-only helpers.)
|
|
9
|
+
// ---- Noise-file matcher (lockfiles / generated / vendored artifacts) ----
|
|
10
|
+
// Globs for files whose diffs are noise to a human reviewer: stripped from the diff so the
|
|
11
|
+
// agent spends its budget on real code, not a 4000-line lockfile churn. Only the three glob
|
|
12
|
+
// shapes below are supported (exact basename, `*.ext` suffix, `**/segment/**` path segment).
|
|
13
|
+
//
|
|
14
|
+
// ⚠ THIS LIST IS A PAID AGENT'S DIFF BUDGET — DO NOT WIDEN IT FOR ANOTHER FEATURE. It deliberately
|
|
15
|
+
// keeps `.md`, `.yml`, `.json` and `docs/` IN the diff, because a review agent reading a config
|
|
16
|
+
// change is doing its job. The large-PR flag needs a much broader "is this code?" answer, and it
|
|
17
|
+
// has its OWN classifier for exactly that reason: `isNonCodeFile` in `db/code-loc.ts`, which
|
|
18
|
+
// documents how the two (and the frontend's narrower `isLockFile`) relate. Editing this list to
|
|
19
|
+
// satisfy that feature would silently change what Claude Review reviews, with no test to catch it.
|
|
20
|
+
export const NOISE_GLOBS = [
|
|
21
|
+
'package-lock.json',
|
|
22
|
+
'pnpm-lock.yaml',
|
|
23
|
+
'yarn.lock',
|
|
24
|
+
'npm-shrinkwrap.json',
|
|
25
|
+
'Cargo.lock',
|
|
26
|
+
'poetry.lock',
|
|
27
|
+
'Gemfile.lock',
|
|
28
|
+
'composer.lock',
|
|
29
|
+
'go.sum',
|
|
30
|
+
'*.lock',
|
|
31
|
+
'*.min.js',
|
|
32
|
+
'*.min.css',
|
|
33
|
+
'*.map',
|
|
34
|
+
'*.snap',
|
|
35
|
+
'**/dist/**',
|
|
36
|
+
'**/build/**',
|
|
37
|
+
'**/node_modules/**',
|
|
38
|
+
'**/__snapshots__/**',
|
|
39
|
+
];
|
|
40
|
+
function normalizePath(path) {
|
|
41
|
+
let p = path.replace(/\\/g, '/');
|
|
42
|
+
if (p.startsWith('./'))
|
|
43
|
+
p = p.slice(2);
|
|
44
|
+
return p;
|
|
45
|
+
}
|
|
46
|
+
function basename(path) {
|
|
47
|
+
const p = normalizePath(path);
|
|
48
|
+
const idx = p.lastIndexOf('/');
|
|
49
|
+
return idx === -1 ? p : p.slice(idx + 1);
|
|
50
|
+
}
|
|
51
|
+
function matchesGlob(glob, fullPath, base) {
|
|
52
|
+
const segMatch = /^\*\*\/(.+)\/\*\*$/.exec(glob);
|
|
53
|
+
if (segMatch) {
|
|
54
|
+
const segment = segMatch[1];
|
|
55
|
+
if (!segment)
|
|
56
|
+
return false;
|
|
57
|
+
const needle = `/${segment}/`;
|
|
58
|
+
return fullPath === segment || fullPath.startsWith(`${segment}/`) || fullPath.includes(needle);
|
|
59
|
+
}
|
|
60
|
+
if (glob.startsWith('*.')) {
|
|
61
|
+
const suffix = glob.slice(1);
|
|
62
|
+
return base.length > suffix.length && base.endsWith(suffix);
|
|
63
|
+
}
|
|
64
|
+
return base === glob;
|
|
65
|
+
}
|
|
66
|
+
// True if `path` looks like a lockfile / generated / vendored artifact to hide from review.
|
|
67
|
+
export function isNoiseFile(path) {
|
|
68
|
+
const full = normalizePath(path);
|
|
69
|
+
const base = basename(full);
|
|
70
|
+
for (const glob of NOISE_GLOBS) {
|
|
71
|
+
if (matchesGlob(glob, full, base))
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
// ---- Per-file diff analysis (feeds the plugin's decideReviewMode via PreparedReview) ----
|
|
77
|
+
// Path shapes that ARE an interface/contract definition (editing one is very likely a
|
|
78
|
+
// cross-cutting change whose consumers live outside the diff). Matched on the path.
|
|
79
|
+
export const API_PATH_PATTERNS = [
|
|
80
|
+
/\.(proto|thrift|graphql|gql)$/i,
|
|
81
|
+
/\.d\.ts$/i,
|
|
82
|
+
/(^|\/)(openapi|swagger)[^/]*\.(ya?ml|json)$/i,
|
|
83
|
+
/(^|\/)migrations?\//i,
|
|
84
|
+
];
|
|
85
|
+
// Markers that, on a REMOVED or MODIFIED diff line, indicate a change to an exported/public
|
|
86
|
+
// symbol (a signature/removal — the risky case). Over-matching only over-routes (safe).
|
|
87
|
+
export const EXPORT_MARKERS = [
|
|
88
|
+
/\bexport\b/,
|
|
89
|
+
/\bmodule\.exports\b|\bexports\.\w/,
|
|
90
|
+
/\bpub(\s+|\()/,
|
|
91
|
+
/\b(public|protected)\s+\w/,
|
|
92
|
+
/\bfunc\b\s*(\([^)]*\)\s*)?[A-Z]\w*\s*\(/,
|
|
93
|
+
/\b(type|const|var)\s+[A-Z]\w*/,
|
|
94
|
+
/\b__all__\b/,
|
|
95
|
+
];
|
|
96
|
+
function lineTouchesApi(content) {
|
|
97
|
+
return EXPORT_MARKERS.some((re) => re.test(content));
|
|
98
|
+
}
|
|
99
|
+
function pathTouchesApi(path) {
|
|
100
|
+
return API_PATH_PATTERNS.some((re) => re.test(path));
|
|
101
|
+
}
|
|
102
|
+
// Walk one noise-stripped unified diff into per-file change metrics. Header lines
|
|
103
|
+
// (diff/index/---/+++/@@/mode/rename/binary) are skipped; only real body +/- lines count.
|
|
104
|
+
export function analyzeDiff(diff) {
|
|
105
|
+
const out = [];
|
|
106
|
+
for (const seg of splitDiffByFile(diff)) {
|
|
107
|
+
let additions = 0;
|
|
108
|
+
let deletions = 0;
|
|
109
|
+
let isNew = false;
|
|
110
|
+
let apiTouch = pathTouchesApi(seg.path);
|
|
111
|
+
let inHunk = false;
|
|
112
|
+
for (const line of seg.text.split('\n')) {
|
|
113
|
+
if (line.startsWith('@@')) {
|
|
114
|
+
inHunk = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (!inHunk) {
|
|
118
|
+
if (line.startsWith('new file mode') ||
|
|
119
|
+
(line.startsWith('--- ') && line.slice(4).trim() === '/dev/null')) {
|
|
120
|
+
isNew = true;
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const marker = line[0];
|
|
125
|
+
if (marker === '+') {
|
|
126
|
+
additions += 1;
|
|
127
|
+
}
|
|
128
|
+
else if (marker === '-') {
|
|
129
|
+
deletions += 1;
|
|
130
|
+
if (!apiTouch && lineTouchesApi(line.slice(1)))
|
|
131
|
+
apiTouch = true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
out.push({ path: seg.path, additions, deletions, isNew, apiTouch });
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
// ctx.review.prepareReview — fetch the PR's diff (gh CLI), strip noise, analyze per-file
|
|
139
|
+
// metrics, and cap the prompt diff. Returns everything the plugin needs to route + prompt
|
|
140
|
+
// WITHOUT any diff primitive leaving core.
|
|
141
|
+
export async function prepareReview(args) {
|
|
142
|
+
const rawDiff = await fetchPrDiff(args.owner, args.name, args.prNumber);
|
|
143
|
+
const { diff: strippedDiff, excluded } = stripNoiseFromDiff(rawDiff, isNoiseFile);
|
|
144
|
+
const fileMetrics = analyzeDiff(strippedDiff);
|
|
145
|
+
const changedFiles = fileMetrics.map((f) => f.path);
|
|
146
|
+
// Diff-size cap (feature-flagged): shrink only the PROMPT's diff body at a whole-file
|
|
147
|
+
// boundary. Routing ran on the full stripped diff and anchoring still uses it.
|
|
148
|
+
let promptDiff = strippedDiff;
|
|
149
|
+
let omittedFiles = [];
|
|
150
|
+
let diffCapped = false;
|
|
151
|
+
if (config.reviewDiffCapEnabled) {
|
|
152
|
+
const capped = capDiff(strippedDiff, config.reviewDiffCapChars);
|
|
153
|
+
promptDiff = capped.diff;
|
|
154
|
+
omittedFiles = capped.omittedFiles;
|
|
155
|
+
diffCapped = capped.capped;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
strippedDiff,
|
|
159
|
+
promptDiff,
|
|
160
|
+
changedFiles,
|
|
161
|
+
excludedFiles: excluded,
|
|
162
|
+
omittedFiles,
|
|
163
|
+
fileMetrics,
|
|
164
|
+
diffBytes: strippedDiff.length,
|
|
165
|
+
diffCapped,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=prepare.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const RATES = {
|
|
2
|
+
// Opus 5.5 — the Claude Review AND AI Fix default. $4 in / $20 out; cache write $5 (1.25×),
|
|
3
|
+
// read $0.20.
|
|
4
|
+
'claude-opus-5-5': { input: 4, output: 20, cacheWrite: 5, cacheRead: 0.2 },
|
|
5
|
+
// Sonnet 5 (≤200K tier) — Sonnet pricing, near-Opus quality.
|
|
6
|
+
'claude-sonnet-5': { input: 3, output: 15, cacheWrite: 3.75, cacheRead: 0.3 },
|
|
7
|
+
};
|
|
8
|
+
/** A running USD estimate from accumulated turn usage. Falls back to Sonnet rates. */
|
|
9
|
+
export function estimateCostUsd(model, u) {
|
|
10
|
+
const r = RATES[model] ?? RATES['claude-sonnet-5'];
|
|
11
|
+
return ((u.inputTokens * r.input +
|
|
12
|
+
u.outputTokens * r.output +
|
|
13
|
+
u.cacheReadTokens * r.cacheRead +
|
|
14
|
+
u.cacheCreationTokens * r.cacheWrite) /
|
|
15
|
+
1_000_000);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=pricing.js.map
|