@in-the-loop-labs/pair-review 1.2.1 → 1.2.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": "@in-the-loop-labs/pair-review",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Your AI-powered code review partner - Close the feedback loop with AI coding agents",
|
|
5
5
|
"main": "src/server.js",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test:e2e:debug": "playwright test --debug",
|
|
31
31
|
"generate:skill-prompts": "node scripts/generate-skill-prompts.js",
|
|
32
32
|
"changeset": "changeset",
|
|
33
|
-
"version": "changeset version && node scripts/sync-plugin-versions.js && git add package.json package-lock.json CHANGELOG.md .changeset .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json plugin-code-critic/.claude-plugin/plugin.json && git commit -m 'RELEASING: Bump versions'",
|
|
33
|
+
"version": "changeset version && npm install --package-lock-only && node scripts/sync-plugin-versions.js && git add package.json package-lock.json CHANGELOG.md .changeset .claude-plugin/marketplace.json plugin/.claude-plugin/plugin.json plugin-code-critic/.claude-plugin/plugin.json && git commit -m 'RELEASING: Bump versions'",
|
|
34
34
|
"release": "npm whoami > /dev/null || { echo 'Error: Not logged in to npm. Run: npm login'; exit 1; } && npm run version && changeset tag && npm publish && git push && git push --tags"
|
|
35
35
|
},
|
|
36
36
|
"keywords": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pair-review",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.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": "1.2.
|
|
3
|
+
"version": "1.2.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/src/github/parser.js
CHANGED
|
@@ -218,6 +218,62 @@ class PRArgumentParser {
|
|
|
218
218
|
return process.cwd();
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Check if a directory is a git repository that matches the specified owner/repo.
|
|
223
|
+
* Compares the git remote origin URL against the expected owner/repo.
|
|
224
|
+
*
|
|
225
|
+
* @param {string} directory - Directory path to check
|
|
226
|
+
* @param {string} expectedOwner - Expected repository owner
|
|
227
|
+
* @param {string} expectedRepo - Expected repository name
|
|
228
|
+
* @returns {Promise<boolean>} True if the directory is a matching git repository
|
|
229
|
+
*/
|
|
230
|
+
async isMatchingRepository(directory, expectedOwner, expectedRepo) {
|
|
231
|
+
try {
|
|
232
|
+
// Use _createGitForDirectory for testability (can be overridden in tests)
|
|
233
|
+
const git = this._createGitForDirectory(directory);
|
|
234
|
+
|
|
235
|
+
// Check if it's a git repository
|
|
236
|
+
const isRepo = await git.checkIsRepo();
|
|
237
|
+
if (!isRepo) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Get remote origin URL
|
|
242
|
+
const remotes = await git.getRemotes(true);
|
|
243
|
+
const origin = remotes.find(remote => remote.name === 'origin');
|
|
244
|
+
|
|
245
|
+
if (!origin) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const remoteUrl = origin.refs.fetch || origin.refs.push;
|
|
250
|
+
if (!remoteUrl) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Parse the owner/repo from the remote URL
|
|
255
|
+
const { owner, repo } = this.parseRepositoryFromURL(remoteUrl);
|
|
256
|
+
|
|
257
|
+
// Compare case-insensitively (GitHub repos are case-insensitive)
|
|
258
|
+
return owner.toLowerCase() === expectedOwner.toLowerCase() &&
|
|
259
|
+
repo.toLowerCase() === expectedRepo.toLowerCase();
|
|
260
|
+
} catch (error) {
|
|
261
|
+
// Any error means the directory doesn't match
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Create a git instance for a given directory.
|
|
268
|
+
* This method exists for testability - tests can override it.
|
|
269
|
+
* @param {string} directory - Directory path
|
|
270
|
+
* @returns {Object} simpleGit instance
|
|
271
|
+
* @private
|
|
272
|
+
*/
|
|
273
|
+
_createGitForDirectory(directory) {
|
|
274
|
+
return simpleGit(directory);
|
|
275
|
+
}
|
|
276
|
+
|
|
221
277
|
/**
|
|
222
278
|
* Check if current directory is a git repository
|
|
223
279
|
* @returns {Promise<boolean>} Whether current directory is a git repo
|
package/src/main.js
CHANGED
|
@@ -9,7 +9,7 @@ const { startServer } = require('./server');
|
|
|
9
9
|
const Analyzer = require('./ai/analyzer');
|
|
10
10
|
const { applyConfigOverrides } = require('./ai');
|
|
11
11
|
const { handleLocalReview, findMainGitRoot } = require('./local-review');
|
|
12
|
-
const { storePRData, registerRepositoryLocation } = require('./setup/pr-setup');
|
|
12
|
+
const { storePRData, registerRepositoryLocation, findRepositoryPath } = require('./setup/pr-setup');
|
|
13
13
|
const { normalizeRepository, resolveRenamedFile, resolveRenamedFileOld } = require('./utils/paths');
|
|
14
14
|
const logger = require('./utils/logger');
|
|
15
15
|
const simpleGit = require('simple-git');
|
|
@@ -484,16 +484,39 @@ async function handlePullRequest(args, config, db, flags = {}) {
|
|
|
484
484
|
console.log('Fetching pull request data from GitHub...');
|
|
485
485
|
const prData = await githubClient.fetchPullRequest(prInfo.owner, prInfo.repo, prInfo.number);
|
|
486
486
|
|
|
487
|
-
//
|
|
487
|
+
// Determine repository path: only use cwd if it matches the target repo
|
|
488
488
|
const currentDir = parser.getCurrentDirectory();
|
|
489
|
+
const isMatchingRepo = await parser.isMatchingRepository(currentDir, prInfo.owner, prInfo.repo);
|
|
489
490
|
|
|
490
|
-
|
|
491
|
-
|
|
491
|
+
let repositoryPath;
|
|
492
|
+
if (isMatchingRepo) {
|
|
493
|
+
// Current directory is a checkout of the target repository
|
|
494
|
+
repositoryPath = currentDir;
|
|
495
|
+
// Register the known repository location for future web UI usage
|
|
496
|
+
await registerRepositoryLocation(db, currentDir, prInfo.owner, prInfo.repo);
|
|
497
|
+
} else {
|
|
498
|
+
// Current directory is not the target repository - find or clone it
|
|
499
|
+
console.log(`Current directory is not a checkout of ${prInfo.owner}/${prInfo.repo}, locating repository...`);
|
|
500
|
+
const repository = normalizeRepository(prInfo.owner, prInfo.repo);
|
|
501
|
+
const result = await findRepositoryPath({
|
|
502
|
+
db,
|
|
503
|
+
owner: prInfo.owner,
|
|
504
|
+
repo: prInfo.repo,
|
|
505
|
+
repository,
|
|
506
|
+
prNumber: prInfo.number,
|
|
507
|
+
onProgress: (progress) => {
|
|
508
|
+
if (progress.message) {
|
|
509
|
+
console.log(progress.message);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
repositoryPath = result.repositoryPath;
|
|
514
|
+
}
|
|
492
515
|
|
|
493
516
|
// Setup git worktree
|
|
494
517
|
console.log('Setting up git worktree...');
|
|
495
518
|
const worktreeManager = new GitWorktreeManager(db);
|
|
496
|
-
const worktreePath = await worktreeManager.createWorktreeForPR(prInfo, prData,
|
|
519
|
+
const worktreePath = await worktreeManager.createWorktreeForPR(prInfo, prData, repositoryPath);
|
|
497
520
|
|
|
498
521
|
// Generate unified diff
|
|
499
522
|
console.log('Generating unified diff...');
|
|
@@ -701,7 +724,6 @@ async function performHeadlessReview(args, config, db, flags, options) {
|
|
|
701
724
|
console.log('Fetching pull request data from GitHub...');
|
|
702
725
|
const prData = await githubClient.fetchPullRequest(prInfo.owner, prInfo.repo, prInfo.number);
|
|
703
726
|
|
|
704
|
-
const repository = normalizeRepository(prInfo.owner, prInfo.repo);
|
|
705
727
|
let worktreePath;
|
|
706
728
|
let diff;
|
|
707
729
|
let changedFiles;
|
|
@@ -709,6 +731,16 @@ async function performHeadlessReview(args, config, db, flags, options) {
|
|
|
709
731
|
// Determine working directory: --use-checkout uses current directory
|
|
710
732
|
if (flags.useCheckout) {
|
|
711
733
|
worktreePath = process.cwd();
|
|
734
|
+
|
|
735
|
+
// Verify cwd matches the target repository when using --use-checkout
|
|
736
|
+
const isMatchingRepo = await parser.isMatchingRepository(worktreePath, prInfo.owner, prInfo.repo);
|
|
737
|
+
if (!isMatchingRepo) {
|
|
738
|
+
throw new Error(
|
|
739
|
+
`--use-checkout requires running from a checkout of ${prInfo.owner}/${prInfo.repo}, ` +
|
|
740
|
+
`but current directory does not match. Either cd to the correct repository or remove --use-checkout.`
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
|
|
712
744
|
await registerRepositoryLocation(db, worktreePath, prInfo.owner, prInfo.repo);
|
|
713
745
|
console.log(`Using current checkout at ${worktreePath}`);
|
|
714
746
|
|
|
@@ -752,13 +784,37 @@ async function performHeadlessReview(args, config, db, flags, options) {
|
|
|
752
784
|
return result;
|
|
753
785
|
});
|
|
754
786
|
} else {
|
|
755
|
-
// Use worktree approach
|
|
787
|
+
// Use worktree approach - only use cwd if it matches the target repo
|
|
756
788
|
const currentDir = parser.getCurrentDirectory();
|
|
757
|
-
await
|
|
789
|
+
const isMatchingRepo = await parser.isMatchingRepository(currentDir, prInfo.owner, prInfo.repo);
|
|
790
|
+
|
|
791
|
+
let repositoryPath;
|
|
792
|
+
if (isMatchingRepo) {
|
|
793
|
+
// Current directory is a checkout of the target repository
|
|
794
|
+
repositoryPath = currentDir;
|
|
795
|
+
await registerRepositoryLocation(db, currentDir, prInfo.owner, prInfo.repo);
|
|
796
|
+
} else {
|
|
797
|
+
// Current directory is not the target repository - find or clone it
|
|
798
|
+
console.log(`Current directory is not a checkout of ${prInfo.owner}/${prInfo.repo}, locating repository...`);
|
|
799
|
+
const repository = normalizeRepository(prInfo.owner, prInfo.repo);
|
|
800
|
+
const result = await findRepositoryPath({
|
|
801
|
+
db,
|
|
802
|
+
owner: prInfo.owner,
|
|
803
|
+
repo: prInfo.repo,
|
|
804
|
+
repository,
|
|
805
|
+
prNumber: prInfo.number,
|
|
806
|
+
onProgress: (progress) => {
|
|
807
|
+
if (progress.message) {
|
|
808
|
+
console.log(progress.message);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
repositoryPath = result.repositoryPath;
|
|
813
|
+
}
|
|
758
814
|
|
|
759
815
|
console.log('Setting up git worktree...');
|
|
760
816
|
const worktreeManager = new GitWorktreeManager(db);
|
|
761
|
-
worktreePath = await worktreeManager.createWorktreeForPR(prInfo, prData,
|
|
817
|
+
worktreePath = await worktreeManager.createWorktreeForPR(prInfo, prData, repositoryPath);
|
|
762
818
|
|
|
763
819
|
console.log('Generating unified diff...');
|
|
764
820
|
diff = await worktreeManager.generateUnifiedDiff(worktreePath, prData);
|