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,262 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join } from 'node:path';
|
|
4
|
+
import { promisify } from 'node:util';
|
|
5
|
+
import { config } from '../config.js';
|
|
6
|
+
import { getAccessToken, getAccountById } from '../auth/account.js';
|
|
7
|
+
import { cleanupCloneCache, prepWorktree, removeWorktreeLocked, } from '../review/clone-manager.js';
|
|
8
|
+
import { fetchPrHeadInfo } from '../github/mutations.js';
|
|
9
|
+
import { codedError, pushForceWithLease, pushRef } from './git.js';
|
|
10
|
+
import { protectedRefsFor } from './git-ops.js';
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
// CORE (free tier): "Update branch from trunk" — rebase or merge the trunk into a PR's own
|
|
13
|
+
// head branch and push — plus `hasConflictMarkers`, which the in-app conflict resolver
|
|
14
|
+
// (src/conflict/) imports. Same discipline as git.ts / clone-manager.ts: args are ALWAYS arrays
|
|
15
|
+
// (no shell), a tokenized URL is passed per-op and never persisted, and there is no agent here
|
|
16
|
+
// at all. ANY conflict aborts the rebase/merge and throws CONFLICTS_UNRESOLVED — nothing is
|
|
17
|
+
// ever resolved on this path, so a tree with conflicts is never pushed.
|
|
18
|
+
//
|
|
19
|
+
// (This file used to also hold AI Fix's trunk reconciliation — merge preview, rebase-resolve,
|
|
20
|
+
// merge-resolve-and-push, push-resolved, and the agentic conflict resolver they called. All of
|
|
21
|
+
// it was removed: an AI Fix now pushes as-is through git-ops.ts `applyAndPush`.)
|
|
22
|
+
// Non-interactive git env: no credential prompt, no editor, no merge-message editor.
|
|
23
|
+
const GIT_ENV = {
|
|
24
|
+
...process.env,
|
|
25
|
+
GIT_TERMINAL_PROMPT: '0',
|
|
26
|
+
GIT_EDITOR: 'true',
|
|
27
|
+
GIT_SEQUENCE_EDITOR: 'true',
|
|
28
|
+
GIT_MERGE_AUTOEDIT: 'no',
|
|
29
|
+
};
|
|
30
|
+
/** Run git, throwing on a non-zero exit (for ops that MUST succeed). */
|
|
31
|
+
async function git(args, cwd) {
|
|
32
|
+
const { stdout, stderr } = await execFileAsync('git', args, {
|
|
33
|
+
cwd,
|
|
34
|
+
env: GIT_ENV,
|
|
35
|
+
timeout: 120_000,
|
|
36
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
37
|
+
});
|
|
38
|
+
return { ok: true, code: 0, stdout, stderr };
|
|
39
|
+
}
|
|
40
|
+
/** Run git WITHOUT throwing; returns the exit code + output (for merge/rebase). */
|
|
41
|
+
async function gitTry(args, cwd) {
|
|
42
|
+
try {
|
|
43
|
+
const { stdout, stderr } = await execFileAsync('git', args, {
|
|
44
|
+
cwd,
|
|
45
|
+
env: GIT_ENV,
|
|
46
|
+
timeout: 120_000,
|
|
47
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
48
|
+
});
|
|
49
|
+
return { ok: true, code: 0, stdout, stderr };
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const e = err;
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
code: typeof e.code === 'number' ? e.code : 1,
|
|
56
|
+
stdout: e.stdout ?? '',
|
|
57
|
+
stderr: e.stderr ?? (err instanceof Error ? err.message : String(err)),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function tokenizedUrl(owner, name, token) {
|
|
62
|
+
return `https://x-access-token:${token}@github.com/${owner}/${name}.git`;
|
|
63
|
+
}
|
|
64
|
+
function lines(s) {
|
|
65
|
+
return s
|
|
66
|
+
.split('\n')
|
|
67
|
+
.map((l) => l.trim())
|
|
68
|
+
.filter(Boolean);
|
|
69
|
+
}
|
|
70
|
+
async function identFor(accountId) {
|
|
71
|
+
const account = await getAccountById(accountId);
|
|
72
|
+
const name = account?.displayName || account?.githubLogin || 'pierre-review';
|
|
73
|
+
const email = account?.githubLogin
|
|
74
|
+
? `${account.githubLogin}@users.noreply.github.com`
|
|
75
|
+
: 'pierre-review@users.noreply.github.com';
|
|
76
|
+
return { name, email };
|
|
77
|
+
}
|
|
78
|
+
function identArgs(ident) {
|
|
79
|
+
return ['-c', `user.name=${ident.name}`, '-c', `user.email=${ident.email}`];
|
|
80
|
+
}
|
|
81
|
+
async function scheduleCleanup(repoCloneDir) {
|
|
82
|
+
if (!repoCloneDir)
|
|
83
|
+
return;
|
|
84
|
+
setImmediate(() => {
|
|
85
|
+
try {
|
|
86
|
+
cleanupCloneCache();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* advisory */
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async function teardown(owner, name, repoCloneDir, worktreePath) {
|
|
94
|
+
if (repoCloneDir && worktreePath) {
|
|
95
|
+
await removeWorktreeLocked(owner, name, repoCloneDir, worktreePath).catch(() => { });
|
|
96
|
+
}
|
|
97
|
+
void scheduleCleanup(repoCloneDir);
|
|
98
|
+
}
|
|
99
|
+
/** Fetch a branch into the worktree's object store and return its tip sha. */
|
|
100
|
+
async function fetchTrunk(worktree, owner, name, token, trunk) {
|
|
101
|
+
const res = await gitTry(['fetch', '--no-tags', '--force', tokenizedUrl(owner, name, token), trunk], worktree);
|
|
102
|
+
if (!res.ok) {
|
|
103
|
+
throw codedError('TRUNK_FETCH_FAILED', `couldn't fetch trunk '${trunk}': ${res.stderr || res.stdout}`);
|
|
104
|
+
}
|
|
105
|
+
const { stdout } = await git(['rev-parse', 'FETCH_HEAD'], worktree);
|
|
106
|
+
return stdout.trim();
|
|
107
|
+
}
|
|
108
|
+
/** True if `ancestor` is an ancestor of `descendant` (i.e. already contained). */
|
|
109
|
+
async function isAncestor(worktree, ancestor, descendant) {
|
|
110
|
+
const res = await gitTry(['merge-base', '--is-ancestor', ancestor, descendant], worktree);
|
|
111
|
+
return res.ok;
|
|
112
|
+
}
|
|
113
|
+
/** The currently-unmerged (conflicted) paths. */
|
|
114
|
+
async function conflictedFiles(worktree) {
|
|
115
|
+
const { stdout } = await git(['diff', '--name-only', '--diff-filter=U'], worktree);
|
|
116
|
+
return lines(stdout);
|
|
117
|
+
}
|
|
118
|
+
// A leftover conflict marker: `<<<<<<< `, `>>>>>>> ` or diff3's `||||||| ` (the
|
|
119
|
+
// `=======` divider alone is ambiguous — real files contain it — so we rely on the
|
|
120
|
+
// unambiguous open/close markers plus git's own unmerged-index check).
|
|
121
|
+
const MARKER_RE = /^(<{7}|>{7}|\|{7})[ \t]/m;
|
|
122
|
+
/** True if `text` still contains an (unambiguous) unresolved conflict marker. */
|
|
123
|
+
export function hasConflictMarkers(text) {
|
|
124
|
+
return MARKER_RE.test(text);
|
|
125
|
+
}
|
|
126
|
+
/** True if a rebase or merge is currently in progress in this worktree. */
|
|
127
|
+
async function opInProgress(worktree, kind) {
|
|
128
|
+
const probes = kind === 'rebase' ? ['rebase-merge', 'rebase-apply'] : ['MERGE_HEAD'];
|
|
129
|
+
for (const p of probes) {
|
|
130
|
+
try {
|
|
131
|
+
const { stdout } = await git(['rev-parse', '--git-path', p], worktree);
|
|
132
|
+
const rel = stdout.trim();
|
|
133
|
+
const abs = isAbsolute(rel) ? rel : join(worktree, rel);
|
|
134
|
+
if (existsSync(abs))
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
/* ignore */
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
// ---- rebase the checked-out branch onto the trunk (aborts on the first conflict) ----
|
|
144
|
+
async function runRebase(worktree, trunkSha, ident) {
|
|
145
|
+
const gc = identArgs(ident);
|
|
146
|
+
let res = await gitTry([...gc, 'rebase', trunkSha], worktree);
|
|
147
|
+
let step = 0;
|
|
148
|
+
while (!res.ok) {
|
|
149
|
+
if (!(await opInProgress(worktree, 'rebase'))) {
|
|
150
|
+
// The rebase never started / hard-failed (not a conflict stop).
|
|
151
|
+
throw codedError('REBASE_FAILED', res.stderr || res.stdout || 'rebase failed');
|
|
152
|
+
}
|
|
153
|
+
const files = await conflictedFiles(worktree);
|
|
154
|
+
if (files.length > 0) {
|
|
155
|
+
await gitTry(['rebase', '--abort'], worktree);
|
|
156
|
+
throw codedError('CONFLICTS_UNRESOLVED', `rebase conflicts in ${files.join(', ')}`);
|
|
157
|
+
}
|
|
158
|
+
// A stop with no conflicted file (e.g. a commit that became empty): continue or skip it,
|
|
159
|
+
// capped so a pathological history can't spin.
|
|
160
|
+
if (++step > config.aiFixRebaseMaxSteps) {
|
|
161
|
+
await gitTry(['rebase', '--abort'], worktree);
|
|
162
|
+
throw codedError('REBASE_FAILED', `rebase exceeded ${config.aiFixRebaseMaxSteps} conflict steps`);
|
|
163
|
+
}
|
|
164
|
+
res = await continueOrSkip(worktree, gc);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// `git rebase --continue`, falling back to `--skip` when a commit became empty (its
|
|
168
|
+
// changes are already on the trunk).
|
|
169
|
+
async function continueOrSkip(worktree, gc) {
|
|
170
|
+
const cont = await gitTry([...gc, 'rebase', '--continue'], worktree);
|
|
171
|
+
if (cont.ok)
|
|
172
|
+
return cont;
|
|
173
|
+
const blob = `${cont.stderr}\n${cont.stdout}`.toLowerCase();
|
|
174
|
+
if (blob.includes('no changes') ||
|
|
175
|
+
blob.includes('nothing to commit') ||
|
|
176
|
+
blob.includes('did you forget') ||
|
|
177
|
+
blob.includes('patch is empty')) {
|
|
178
|
+
return gitTry([...gc, 'rebase', '--skip'], worktree);
|
|
179
|
+
}
|
|
180
|
+
return cont;
|
|
181
|
+
}
|
|
182
|
+
// ---- merge the trunk into the checked-out branch (aborts on any conflict) ----
|
|
183
|
+
async function runMerge(worktree, trunkSha, trunk, branchLabel, ident) {
|
|
184
|
+
const gc = identArgs(ident);
|
|
185
|
+
if (await isAncestor(worktree, trunkSha, 'HEAD')) {
|
|
186
|
+
return; // already contains the trunk
|
|
187
|
+
}
|
|
188
|
+
const res = await gitTry([...gc, 'merge', '--no-ff', '--no-edit', '-m', `Merge ${trunk} into ${branchLabel}`, trunkSha], worktree);
|
|
189
|
+
if (res.ok)
|
|
190
|
+
return;
|
|
191
|
+
const files = await conflictedFiles(worktree);
|
|
192
|
+
if (files.length === 0) {
|
|
193
|
+
await gitTry(['merge', '--abort'], worktree);
|
|
194
|
+
throw codedError('MERGE_FAILED', res.stderr || res.stdout || 'merge failed');
|
|
195
|
+
}
|
|
196
|
+
await gitTry(['merge', '--abort'], worktree);
|
|
197
|
+
throw codedError('CONFLICTS_UNRESOLVED', `merge conflicts in ${files.join(', ')}`);
|
|
198
|
+
}
|
|
199
|
+
// ---- CORE (free tier): update a PR's OWN branch from its base/trunk ----
|
|
200
|
+
// Rebase (default) or merge the trunk into the PR's head branch and push. This is the local,
|
|
201
|
+
// clone-based path for the free-tier "Update branch from trunk": on ANY conflict runRebase /
|
|
202
|
+
// runMerge abort the op and throw CONFLICTS_UNRESOLVED (nothing here resolves a conflict — the
|
|
203
|
+
// in-app resolver in src/conflict/ is a separate, reader-driven flow). No stored patch is
|
|
204
|
+
// involved: we check the PR head out as-is and reconcile it.
|
|
205
|
+
export async function updatePrBranchFromTrunk(args) {
|
|
206
|
+
const { accountId, owner, name, prNumber, headRef, headSha, trunk, strategy } = args;
|
|
207
|
+
const token = await getAccessToken(accountId);
|
|
208
|
+
const ident = await identFor(accountId);
|
|
209
|
+
// The head must not have moved out from under us, and a fork head must be pushable.
|
|
210
|
+
const info = await fetchPrHeadInfo(token, owner, name, prNumber);
|
|
211
|
+
if (info.headSha !== headSha) {
|
|
212
|
+
throw codedError('HEAD_MOVED', `the PR head advanced (now ${info.headSha.slice(0, 7)}, expected ${headSha.slice(0, 7)})`);
|
|
213
|
+
}
|
|
214
|
+
if (info.isFork && !info.maintainerCanModify) {
|
|
215
|
+
throw codedError('PUSH_DENIED', 'the PR head is a fork this account cannot push to — update it on GitHub instead');
|
|
216
|
+
}
|
|
217
|
+
// The PR head branch lives in the HEAD repo, which for a fork PR is NOT the base (watched)
|
|
218
|
+
// repo — push there, or a merge would create a stray branch on the base and a rebase's lease
|
|
219
|
+
// would target the wrong ref. The clone/fetch still uses the base repo below (pull/N/head
|
|
220
|
+
// resolves there); only the PUSH target is the head repo. full_name is always `owner/name`.
|
|
221
|
+
const slash = info.headRepoFullName.indexOf('/');
|
|
222
|
+
const headOwner = slash > 0 ? info.headRepoFullName.slice(0, slash) : owner;
|
|
223
|
+
const headName = slash > 0 ? info.headRepoFullName.slice(slash + 1) : name;
|
|
224
|
+
// The push lands in the HEAD repo, which for a fork PR is not the watched one — then there
|
|
225
|
+
// is nothing of ours to protect there (see protectedRefsFor).
|
|
226
|
+
const push = {
|
|
227
|
+
worktree: '',
|
|
228
|
+
owner: headOwner,
|
|
229
|
+
name: headName,
|
|
230
|
+
token,
|
|
231
|
+
committish: 'HEAD',
|
|
232
|
+
remoteBranch: headRef,
|
|
233
|
+
protect: await protectedRefsFor(accountId, owner, name, prNumber, headOwner, headName),
|
|
234
|
+
};
|
|
235
|
+
let repoCloneDir = null;
|
|
236
|
+
let worktreePath = null;
|
|
237
|
+
try {
|
|
238
|
+
// Check out the PR head branch (prepWorktree checks out at the given sha).
|
|
239
|
+
({ repoCloneDir, worktreePath } = await prepWorktree(owner, name, prNumber, headSha, token));
|
|
240
|
+
const trunkSha = await fetchTrunk(worktreePath, owner, name, token, trunk);
|
|
241
|
+
if (strategy === 'rebase') {
|
|
242
|
+
await runRebase(worktreePath, trunkSha, ident); // throws CONFLICTS_UNRESOLVED on conflict
|
|
243
|
+
const newSha = (await git(['rev-parse', 'HEAD'], worktreePath)).stdout.trim();
|
|
244
|
+
// Rewriting history requires a force push; the lease pins to the sha we cloned so a
|
|
245
|
+
// concurrent push aborts it (never blows away someone else's work).
|
|
246
|
+
await pushForceWithLease({ ...push, worktree: worktreePath, leaseSha: headSha });
|
|
247
|
+
return { headSha: newSha, strategy };
|
|
248
|
+
}
|
|
249
|
+
await runMerge(worktreePath, trunkSha, trunk, headRef, ident); // throws on conflict
|
|
250
|
+
const newSha = (await git(['rev-parse', 'HEAD'], worktreePath)).stdout.trim();
|
|
251
|
+
// A merge only adds a commit (the old head is its ancestor) → a plain push. Already
|
|
252
|
+
// up-to-date (runMerge no-op) leaves HEAD unchanged → nothing to push.
|
|
253
|
+
if (newSha !== headSha) {
|
|
254
|
+
await pushRef({ ...push, worktree: worktreePath });
|
|
255
|
+
}
|
|
256
|
+
return { headSha: newSha, strategy };
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
await teardown(owner, name, repoCloneDir, worktreePath);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { lstatSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { join, sep } from 'node:path';
|
|
3
|
+
import { codedError } from './git.js';
|
|
4
|
+
// Where a write is allowed to land inside a checked-out worktree.
|
|
5
|
+
//
|
|
6
|
+
// File paths reach the coding seams from adapter plans whose locations can be influenced by
|
|
7
|
+
// REPO CONTENT (a T3 manifest proposes a configPath), i.e. attacker-suppliable in cloud. Two
|
|
8
|
+
// guards, and NEITHER is sufficient alone:
|
|
9
|
+
// - assertSafeTargetPath is LEXICAL and runs before any side effect;
|
|
10
|
+
// - resolveContainedTargetPath is FILESYSTEM and runs after checkout, immediately before
|
|
11
|
+
// the write, because a committed symlink is a real directory entry and no amount of
|
|
12
|
+
// string inspection can see it.
|
|
13
|
+
/**
|
|
14
|
+
* Lexical: repo-relative, no traversal, no drive letter, no `.git` segment at ANY depth,
|
|
15
|
+
* case-folded. Throws a coded 'APPLY_FAILED'.
|
|
16
|
+
* ⚠ `.git` is checked per SEGMENT and case-INSENSITIVELY: the predecessor anchored it at the
|
|
17
|
+
* root (`norm === '.git' || norm.startsWith('.git/')`), so `a/.git/hooks/pre-commit` — a hook
|
|
18
|
+
* git runs on the next commit — sailed through, and `.GIT/config` did too on macOS.
|
|
19
|
+
*/
|
|
20
|
+
export function assertSafeTargetPath(path) {
|
|
21
|
+
const norm = path.replace(/\\/g, '/');
|
|
22
|
+
const segments = norm.split('/');
|
|
23
|
+
if (!norm ||
|
|
24
|
+
norm.startsWith('/') ||
|
|
25
|
+
/^[A-Za-z]:/.test(norm) ||
|
|
26
|
+
segments.some((s) => s === '' || s === '.' || s === '..' || s.toLowerCase() === '.git')) {
|
|
27
|
+
throw codedError('APPLY_FAILED', `invalid target path: ${path}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Containment: resolve every EXISTING component of `<root>/<path>` and assert the result
|
|
32
|
+
* stays inside `<root>`. Returns the absolute path to write to.
|
|
33
|
+
*
|
|
34
|
+
* ⚠ A COMMITTED SYMLINK IS A REAL DIRECTORY ENTRY, so `link/evil.txt` is traversal-free text
|
|
35
|
+
* that writes wherever `link` points — outside the worktree, with no `..` anywhere in it. An
|
|
36
|
+
* in-tree symlink is REFUSED outright rather than followed: git records the LINK, not the
|
|
37
|
+
* target, so a repo can ship one and nothing about writing through it is ever intended.
|
|
38
|
+
* ⚠ realpath the ROOT too — /tmp is a symlink to /private/tmp on macOS, and comparing an
|
|
39
|
+
* unresolved root against a resolved child refuses every legitimate write.
|
|
40
|
+
*
|
|
41
|
+
* Call AFTER checkout, IMMEDIATELY before the mkdir/write.
|
|
42
|
+
*/
|
|
43
|
+
export function resolveContainedTargetPath(root, path) {
|
|
44
|
+
assertSafeTargetPath(path);
|
|
45
|
+
let realRoot;
|
|
46
|
+
try {
|
|
47
|
+
realRoot = realpathSync(root);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw codedError('APPLY_FAILED', `worktree root is not readable: ${root}`);
|
|
51
|
+
}
|
|
52
|
+
let current = realRoot;
|
|
53
|
+
for (const segment of path.replace(/\\/g, '/').split('/')) {
|
|
54
|
+
const next = join(current, segment);
|
|
55
|
+
let entry;
|
|
56
|
+
try {
|
|
57
|
+
entry = lstatSync(next);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Nothing there yet — this segment and everything after it is ours to create, and a
|
|
61
|
+
// path that does not exist cannot redirect us anywhere.
|
|
62
|
+
current = next;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (entry.isSymbolicLink()) {
|
|
66
|
+
throw codedError('APPLY_FAILED', `refusing to write through the symlink ${segment} in ${path}`);
|
|
67
|
+
}
|
|
68
|
+
current = next;
|
|
69
|
+
}
|
|
70
|
+
if (current !== realRoot && !current.startsWith(realRoot + sep)) {
|
|
71
|
+
throw codedError('APPLY_FAILED', `target path escapes the worktree: ${path}`);
|
|
72
|
+
}
|
|
73
|
+
return current;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=safe-path.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { loadZod } from "../ai/runtime.js";
|
|
2
|
+
// The structured-output contract for the in-process `submit_fix` MCP tool. The agent
|
|
3
|
+
// EDITS files in the worktree and calls this ONCE at the end to report what it did.
|
|
4
|
+
// We deliberately capture ONLY a prose summary + a commit message here — the actual
|
|
5
|
+
// changeset (patch + file list) is derived from `git` after the run, never from the
|
|
6
|
+
// agent's self-report (which miscounts / omits files). Exported as a raw zod shape
|
|
7
|
+
// (what `tool()` wants) plus an assembled object schema for validation.
|
|
8
|
+
export function buildSubmitFixShape(z) {
|
|
9
|
+
return {
|
|
10
|
+
// A concise, user-facing description of the fix that was applied.
|
|
11
|
+
summary: z.string(),
|
|
12
|
+
// A conventional-commit-style message for the commit the host will create.
|
|
13
|
+
commitMessage: z.string(),
|
|
14
|
+
// The per-change report: ONE entry per file the agent changed — what changed and why, and the
|
|
15
|
+
// refs of the task items (F3, T1, S1-AC2, C1, P2 …) that change addresses. Optional at the
|
|
16
|
+
// schema level so a run that changed nothing is not made to invent one; the prompt asks for it.
|
|
17
|
+
// As with `summary`, this is a SELF-REPORT stored as commentary: the authoritative changeset
|
|
18
|
+
// still comes from git, and the manager drops any ref the run was not shown.
|
|
19
|
+
changes: z
|
|
20
|
+
.array(z.object({
|
|
21
|
+
path: z.string(),
|
|
22
|
+
// 1–3 sentences: what changed and why.
|
|
23
|
+
summary: z.string(),
|
|
24
|
+
refs: z.array(z.string()).optional(),
|
|
25
|
+
}))
|
|
26
|
+
.optional(),
|
|
27
|
+
// Items the agent deliberately did NOT fix, each with the reason (wrong, out of scope, needs
|
|
28
|
+
// a person, already done …).
|
|
29
|
+
unaddressed: z
|
|
30
|
+
.array(z.object({ ref: z.string(), reason: z.string() }))
|
|
31
|
+
.optional(),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** The raw shape `tool()` wants, built from the runtime's zod. */
|
|
35
|
+
export async function submitFixShape() {
|
|
36
|
+
return buildSubmitFixShape(await loadZod());
|
|
37
|
+
}
|
|
38
|
+
/** The assembled object schema, for validation. */
|
|
39
|
+
export async function submitFixSchema() {
|
|
40
|
+
const z = await loadZod();
|
|
41
|
+
return z.object(buildSubmitFixShape(z));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=schema.js.map
|