@in-the-loop-labs/pair-review 1.6.1 → 2.0.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 (68) hide show
  1. package/README.md +77 -4
  2. package/package.json +1 -1
  3. package/plugin/.claude-plugin/plugin.json +1 -1
  4. package/plugin/skills/review-requests/SKILL.md +4 -1
  5. package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
  6. package/plugin-code-critic/skills/analyze/SKILL.md +4 -3
  7. package/public/css/pr.css +1875 -144
  8. package/public/js/CONVENTIONS.md +16 -0
  9. package/public/js/components/AIPanel.js +66 -0
  10. package/public/js/components/AnalysisConfigModal.js +2 -2
  11. package/public/js/components/ChatPanel.js +2952 -0
  12. package/public/js/components/CouncilProgressModal.js +28 -18
  13. package/public/js/components/KeyboardShortcuts.js +3 -0
  14. package/public/js/components/PanelGroup.js +723 -0
  15. package/public/js/components/PreviewModal.js +3 -8
  16. package/public/js/components/StatusIndicator.js +2 -2
  17. package/public/js/components/Toast.js +22 -1
  18. package/public/js/components/VoiceCentricConfigTab.js +2 -2
  19. package/public/js/index.js +8 -0
  20. package/public/js/local.js +25 -682
  21. package/public/js/modules/analysis-history.js +19 -66
  22. package/public/js/modules/comment-manager.js +57 -19
  23. package/public/js/modules/diff-context.js +176 -0
  24. package/public/js/modules/diff-renderer.js +30 -0
  25. package/public/js/modules/file-comment-manager.js +126 -105
  26. package/public/js/modules/file-list-merger.js +64 -0
  27. package/public/js/modules/panel-resizer.js +25 -6
  28. package/public/js/modules/suggestion-manager.js +40 -125
  29. package/public/js/pr.js +974 -178
  30. package/public/js/repo-settings.js +36 -6
  31. package/public/js/utils/category-emoji.js +44 -0
  32. package/public/js/utils/time.js +32 -0
  33. package/public/local.html +107 -71
  34. package/public/pr.html +107 -71
  35. package/public/repo-settings.html +32 -0
  36. package/src/ai/analyzer.js +8 -4
  37. package/src/ai/claude-provider.js +22 -11
  38. package/src/ai/copilot-provider.js +39 -9
  39. package/src/ai/cursor-agent-provider.js +36 -7
  40. package/src/ai/gemini-provider.js +17 -4
  41. package/src/ai/prompts/config.js +7 -1
  42. package/src/ai/provider-availability.js +1 -1
  43. package/src/ai/provider.js +25 -37
  44. package/src/ai/stream-parser.js +1 -1
  45. package/src/chat/CONVENTIONS.md +18 -0
  46. package/src/chat/pi-bridge.js +491 -0
  47. package/src/chat/prompt-builder.js +262 -0
  48. package/src/chat/session-manager.js +619 -0
  49. package/src/config.js +14 -0
  50. package/src/database.js +322 -15
  51. package/src/main.js +4 -17
  52. package/src/routes/analyses.js +721 -0
  53. package/src/routes/chat.js +655 -0
  54. package/src/routes/config.js +29 -8
  55. package/src/routes/context-files.js +223 -0
  56. package/src/routes/local.js +225 -1133
  57. package/src/routes/mcp.js +39 -30
  58. package/src/routes/pr.js +410 -52
  59. package/src/routes/reviews.js +1035 -0
  60. package/src/routes/shared.js +5 -30
  61. package/src/server.js +34 -12
  62. package/src/sse/review-events.js +46 -0
  63. package/src/utils/auto-context.js +88 -0
  64. package/src/utils/category-emoji.js +33 -0
  65. package/src/utils/diff-file-list.js +57 -0
  66. package/public/js/components/ProgressModal.js +0 -705
  67. package/src/routes/analysis.js +0 -1600
  68. package/src/routes/comments.js +0 -534
@@ -21,7 +21,8 @@ const BIN_DIR = path.join(__dirname, '..', '..', 'bin');
21
21
  */
22
22
  const GEMINI_MODELS = [
23
23
  {
24
- id: 'gemini-3-flash-preview',
24
+ id: 'gemini-3-flash',
25
+ aliases: ['gemini-3-flash-preview'],
25
26
  name: '3.0 Flash',
26
27
  tier: 'fast',
27
28
  tagline: 'Rapid Sanity Check',
@@ -40,16 +41,28 @@ const GEMINI_MODELS = [
40
41
  default: true
41
42
  },
42
43
  {
43
- id: 'gemini-3-pro-preview',
44
+ id: 'gemini-3-pro',
45
+ aliases: ['gemini-3-pro-preview'],
44
46
  name: '3.0 Pro',
45
47
  tier: 'thorough',
46
48
  tagline: 'Architectural Audit',
47
49
  description: 'Most intelligent Gemini model—advanced reasoning for deep architectural analysis',
48
50
  badge: 'Deep Dive',
49
51
  badgeClass: 'badge-power'
52
+ },
53
+ {
54
+ id: 'gemini-3.1-pro',
55
+ name: '3.1 Pro',
56
+ tier: 'thorough',
57
+ tagline: 'Latest & Greatest',
58
+ description: 'Newest Gemini model—cutting-edge reasoning for complex architectural reviews',
59
+ badge: 'Latest',
60
+ badgeClass: 'badge-power'
50
61
  }
51
62
  ];
52
63
 
64
+ const DEFAULT_GEMINI_MODEL = 'gemini-2.5-pro';
65
+
53
66
  class GeminiProvider extends AIProvider {
54
67
  /**
55
68
  * @param {string} model - Model identifier
@@ -59,7 +72,7 @@ class GeminiProvider extends AIProvider {
59
72
  * @param {Object} configOverrides.env - Additional environment variables
60
73
  * @param {Object[]} configOverrides.models - Custom model definitions
61
74
  */
62
- constructor(model = 'gemini-2.5-pro', configOverrides = {}) {
75
+ constructor(model = DEFAULT_GEMINI_MODEL, configOverrides = {}) {
63
76
  super(model);
64
77
 
65
78
  // Command precedence: ENV > config > default
@@ -690,7 +703,7 @@ class GeminiProvider extends AIProvider {
690
703
  }
691
704
 
692
705
  static getDefaultModel() {
693
- return 'gemini-2.5-pro';
706
+ return DEFAULT_GEMINI_MODEL;
694
707
  }
695
708
 
696
709
  static getInstallInstructions() {
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Defines tier mappings for the prompt system.
6
6
  * Note: Provider-specific model-to-tier mappings are defined in each provider's
7
- * getModels() method. Use getTierForModel() from src/ai/provider.js to query them.
7
+ * getModels() method. The tier is persisted on each analysis_runs record at creation time.
8
8
  */
9
9
 
10
10
  /**
@@ -21,6 +21,11 @@ const TIER_ALIASES = {
21
21
  */
22
22
  const TIERS = ['fast', 'balanced', 'thorough'];
23
23
 
24
+ /**
25
+ * All accepted tier values (internal tiers + user-facing aliases)
26
+ */
27
+ const VALID_TIERS = [...TIERS, ...Object.keys(TIER_ALIASES)];
28
+
24
29
  /**
25
30
  * Prompt types (analysis levels)
26
31
  */
@@ -44,6 +49,7 @@ function resolveTier(tierOrAlias) {
44
49
  module.exports = {
45
50
  TIER_ALIASES,
46
51
  TIERS,
52
+ VALID_TIERS,
47
53
  PROMPT_TYPES,
48
54
  resolveTier
49
55
  };
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * Provider Availability Module
4
4
  *
5
- * Manages background checking of AI provider availability at server startup.
5
+ * Manages checking of AI provider availability at server startup.
6
6
  * Caches results and exposes them for the /api/providers endpoint.
7
7
  */
8
8
 
@@ -10,6 +10,7 @@ const path = require('path');
10
10
  const { spawn } = require('child_process');
11
11
  const logger = require('../utils/logger');
12
12
  const { extractJSON } = require('../utils/json-extractor');
13
+ const { TIERS, TIER_ALIASES } = require('./prompts/config');
13
14
 
14
15
  // Directory containing bin scripts (git-diff-lines, etc.)
15
16
  const BIN_DIR = path.join(__dirname, '..', '..', 'bin');
@@ -345,23 +346,10 @@ function prettifyModelId(id) {
345
346
  .replace(/\b\w/g, c => c.toUpperCase()); // Capitalize each word
346
347
  }
347
348
 
348
- /**
349
- * Canonical model tiers
350
- */
351
- const CANONICAL_TIERS = ['fast', 'balanced', 'thorough'];
352
-
353
- /**
354
- * Tier aliases that map to canonical tiers
355
- */
356
- const TIER_ALIASES = {
357
- free: 'fast',
358
- premium: 'thorough'
359
- };
360
-
361
349
  /**
362
350
  * All valid tier values (canonical + aliases)
363
351
  */
364
- const VALID_TIERS = [...CANONICAL_TIERS, ...Object.keys(TIER_ALIASES)];
352
+ const VALID_TIERS = [...TIERS, ...Object.keys(TIER_ALIASES)];
365
353
 
366
354
  /**
367
355
  * Normalize a tier to its canonical form
@@ -603,27 +591,6 @@ function createProvider(providerId, model = null) {
603
591
  return new ProviderClass(actualModel, { ...(overrides || {}), yolo: yoloMode });
604
592
  }
605
593
 
606
- /**
607
- * Get tier for a specific model from a provider
608
- * Queries the provider's model definitions (or config overrides) to find the tier
609
- * @param {string} providerId - Provider ID (e.g., 'claude', 'gemini')
610
- * @param {string} modelId - Model ID (e.g., 'sonnet', 'gemini-2.5-pro')
611
- * @returns {string|null} Tier name or null if provider or model not found
612
- */
613
- function getTierForModel(providerId, modelId) {
614
- const ProviderClass = providerRegistry.get(providerId);
615
- if (!ProviderClass) {
616
- return null;
617
- }
618
-
619
- // Merge config models with built-in models
620
- const overrides = providerConfigOverrides.get(providerId);
621
- const models = mergeModels(ProviderClass.getModels(), overrides?.models);
622
-
623
- const model = models.find(m => m.id === modelId);
624
- return model?.tier || null;
625
- }
626
-
627
594
  /**
628
595
  * Test availability of a provider with timeout
629
596
  * @param {string} providerId - Provider ID
@@ -656,6 +623,27 @@ async function testProviderAvailability(providerId, timeout = 10000) {
656
623
  }
657
624
  }
658
625
 
626
+ /**
627
+ * Get tier for a specific model from a provider
628
+ * Queries the provider's model definitions (or config overrides) to find the tier
629
+ * @param {string} providerId - Provider ID (e.g., 'claude', 'gemini')
630
+ * @param {string} modelId - Model ID (e.g., 'sonnet', 'gemini-2.5-pro')
631
+ * @returns {string|null} Tier name or null if provider or model not found
632
+ */
633
+ function getTierForModel(providerId, modelId) {
634
+ const ProviderClass = providerRegistry.get(providerId);
635
+ if (!ProviderClass) {
636
+ return null;
637
+ }
638
+
639
+ // Merge config models with built-in models
640
+ const overrides = providerConfigOverrides.get(providerId);
641
+ const models = mergeModels(ProviderClass.getModels(), overrides?.models);
642
+
643
+ const model = models.find(m => m.id === modelId);
644
+ return model?.tier || null;
645
+ }
646
+
659
647
  module.exports = {
660
648
  AIProvider,
661
649
  MODEL_TIERS,
@@ -665,12 +653,12 @@ module.exports = {
665
653
  getRegisteredProviderIds,
666
654
  getAllProvidersInfo,
667
655
  createProvider,
668
- getTierForModel,
669
656
  testProviderAvailability,
670
657
  // Config override support
671
658
  applyConfigOverrides,
672
659
  getProviderConfigOverrides,
673
660
  inferModelDefaults,
674
661
  resolveDefaultModel,
675
- prettifyModelId
662
+ prettifyModelId,
663
+ getTierForModel
676
664
  };
@@ -3,7 +3,7 @@
3
3
  * Stream Parser - Side-channel parser for real-time AI streaming events
4
4
  *
5
5
  * Reads stdout data incrementally from provider processes and emits normalized
6
- * events for display in the ProgressModal. This is a read-only side channel;
6
+ * events for display in the progress modal. This is a read-only side channel;
7
7
  * the existing stdout buffering and final JSON extraction remain untouched.
8
8
  *
9
9
  * Normalized event shape:
@@ -0,0 +1,18 @@
1
+ # Chat Module Conventions
2
+
3
+ ## Action handler pattern (ChatPanel.js)
4
+
5
+ All action handlers (adopt, update, dismiss) follow identical structure:
6
+ 1. Guard: if streaming or no `_contextItemId`, return early
7
+ 2. Set `_pendingActionContext` with `{ type, itemId }`
8
+ 3. Set `inputEl.value` to clean, human-readable text (NO item IDs)
9
+ 4. Call `sendMessage()`
10
+
11
+ See `_handleAdoptClick` as the canonical example. New action handlers must follow this pattern.
12
+
13
+ ## Action metadata separation
14
+
15
+ Item IDs never appear in user-visible message text. They flow through:
16
+ `_pendingActionContext` (frontend) → `actionContext` (API payload) → `[Action: ...]` hint (agent-facing message in session-manager.js)
17
+
18
+ This keeps the chat transcript clean while giving the agent structured metadata it can parse reliably.