@in-the-loop-labs/pair-review 3.5.2 → 3.7.0

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.
Files changed (93) hide show
  1. package/README.md +4 -0
  2. package/package.json +1 -1
  3. package/plugin/.claude-plugin/plugin.json +1 -1
  4. package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
  5. package/public/css/analysis-config.css +1807 -0
  6. package/public/css/pr.css +1029 -2169
  7. package/public/index.html +11 -0
  8. package/public/js/components/AIPanel.js +39 -23
  9. package/public/js/components/AdvancedConfigTab.js +56 -4
  10. package/public/js/components/AnalysisConfigModal.js +41 -25
  11. package/public/js/components/ChatPanel.js +163 -3
  12. package/public/js/components/KeyboardShortcuts.js +10 -26
  13. package/public/js/components/ReviewModal.js +135 -13
  14. package/public/js/components/TourBar.js +248 -0
  15. package/public/js/components/VoiceCentricConfigTab.js +36 -0
  16. package/public/js/index.js +175 -16
  17. package/public/js/local.js +64 -8
  18. package/public/js/modules/cancel-background-job.js +183 -0
  19. package/public/js/modules/hunk-summary-renderer.js +116 -0
  20. package/public/js/modules/storage-cleanup.js +16 -0
  21. package/public/js/modules/suggestion-manager.js +25 -1
  22. package/public/js/modules/tour-renderer.js +755 -0
  23. package/public/js/pr.js +1826 -56
  24. package/public/js/repo-links.js +328 -0
  25. package/public/js/utils/modal-detection.js +77 -0
  26. package/public/js/utils/provider-model.js +88 -0
  27. package/public/js/utils/storage-keys.js +50 -0
  28. package/public/local.html +24 -0
  29. package/public/pr.html +24 -0
  30. package/public/repo-settings.html +1 -0
  31. package/public/setup.html +2 -0
  32. package/src/ai/abort-signal-wiring.js +130 -0
  33. package/src/ai/analyzer.js +125 -18
  34. package/src/ai/background-queue.js +290 -0
  35. package/src/ai/claude-cli.js +1 -1
  36. package/src/ai/claude-provider.js +50 -7
  37. package/src/ai/codex-provider.js +28 -5
  38. package/src/ai/copilot-provider.js +22 -3
  39. package/src/ai/cursor-agent-provider.js +22 -6
  40. package/src/ai/executable-provider.js +4 -19
  41. package/src/ai/gemini-provider.js +22 -5
  42. package/src/ai/hunk-hashing.js +161 -0
  43. package/src/ai/index.js +2 -0
  44. package/src/ai/opencode-provider.js +21 -5
  45. package/src/ai/pi-provider.js +21 -5
  46. package/src/ai/prompts/hunk-summary.js +199 -0
  47. package/src/ai/prompts/tour.js +232 -0
  48. package/src/ai/provider.js +21 -1
  49. package/src/ai/summary-generator.js +469 -0
  50. package/src/ai/tour-generator.js +568 -0
  51. package/src/config.js +778 -10
  52. package/src/database.js +282 -1
  53. package/src/external/github-adapter.js +114 -25
  54. package/src/git/base-branch.js +11 -4
  55. package/src/github/client.js +482 -588
  56. package/src/github/errors.js +55 -0
  57. package/src/github/impl/graphql/pending-review-comments.js +230 -0
  58. package/src/github/impl/graphql/pending-review.js +153 -0
  59. package/src/github/impl/graphql/review-lifecycle.js +161 -0
  60. package/src/github/impl/graphql/stack-walker.js +210 -0
  61. package/src/github/impl/host/pending-review-comments.js +338 -0
  62. package/src/github/impl/rest/pending-review.js +251 -0
  63. package/src/github/impl/rest/review-lifecycle.js +226 -0
  64. package/src/github/impl/rest/stack-walker.js +309 -0
  65. package/src/github/operations/pending-review-comments.js +79 -0
  66. package/src/github/operations/pending-review.js +89 -0
  67. package/src/github/operations/review-lifecycle.js +126 -0
  68. package/src/github/operations/stack-walker.js +87 -0
  69. package/src/github/parser.js +230 -4
  70. package/src/github/stack-walker.js +14 -189
  71. package/src/links/repo-links.js +230 -0
  72. package/src/local-review.js +201 -172
  73. package/src/main.js +133 -30
  74. package/src/routes/analyses.js +30 -7
  75. package/src/routes/bulk-analysis-configs.js +295 -0
  76. package/src/routes/config.js +118 -3
  77. package/src/routes/context-files.js +2 -29
  78. package/src/routes/external-comments.js +20 -10
  79. package/src/routes/github-collections.js +3 -1
  80. package/src/routes/local.js +410 -13
  81. package/src/routes/mcp.js +47 -4
  82. package/src/routes/middleware/validate-review-id.js +53 -0
  83. package/src/routes/pr.js +556 -71
  84. package/src/routes/reviews.js +145 -29
  85. package/src/routes/setup.js +8 -3
  86. package/src/routes/stack-analysis.js +33 -9
  87. package/src/routes/worktrees.js +3 -2
  88. package/src/server.js +2 -0
  89. package/src/setup/pr-setup.js +37 -11
  90. package/src/setup/stack-setup.js +13 -3
  91. package/src/single-port.js +6 -3
  92. package/src/utils/diff-hunks.js +65 -0
  93. package/src/utils/json-extractor.js +5 -2
@@ -21,6 +21,11 @@ function cleanupLegacyLocalStorage() {
21
21
  'settingsReferrer', // Unscoped version (now uses settingsReferrer:${repo})
22
22
  ];
23
23
 
24
+ // Legacy key prefixes (one entry per review id) that we need to sweep
25
+ const legacyPrefixes = [
26
+ 'pair-review:dismissed-summaries:' // Replaced by per-file toggle in v3.4
27
+ ];
28
+
24
29
  // Remove known legacy keys
25
30
  legacyKeys.forEach(key => {
26
31
  if (localStorage.getItem(key) !== null) {
@@ -28,6 +33,17 @@ function cleanupLegacyLocalStorage() {
28
33
  console.log(`[cleanup] Removed legacy localStorage key: ${key}`);
29
34
  }
30
35
  });
36
+
37
+ // Sweep prefixed keys. localStorage.length and key(i) iteration in
38
+ // reverse order so removals don't shift indexes we still need to read.
39
+ for (let i = localStorage.length - 1; i >= 0; i--) {
40
+ const key = localStorage.key(i);
41
+ if (!key) continue;
42
+ if (legacyPrefixes.some(prefix => key.startsWith(prefix))) {
43
+ localStorage.removeItem(key);
44
+ console.log(`[cleanup] Removed legacy localStorage key: ${key}`);
45
+ }
46
+ }
31
47
  }
32
48
 
33
49
  // Export for use in other modules
@@ -258,11 +258,35 @@ class SuggestionManager {
258
258
  existingSuggestionRows.forEach(row => row.remove());
259
259
  console.log(`[UI] Removed ${existingSuggestionRows.length} existing suggestion rows`);
260
260
 
261
+ // Only inline (line-targeted) suggestions need the diff body rendered
262
+ // ahead of time. File-level findings (is_file_level === 1 or no
263
+ // line_start) are handed to FileCommentManager and rendered above the
264
+ // diff — they never scan <tr> rows or call findHiddenSuggestions, so
265
+ // forcing their bodies to render would recreate the eager-render cost
266
+ // the lazy-body change removed (reviews dominated by file-level analyses
267
+ // are a common repository-wide pattern). Filter once and reuse below.
268
+ const inlineSuggestions = suggestions.filter(
269
+ s => s.file && s.line_start != null && s.is_file_level !== 1
270
+ );
271
+
272
+ // Render the lazy body of every file an INLINE suggestion targets BEFORE
273
+ // scanning rows. With lazy rendering an unrendered file has zero rows, so
274
+ // without this findHiddenSuggestions() would flag every line as "hidden"
275
+ // and we'd gap-expand against bodies that aren't even built yet.
276
+ // ensureFileBodyRendered is a cheap no-op for files already rendered or
277
+ // not in the diff.
278
+ if (this.prManager?.ensureFileBodyRendered) {
279
+ const targetFiles = new Set(inlineSuggestions.map(s => s.file));
280
+ for (const file of targetFiles) {
281
+ await this.prManager.ensureFileBodyRendered(file);
282
+ }
283
+ }
284
+
261
285
  // Auto-expand hidden lines for suggestions that target non-visible lines
262
286
  // Pass the side parameter so expandForSuggestion knows which coordinate system to use:
263
287
  // - RIGHT side = NEW coordinates (modified file, most common for AI suggestions)
264
288
  // - LEFT side = OLD coordinates (deleted lines from original file)
265
- const hiddenSuggestions = this.findHiddenSuggestions(suggestions);
289
+ const hiddenSuggestions = this.findHiddenSuggestions(inlineSuggestions);
266
290
  if (hiddenSuggestions.length > 0) {
267
291
  console.log(`[UI] Found ${hiddenSuggestions.length} suggestions targeting hidden lines, expanding...`);
268
292
  for (const hidden of hiddenSuggestions) {