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,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Myers O(ND) with the linear-space divide-and-conquer refinement, common prefix and suffix
|
|
3
|
+
* trimmed at every level.
|
|
4
|
+
*
|
|
5
|
+
* `maxD` is a BUDGET over the whole recursion, not per call: a pathological pair (a file
|
|
6
|
+
* rewritten wholesale) would otherwise spend minutes proving what a single whole-file region
|
|
7
|
+
* says just as well. Returns `null` when the budget runs out — the caller degrades that file
|
|
8
|
+
* to one conflict spanning everything, which is honest and still resolvable.
|
|
9
|
+
*/
|
|
10
|
+
export function diffTokens(a, b, maxD) {
|
|
11
|
+
const out = [];
|
|
12
|
+
const budget = { left: Math.max(1, maxD) };
|
|
13
|
+
if (!bisectInto(a, b, 0, a.length, 0, b.length, out, budget))
|
|
14
|
+
return null;
|
|
15
|
+
return coalesce(out);
|
|
16
|
+
}
|
|
17
|
+
/** Merge changes that touch, so the caller never sees two adjacent ranges with nothing
|
|
18
|
+
* between them. The recursion can produce those at a split point. */
|
|
19
|
+
function coalesce(changes) {
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const c of changes) {
|
|
22
|
+
const prev = out[out.length - 1];
|
|
23
|
+
if (prev && prev.aEnd === c.aStart && prev.bEnd === c.bStart) {
|
|
24
|
+
prev.aEnd = c.aEnd;
|
|
25
|
+
prev.bEnd = c.bEnd;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
out.push({ ...c });
|
|
29
|
+
}
|
|
30
|
+
return out.filter((c) => c.aEnd > c.aStart || c.bEnd > c.bStart);
|
|
31
|
+
}
|
|
32
|
+
function bisectInto(a, b, a0, a1, b0, b1, out, budget) {
|
|
33
|
+
// Trim the common prefix and suffix first: it is what keeps `d` small on a real edit, and
|
|
34
|
+
// it is also what makes the base cases below reachable at all.
|
|
35
|
+
while (a0 < a1 && b0 < b1 && a[a0] === b[b0]) {
|
|
36
|
+
a0++;
|
|
37
|
+
b0++;
|
|
38
|
+
}
|
|
39
|
+
while (a0 < a1 && b0 < b1 && a[a1 - 1] === b[b1 - 1]) {
|
|
40
|
+
a1--;
|
|
41
|
+
b1--;
|
|
42
|
+
}
|
|
43
|
+
if (a0 === a1 && b0 === b1)
|
|
44
|
+
return true;
|
|
45
|
+
if (a0 === a1 || b0 === b1) {
|
|
46
|
+
// Pure insertion or pure deletion — nothing left to align.
|
|
47
|
+
out.push({ aStart: a0, aEnd: a1, bStart: b0, bEnd: b1 });
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
const split = middleSnake(a, b, a0, a1, b0, b1, budget);
|
|
51
|
+
if (split.kind === 'budget')
|
|
52
|
+
return false;
|
|
53
|
+
if (split.kind === 'replace') {
|
|
54
|
+
// The frontiers never met inside `max`, which for a correct Myers should not happen.
|
|
55
|
+
// Falling back to a whole-range replacement is coarser than optimal but never WRONG,
|
|
56
|
+
// and it is a great deal better than reporting a wrong split.
|
|
57
|
+
out.push({ aStart: a0, aEnd: a1, bStart: b0, bEnd: b1 });
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (!bisectInto(a, b, a0, split.x, b0, split.y, out, budget))
|
|
61
|
+
return false;
|
|
62
|
+
if (!bisectInto(a, b, split.x, a1, split.y, b1, out, budget))
|
|
63
|
+
return false;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Find the middle snake of the optimal edit script (Myers §4b) — the forward and reverse
|
|
68
|
+
* frontiers advance one `d` at a time until they overlap, and the overlap point splits the
|
|
69
|
+
* problem in half with O(N+M) memory.
|
|
70
|
+
*/
|
|
71
|
+
function middleSnake(a, b, a0, a1, b0, b1, budget) {
|
|
72
|
+
const n = a1 - a0;
|
|
73
|
+
const m = b1 - b0;
|
|
74
|
+
const max = Math.ceil((n + m) / 2);
|
|
75
|
+
const offset = max;
|
|
76
|
+
const len = 2 * max + 2;
|
|
77
|
+
const vf = new Int32Array(len).fill(-1);
|
|
78
|
+
const vr = new Int32Array(len).fill(-1);
|
|
79
|
+
vf[offset + 1] = 0;
|
|
80
|
+
vr[offset + 1] = 0;
|
|
81
|
+
const delta = n - m;
|
|
82
|
+
// When delta is odd the frontiers can only meet on a FORWARD step, and vice versa.
|
|
83
|
+
const front = (delta & 1) !== 0;
|
|
84
|
+
let k1start = 0;
|
|
85
|
+
let k1end = 0;
|
|
86
|
+
let k2start = 0;
|
|
87
|
+
let k2end = 0;
|
|
88
|
+
// ⚠ `d <= max`, not `d < max`. The frontiers meet at `d = ceil(D/2)` and `D` can be the
|
|
89
|
+
// full `n + m` — a one-token replacement is D = 2, max = 1, and a `d < max` loop never
|
|
90
|
+
// runs the step that finds it. That off-by-one turns every small edit into a whole-range
|
|
91
|
+
// replacement, which still merges correctly and reads as one enormous conflict on screen.
|
|
92
|
+
for (let d = 0; d <= max; d++) {
|
|
93
|
+
if (budget.left-- <= 0)
|
|
94
|
+
return { kind: 'budget' };
|
|
95
|
+
for (let k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
|
|
96
|
+
const k1off = offset + k1;
|
|
97
|
+
let x1;
|
|
98
|
+
if (k1 === -d || (k1 !== d && (vf[k1off - 1] ?? -1) < (vf[k1off + 1] ?? -1))) {
|
|
99
|
+
x1 = vf[k1off + 1] ?? 0;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
x1 = (vf[k1off - 1] ?? 0) + 1;
|
|
103
|
+
}
|
|
104
|
+
let y1 = x1 - k1;
|
|
105
|
+
while (x1 < n && y1 < m && a[a0 + x1] === b[b0 + y1]) {
|
|
106
|
+
x1++;
|
|
107
|
+
y1++;
|
|
108
|
+
}
|
|
109
|
+
vf[k1off] = x1;
|
|
110
|
+
if (x1 > n) {
|
|
111
|
+
k1end += 2;
|
|
112
|
+
}
|
|
113
|
+
else if (y1 > m) {
|
|
114
|
+
k1start += 2;
|
|
115
|
+
}
|
|
116
|
+
else if (front) {
|
|
117
|
+
const k2off = offset + delta - k1;
|
|
118
|
+
if (k2off >= 0 && k2off < len && (vr[k2off] ?? -1) !== -1) {
|
|
119
|
+
const x2 = n - (vr[k2off] ?? 0);
|
|
120
|
+
if (x1 >= x2)
|
|
121
|
+
return { kind: 'split', x: a0 + x1, y: b0 + y1 };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
for (let k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
|
|
126
|
+
const k2off = offset + k2;
|
|
127
|
+
let x2;
|
|
128
|
+
if (k2 === -d || (k2 !== d && (vr[k2off - 1] ?? -1) < (vr[k2off + 1] ?? -1))) {
|
|
129
|
+
x2 = vr[k2off + 1] ?? 0;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
x2 = (vr[k2off - 1] ?? 0) + 1;
|
|
133
|
+
}
|
|
134
|
+
let y2 = x2 - k2;
|
|
135
|
+
while (x2 < n && y2 < m && a[a0 + n - x2 - 1] === b[b0 + m - y2 - 1]) {
|
|
136
|
+
x2++;
|
|
137
|
+
y2++;
|
|
138
|
+
}
|
|
139
|
+
vr[k2off] = x2;
|
|
140
|
+
if (x2 > n) {
|
|
141
|
+
k2end += 2;
|
|
142
|
+
}
|
|
143
|
+
else if (y2 > m) {
|
|
144
|
+
k2start += 2;
|
|
145
|
+
}
|
|
146
|
+
else if (!front) {
|
|
147
|
+
const k1off = offset + delta - k2;
|
|
148
|
+
if (k1off >= 0 && k1off < len && (vf[k1off] ?? -1) !== -1) {
|
|
149
|
+
const x1 = vf[k1off] ?? 0;
|
|
150
|
+
const y1 = x1 - (k1off - offset);
|
|
151
|
+
if (x1 >= n - x2)
|
|
152
|
+
return { kind: 'split', x: a0 + x1, y: b0 + y1 };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return { kind: 'replace' };
|
|
158
|
+
}
|
|
159
|
+
function toHunks(changes) {
|
|
160
|
+
return changes.map((c) => ({
|
|
161
|
+
baseStart: c.aStart,
|
|
162
|
+
baseEnd: c.aEnd,
|
|
163
|
+
sideStart: c.bStart,
|
|
164
|
+
sideEnd: c.bEnd,
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* The classic diff3 chunker. Takes both sides' diffs AGAINST THE BASE and produces one
|
|
169
|
+
* ordered region list covering the base end to end.
|
|
170
|
+
*
|
|
171
|
+
* ⚠ ADJACENT CHANGES ABSORB INTO ONE REGION (`hunk.baseStart <= hi`, zero unchanged tokens
|
|
172
|
+
* between). Two edits that touch are one contested region: offering them separately lets the
|
|
173
|
+
* reader take ours from the first and theirs from the second and produce text neither author
|
|
174
|
+
* wrote.
|
|
175
|
+
*
|
|
176
|
+
* ⚠ `both_same` is a real kind and is NOT collapsed into `ours_only`. Collapsing it makes the
|
|
177
|
+
* wand's "what I did" count wrong — it settled nothing there, both authors already agreed.
|
|
178
|
+
*/
|
|
179
|
+
export function threeWayChunks(base, ours, theirs, oursChanges, theirsChanges) {
|
|
180
|
+
const oursHunks = toHunks(oursChanges);
|
|
181
|
+
const theirsHunks = toHunks(theirsChanges);
|
|
182
|
+
const regions = [];
|
|
183
|
+
let i = 0;
|
|
184
|
+
let j = 0;
|
|
185
|
+
let basePos = 0;
|
|
186
|
+
while (i < oursHunks.length || j < theirsHunks.length) {
|
|
187
|
+
const nextOurs = oursHunks[i];
|
|
188
|
+
const nextTheirs = theirsHunks[j];
|
|
189
|
+
let lo;
|
|
190
|
+
if (nextOurs && nextTheirs)
|
|
191
|
+
lo = Math.min(nextOurs.baseStart, nextTheirs.baseStart);
|
|
192
|
+
else if (nextOurs)
|
|
193
|
+
lo = nextOurs.baseStart;
|
|
194
|
+
else if (nextTheirs)
|
|
195
|
+
lo = nextTheirs.baseStart;
|
|
196
|
+
else
|
|
197
|
+
break;
|
|
198
|
+
let hi = lo;
|
|
199
|
+
const groupOurs = [];
|
|
200
|
+
const groupTheirs = [];
|
|
201
|
+
// Absorb every hunk from either side that starts at or before the group's current end.
|
|
202
|
+
for (;;) {
|
|
203
|
+
const o = oursHunks[i];
|
|
204
|
+
const t = theirsHunks[j];
|
|
205
|
+
if (o && o.baseStart <= hi) {
|
|
206
|
+
groupOurs.push(o);
|
|
207
|
+
hi = Math.max(hi, o.baseEnd);
|
|
208
|
+
i++;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (t && t.baseStart <= hi) {
|
|
212
|
+
groupTheirs.push(t);
|
|
213
|
+
hi = Math.max(hi, t.baseEnd);
|
|
214
|
+
j++;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
if (lo > basePos) {
|
|
220
|
+
regions.push(unchangedRegion(basePos, lo, oursHunks, theirsHunks));
|
|
221
|
+
}
|
|
222
|
+
const oursRange = mapRange(lo, hi, groupOurs, oursHunks);
|
|
223
|
+
const theirsRange = mapRange(lo, hi, groupTheirs, theirsHunks);
|
|
224
|
+
const kind = classify(base.slice(lo, hi), ours.slice(oursRange.start, oursRange.end), theirs.slice(theirsRange.start, theirsRange.end));
|
|
225
|
+
regions.push({
|
|
226
|
+
kind,
|
|
227
|
+
baseStart: lo,
|
|
228
|
+
baseEnd: hi,
|
|
229
|
+
oursStart: oursRange.start,
|
|
230
|
+
oursEnd: oursRange.end,
|
|
231
|
+
theirsStart: theirsRange.start,
|
|
232
|
+
theirsEnd: theirsRange.end,
|
|
233
|
+
});
|
|
234
|
+
basePos = hi;
|
|
235
|
+
}
|
|
236
|
+
if (basePos < base.length) {
|
|
237
|
+
regions.push(unchangedRegion(basePos, base.length, oursHunks, theirsHunks));
|
|
238
|
+
}
|
|
239
|
+
return regions;
|
|
240
|
+
}
|
|
241
|
+
function unchangedRegion(lo, hi, oursHunks, theirsHunks) {
|
|
242
|
+
const o = mapRange(lo, hi, [], oursHunks);
|
|
243
|
+
const t = mapRange(lo, hi, [], theirsHunks);
|
|
244
|
+
return {
|
|
245
|
+
kind: 'unchanged',
|
|
246
|
+
baseStart: lo,
|
|
247
|
+
baseEnd: hi,
|
|
248
|
+
oursStart: o.start,
|
|
249
|
+
oursEnd: o.end,
|
|
250
|
+
theirsStart: t.start,
|
|
251
|
+
theirsEnd: t.end,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Map a base range onto one side.
|
|
256
|
+
*
|
|
257
|
+
* With hunks in the group, the group's own first and last hunk anchor it: the run between
|
|
258
|
+
* `lo` and the first hunk's start is unchanged on this side, so it shifts by the same amount,
|
|
259
|
+
* and likewise at the tail. Without any, the whole range is unchanged on this side and the
|
|
260
|
+
* mapping is the cumulative offset of every hunk that ended before `lo`.
|
|
261
|
+
*/
|
|
262
|
+
function mapRange(lo, hi, group, allHunks) {
|
|
263
|
+
const first = group[0];
|
|
264
|
+
const last = group[group.length - 1];
|
|
265
|
+
if (first && last) {
|
|
266
|
+
return {
|
|
267
|
+
start: first.sideStart - (first.baseStart - lo),
|
|
268
|
+
end: last.sideEnd + (hi - last.baseEnd),
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
let offset = 0;
|
|
272
|
+
for (const h of allHunks) {
|
|
273
|
+
if (h.baseEnd <= lo)
|
|
274
|
+
offset += h.sideEnd - h.sideStart - (h.baseEnd - h.baseStart);
|
|
275
|
+
else
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
return { start: lo + offset, end: hi + offset };
|
|
279
|
+
}
|
|
280
|
+
function sameTokens(a, b) {
|
|
281
|
+
if (a.length !== b.length)
|
|
282
|
+
return false;
|
|
283
|
+
for (let k = 0; k < a.length; k++)
|
|
284
|
+
if (a[k] !== b[k])
|
|
285
|
+
return false;
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
/** Classify by CONTENT, not by which hunk lists contributed. After absorption a group can
|
|
289
|
+
* hold a hunk from a side whose net text is identical to the base's, and calling that a
|
|
290
|
+
* change would show the reader a choice between two identical things. */
|
|
291
|
+
function classify(base, ours, theirs) {
|
|
292
|
+
const oursSame = sameTokens(base, ours);
|
|
293
|
+
const theirsSame = sameTokens(base, theirs);
|
|
294
|
+
if (oursSame && theirsSame)
|
|
295
|
+
return 'unchanged';
|
|
296
|
+
if (theirsSame)
|
|
297
|
+
return 'ours_only';
|
|
298
|
+
if (oursSame)
|
|
299
|
+
return 'theirs_only';
|
|
300
|
+
if (sameTokens(ours, theirs))
|
|
301
|
+
return 'both_same';
|
|
302
|
+
return 'conflict';
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
const execFileAsync = promisify(execFile);
|
|
4
|
+
/**
|
|
5
|
+
* The git environment for EVERY command the conflict resolver runs — discovery and landing
|
|
6
|
+
* alike. Stricter than `coding/merge.ts`'s GIT_ENV on purpose.
|
|
7
|
+
*
|
|
8
|
+
* A developer's global git config must not change what we COMPUTE or what we COMMIT:
|
|
9
|
+
* `merge.conflictStyle`, `diff.algorithm`, `diff.renames` and custom merge drivers all move
|
|
10
|
+
* the answer. MEASURED: the same `merge-tree` on the same two commits wrote tree 0bc8605
|
|
11
|
+
* with global config nulled and bf04533 with `merge.conflictStyle=zdiff3`.
|
|
12
|
+
*
|
|
13
|
+
* ⚠ EXPORTED because the land path seeds from merge-tree's tree — the user must not review
|
|
14
|
+
* one merge and push another. In-tree `.gitattributes` stays honoured on purpose: it is part
|
|
15
|
+
* of the repository, and turning it off would change how the repo's own owners merge.
|
|
16
|
+
*/
|
|
17
|
+
export const CONFLICT_GIT_ENV = {
|
|
18
|
+
...process.env,
|
|
19
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
20
|
+
GIT_EDITOR: 'true',
|
|
21
|
+
GIT_CONFIG_GLOBAL: '/dev/null',
|
|
22
|
+
GIT_CONFIG_SYSTEM: '/dev/null',
|
|
23
|
+
GIT_ATTR_NOSYSTEM: '1',
|
|
24
|
+
GIT_OPTIONAL_LOCKS: '0',
|
|
25
|
+
GIT_ADVICE: '0',
|
|
26
|
+
};
|
|
27
|
+
// `merge-tree -z`, `diff-tree -z` and `cat-file --batch` are NUL-delimited and byte-oriented,
|
|
28
|
+
// and blobs are arbitrary bytes. Decoding stdout to a string anywhere in this pipeline is how
|
|
29
|
+
// a Latin-1 file becomes U+FFFD, so stdout is a Buffer the whole way down and only the strict
|
|
30
|
+
// UTF-8 gate in model.ts is allowed to turn bytes into text.
|
|
31
|
+
const MAX_BUFFER = 256 * 1024 * 1024;
|
|
32
|
+
const TIMEOUT_MS = 120_000;
|
|
33
|
+
/** Run git, throwing on a non-zero exit (for the commands that MUST succeed). */
|
|
34
|
+
export async function git(args, cwd, stdin, env) {
|
|
35
|
+
const res = await gitTry(args, cwd, stdin, env);
|
|
36
|
+
if (res.code !== 0) {
|
|
37
|
+
throw new Error(`git ${args.slice(0, 3).join(' ')} failed (${res.code}): ${res.stderr.slice(0, 500)}`);
|
|
38
|
+
}
|
|
39
|
+
return { stdout: res.stdout, stderr: res.stderr };
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run git WITHOUT throwing; returns the exit code plus output.
|
|
43
|
+
*
|
|
44
|
+
* `env` is MERGED OVER `CONFLICT_GIT_ENV`, for the two variables the land path needs and
|
|
45
|
+
* that have no command-line equivalent: `GIT_INDEX_FILE` (the scratch index every tree is
|
|
46
|
+
* built in, so the shared clone's own index is never touched) and `GIT_AUTHOR_*` (a rebased
|
|
47
|
+
* commit keeps the original author while the account is the committer).
|
|
48
|
+
*
|
|
49
|
+
* ⚠ Several commands here exit non-zero on complete success — `merge-tree` exits 1 when it
|
|
50
|
+
* finds conflicts, and the by-OID prefetch exits non-zero even when every object landed.
|
|
51
|
+
* Callers branch on the OUTPUT, never on the code alone.
|
|
52
|
+
*/
|
|
53
|
+
export async function gitTry(args, cwd, stdin, env) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const child = execFile('git', args, {
|
|
56
|
+
cwd,
|
|
57
|
+
// ⚠ The overrides go AFTER the constant, never before: `GIT_INDEX_FILE` and the
|
|
58
|
+
// `GIT_AUTHOR_*` ident the land path sets must be additions to the hardened env, not
|
|
59
|
+
// a replacement for it. A caller cannot re-open `GIT_CONFIG_GLOBAL` by accident, only
|
|
60
|
+
// deliberately, and nothing does.
|
|
61
|
+
env: env ? { ...CONFLICT_GIT_ENV, ...env } : CONFLICT_GIT_ENV,
|
|
62
|
+
timeout: TIMEOUT_MS,
|
|
63
|
+
maxBuffer: MAX_BUFFER,
|
|
64
|
+
encoding: 'buffer',
|
|
65
|
+
}, (err, stdout, stderr) => {
|
|
66
|
+
const out = Buffer.isBuffer(stdout) ? stdout : Buffer.from(String(stdout));
|
|
67
|
+
const errText = Buffer.isBuffer(stderr) ? stderr.toString('utf8') : String(stderr);
|
|
68
|
+
if (err) {
|
|
69
|
+
const code = err.code;
|
|
70
|
+
// A numeric `code` is git's exit status. A string one (ENOENT, ETIMEDOUT) means the
|
|
71
|
+
// process never ran or was killed, which is a real failure, not a git verdict.
|
|
72
|
+
if (typeof code === 'number') {
|
|
73
|
+
resolve({ code, stdout: out, stderr: errText });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
reject(err);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
resolve({ code: 0, stdout: out, stderr: errText });
|
|
80
|
+
});
|
|
81
|
+
if (stdin !== undefined) {
|
|
82
|
+
child.stdin?.end(stdin);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* `git merge-tree --write-tree` needs git 2.38. Probed ONCE per process and memoised — the
|
|
88
|
+
* open route turns a false into `git_too_old` and the overlay prints it once.
|
|
89
|
+
*
|
|
90
|
+
* ⚠ This is NOT on `/api/me`. `MeResponse.conflictResolver` is `!config.isCloud` and shells
|
|
91
|
+
* out to nothing; probing git on every SPA boot to answer a question the open route can
|
|
92
|
+
* answer once is the trade this replaced.
|
|
93
|
+
*/
|
|
94
|
+
let versionProbe = null;
|
|
95
|
+
export function gitSupportsMergeTree(cwd = process.cwd()) {
|
|
96
|
+
versionProbe ??= (async () => {
|
|
97
|
+
try {
|
|
98
|
+
const { stdout } = await gitTry(['--version'], cwd);
|
|
99
|
+
const m = /git version (\d+)\.(\d+)/.exec(stdout.toString('utf8'));
|
|
100
|
+
if (!m)
|
|
101
|
+
return false;
|
|
102
|
+
const major = Number(m[1]);
|
|
103
|
+
const minor = Number(m[2]);
|
|
104
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor))
|
|
105
|
+
return false;
|
|
106
|
+
return major > 2 || (major === 2 && minor >= 38);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
})();
|
|
112
|
+
return versionProbe;
|
|
113
|
+
}
|
|
114
|
+
/** Test seam only: forget the memoised probe. */
|
|
115
|
+
export function resetGitVersionProbe() {
|
|
116
|
+
versionProbe = null;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { CONFLICT_MODEL_VERSION } from '../shared/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* The model hash. Echoed on the commit body; a mismatch is `ModelStale` and nothing is
|
|
5
|
+
* written, because a decision made against different bytes is not a decision.
|
|
6
|
+
*
|
|
7
|
+
* ⚠ `CONFLICT_MODEL_VERSION` IS FOLDED IN, so a change to the fold or the chunker
|
|
8
|
+
* self-invalidates every live session rather than landing bytes the user did not choose —
|
|
9
|
+
* the `PERIOD_METRICS_SCHEMA_VERSION` discipline.
|
|
10
|
+
*
|
|
11
|
+
* ⚠ ONLY MODEL FACTS GO IN. Nothing `Date.now()`-derived, nothing hydrated, nothing that
|
|
12
|
+
* depends on how the session was OPENED: `autoApply` changes which decision a region starts
|
|
13
|
+
* on, not what the region IS, and folding it in would make the same model hash two different
|
|
14
|
+
* values depending on a checkbox.
|
|
15
|
+
*/
|
|
16
|
+
export function conflictModelHash(model) {
|
|
17
|
+
const h = createHash('sha256');
|
|
18
|
+
const put = (s) => {
|
|
19
|
+
h.update(s);
|
|
20
|
+
h.update('\0');
|
|
21
|
+
};
|
|
22
|
+
put(`v${CONFLICT_MODEL_VERSION}`);
|
|
23
|
+
put(model.headSha);
|
|
24
|
+
put(model.baseSha);
|
|
25
|
+
put(model.mergedTreeSha);
|
|
26
|
+
put(model.mergeBaseSha ?? '');
|
|
27
|
+
put(model.mergeBaseIsVirtual ? '1' : '0');
|
|
28
|
+
put(String(model.totalConflictedPaths));
|
|
29
|
+
put(model.truncated ? '1' : '0');
|
|
30
|
+
put(model.renameDetection);
|
|
31
|
+
for (const f of model.files) {
|
|
32
|
+
put(String(f.index));
|
|
33
|
+
put(f.path);
|
|
34
|
+
put(f.unsupported ?? '');
|
|
35
|
+
put(f.stage2Mode ?? '');
|
|
36
|
+
put(`${f.terminators.base ? 1 : 0}${f.terminators.ours ? 1 : 0}${f.terminators.theirs ? 1 : 0}`);
|
|
37
|
+
put(String(f.regions.length));
|
|
38
|
+
for (const r of f.regions) {
|
|
39
|
+
put(String(r.id));
|
|
40
|
+
put(r.kind);
|
|
41
|
+
// The fingerprint already covers all three sides' bytes, so hashing it rather than the
|
|
42
|
+
// lines keeps this linear in region count instead of in file size.
|
|
43
|
+
put(r.fingerprint);
|
|
44
|
+
put(r.wand?.decision ?? '');
|
|
45
|
+
put(r.wand?.reason ?? '');
|
|
46
|
+
put(r.mergedLines === null ? '' : regionMergeDigest(r));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return h.digest('hex');
|
|
50
|
+
}
|
|
51
|
+
function regionMergeDigest(region) {
|
|
52
|
+
const h = createHash('sha256');
|
|
53
|
+
for (const line of region.mergedLines ?? []) {
|
|
54
|
+
h.update(line);
|
|
55
|
+
h.update('\0');
|
|
56
|
+
}
|
|
57
|
+
return h.digest('hex');
|
|
58
|
+
}
|
|
59
|
+
/** sha256 of `base \0 ours \0 theirs`. The CONTENT pin the Pro suggestion route checks
|
|
60
|
+
* against the id's ADDRESS — a region can keep its id while its bytes move only if the
|
|
61
|
+
* whole model was rebuilt, and then this differs. */
|
|
62
|
+
export function regionFingerprint(base, ours, theirs) {
|
|
63
|
+
const h = createHash('sha256');
|
|
64
|
+
for (const side of [base, ours, theirs]) {
|
|
65
|
+
for (const line of side) {
|
|
66
|
+
h.update(line);
|
|
67
|
+
h.update('\n');
|
|
68
|
+
}
|
|
69
|
+
h.update('\0');
|
|
70
|
+
}
|
|
71
|
+
return h.digest('hex');
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=hash.js.map
|