@in-the-loop-labs/pair-review 2.6.3 → 2.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 (43) hide show
  1. package/package.json +1 -1
  2. package/plugin/.claude-plugin/plugin.json +1 -1
  3. package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
  4. package/public/css/pr.css +194 -0
  5. package/public/index.html +168 -3
  6. package/public/js/components/AIPanel.js +16 -2
  7. package/public/js/components/ChatPanel.js +41 -6
  8. package/public/js/components/ConfirmDialog.js +21 -2
  9. package/public/js/components/CouncilProgressModal.js +13 -0
  10. package/public/js/components/DiffOptionsDropdown.js +410 -23
  11. package/public/js/components/SuggestionNavigator.js +12 -5
  12. package/public/js/components/TabTitle.js +96 -0
  13. package/public/js/components/Toast.js +6 -0
  14. package/public/js/index.js +648 -43
  15. package/public/js/local.js +569 -76
  16. package/public/js/modules/analysis-history.js +3 -2
  17. package/public/js/modules/comment-manager.js +5 -0
  18. package/public/js/modules/comment-minimizer.js +304 -0
  19. package/public/js/pr.js +82 -6
  20. package/public/local.html +14 -0
  21. package/public/pr.html +3 -0
  22. package/src/ai/analyzer.js +17 -11
  23. package/src/config.js +2 -0
  24. package/src/database.js +590 -39
  25. package/src/git/base-branch.js +173 -0
  26. package/src/git/sha-abbrev.js +35 -0
  27. package/src/github/client.js +32 -1
  28. package/src/hooks/hook-runner.js +100 -0
  29. package/src/hooks/payloads.js +212 -0
  30. package/src/local-review.js +468 -129
  31. package/src/local-scope.js +58 -0
  32. package/src/main.js +55 -4
  33. package/src/routes/analyses.js +73 -10
  34. package/src/routes/chat.js +33 -0
  35. package/src/routes/config.js +1 -0
  36. package/src/routes/local.js +734 -68
  37. package/src/routes/mcp.js +20 -10
  38. package/src/routes/pr.js +90 -12
  39. package/src/routes/setup.js +1 -0
  40. package/src/routes/worktrees.js +212 -148
  41. package/src/server.js +30 -0
  42. package/src/setup/local-setup.js +46 -5
  43. package/src/setup/pr-setup.js +28 -5
@@ -18,6 +18,7 @@ const { normalizeRepository } = require('../utils/paths');
18
18
  const { findMainGitRoot } = require('../local-review');
19
19
  const { getConfigDir, getMonorepoPath, resolveMonorepoOptions, DEFAULT_CHECKOUT_TIMEOUT_MS } = require('../config');
20
20
  const logger = require('../utils/logger');
21
+ const { fireReviewStartedHook } = require('../hooks/payloads');
21
22
  const simpleGit = require('simple-git');
22
23
  const fs = require('fs').promises;
23
24
  const path = require('path');
@@ -70,13 +71,15 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
70
71
  SELECT id FROM pr_metadata WHERE pr_number = ? AND repository = ? COLLATE NOCASE
71
72
  `, [prInfo.number, repository]);
72
73
 
74
+ const now = new Date().toISOString();
75
+
73
76
  if (existingPR) {
74
77
  // Update existing PR metadata (preserves ID)
75
78
  await run(db, `
76
79
  UPDATE pr_metadata
77
80
  SET title = ?, description = ?, author = ?,
78
81
  base_branch = ?, head_branch = ?, pr_data = ?,
79
- updated_at = CURRENT_TIMESTAMP
82
+ updated_at = CURRENT_TIMESTAMP, last_accessed_at = ?
80
83
  WHERE id = ?
81
84
  `, [
82
85
  prData.title,
@@ -85,6 +88,7 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
85
88
  prData.base_branch,
86
89
  prData.head_branch,
87
90
  JSON.stringify(extendedPRData),
91
+ now,
88
92
  existingPR.id
89
93
  ]);
90
94
  logger.info(`Updated existing PR metadata (ID: ${existingPR.id})`);
@@ -92,8 +96,8 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
92
96
  // Insert new PR metadata
93
97
  const result = await run(db, `
94
98
  INSERT INTO pr_metadata
95
- (pr_number, repository, title, description, author, base_branch, head_branch, pr_data)
96
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
99
+ (pr_number, repository, title, description, author, base_branch, head_branch, pr_data, last_accessed_at)
100
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
97
101
  `, [
98
102
  prInfo.number,
99
103
  repository,
@@ -102,7 +106,8 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
102
106
  prData.author,
103
107
  prData.base_branch,
104
108
  prData.head_branch,
105
- JSON.stringify(extendedPRData)
109
+ JSON.stringify(extendedPRData),
110
+ now
106
111
  ]);
107
112
  logger.info(`Created new PR metadata (ID: ${result.lastID})`);
108
113
  }
@@ -115,6 +120,9 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
115
120
  SELECT id FROM reviews WHERE pr_number = ? AND repository = ? COLLATE NOCASE
116
121
  `, [prInfo.number, repository]);
117
122
 
123
+ let isNewReview;
124
+ let reviewId;
125
+
118
126
  if (existingReview) {
119
127
  // Update existing review (preserves ID)
120
128
  await run(db, `
@@ -128,6 +136,8 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
128
136
  }),
129
137
  existingReview.id
130
138
  ]);
139
+ isNewReview = false;
140
+ reviewId = existingReview.id;
131
141
  logger.info(`Updated existing review (ID: ${existingReview.id})`);
132
142
  } else {
133
143
  // Insert new review
@@ -143,6 +153,8 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
143
153
  created_at: new Date().toISOString()
144
154
  })
145
155
  ]);
156
+ isNewReview = true;
157
+ reviewId = result.lastID;
146
158
  logger.info(`Created new review (ID: ${result.lastID})`);
147
159
  }
148
160
 
@@ -150,6 +162,8 @@ async function storePRData(db, prInfo, prData, diff, changedFiles, worktreePath,
150
162
  await run(db, 'COMMIT');
151
163
  logger.info(`Stored PR data for ${repository} #${prInfo.number}`);
152
164
 
165
+ return { isNewReview, reviewId };
166
+
153
167
  } catch (error) {
154
168
  // Rollback transaction on error
155
169
  await run(db, 'ROLLBACK');
@@ -465,7 +479,7 @@ async function setupPRReview({ db, owner, repo, prNumber, githubToken, config, o
465
479
  // Step: store - Persist PR data and register repository location
466
480
  // ------------------------------------------------------------------
467
481
  progress({ step: 'store', status: 'running', message: 'Storing pull request data...' });
468
- await storePRData(db, prInfo, prData, diff, changedFiles, worktreePath);
482
+ const { isNewReview, reviewId } = await storePRData(db, prInfo, prData, diff, changedFiles, worktreePath);
469
483
 
470
484
  // Register the repository path for future sessions if it wasn't already known
471
485
  if (knownPath === null && repositoryPath) {
@@ -478,6 +492,15 @@ async function setupPRReview({ db, owner, repo, prNumber, githubToken, config, o
478
492
  }
479
493
  progress({ step: 'store', status: 'completed', message: 'Pull request data stored.' });
480
494
 
495
+ // Fire review.started hook for new reviews (non-blocking).
496
+ // The GET route fires review.loaded on page load; firing review.started
497
+ // here ensures the first-time event isn't lost because storePRData already
498
+ // created the review record before the GET route's getOrCreate runs.
499
+ if (isNewReview) {
500
+ fireReviewStartedHook({ reviewId, prNumber, owner, repo, prData, config })
501
+ .catch(err => { logger.warn(`Review hook failed: ${err.message}`); });
502
+ }
503
+
481
504
  // ------------------------------------------------------------------
482
505
  // Return the review URL and title for the caller
483
506
  // ------------------------------------------------------------------