@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/README.md
CHANGED
|
@@ -65,7 +65,7 @@ pair-review is a local web application for keeping humans in the loop with AI co
|
|
|
65
65
|
- **Local-First**: All data and processing happens on your machine - no cloud dependencies
|
|
66
66
|
- **GitHub-Familiar UI**: Interface feels instantly familiar to GitHub users
|
|
67
67
|
- **Human-in-the-Loop**: AI suggests, you decide
|
|
68
|
-
- **Multiple AI Providers**: Support for Claude,
|
|
68
|
+
- **Multiple AI Providers**: Support for Claude, Antigravity, Codex, Copilot, OpenCode, Cursor, and Pi. Use your existing subscription!
|
|
69
69
|
- **Progressive**: Start simple with manual review, add AI analysis when you need it
|
|
70
70
|
|
|
71
71
|
## Workflows
|
|
@@ -437,7 +437,7 @@ pair-review supports several environment variables for customizing behavior:
|
|
|
437
437
|
|----------|-------------|---------|
|
|
438
438
|
| `GITHUB_TOKEN` | GitHub Personal Access Token (takes precedence over config file) | - |
|
|
439
439
|
| `PAIR_REVIEW_CLAUDE_CMD` | Custom command to invoke Claude CLI | `claude` |
|
|
440
|
-
| `
|
|
440
|
+
| `PAIR_REVIEW_ANTIGRAVITY_CMD` | Custom command to invoke Antigravity CLI | `agy` |
|
|
441
441
|
| `PAIR_REVIEW_CODEX_CMD` | Custom command to invoke Codex CLI | `codex` |
|
|
442
442
|
| `PAIR_REVIEW_COPILOT_CMD` | Custom command to invoke Copilot CLI | `copilot` |
|
|
443
443
|
| `PAIR_REVIEW_OPENCODE_CMD` | Custom command to invoke OpenCode CLI | `opencode` |
|
|
@@ -466,8 +466,8 @@ PAIR_REVIEW_CLAUDE_CMD="/usr/local/bin/claude" pair-review 123
|
|
|
466
466
|
# Use a wrapper command (supports multi-word commands)
|
|
467
467
|
PAIR_REVIEW_CLAUDE_CMD="devx claude" pair-review 123
|
|
468
468
|
|
|
469
|
-
# Use
|
|
470
|
-
|
|
469
|
+
# Use a custom path for the Antigravity CLI
|
|
470
|
+
PAIR_REVIEW_ANTIGRAVITY_CMD="/usr/local/bin/agy" pair-review --local
|
|
471
471
|
|
|
472
472
|
# Force a specific model for this review
|
|
473
473
|
PAIR_REVIEW_MODEL="opus" pair-review 123
|
|
@@ -491,7 +491,7 @@ Create a Personal Access Token (PAT) with these scopes:
|
|
|
491
491
|
pair-review integrates with AI providers via their CLI tools:
|
|
492
492
|
|
|
493
493
|
- **Claude**: Uses Claude Code CLI
|
|
494
|
-
- **
|
|
494
|
+
- **Antigravity**: Uses the Antigravity CLI (`agy`), the successor to the Gemini CLI. Install it with `curl -fsSL https://antigravity.google/cli/install.sh | bash` (macOS/Linux; it is not an npm package) — see the [Antigravity docs](https://antigravity.google/docs). Antigravity is an analysis-only provider: it runs as an agentic reviewer in non-interactive print mode and has **no chat/ACP mode**.
|
|
495
495
|
- **Codex**: Uses Codex CLI
|
|
496
496
|
- **GitHub Copilot**: Uses Copilot CLI
|
|
497
497
|
- **OpenCode**: Uses OpenCode CLI (requires model configuration)
|
|
@@ -502,7 +502,7 @@ You can select your preferred provider and model in the repository settings UI.
|
|
|
502
502
|
|
|
503
503
|
#### Built-in vs. Configurable Providers
|
|
504
504
|
|
|
505
|
-
Most providers (Claude,
|
|
505
|
+
Most providers (Claude, Antigravity, Codex, Copilot) come with built-in model definitions. **OpenCode and Pi are different** - they have no built-in models and require you to configure which models to use.
|
|
506
506
|
|
|
507
507
|
#### Configuring Custom Models
|
|
508
508
|
|
|
@@ -648,7 +648,7 @@ The key difference from Review Council is consolidation order: Advanced consolid
|
|
|
648
648
|
|
|
649
649
|
- **Large changesets with Opus 4.6**: Use review council instructions to tell the model to use a Team — it will spawn sub-agents to divide the work
|
|
650
650
|
- **Multi-model parallel review**: Give each model a specific focus area (security, performance, correctness) via per-model instructions and get combined results
|
|
651
|
-
- **Cross-model perspectives**: Run the same analysis across different providers (e.g., Claude,
|
|
651
|
+
- **Cross-model perspectives**: Run the same analysis across different providers (e.g., Claude, Antigravity, GPT) — where models agree signals high-confidence findings, but unique outliers from a single model can be equally valuable, catching issue types that only that model excels at spotting
|
|
652
652
|
|
|
653
653
|
### Chat
|
|
654
654
|
|
|
@@ -683,7 +683,7 @@ Configure your preferred models in `providers.pi.models` — see [AI Provider Co
|
|
|
683
683
|
}
|
|
684
684
|
```
|
|
685
685
|
|
|
686
|
-
Available chat provider IDs: `pi`, `claude`, `codex`, `copilot-acp`, `
|
|
686
|
+
Available chat provider IDs: `pi`, `claude`, `codex`, `copilot-acp`, `opencode-acp`, `cursor-acp`. Each supports `command`, `args` (replaces defaults), `extra_args` (appends), and `env` overrides. Codex chat also supports `sandbox`: use `workspace-write` by default, or `read-only` for discussion-only sessions. (Antigravity is an analysis-only provider — it has no ACP mode, so there is no Antigravity chat provider.)
|
|
687
687
|
|
|
688
688
|
**Keyboard shortcut:** Press `p` then `c` to toggle the chat panel.
|
|
689
689
|
|
|
@@ -959,8 +959,8 @@ These commands update your MCP configuration in `~/.claude/settings.json` (user-
|
|
|
959
959
|
|
|
960
960
|
### Prerequisites
|
|
961
961
|
|
|
962
|
-
- Node.js
|
|
963
|
-
- [pnpm](https://pnpm.io/)
|
|
962
|
+
- Node.js 22.0.0 or higher
|
|
963
|
+
- [pnpm](https://pnpm.io/) 11.x
|
|
964
964
|
- Git
|
|
965
965
|
|
|
966
966
|
### Running Locally
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@in-the-loop-labs/pair-review",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
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": {
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
22
|
+
"node": ">=22.0.0"
|
|
23
23
|
},
|
|
24
|
+
"packageManager": "pnpm@11.9.0",
|
|
24
25
|
"scripts": {
|
|
25
26
|
"start": "node src/server.js",
|
|
26
27
|
"dev": "node bin/pair-review.js",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
"cursor",
|
|
48
49
|
"copilot",
|
|
49
50
|
"codex",
|
|
50
|
-
"
|
|
51
|
+
"antigravity"
|
|
51
52
|
],
|
|
52
53
|
"author": "Tim Perkins",
|
|
53
54
|
"license": "Apache-2.0",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"@agentclientprotocol/sdk": "^0.14.1",
|
|
64
65
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
65
66
|
"@octokit/rest": "^19.0.11",
|
|
66
|
-
"better-sqlite3": "^11.
|
|
67
|
+
"better-sqlite3": "^12.11.1",
|
|
67
68
|
"express": "^4.18.2",
|
|
68
69
|
"glob": "^13.0.6",
|
|
69
70
|
"markdown-it": "^13.0.2",
|
|
@@ -83,10 +84,5 @@
|
|
|
83
84
|
"jsdom": "^29.0.1",
|
|
84
85
|
"supertest": "^7.1.4",
|
|
85
86
|
"vitest": "^4.0.16"
|
|
86
|
-
},
|
|
87
|
-
"pnpm": {
|
|
88
|
-
"onlyBuiltDependencies": [
|
|
89
|
-
"better-sqlite3"
|
|
90
|
-
]
|
|
91
87
|
}
|
|
92
88
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pair-review",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
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": "
|
|
3
|
+
"version": "4.0.0",
|
|
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/public/css/pr.css
CHANGED
|
@@ -6939,66 +6939,6 @@ body:not([data-theme="dark"]) .theme-icon-light {
|
|
|
6939
6939
|
border-color: var(--color-border-secondary);
|
|
6940
6940
|
}
|
|
6941
6941
|
|
|
6942
|
-
/* ============================================
|
|
6943
|
-
Analysis Progress Dots
|
|
6944
|
-
============================================ */
|
|
6945
|
-
|
|
6946
|
-
.analysis-progress-dots {
|
|
6947
|
-
display: flex;
|
|
6948
|
-
align-items: center;
|
|
6949
|
-
gap: 6px;
|
|
6950
|
-
margin-right: 8px;
|
|
6951
|
-
}
|
|
6952
|
-
|
|
6953
|
-
.progress-dot {
|
|
6954
|
-
width: 8px;
|
|
6955
|
-
height: 8px;
|
|
6956
|
-
border-radius: 50%;
|
|
6957
|
-
border: 1.5px solid var(--color-accent-ai);
|
|
6958
|
-
background: transparent;
|
|
6959
|
-
transition: all 0.3s ease;
|
|
6960
|
-
}
|
|
6961
|
-
|
|
6962
|
-
.progress-dot.active {
|
|
6963
|
-
animation: pulse-dot 1.2s ease-in-out infinite;
|
|
6964
|
-
}
|
|
6965
|
-
|
|
6966
|
-
.progress-dot.completed {
|
|
6967
|
-
background: var(--color-accent-ai);
|
|
6968
|
-
border-color: var(--color-accent-ai);
|
|
6969
|
-
}
|
|
6970
|
-
|
|
6971
|
-
.progress-dot.error {
|
|
6972
|
-
background: #ef4444;
|
|
6973
|
-
border-color: #ef4444;
|
|
6974
|
-
}
|
|
6975
|
-
|
|
6976
|
-
@keyframes pulse-dot {
|
|
6977
|
-
0%, 100% {
|
|
6978
|
-
transform: scale(1);
|
|
6979
|
-
opacity: 1;
|
|
6980
|
-
}
|
|
6981
|
-
50% {
|
|
6982
|
-
transform: scale(1.3);
|
|
6983
|
-
opacity: 0.9;
|
|
6984
|
-
}
|
|
6985
|
-
}
|
|
6986
|
-
|
|
6987
|
-
/* Dark theme for progress dots */
|
|
6988
|
-
[data-theme="dark"] .progress-dot {
|
|
6989
|
-
border-color: var(--color-accent-ai-dark);
|
|
6990
|
-
}
|
|
6991
|
-
|
|
6992
|
-
[data-theme="dark"] .progress-dot.completed {
|
|
6993
|
-
background: var(--color-accent-ai-dark);
|
|
6994
|
-
border-color: var(--color-accent-ai-dark);
|
|
6995
|
-
}
|
|
6996
|
-
|
|
6997
|
-
[data-theme="dark"] .progress-dot.error {
|
|
6998
|
-
background: #f87171;
|
|
6999
|
-
border-color: #f87171;
|
|
7000
|
-
}
|
|
7001
|
-
|
|
7002
6942
|
/* Magical Analyze Button - Amber AI CTA */
|
|
7003
6943
|
#analyze-btn {
|
|
7004
6944
|
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
|
@@ -12959,6 +12899,18 @@ body.resizing * {
|
|
|
12959
12899
|
animation: pulse-dot 1.2s ease-in-out infinite;
|
|
12960
12900
|
}
|
|
12961
12901
|
|
|
12902
|
+
/* Pulse used by the running/setting-up stack-progress status icons above. */
|
|
12903
|
+
@keyframes pulse-dot {
|
|
12904
|
+
0%, 100% {
|
|
12905
|
+
transform: scale(1);
|
|
12906
|
+
opacity: 1;
|
|
12907
|
+
}
|
|
12908
|
+
50% {
|
|
12909
|
+
transform: scale(1.3);
|
|
12910
|
+
opacity: 0.9;
|
|
12911
|
+
}
|
|
12912
|
+
}
|
|
12913
|
+
|
|
12962
12914
|
.status-pending .stack-progress-status-icon {
|
|
12963
12915
|
color: var(--color-text-muted, #d1d9e0);
|
|
12964
12916
|
}
|
|
@@ -170,7 +170,7 @@ class AdvancedConfigTab {
|
|
|
170
170
|
/**
|
|
171
171
|
* Set default orchestration provider/model for new councils.
|
|
172
172
|
* Falls back to 'claude'/'sonnet' if not provided.
|
|
173
|
-
* @param {string} provider - Default provider ID (e.g., 'claude', '
|
|
173
|
+
* @param {string} provider - Default provider ID (e.g., 'claude', 'antigravity')
|
|
174
174
|
* @param {string} model - Default model ID (e.g., 'sonnet', 'opus')
|
|
175
175
|
*/
|
|
176
176
|
setDefaultOrchestration(provider, model) {
|
|
@@ -627,7 +627,7 @@ class AnalysisConfigModal {
|
|
|
627
627
|
selectModel(modelId) {
|
|
628
628
|
// Guard: never store a model that doesn't belong to the current provider.
|
|
629
629
|
// Callers can pass a model resolved from a different scope (e.g. 'opus'
|
|
630
|
-
// while the provider is '
|
|
630
|
+
// while the provider is 'antigravity'); fall back to the provider's default so
|
|
631
631
|
// the UI always shows a selected card and submissions carry a valid pair.
|
|
632
632
|
if (modelId && this.models.length && !this.models.some(m => m.id === modelId)) {
|
|
633
633
|
const fallback = this.models.find(m => m.default) || this.models[0];
|
|
@@ -952,7 +952,7 @@ class AnalysisConfigModal {
|
|
|
952
952
|
}
|
|
953
953
|
// Only honour currentModel when it belongs to the selected provider.
|
|
954
954
|
// selectProvider() has already chosen a valid model for the provider (via
|
|
955
|
-
// tier matching); applying a foreign model here (e.g. 'opus' under '
|
|
955
|
+
// tier matching); applying a foreign model here (e.g. 'opus' under 'antigravity')
|
|
956
956
|
// would leave no model card selected and submit an invalid pair.
|
|
957
957
|
if (options.currentModel && this.models.some(m => m.id === options.currentModel)) {
|
|
958
958
|
this.selectModel(options.currentModel);
|
|
@@ -304,16 +304,6 @@ class CouncilProgressModal {
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
// Update toolbar progress dots
|
|
308
|
-
const manager = window.prManager || window.localManager;
|
|
309
|
-
if (manager?.updateProgressDot) {
|
|
310
|
-
for (let level = 1; level <= 4; level++) {
|
|
311
|
-
if (status.levels[level]) {
|
|
312
|
-
manager.updateProgressDot(level, status.levels[level].status);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
307
|
// Terminal states
|
|
318
308
|
if (status.status === 'completed') {
|
|
319
309
|
this._handleCompletion(status);
|
package/public/js/local.js
CHANGED
|
@@ -346,7 +346,7 @@ class LocalManager {
|
|
|
346
346
|
const lastCouncilId = reviewSettings.last_council_id;
|
|
347
347
|
|
|
348
348
|
// Resolve provider and model as a MATCHED pair so the council/advanced tabs
|
|
349
|
-
// are never seeded with a cross-provider model (e.g.
|
|
349
|
+
// are never seeded with a cross-provider model (e.g. antigravity + opus), which
|
|
350
350
|
// would blank the model <select> and be rejected by the backend.
|
|
351
351
|
const providersInfo = await manager._getProvidersInfo();
|
|
352
352
|
const { provider: currentProvider, model: currentModel } = window.resolveProviderModelPair([
|
|
@@ -1893,55 +1893,6 @@ class LocalManager {
|
|
|
1893
1893
|
};
|
|
1894
1894
|
document.addEventListener('keydown', keyHandler);
|
|
1895
1895
|
}
|
|
1896
|
-
|
|
1897
|
-
/**
|
|
1898
|
-
* Get the progress dots container element
|
|
1899
|
-
* @returns {HTMLElement|null}
|
|
1900
|
-
*/
|
|
1901
|
-
getProgressDotsContainer() {
|
|
1902
|
-
return document.getElementById('analysis-progress-dots');
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
/**
|
|
1906
|
-
* Update a specific progress dot during analysis
|
|
1907
|
-
* Maps level numbers to phase names:
|
|
1908
|
-
* - Level 4 -> orchestration (finalization)
|
|
1909
|
-
* - Level 1 -> level1
|
|
1910
|
-
* - Level 2 -> level2
|
|
1911
|
-
* - Level 3 -> level3
|
|
1912
|
-
* @param {number} level - The level number (1, 2, 3, or 4)
|
|
1913
|
-
* @param {string} status - The status ('running', 'completed', 'failed')
|
|
1914
|
-
*/
|
|
1915
|
-
updateProgressDot(level, status) {
|
|
1916
|
-
const container = this.getProgressDotsContainer();
|
|
1917
|
-
if (!container) return;
|
|
1918
|
-
|
|
1919
|
-
// Map levels to dot phases
|
|
1920
|
-
const phaseMap = {
|
|
1921
|
-
4: 'orchestration', // Orchestration/finalization is level 4 in progress modal
|
|
1922
|
-
1: 'level1',
|
|
1923
|
-
2: 'level2',
|
|
1924
|
-
3: 'level3'
|
|
1925
|
-
};
|
|
1926
|
-
|
|
1927
|
-
const phase = phaseMap[level];
|
|
1928
|
-
if (!phase) return;
|
|
1929
|
-
|
|
1930
|
-
const dot = container.querySelector(`[data-phase="${phase}"]`);
|
|
1931
|
-
if (!dot) return;
|
|
1932
|
-
|
|
1933
|
-
// Remove existing states
|
|
1934
|
-
dot.classList.remove('active', 'completed', 'error');
|
|
1935
|
-
|
|
1936
|
-
// Apply new state
|
|
1937
|
-
if (status === 'running') {
|
|
1938
|
-
dot.classList.add('active');
|
|
1939
|
-
} else if (status === 'completed') {
|
|
1940
|
-
dot.classList.add('completed');
|
|
1941
|
-
} else if (status === 'failed') {
|
|
1942
|
-
dot.classList.add('error');
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
1896
|
}
|
|
1946
1897
|
|
|
1947
1898
|
// Initialize LocalManager when in local mode
|
package/public/js/pr.js
CHANGED
|
@@ -704,7 +704,7 @@ class PRManager {
|
|
|
704
704
|
// Resolve provider and model as a MATCHED pair. Resolving each half
|
|
705
705
|
// independently (repo || app || hardcoded) can mix a provider from one
|
|
706
706
|
// scope with a model from another, yielding an invalid pair (e.g.
|
|
707
|
-
//
|
|
707
|
+
// antigravity/opus) that startAnalysis would forward to the backend as-is.
|
|
708
708
|
const providers = providersInfo || await this._getProvidersInfo();
|
|
709
709
|
const { provider, model } = window.resolveProviderModelPair([
|
|
710
710
|
{ provider: repoSettings?.default_provider, model: repoSettings?.default_model },
|
|
@@ -3053,7 +3053,7 @@ class PRManager {
|
|
|
3053
3053
|
]);
|
|
3054
3054
|
|
|
3055
3055
|
// Resolve provider and model as a MATCHED pair so the council/advanced tabs
|
|
3056
|
-
// are never seeded with a cross-provider model (e.g.
|
|
3056
|
+
// are never seeded with a cross-provider model (e.g. antigravity + opus), which
|
|
3057
3057
|
// would blank the model <select> and be rejected by the backend.
|
|
3058
3058
|
const providersInfo = await this._getProvidersInfo();
|
|
3059
3059
|
const { provider: currentProvider, model: currentModel } = window.resolveProviderModelPair([
|
|
@@ -6588,9 +6588,6 @@ class PRManager {
|
|
|
6588
6588
|
} else {
|
|
6589
6589
|
btn.innerHTML = '<span class="analyzing-icon">✨</span> Analyzing...';
|
|
6590
6590
|
}
|
|
6591
|
-
|
|
6592
|
-
// Show progress dots
|
|
6593
|
-
this.showProgressDots();
|
|
6594
6591
|
}
|
|
6595
6592
|
|
|
6596
6593
|
/**
|
|
@@ -6615,9 +6612,6 @@ class PRManager {
|
|
|
6615
6612
|
}
|
|
6616
6613
|
btn.disabled = true;
|
|
6617
6614
|
|
|
6618
|
-
// Complete all progress dots (they'll be hidden when button resets)
|
|
6619
|
-
this.completeAllProgressDots();
|
|
6620
|
-
|
|
6621
6615
|
// Revert to normal after 2 seconds
|
|
6622
6616
|
setTimeout(() => this.resetButton(), 2000);
|
|
6623
6617
|
}
|
|
@@ -6645,114 +6639,6 @@ class PRManager {
|
|
|
6645
6639
|
} else {
|
|
6646
6640
|
btn.innerHTML = 'Analyze with AI';
|
|
6647
6641
|
}
|
|
6648
|
-
|
|
6649
|
-
// Hide progress dots when resetting
|
|
6650
|
-
this.hideProgressDots();
|
|
6651
|
-
}
|
|
6652
|
-
|
|
6653
|
-
// ============================================
|
|
6654
|
-
// Progress Dots Controller
|
|
6655
|
-
// ============================================
|
|
6656
|
-
|
|
6657
|
-
/**
|
|
6658
|
-
* Get the progress dots container
|
|
6659
|
-
* @returns {HTMLElement|null}
|
|
6660
|
-
*/
|
|
6661
|
-
getProgressDotsContainer() {
|
|
6662
|
-
return document.getElementById('analysis-progress-dots');
|
|
6663
|
-
}
|
|
6664
|
-
|
|
6665
|
-
/**
|
|
6666
|
-
* Show progress dots (called when analysis starts)
|
|
6667
|
-
*/
|
|
6668
|
-
showProgressDots() {
|
|
6669
|
-
const container = this.getProgressDotsContainer();
|
|
6670
|
-
if (!container) return;
|
|
6671
|
-
|
|
6672
|
-
container.style.display = 'flex';
|
|
6673
|
-
|
|
6674
|
-
// Reset all dots to initial state
|
|
6675
|
-
const dots = container.querySelectorAll('.progress-dot');
|
|
6676
|
-
dots.forEach(dot => {
|
|
6677
|
-
dot.classList.remove('active', 'completed', 'error');
|
|
6678
|
-
});
|
|
6679
|
-
|
|
6680
|
-
// Set first dot (orchestration) as active
|
|
6681
|
-
const firstDot = container.querySelector('[data-phase="orchestration"]');
|
|
6682
|
-
if (firstDot) {
|
|
6683
|
-
firstDot.classList.add('active');
|
|
6684
|
-
}
|
|
6685
|
-
}
|
|
6686
|
-
|
|
6687
|
-
/**
|
|
6688
|
-
* Hide progress dots (called when analysis completes or is cancelled)
|
|
6689
|
-
*/
|
|
6690
|
-
hideProgressDots() {
|
|
6691
|
-
const container = this.getProgressDotsContainer();
|
|
6692
|
-
if (!container) return;
|
|
6693
|
-
|
|
6694
|
-
container.style.display = 'none';
|
|
6695
|
-
|
|
6696
|
-
// Reset all dots
|
|
6697
|
-
const dots = container.querySelectorAll('.progress-dot');
|
|
6698
|
-
dots.forEach(dot => {
|
|
6699
|
-
dot.classList.remove('active', 'completed', 'error');
|
|
6700
|
-
});
|
|
6701
|
-
}
|
|
6702
|
-
|
|
6703
|
-
/**
|
|
6704
|
-
* Update progress dots based on level status
|
|
6705
|
-
* Maps level numbers to phases:
|
|
6706
|
-
* - Level 4 (orchestration/finalization) -> orchestration
|
|
6707
|
-
* - Level 1 -> level1
|
|
6708
|
-
* - Level 2 -> level2
|
|
6709
|
-
* - Level 3 -> level3
|
|
6710
|
-
* @param {number} level - The level number (1, 2, 3, or 4)
|
|
6711
|
-
* @param {string} status - The status ('running', 'completed', 'failed')
|
|
6712
|
-
*/
|
|
6713
|
-
updateProgressDot(level, status) {
|
|
6714
|
-
const container = this.getProgressDotsContainer();
|
|
6715
|
-
if (!container) return;
|
|
6716
|
-
|
|
6717
|
-
// Map levels to dot phases
|
|
6718
|
-
const phaseMap = {
|
|
6719
|
-
4: 'orchestration', // Orchestration/finalization is level 4 in progress modal
|
|
6720
|
-
1: 'level1',
|
|
6721
|
-
2: 'level2',
|
|
6722
|
-
3: 'level3'
|
|
6723
|
-
};
|
|
6724
|
-
|
|
6725
|
-
const phase = phaseMap[level];
|
|
6726
|
-
if (!phase) return;
|
|
6727
|
-
|
|
6728
|
-
const dot = container.querySelector(`[data-phase="${phase}"]`);
|
|
6729
|
-
if (!dot) return;
|
|
6730
|
-
|
|
6731
|
-
// Remove existing states
|
|
6732
|
-
dot.classList.remove('active', 'completed', 'error');
|
|
6733
|
-
|
|
6734
|
-
// Apply new state
|
|
6735
|
-
if (status === 'running') {
|
|
6736
|
-
dot.classList.add('active');
|
|
6737
|
-
} else if (status === 'completed') {
|
|
6738
|
-
dot.classList.add('completed');
|
|
6739
|
-
} else if (status === 'failed') {
|
|
6740
|
-
dot.classList.add('error');
|
|
6741
|
-
}
|
|
6742
|
-
}
|
|
6743
|
-
|
|
6744
|
-
/**
|
|
6745
|
-
* Complete all progress dots (called briefly before hiding)
|
|
6746
|
-
*/
|
|
6747
|
-
completeAllProgressDots() {
|
|
6748
|
-
const container = this.getProgressDotsContainer();
|
|
6749
|
-
if (!container) return;
|
|
6750
|
-
|
|
6751
|
-
const dots = container.querySelectorAll('.progress-dot');
|
|
6752
|
-
dots.forEach(dot => {
|
|
6753
|
-
dot.classList.remove('active', 'error');
|
|
6754
|
-
dot.classList.add('completed');
|
|
6755
|
-
});
|
|
6756
6642
|
}
|
|
6757
6643
|
|
|
6758
6644
|
/**
|
|
@@ -6960,7 +6846,7 @@ class PRManager {
|
|
|
6960
6846
|
const lastCouncilId = reviewSettings.last_council_id;
|
|
6961
6847
|
|
|
6962
6848
|
// Resolve provider and model as a MATCHED pair so the council/advanced tabs
|
|
6963
|
-
// are never seeded with a cross-provider model (e.g.
|
|
6849
|
+
// are never seeded with a cross-provider model (e.g. antigravity + opus), which
|
|
6964
6850
|
// would blank the model <select> and be rejected by the backend.
|
|
6965
6851
|
const providersInfo = await this._getProvidersInfo();
|
|
6966
6852
|
const { provider: currentProvider, model: currentModel } = window.resolveProviderModelPair([
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* settings, then app config) and historically each half was resolved
|
|
7
7
|
* independently with `repo || app || hardcoded`. When a scope overrides only
|
|
8
8
|
* ONE half, the halves can come from different scopes and produce an invalid
|
|
9
|
-
* pair such as `
|
|
9
|
+
* pair such as `antigravity` + `opus` (an Anthropic model). The modal then shows no
|
|
10
10
|
* selected model, and the non-modal auto-analyze path posts the broken pair
|
|
11
11
|
* straight to the backend.
|
|
12
12
|
*
|
package/public/local.html
CHANGED
|
@@ -435,13 +435,6 @@
|
|
|
435
435
|
</div>
|
|
436
436
|
<div class="diff-stats" id="diff-stats"></div>
|
|
437
437
|
<div class="toolbar-actions">
|
|
438
|
-
<!-- Progress Dots (visible during analysis) -->
|
|
439
|
-
<div class="analysis-progress-dots" id="analysis-progress-dots" style="display: none;">
|
|
440
|
-
<span class="progress-dot" data-phase="level1" title="Level 1: Diff"></span>
|
|
441
|
-
<span class="progress-dot" data-phase="level2" title="Level 2: File"></span>
|
|
442
|
-
<span class="progress-dot" data-phase="level3" title="Level 3: Codebase"></span>
|
|
443
|
-
<span class="progress-dot" data-phase="orchestration" title="Consolidation"></span>
|
|
444
|
-
</div>
|
|
445
438
|
<button class="btn btn-sm btn-secondary" id="analyze-btn" title="Analyze with AI">
|
|
446
439
|
<svg class="analyze-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
|
447
440
|
<path d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z"/>
|
package/public/pr.html
CHANGED
|
@@ -231,13 +231,6 @@
|
|
|
231
231
|
</div>
|
|
232
232
|
<div class="diff-stats" id="diff-stats"></div>
|
|
233
233
|
<div class="toolbar-actions">
|
|
234
|
-
<!-- Progress Dots (visible during analysis) -->
|
|
235
|
-
<div class="analysis-progress-dots" id="analysis-progress-dots" style="display: none;">
|
|
236
|
-
<span class="progress-dot" data-phase="level1" title="Level 1: Diff"></span>
|
|
237
|
-
<span class="progress-dot" data-phase="level2" title="Level 2: File"></span>
|
|
238
|
-
<span class="progress-dot" data-phase="level3" title="Level 3: Codebase"></span>
|
|
239
|
-
<span class="progress-dot" data-phase="orchestration" title="Consolidation"></span>
|
|
240
|
-
</div>
|
|
241
234
|
<button class="btn btn-sm btn-secondary" id="analyze-btn" title="Analyze with AI">
|
|
242
235
|
<svg class="analyze-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
|
|
243
236
|
<path d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z"/>
|
package/src/ai/analyzer.js
CHANGED
|
@@ -342,8 +342,8 @@ A suggestion is a duplicate if it targets the same file and overlapping lines an
|
|
|
342
342
|
class Analyzer {
|
|
343
343
|
/**
|
|
344
344
|
* @param {Object} database - Database instance
|
|
345
|
-
* @param {string} model - Model to use (e.g., 'opus', 'gemini-
|
|
346
|
-
* @param {string} provider - Provider ID (e.g., 'claude', '
|
|
345
|
+
* @param {string} model - Model to use (e.g., 'opus', 'gemini-3.1-pro-low')
|
|
346
|
+
* @param {string} provider - Provider ID (e.g., 'claude', 'antigravity'). Defaults to 'claude'.
|
|
347
347
|
* @param {Object} providerOverrides - Per-call config overrides passed to createProvider (optional)
|
|
348
348
|
* @param {Object|null} providerOverridesMap - Per-provider overrides map for council mode (provider ID → overrides)
|
|
349
349
|
*/
|
|
@@ -860,8 +860,8 @@ class Analyzer {
|
|
|
860
860
|
*
|
|
861
861
|
* Uses the bare command name since BIN_DIR is added to PATH in all
|
|
862
862
|
* providers. Using the absolute path causes issues with AI providers
|
|
863
|
-
* that pattern-match allowed
|
|
864
|
-
* 'git-diff-lines', not the full path).
|
|
863
|
+
* that pattern-match allowed shell commands by their bare command name
|
|
864
|
+
* (matching 'git-diff-lines', not the full path).
|
|
865
865
|
*
|
|
866
866
|
* @param {string|null} worktreePath - Path to the git worktree
|
|
867
867
|
* @returns {string} e.g. `git-diff-lines` or `git-diff-lines --cwd "/path"`
|