@in-the-loop-labs/pair-review 3.3.2 → 3.3.4
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 +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
- package/public/css/pr.css +25 -2
- package/public/js/components/TimeoutSelect.js +4 -0
- package/public/js/local.js +4 -3
- package/public/js/modules/analysis-history.js +47 -22
- package/public/js/pr.js +65 -5
- package/src/ai/analyzer.js +48 -15
- package/src/ai/claude-provider.js +27 -15
- package/src/database.js +34 -8
- package/src/git/diff-flags.js +4 -2
- package/src/git/worktree-pool-lifecycle.js +12 -3
- package/src/git/worktree.js +172 -33
- package/src/main.js +7 -1
- package/src/routes/analyses.js +3 -1
- package/src/routes/pr.js +25 -27
- package/src/routes/reviews.js +19 -15
- package/src/setup/pr-setup.js +19 -6
- package/src/utils/diff-file-content.js +110 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Copyright 2026 Tim Perkins (tjwp) | SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Helpers for resolving file content directly from a cached diff snapshot.
|
|
4
|
+
*
|
|
5
|
+
* The diff's `index <old>..<new>` line is more precise than a repo-wide
|
|
6
|
+
* `base_sha` because it identifies the exact blob used for that file in the
|
|
7
|
+
* rendered patch, even if cached PR metadata drifts.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
function parseDiffGitPaths(headerLine) {
|
|
11
|
+
if (!headerLine.startsWith('diff --git ')) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const rest = headerLine.slice('diff --git '.length);
|
|
16
|
+
const quotedMatch = rest.match(/^"a\/(.+)" "b\/(.+)"$/);
|
|
17
|
+
if (quotedMatch) {
|
|
18
|
+
return {
|
|
19
|
+
oldPath: quotedMatch[1].replace(/\\"/g, '"'),
|
|
20
|
+
newPath: quotedMatch[2].replace(/\\"/g, '"')
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const plainMatch = rest.match(/^a\/(.+?) b\/(.+)$/);
|
|
25
|
+
if (!plainMatch) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
oldPath: plainMatch[1],
|
|
31
|
+
newPath: plainMatch[2]
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findFileBlobInfoInDiff(diffText, fileName) {
|
|
36
|
+
if (!diffText || !fileName) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const lines = diffText.split('\n');
|
|
41
|
+
let matchedPaths = null;
|
|
42
|
+
|
|
43
|
+
for (const line of lines) {
|
|
44
|
+
if (line.startsWith('diff --git ')) {
|
|
45
|
+
const paths = parseDiffGitPaths(line);
|
|
46
|
+
matchedPaths = paths && (paths.oldPath === fileName || paths.newPath === fileName)
|
|
47
|
+
? paths
|
|
48
|
+
: null;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!matchedPaths || !line.startsWith('index ')) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const match = line.match(/^index ([0-9a-f]+)\.\.([0-9a-f]+)(?: \d+)?$/i);
|
|
57
|
+
if (!match) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
...matchedPaths,
|
|
63
|
+
oldBlob: match[1],
|
|
64
|
+
newBlob: match[2]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isZeroObjectId(objectId) {
|
|
72
|
+
return typeof objectId === 'string' && /^0+$/.test(objectId);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveOriginalFileContentSpecs(prData, fileName) {
|
|
76
|
+
if (!prData || typeof prData !== 'object') {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const blobInfo = findFileBlobInfoInDiff(prData.diff, fileName);
|
|
81
|
+
const specs = [];
|
|
82
|
+
|
|
83
|
+
if (blobInfo?.oldBlob && !isZeroObjectId(blobInfo.oldBlob)) {
|
|
84
|
+
specs.push({
|
|
85
|
+
gitSpec: blobInfo.oldBlob,
|
|
86
|
+
source: 'diff blob'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (prData.base_sha) {
|
|
91
|
+
const originalPath = blobInfo?.oldPath || fileName;
|
|
92
|
+
specs.push({
|
|
93
|
+
gitSpec: `${prData.base_sha}:${originalPath}`,
|
|
94
|
+
source: 'base commit'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return specs;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resolveOriginalFileContentSpec(prData, fileName) {
|
|
102
|
+
return resolveOriginalFileContentSpecs(prData, fileName)[0] || null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = {
|
|
106
|
+
findFileBlobInfoInDiff,
|
|
107
|
+
parseDiffGitPaths,
|
|
108
|
+
resolveOriginalFileContentSpecs,
|
|
109
|
+
resolveOriginalFileContentSpec
|
|
110
|
+
};
|