@in-the-loop-labs/pair-review 3.9.0 → 4.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.
- package/README.md +10 -10
- package/package.json +5 -9
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +1 -1
- package/public/css/pr.css +12 -60
- package/public/js/components/AdvancedConfigTab.js +1 -1
- package/public/js/components/AnalysisConfigModal.js +2 -2
- package/public/js/components/CouncilProgressModal.js +0 -10
- package/public/js/local.js +1 -50
- package/public/js/pr.js +3 -117
- package/public/js/utils/provider-model.js +1 -1
- package/public/local.html +0 -7
- package/public/pr.html +0 -7
- package/src/ai/analyzer.js +4 -4
- package/src/ai/antigravity-provider.js +594 -0
- package/src/ai/claude-provider.js +29 -7
- package/src/ai/index.js +1 -1
- package/src/ai/opencode-provider.js +1 -1
- package/src/ai/provider.js +5 -5
- package/src/ai/stream-parser.js +1 -52
- package/src/chat/acp-bridge.js +1 -1
- package/src/chat/chat-providers.js +1 -9
- package/src/config.js +3 -3
- package/src/database.js +1 -1
- package/src/main.js +6 -5
- package/src/routes/config.js +2 -2
- package/src/ai/gemini-provider.js +0 -752
package/src/ai/provider.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* AI Provider Abstraction Layer
|
|
4
4
|
*
|
|
5
|
-
* Defines a common interface for AI providers (Claude,
|
|
5
|
+
* Defines a common interface for AI providers (Claude, Antigravity, etc.)
|
|
6
6
|
* and provides a factory function to create provider instances.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -688,7 +688,7 @@ function getProviderConfigOverrides(providerId) {
|
|
|
688
688
|
|
|
689
689
|
/**
|
|
690
690
|
* Register a provider class
|
|
691
|
-
* @param {string} id - Provider ID (e.g., 'claude', '
|
|
691
|
+
* @param {string} id - Provider ID (e.g., 'claude', 'antigravity')
|
|
692
692
|
* @param {typeof AIProvider} providerClass - The provider class
|
|
693
693
|
*/
|
|
694
694
|
function registerProvider(id, providerClass) {
|
|
@@ -908,7 +908,7 @@ function getAllProvidersInfo() {
|
|
|
908
908
|
|
|
909
909
|
/**
|
|
910
910
|
* Create a provider instance
|
|
911
|
-
* @param {string} providerId - Provider ID (e.g., 'claude', '
|
|
911
|
+
* @param {string} providerId - Provider ID (e.g., 'claude', 'antigravity')
|
|
912
912
|
* @param {string} model - Model to use (optional, uses default if not specified)
|
|
913
913
|
* @param {Object} overrides - Per-call config overrides that supersede global providerConfigOverrides (optional)
|
|
914
914
|
* @returns {AIProvider}
|
|
@@ -1011,8 +1011,8 @@ async function testProviderAvailability(providerId, timeoutMs) {
|
|
|
1011
1011
|
* Matches against both the canonical model `id` and any `aliases` so legacy
|
|
1012
1012
|
* model IDs (e.g. `gpt-5.4` before reasoning-effort variants were introduced)
|
|
1013
1013
|
* still resolve their tier for historical analysis runs.
|
|
1014
|
-
* @param {string} providerId - Provider ID (e.g., 'claude', '
|
|
1015
|
-
* @param {string} modelId - Model ID (e.g., 'sonnet', 'gemini-
|
|
1014
|
+
* @param {string} providerId - Provider ID (e.g., 'claude', 'antigravity')
|
|
1015
|
+
* @param {string} modelId - Model ID (e.g., 'sonnet', 'gemini-3.1-pro-low')
|
|
1016
1016
|
* @returns {string|null} Tier name or null if provider or model not found
|
|
1017
1017
|
*/
|
|
1018
1018
|
function getTierForModel(providerId, modelId) {
|
package/src/ai/stream-parser.js
CHANGED
|
@@ -212,56 +212,6 @@ function parseCodexLine(line, options = {}) {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
/**
|
|
216
|
-
* Parse a single Gemini stream-json JSONL line into a normalized event.
|
|
217
|
-
* Returns null if the line should not be emitted.
|
|
218
|
-
*
|
|
219
|
-
* Gemini stream-json event types:
|
|
220
|
-
* - message (role: "assistant") → assistant_text
|
|
221
|
-
* - tool_use (tool_name, parameters) → tool_use
|
|
222
|
-
* - init, message (role: "user"), tool_result, result → filtered out
|
|
223
|
-
*
|
|
224
|
-
* @param {string} line - A single JSONL line from Gemini stdout
|
|
225
|
-
* @param {Object} [options] - Parse options
|
|
226
|
-
* @param {string} [options.cwd] - Working directory to strip from file paths
|
|
227
|
-
* @returns {{ type: string, text: string, timestamp: number } | null}
|
|
228
|
-
*/
|
|
229
|
-
function parseGeminiLine(line, options = {}) {
|
|
230
|
-
if (!line || !line.trim()) return null;
|
|
231
|
-
|
|
232
|
-
const { cwd } = options;
|
|
233
|
-
|
|
234
|
-
try {
|
|
235
|
-
const event = JSON.parse(line);
|
|
236
|
-
const eventType = event.type;
|
|
237
|
-
|
|
238
|
-
if (eventType === 'message' && event.role === 'assistant' && event.content && event.content.trim()) {
|
|
239
|
-
return {
|
|
240
|
-
type: 'assistant_text',
|
|
241
|
-
text: truncateSnippet(event.content),
|
|
242
|
-
timestamp: Date.now()
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (eventType === 'tool_use') {
|
|
247
|
-
const toolName = event.tool_name || 'unknown';
|
|
248
|
-
const detail = extractToolDetail(event.parameters, cwd);
|
|
249
|
-
const text = detail ? `${toolName}: ${detail}` : toolName;
|
|
250
|
-
return {
|
|
251
|
-
type: 'tool_use',
|
|
252
|
-
text: truncateSnippet(text),
|
|
253
|
-
timestamp: Date.now()
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// init, user messages, tool_result, result — never emit
|
|
258
|
-
return null;
|
|
259
|
-
} catch {
|
|
260
|
-
// Best-effort side channel — silently ignore non-JSON or malformed lines.
|
|
261
|
-
return null;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
215
|
/**
|
|
266
216
|
* Parse a single OpenCode JSONL line into a normalized event.
|
|
267
217
|
* Returns null if the line should not be emitted.
|
|
@@ -323,7 +273,7 @@ function parseOpenCodeLine(line, options = {}) {
|
|
|
323
273
|
*/
|
|
324
274
|
class StreamParser {
|
|
325
275
|
/**
|
|
326
|
-
* @param {Function} parseLine - Provider-specific line parser (e.g. parseClaudeLine,
|
|
276
|
+
* @param {Function} parseLine - Provider-specific line parser (e.g. parseClaudeLine, parseCodexLine)
|
|
327
277
|
* @param {Function} onEvent - Callback receiving normalized events { type, text, timestamp }
|
|
328
278
|
* @param {Object} [options] - Options passed through to parseLine (e.g. { cwd })
|
|
329
279
|
*/
|
|
@@ -636,7 +586,6 @@ module.exports = {
|
|
|
636
586
|
extractToolDetail,
|
|
637
587
|
parseClaudeLine,
|
|
638
588
|
parseCodexLine,
|
|
639
|
-
parseGeminiLine,
|
|
640
589
|
parseOpenCodeLine,
|
|
641
590
|
parseCursorAgentLine,
|
|
642
591
|
parsePiLine,
|
package/src/chat/acp-bridge.js
CHANGED
|
@@ -82,7 +82,7 @@ class AcpBridge extends EventEmitter {
|
|
|
82
82
|
const args = [...this.acpArgs];
|
|
83
83
|
const useShell = this.useShell;
|
|
84
84
|
|
|
85
|
-
// For multi-word commands (e.g. "devx
|
|
85
|
+
// For multi-word commands (e.g. "devx claude"), use shell mode
|
|
86
86
|
const spawnCmd = useShell ? `${command} ${args.join(' ')}` : command;
|
|
87
87
|
const spawnArgs = useShell ? [] : args;
|
|
88
88
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Chat Provider Registry
|
|
4
4
|
*
|
|
5
|
-
* Defines named chat providers (Pi, Copilot,
|
|
5
|
+
* Defines named chat providers (Pi, Copilot, OpenCode, Claude, Codex, Cursor) with their
|
|
6
6
|
* default commands/args, config overrides, and availability checks.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -32,14 +32,6 @@ const CHAT_PROVIDERS = {
|
|
|
32
32
|
args: ['--acp', '--stdio'],
|
|
33
33
|
env: {},
|
|
34
34
|
},
|
|
35
|
-
'gemini-acp': {
|
|
36
|
-
id: 'gemini-acp',
|
|
37
|
-
name: 'Gemini (ACP)',
|
|
38
|
-
type: 'acp',
|
|
39
|
-
command: 'gemini',
|
|
40
|
-
args: ['--experimental-acp'],
|
|
41
|
-
env: {},
|
|
42
|
-
},
|
|
43
35
|
'opencode-acp': {
|
|
44
36
|
id: 'opencode-acp',
|
|
45
37
|
name: 'OpenCode (ACP)',
|
package/src/config.js
CHANGED
|
@@ -66,8 +66,8 @@ const DEFAULT_CONFIG = {
|
|
|
66
66
|
port: 7247,
|
|
67
67
|
single_port: true, // When true, reuse a single server on the configured port; new invocations delegate to the running server
|
|
68
68
|
theme: "light",
|
|
69
|
-
default_provider: "claude", // AI provider: 'claude', '
|
|
70
|
-
default_model: "opus", // Model within the provider (e.g., 'opus' for Claude, 'gemini-
|
|
69
|
+
default_provider: "claude", // AI provider: 'claude', 'antigravity', 'codex', 'copilot', 'opencode', 'cursor-agent', 'pi'
|
|
70
|
+
default_model: "opus", // Model within the provider (e.g., 'opus' for Claude, 'gemini-3.1-pro-low' for Antigravity)
|
|
71
71
|
tours: {
|
|
72
72
|
enabled: false, // When true, the guided-tour feature is available (toolbar button visible, etc.)
|
|
73
73
|
auto_generate: true, // When true, a tour generation job is kicked off automatically on review load
|
|
@@ -89,7 +89,7 @@ const DEFAULT_CONFIG = {
|
|
|
89
89
|
db_name: "", // Custom database filename (default: database.db). Useful for per-worktree isolation.
|
|
90
90
|
yolo: false, // When true, skips fine-grained AI provider permission setup (equivalent to --yolo CLI flag)
|
|
91
91
|
enable_chat: true, // When true, enables the chat panel feature (uses chat_provider)
|
|
92
|
-
chat_provider: "pi", // Chat provider: 'pi', 'copilot-acp', '
|
|
92
|
+
chat_provider: "pi", // Chat provider: 'pi', 'copilot-acp', 'opencode-acp', 'cursor-acp', 'codex'
|
|
93
93
|
comment_format: "legacy", // Comment format preset or custom template for adopted suggestions
|
|
94
94
|
chat: { enable_shortcuts: true, enter_to_send: true }, // Chat panel settings (enable_shortcuts: show action shortcut buttons, enter_to_send: Enter sends message instead of newline)
|
|
95
95
|
providers: {}, // Custom AI analysis provider configurations (overrides built-in defaults)
|
package/src/database.js
CHANGED
|
@@ -5007,7 +5007,7 @@ class AnalysisRunRepository {
|
|
|
5007
5007
|
* @param {Object} runInfo - Run information
|
|
5008
5008
|
* @param {string} runInfo.id - Unique run ID (UUID)
|
|
5009
5009
|
* @param {number} runInfo.reviewId - Review ID (references reviews.id, works for both PR and local modes)
|
|
5010
|
-
* @param {string} [runInfo.provider] - AI provider (claude,
|
|
5010
|
+
* @param {string} [runInfo.provider] - AI provider (claude, antigravity, etc.)
|
|
5011
5011
|
* @param {string} [runInfo.model] - AI model name
|
|
5012
5012
|
* @param {string} [runInfo.customInstructions] - Merged custom instructions (kept for backward compatibility)
|
|
5013
5013
|
* @param {string} [runInfo.globalInstructions] - Global instructions from ~/.pair-review/global-instructions.md
|
package/src/main.js
CHANGED
|
@@ -196,9 +196,10 @@ OPTIONS:
|
|
|
196
196
|
Available models: opus, sonnet, haiku (Claude Code);
|
|
197
197
|
also: fable-5-xhigh, fable-5-high, opus-4.8-xhigh,
|
|
198
198
|
opus-4.8-high, opus-4.7-xhigh, opus-4.7-high,
|
|
199
|
-
opus-4.6-high, opus-4.6-1m, sonnet-
|
|
199
|
+
opus-4.6-high, opus-4.6-1m, sonnet-5-xhigh,
|
|
200
|
+
sonnet-5-high, sonnet-4.6
|
|
200
201
|
(opus is Opus 4.8 XHigh, the default)
|
|
201
|
-
or use provider-specific models with
|
|
202
|
+
or use provider-specific models with Antigravity/Codex
|
|
202
203
|
--council <handle> Run analysis with a saved council (multi-voice). Handle is a
|
|
203
204
|
council name, name-slug, or id (prefix). See --list-councils.
|
|
204
205
|
--list-councils List saved councils (handles to use with --council) and exit
|
|
@@ -228,7 +229,7 @@ EXAMPLES:
|
|
|
228
229
|
ENVIRONMENT VARIABLES:
|
|
229
230
|
GITHUB_TOKEN GitHub Personal Access Token (takes precedence over config file)
|
|
230
231
|
PAIR_REVIEW_CLAUDE_CMD Custom command to invoke Claude CLI (default: claude)
|
|
231
|
-
|
|
232
|
+
PAIR_REVIEW_ANTIGRAVITY_CMD Custom command to invoke Antigravity CLI (default: agy)
|
|
232
233
|
PAIR_REVIEW_CODEX_CMD Custom command to invoke Codex CLI (default: codex)
|
|
233
234
|
PAIR_REVIEW_MODEL Override the AI model (same as --model flag, default: opus)
|
|
234
235
|
|
|
@@ -582,7 +583,7 @@ GITHUB TOKEN:
|
|
|
582
583
|
ENVIRONMENT VARIABLES:
|
|
583
584
|
GITHUB_TOKEN GitHub Personal Access Token (takes precedence over config file)
|
|
584
585
|
PAIR_REVIEW_CLAUDE_CMD Custom Claude CLI command (default: claude)
|
|
585
|
-
|
|
586
|
+
PAIR_REVIEW_ANTIGRAVITY_CMD Custom Antigravity CLI command (default: agy)
|
|
586
587
|
PAIR_REVIEW_CODEX_CMD Custom Codex CLI command (default: codex)
|
|
587
588
|
PAIR_REVIEW_MODEL Default AI model (e.g., opus, sonnet, haiku)
|
|
588
589
|
PAIR_REVIEW_DB_NAME Custom database filename (overrides config)
|
|
@@ -593,7 +594,7 @@ LOCAL CONFIG:
|
|
|
593
594
|
|
|
594
595
|
AI PROVIDERS:
|
|
595
596
|
Claude (default): Requires 'claude' CLI installed
|
|
596
|
-
|
|
597
|
+
Antigravity: Requires 'agy' CLI installed
|
|
597
598
|
Codex: Requires 'codex' CLI installed
|
|
598
599
|
|
|
599
600
|
Select provider per-repository in the web UI settings.
|
package/src/routes/config.js
CHANGED
|
@@ -100,7 +100,7 @@ router.get('/runtime-config.js', (req, res) => {
|
|
|
100
100
|
* An explicitly configured model wins (the user opted into it). When no model is
|
|
101
101
|
* configured, derive it from the selected provider's own default rather than the
|
|
102
102
|
* provider-agnostic global default — otherwise a provider-only override (e.g.
|
|
103
|
-
* `default_provider: '
|
|
103
|
+
* `default_provider: 'antigravity'`) would pair with an Anthropic model like 'opus'.
|
|
104
104
|
*
|
|
105
105
|
* @param {Object} config - Configuration object
|
|
106
106
|
* @returns {{ provider: string, model: string }}
|
|
@@ -111,7 +111,7 @@ function resolveDefaultProviderModel(config) {
|
|
|
111
111
|
const explicitModel = config.default_model || config.model;
|
|
112
112
|
// Only honour an explicit model if it actually belongs to the selected provider.
|
|
113
113
|
// `DEFAULT_CONFIG.default_model` is always populated (e.g. 'opus'), so a
|
|
114
|
-
// provider-only override like `default_provider: '
|
|
114
|
+
// provider-only override like `default_provider: 'antigravity'` would otherwise inherit
|
|
115
115
|
// a foreign Anthropic model and return a mismatched pair. When the model does not
|
|
116
116
|
// belong to the provider, derive a coherent default from the provider itself.
|
|
117
117
|
// Match by canonical id OR alias so a config naming an alias (e.g. 'opus') is
|