@in-the-loop-labs/pair-review 2.6.2 → 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 (50) hide show
  1. package/bin/git-diff-lines +1 -1
  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/plugin-code-critic/skills/analyze/scripts/git-diff-lines +1 -1
  6. package/public/css/pr.css +201 -0
  7. package/public/index.html +168 -3
  8. package/public/js/components/AIPanel.js +16 -2
  9. package/public/js/components/ChatPanel.js +41 -6
  10. package/public/js/components/ConfirmDialog.js +21 -2
  11. package/public/js/components/CouncilProgressModal.js +13 -0
  12. package/public/js/components/DiffOptionsDropdown.js +410 -23
  13. package/public/js/components/SuggestionNavigator.js +12 -5
  14. package/public/js/components/TabTitle.js +96 -0
  15. package/public/js/components/Toast.js +6 -0
  16. package/public/js/index.js +648 -43
  17. package/public/js/local.js +569 -76
  18. package/public/js/modules/analysis-history.js +3 -2
  19. package/public/js/modules/comment-manager.js +5 -0
  20. package/public/js/modules/comment-minimizer.js +304 -0
  21. package/public/js/pr.js +82 -6
  22. package/public/local.html +14 -0
  23. package/public/pr.html +3 -0
  24. package/src/ai/analyzer.js +22 -16
  25. package/src/ai/cursor-agent-provider.js +21 -12
  26. package/src/chat/prompt-builder.js +3 -3
  27. package/src/config.js +2 -0
  28. package/src/database.js +590 -39
  29. package/src/git/base-branch.js +173 -0
  30. package/src/git/sha-abbrev.js +35 -0
  31. package/src/git/worktree.js +3 -2
  32. package/src/github/client.js +32 -1
  33. package/src/hooks/hook-runner.js +100 -0
  34. package/src/hooks/payloads.js +212 -0
  35. package/src/local-review.js +468 -129
  36. package/src/local-scope.js +58 -0
  37. package/src/main.js +57 -6
  38. package/src/routes/analyses.js +73 -10
  39. package/src/routes/chat.js +33 -0
  40. package/src/routes/config.js +1 -0
  41. package/src/routes/github-collections.js +2 -2
  42. package/src/routes/local.js +734 -68
  43. package/src/routes/mcp.js +20 -10
  44. package/src/routes/pr.js +92 -14
  45. package/src/routes/setup.js +1 -0
  46. package/src/routes/worktrees.js +212 -148
  47. package/src/server.js +30 -0
  48. package/src/setup/local-setup.js +46 -5
  49. package/src/setup/pr-setup.js +28 -5
  50. package/src/utils/diff-file-list.js +1 -1
@@ -659,7 +659,7 @@ Do NOT create suggestions for any files not in this list. If you cannot find iss
659
659
  async getChangedFilesList(worktreePath, prMetadata) {
660
660
  try {
661
661
  const { stdout } = await execPromise(
662
- `git diff ${prMetadata.base_sha}...${prMetadata.head_sha} --name-only`,
662
+ `git diff --no-ext-diff ${prMetadata.base_sha}...${prMetadata.head_sha} --name-only`,
663
663
  { cwd: worktreePath }
664
664
  );
665
665
  return stdout.trim().split('\n').filter(f => f.length > 0);
@@ -670,22 +670,20 @@ Do NOT create suggestions for any files not in this list. If you cannot find iss
670
670
  }
671
671
 
672
672
  /**
673
- * Get list of changed files for local mode analysis
674
- * Includes unstaged changes and untracked files only.
675
- *
676
- * Design note: Staged files are intentionally excluded. Local mode focuses on
677
- * reviewing uncommitted working directory changes before they are staged.
678
- * Staged changes are considered "ready to commit" and outside the scope of
679
- * local review at this point.
673
+ * Get list of changed files for local mode analysis.
674
+ * By default includes unstaged changes and untracked files.
675
+ * When `options.includeStaged` is true, also includes staged (git add'd) files.
680
676
  *
681
677
  * @param {string} localPath - Path to the local git repository
678
+ * @param {Object} [options]
679
+ * @param {boolean} [options.includeStaged] - Also include staged files
682
680
  * @returns {Promise<Array<string>>} List of changed file paths
683
681
  */
684
- async getLocalChangedFiles(localPath) {
682
+ async getLocalChangedFiles(localPath, options = {}) {
685
683
  try {
686
- // Get modified tracked files (unstaged only - staged files are excluded by design)
684
+ // Get modified tracked files (unstaged)
687
685
  const { stdout: unstaged } = await execPromise(
688
- 'git diff --name-only',
686
+ 'git diff --no-ext-diff --name-only',
689
687
  { cwd: localPath }
690
688
  );
691
689
 
@@ -695,12 +693,20 @@ Do NOT create suggestions for any files not in this list. If you cannot find iss
695
693
  { cwd: localPath }
696
694
  );
697
695
 
698
- // Combine and dedupe (no staged files - see design note above)
699
- // Filter empty strings immediately after split to handle empty git output
700
696
  const unstagedFiles = unstaged.trim().split('\n').filter(f => f.length > 0);
701
697
  const untrackedFiles = untracked.trim().split('\n').filter(f => f.length > 0);
702
698
  const allFiles = [...unstagedFiles, ...untrackedFiles];
703
699
 
700
+ // Include staged files when scope includes staged
701
+ if (options.includeStaged) {
702
+ const { stdout: staged } = await execPromise(
703
+ 'git diff --no-ext-diff --cached --name-only',
704
+ { cwd: localPath }
705
+ );
706
+ const stagedFiles = staged.trim().split('\n').filter(f => f.length > 0);
707
+ allFiles.push(...stagedFiles);
708
+ }
709
+
704
710
  return [...new Set(allFiles)];
705
711
  } catch (error) {
706
712
  logger.warn(`Could not get local changed files for ${localPath}: ${error.message}`);
@@ -777,7 +783,7 @@ The following files are marked as generated in .gitattributes and should be SKIP
777
783
  ${generatedPatterns.map(p => `- ${p}`).join('\n')}
778
784
 
779
785
  These are auto-generated files (like package-lock.json, build outputs, etc.) that should not be reviewed.
780
- When running git diff, you can exclude these with: git diff ${'{base}'}...${'{head}'} -- ':!pattern' for each pattern.
786
+ When running git diff, you can exclude these with: git diff --no-ext-diff ${'{base}'}...${'{head}'} -- ':!pattern' for each pattern.
781
787
  Or simply ignore any changes to files matching these patterns in your analysis.
782
788
  `;
783
789
  }
@@ -983,10 +989,10 @@ ${prMetadata.description || '(No description provided)'}
983
989
  const isLocal = prMetadata.reviewType === 'local';
984
990
  if (isLocal) {
985
991
  // For local mode, diff against HEAD to see working directory changes
986
- return suffix ? `git diff HEAD ${suffix}` : 'git diff HEAD';
992
+ return suffix ? `git diff --no-ext-diff HEAD ${suffix}` : 'git diff --no-ext-diff HEAD';
987
993
  }
988
994
  // For PR mode, diff between base and head commits
989
- const baseCmd = `git diff ${prMetadata.base_sha}...${prMetadata.head_sha}`;
995
+ const baseCmd = `git diff --no-ext-diff ${prMetadata.base_sha}...${prMetadata.head_sha}`;
990
996
  return suffix ? `${baseCmd} ${suffix}` : baseCmd;
991
997
  }
992
998
 
@@ -30,9 +30,9 @@ const BIN_DIR = path.join(__dirname, '..', '..', 'bin');
30
30
  *
31
31
  * Tier structure:
32
32
  * - free (auto): Cursor's default auto-routing model
33
- * - fast (composer-1, gpt-5.3-codex-fast, gemini-3-flash): Quick analysis
33
+ * - fast (composer-2-fast, gpt-5.3-codex-fast, gemini-3-flash): Quick analysis
34
34
  * - balanced (composer-1.5, sonnet-4.6-thinking, sonnet-4.5-thinking, gemini-3.1-pro): Recommended for most reviews
35
- * - thorough (gpt-5.3-codex-high, gpt-5.3-codex-xhigh, opus-4.5-thinking, opus-4.6-thinking): Deep analysis for complex code
35
+ * - thorough (composer-2, gpt-5.3-codex-high, gpt-5.3-codex-xhigh, opus-4.5-thinking, opus-4.6-thinking): Deep analysis for complex code
36
36
  */
37
37
  const CURSOR_AGENT_MODELS = [
38
38
  {
@@ -45,23 +45,32 @@ const CURSOR_AGENT_MODELS = [
45
45
  badgeClass: 'badge-speed'
46
46
  },
47
47
  {
48
- id: 'composer-1.5',
49
- name: 'Composer 1.5',
50
- tier: 'balanced',
48
+ id: 'composer-2',
49
+ name: 'Composer 2',
50
+ tier: 'thorough',
51
51
  tagline: 'Latest Composer',
52
- description: 'Cursor Composer model—positioned between Sonnet and Opus for multi-file edits',
53
- badge: 'Balanced',
54
- badgeClass: 'badge-balanced'
52
+ description: 'Frontier-level coding model trained with compaction-in-the-loop RL—strong on long-horizon tasks requiring hundreds of actions',
53
+ badge: 'Latest',
54
+ badgeClass: 'badge-power'
55
55
  },
56
56
  {
57
- id: 'composer-1',
58
- name: 'Composer 1',
57
+ id: 'composer-2-fast',
58
+ name: 'Composer 2 Fast',
59
59
  tier: 'fast',
60
- tagline: 'Original Composer',
61
- description: 'Cursor Composer modelgood for quick multi-file editing workflows',
60
+ tagline: 'Fast Composer',
61
+ description: 'Same intelligence as Composer 2 with lower latencydefault for interactive Cursor sessions',
62
62
  badge: 'Fast',
63
63
  badgeClass: 'badge-speed'
64
64
  },
65
+ {
66
+ id: 'composer-1.5',
67
+ name: 'Composer 1.5',
68
+ tier: 'balanced',
69
+ tagline: 'Previous Composer',
70
+ description: 'Previous generation Cursor Composer model—positioned between Sonnet and Opus for multi-file edits',
71
+ badge: 'Previous Gen',
72
+ badgeClass: 'badge-balanced'
73
+ },
65
74
  {
66
75
  id: 'gpt-5.3-codex-fast',
67
76
  name: 'GPT-5.3 Codex Fast',
@@ -124,8 +124,8 @@ function buildReviewContext(review, prData) {
124
124
  lines.push(`This is a local code review for: ${name}`);
125
125
  lines.push('');
126
126
  lines.push('## Viewing Code Changes');
127
- lines.push('The changes under review are **unstaged and untracked local changes**. Staged changes (`git diff --cached`) are treated as already reviewed.');
128
- lines.push('To see the diff under review: `git diff`');
127
+ lines.push('The changes under review are **unstaged and untracked local changes**. Staged changes (`git diff --no-ext-diff --cached`) are treated as already reviewed.');
128
+ lines.push('To see the diff under review: `git diff --no-ext-diff`');
129
129
  lines.push('Do NOT use `git diff HEAD~1` or `git log` — those show committed history, not the changes under review.');
130
130
  } else {
131
131
  const parts = [];
@@ -146,7 +146,7 @@ function buildReviewContext(review, prData) {
146
146
  lines.push('');
147
147
  lines.push('## Viewing Code Changes');
148
148
  lines.push(`The changes under review are the diff between base commit \`${prData.base_sha.substring(0, 8)}\` and head commit \`${prData.head_sha.substring(0, 8)}\`.`);
149
- lines.push(`To see the full diff: \`git diff ${prData.base_sha}...${prData.head_sha}\``);
149
+ lines.push(`To see the full diff: \`git diff --no-ext-diff ${prData.base_sha}...${prData.head_sha}\``);
150
150
  lines.push('Do NOT use `git diff HEAD~1` or `git diff` without arguments — those do not show the PR changes.');
151
151
  }
152
152
  }
package/src/config.js CHANGED
@@ -23,6 +23,7 @@ const DEFAULT_CONFIG = {
23
23
  default_provider: "claude", // AI provider: 'claude', 'gemini', 'codex', 'copilot', 'opencode', 'cursor-agent', 'pi'
24
24
  default_model: "opus", // Model within the provider (e.g., 'opus' for Claude, 'gemini-2.5-pro' for Gemini)
25
25
  worktree_retention_days: 7,
26
+ review_retention_days: 21,
26
27
  dev_mode: false, // When true, disables static file caching for development
27
28
  debug_stream: false, // When true, logs AI provider streaming events (equivalent to --debug-stream CLI flag)
28
29
  db_name: "", // Custom database filename (default: database.db). Useful for per-worktree isolation.
@@ -35,6 +36,7 @@ const DEFAULT_CONFIG = {
35
36
  chat_providers: {}, // Custom chat provider configurations (overrides built-in defaults)
36
37
  monorepos: {}, // Monorepo configurations: { "owner/repo": { path: "~/path/to/clone" } }
37
38
  assisted_by_url: "https://github.com/in-the-loop-labs/pair-review", // URL for "Review assisted by" footer link
39
+ hooks: {}, // Hook commands per event: { "review.started": { "my_hook": { "command": "..." } } }
38
40
  enable_graphite: false // When true, shows Graphite links alongside GitHub links
39
41
  };
40
42