@in-the-loop-labs/pair-review 3.0.1 → 3.0.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pair-review",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "pair-review app integration — Open PRs and local changes in the pair-review web UI, run server-side AI analysis, and address review feedback. Requires the pair-review MCP server.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "in-the-loop-labs",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-critic",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "AI-powered code review analysis — Run three-level AI analysis and implement-review-fix loops directly in your coding agent. Works standalone, no server required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "in-the-loop-labs",
|
package/public/js/local.js
CHANGED
|
@@ -674,6 +674,10 @@ class LocalManager {
|
|
|
674
674
|
// User cancelled — keep old diff, early return
|
|
675
675
|
return;
|
|
676
676
|
}
|
|
677
|
+
// resolved is the response object — merge branchAvailable into result
|
|
678
|
+
if (resolved.branchAvailable !== undefined) {
|
|
679
|
+
result.branchAvailable = resolved.branchAvailable;
|
|
680
|
+
}
|
|
677
681
|
}
|
|
678
682
|
// Branch scope: backend already updated SHA and persisted diff — fall through
|
|
679
683
|
}
|
|
@@ -702,7 +706,7 @@ class LocalManager {
|
|
|
702
706
|
/**
|
|
703
707
|
* Handle a non-branch-scope HEAD SHA change.
|
|
704
708
|
* Shows a 3-option dialog (or auto-updates in silent mode).
|
|
705
|
-
* @returns {
|
|
709
|
+
* @returns {Object|false} The response data object if the session was updated in-place (caller should apply diff),
|
|
706
710
|
* false if cancelled or redirecting away (caller should skip _applyRefreshedDiff)
|
|
707
711
|
*/
|
|
708
712
|
async _resolveHeadChange(result, opts) {
|
|
@@ -771,8 +775,9 @@ class LocalManager {
|
|
|
771
775
|
return false; // navigating away — caller must not fire _applyRefreshedDiff
|
|
772
776
|
}
|
|
773
777
|
|
|
774
|
-
// action === 'updated' — session SHA + diff updated, continue to reload
|
|
775
|
-
|
|
778
|
+
// action === 'updated' — session SHA + diff updated, continue to reload.
|
|
779
|
+
// Return the response data so the caller can extract branchAvailable, etc.
|
|
780
|
+
return data;
|
|
776
781
|
}
|
|
777
782
|
|
|
778
783
|
/**
|
|
@@ -806,6 +811,11 @@ class LocalManager {
|
|
|
806
811
|
// refresh must call unconditionally since the manager won't re-fire its callback.
|
|
807
812
|
await manager.loadAISuggestions(null, manager.selectedRunId);
|
|
808
813
|
|
|
814
|
+
// Update branchAvailable on the scope selector if the backend sent an updated value
|
|
815
|
+
if (result.branchAvailable !== undefined && manager.diffOptionsDropdown) {
|
|
816
|
+
manager.diffOptionsDropdown.branchAvailable = result.branchAvailable;
|
|
817
|
+
}
|
|
818
|
+
|
|
809
819
|
// Clear stale state after successful refresh
|
|
810
820
|
manager._hideStaleBadge();
|
|
811
821
|
manager._stalenessPromise = null;
|
package/src/database.js
CHANGED
|
@@ -2717,6 +2717,7 @@ class ReviewRepository {
|
|
|
2717
2717
|
* @param {Object} [updates.reviewData] - Additional review data (will be JSON stringified)
|
|
2718
2718
|
* @param {string} [updates.customInstructions] - Custom instructions used for AI analysis
|
|
2719
2719
|
* @param {string} [updates.summary] - AI analysis summary
|
|
2720
|
+
* @param {string} [updates.local_head_sha] - Local HEAD SHA
|
|
2720
2721
|
* @param {Date|string} [updates.submittedAt] - Submission timestamp
|
|
2721
2722
|
* @returns {Promise<boolean>} True if record was updated
|
|
2722
2723
|
*/
|
|
@@ -2759,6 +2760,11 @@ class ReviewRepository {
|
|
|
2759
2760
|
params.push(updates.local_head_branch);
|
|
2760
2761
|
}
|
|
2761
2762
|
|
|
2763
|
+
if (updates.local_head_sha !== undefined) {
|
|
2764
|
+
setClauses.push('local_head_sha = ?');
|
|
2765
|
+
params.push(updates.local_head_sha);
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2762
2768
|
if (updates.submittedAt !== undefined) {
|
|
2763
2769
|
setClauses.push('submitted_at = ?');
|
|
2764
2770
|
const submittedAt = updates.submittedAt instanceof Date
|
package/src/routes/local.js
CHANGED
|
@@ -66,6 +66,34 @@ function deleteLocalReviewDiff(reviewId) {
|
|
|
66
66
|
localReviewDiffs.delete(toIntKey(reviewId));
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Check whether branch scope should be selectable in the scope range selector.
|
|
71
|
+
* Returns true when the branch has commits ahead of the base branch.
|
|
72
|
+
* Non-fatal: returns false on any error.
|
|
73
|
+
*/
|
|
74
|
+
async function checkBranchAvailable(localPath, branchName, scopeStart, config, repositoryName) {
|
|
75
|
+
if (includesBranch(scopeStart)) return true;
|
|
76
|
+
if (!branchName || branchName === 'HEAD' || branchName === 'unknown' || !localPath) return false;
|
|
77
|
+
try {
|
|
78
|
+
const baseBranch = require('../git/base-branch');
|
|
79
|
+
const depsOverride = getGitHubToken(config) ? { getGitHubToken: () => getGitHubToken(config) } : undefined;
|
|
80
|
+
const detection = await baseBranch.detectBaseBranch(localPath, branchName, {
|
|
81
|
+
repository: repositoryName,
|
|
82
|
+
enableGraphite: config.enable_graphite === true,
|
|
83
|
+
_deps: depsOverride
|
|
84
|
+
});
|
|
85
|
+
if (detection) {
|
|
86
|
+
// Lazy require to ensure testability via vi.spyOn on the module exports
|
|
87
|
+
const localReview = require('../local-review');
|
|
88
|
+
const commitCount = await localReview.getBranchCommitCount(localPath, detection.baseBranch);
|
|
89
|
+
return commitCount > 0;
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
// Non-fatal — branch stop stays disabled
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
69
97
|
/**
|
|
70
98
|
* Delete a local review session and its in-memory diff cache.
|
|
71
99
|
* Shared by both single-delete and bulk-delete routes.
|
|
@@ -575,26 +603,9 @@ router.get('/api/local/:reviewId', async (req, res) => {
|
|
|
575
603
|
// Determine if Branch stop should be selectable in the scope range selector.
|
|
576
604
|
// This is independent of branchInfo (which guards on no uncommitted changes).
|
|
577
605
|
// Branch is available when: not detached HEAD, not on default branch, and has commits ahead.
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const { getBranchCommitCount } = require('../local-review');
|
|
582
|
-
const { detectBaseBranch } = require('../git/base-branch');
|
|
583
|
-
const config = req.app.get('config') || {};
|
|
584
|
-
const depsOverride = getGitHubToken(config) ? { getGitHubToken: () => getGitHubToken(config) } : undefined;
|
|
585
|
-
const detection = await detectBaseBranch(review.local_path, branchName, {
|
|
586
|
-
repository: repositoryName,
|
|
587
|
-
enableGraphite: config.enable_graphite === true,
|
|
588
|
-
_deps: depsOverride
|
|
589
|
-
});
|
|
590
|
-
if (detection) {
|
|
591
|
-
const commitCount = await getBranchCommitCount(review.local_path, detection.baseBranch);
|
|
592
|
-
branchAvailable = commitCount > 0;
|
|
593
|
-
}
|
|
594
|
-
} catch {
|
|
595
|
-
// Non-fatal — branch stop stays disabled
|
|
596
|
-
}
|
|
597
|
-
}
|
|
606
|
+
const branchAvailable = Boolean(branchInfo) || await checkBranchAvailable(
|
|
607
|
+
review.local_path, branchName, scopeStart, req.app.get('config') || {}, repositoryName
|
|
608
|
+
);
|
|
598
609
|
|
|
599
610
|
// Compute SHA abbreviation length from the repo's git config
|
|
600
611
|
const shaAbbrevLength = getShaAbbrevLength(review.local_path);
|
|
@@ -1364,6 +1375,15 @@ router.post('/api/local/:reviewId/refresh', async (req, res) => {
|
|
|
1364
1375
|
logger.warn(`Could not check HEAD SHA: ${headError.message}`);
|
|
1365
1376
|
}
|
|
1366
1377
|
|
|
1378
|
+
// Recompute branchAvailable so the frontend can update the scope selector
|
|
1379
|
+
// (e.g. after a commit creates the first branch-ahead commit).
|
|
1380
|
+
const config = req.app.get('config') || {};
|
|
1381
|
+
let branchName;
|
|
1382
|
+
try { branchName = await getCurrentBranch(localPath); } catch (_) { branchName = review.local_head_branch || null; }
|
|
1383
|
+
const branchAvailable = await checkBranchAvailable(
|
|
1384
|
+
localPath, branchName, scopeStart, config, review.repository
|
|
1385
|
+
);
|
|
1386
|
+
|
|
1367
1387
|
// Non-branch HEAD change: skip diff computation entirely — the old diff is
|
|
1368
1388
|
// preserved until the user decides (via resolve-head-change) what to do.
|
|
1369
1389
|
// The resolve-head-change endpoint will recompute the diff for whichever
|
|
@@ -1373,6 +1393,7 @@ router.post('/api/local/:reviewId/refresh', async (req, res) => {
|
|
|
1373
1393
|
success: true,
|
|
1374
1394
|
message: 'HEAD changed — awaiting user decision',
|
|
1375
1395
|
headShaChanged,
|
|
1396
|
+
branchAvailable,
|
|
1376
1397
|
previousHeadSha: originalHeadSha,
|
|
1377
1398
|
currentHeadSha: currentHeadSha || null,
|
|
1378
1399
|
stats: {}
|
|
@@ -1397,6 +1418,7 @@ router.post('/api/local/:reviewId/refresh', async (req, res) => {
|
|
|
1397
1418
|
success: true,
|
|
1398
1419
|
message: 'Diff refreshed successfully',
|
|
1399
1420
|
headShaChanged,
|
|
1421
|
+
branchAvailable,
|
|
1400
1422
|
previousHeadSha: originalHeadSha,
|
|
1401
1423
|
currentHeadSha: currentHeadSha || null,
|
|
1402
1424
|
stats: {
|
|
@@ -1453,17 +1475,24 @@ router.post('/api/local/:reviewId/resolve-head-change', async (req, res) => {
|
|
|
1453
1475
|
const scopeEnd = review.local_scope_end || DEFAULT_SCOPE.end;
|
|
1454
1476
|
|
|
1455
1477
|
if (action === 'update') {
|
|
1456
|
-
//
|
|
1457
|
-
|
|
1478
|
+
// Read live branch — may differ from stored value after a checkout.
|
|
1479
|
+
// Lazy require to ensure testability via vi.spyOn on the module exports.
|
|
1480
|
+
let headBranch;
|
|
1481
|
+
try { headBranch = await require('../local-review').getCurrentBranch(localPath); } catch (_) { headBranch = review.local_head_branch || null; }
|
|
1482
|
+
|
|
1483
|
+
// Check for UNIQUE conflict before any mutation.
|
|
1484
|
+
// Use the live branch + new SHA so the conflict check targets the
|
|
1485
|
+
// final identity tuple (localPath, newHeadSha, headBranch).
|
|
1458
1486
|
const conflict = await reviewRepo.getLocalReview(localPath, newHeadSha, headBranch);
|
|
1459
1487
|
if (conflict && conflict.id !== reviewId) {
|
|
1460
1488
|
logger.log('API', `UNIQUE conflict: session #${conflict.id} already exists for this HEAD`, 'yellow');
|
|
1461
1489
|
return res.json({ success: true, action: 'redirect', sessionId: conflict.id });
|
|
1462
1490
|
}
|
|
1463
1491
|
|
|
1464
|
-
//
|
|
1465
|
-
|
|
1466
|
-
|
|
1492
|
+
// Persist SHA and branch together in a single write so SQLite only
|
|
1493
|
+
// ever sees the final identity tuple — no transient intermediate state.
|
|
1494
|
+
await reviewRepo.updateReview(reviewId, { local_head_sha: newHeadSha, local_head_branch: headBranch });
|
|
1495
|
+
logger.log('API', `Updated HEAD SHA and branch on session ${reviewId}`, 'cyan');
|
|
1467
1496
|
|
|
1468
1497
|
// Recompute and persist diff
|
|
1469
1498
|
const scopedResult = await generateScopedDiff(localPath, scopeStart, scopeEnd, review.local_base_branch);
|
|
@@ -1475,7 +1504,14 @@ router.post('/api/local/:reviewId/resolve-head-change', async (req, res) => {
|
|
|
1475
1504
|
logger.warn(`Could not persist diff to database: ${persistError.message}`);
|
|
1476
1505
|
}
|
|
1477
1506
|
|
|
1478
|
-
|
|
1507
|
+
// Recompute branchAvailable — the commit may have created the first
|
|
1508
|
+
// branch-ahead commit, making the Branch scope stop selectable.
|
|
1509
|
+
const config = req.app.get('config') || {};
|
|
1510
|
+
const branchAvailable = await checkBranchAvailable(
|
|
1511
|
+
localPath, headBranch, scopeStart, config, review.repository
|
|
1512
|
+
);
|
|
1513
|
+
|
|
1514
|
+
return res.json({ success: true, action: 'updated', branchAvailable });
|
|
1479
1515
|
}
|
|
1480
1516
|
|
|
1481
1517
|
// action === 'new-session'
|