@in-the-loop-labs/pair-review 3.9.1 → 4.1.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 +92 -17
- package/package.json +6 -9
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +3 -4
- package/plugin-pair-loop/.claude-plugin/plugin.json +19 -0
- package/plugin-pair-loop/skills/loop/SKILL.md +233 -0
- package/public/js/components/AdvancedConfigTab.js +1 -1
- package/public/js/components/AnalysisConfigModal.js +2 -2
- package/public/js/index.js +87 -10
- package/public/js/local.js +20 -10
- package/public/js/pr.js +67 -39
- package/public/js/repo-links.js +11 -3
- package/public/js/utils/analyze-params.js +69 -0
- package/public/js/utils/provider-model.js +67 -2
- package/public/js/vendor/pierre-diffs-worker.js +16158 -0
- package/public/js/vendor/pierre-diffs.js +1880 -0
- package/public/local.html +1 -0
- package/public/pr.html +1 -0
- package/public/setup.html +35 -16
- package/src/ai/analyzer.js +4 -4
- package/src/ai/antigravity-provider.js +594 -0
- 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 +153 -31
- package/src/database.js +128 -4
- package/src/external/github-adapter.js +18 -3
- package/src/github/client.js +37 -0
- package/src/github/parser.js +41 -7
- package/src/interactive-analysis-config.js +2 -2
- package/src/links/repo-links.js +66 -28
- package/src/local-review.js +134 -5
- package/src/local-scope.js +38 -0
- package/src/main.js +203 -37
- package/src/routes/config.js +49 -14
- package/src/routes/external-comments.js +13 -1
- package/src/routes/github-collections.js +175 -13
- package/src/routes/local.js +11 -20
- package/src/routes/pr.js +63 -36
- package/src/routes/setup.js +63 -8
- package/src/routes/shared.js +85 -0
- package/src/routes/stack-analysis.js +39 -3
- package/src/server.js +74 -3
- package/src/setup/local-setup.js +23 -7
- package/src/setup/pr-setup.js +237 -39
- package/src/setup/stack-setup.js +7 -2
- package/src/single-port.js +73 -18
- package/src/utils/host-resolution.js +157 -0
- package/plugin-code-critic/skills/loop/SKILL.md +0 -373
- package/src/ai/gemini-provider.js +0 -752
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
- [AI-Guided Review](#3-ai-guided-review-when-youre-accountable)
|
|
21
21
|
- [Quick Start](#quick-start)
|
|
22
22
|
- [Command Line Interface](#command-line-interface)
|
|
23
|
+
- [Local review scope](#local-review-scope)
|
|
23
24
|
- [Headless analysis mode](#headless-analysis-mode)
|
|
24
25
|
- [Configuration](#configuration)
|
|
25
26
|
- [Environment Variables](#environment-variables)
|
|
@@ -65,7 +66,7 @@ pair-review is a local web application for keeping humans in the loop with AI co
|
|
|
65
66
|
- **Local-First**: All data and processing happens on your machine - no cloud dependencies
|
|
66
67
|
- **GitHub-Familiar UI**: Interface feels instantly familiar to GitHub users
|
|
67
68
|
- **Human-in-the-Loop**: AI suggests, you decide
|
|
68
|
-
- **Multiple AI Providers**: Support for Claude,
|
|
69
|
+
- **Multiple AI Providers**: Support for Claude, Antigravity, Codex, Copilot, OpenCode, Cursor, and Pi. Use your existing subscription!
|
|
69
70
|
- **Progressive**: Start simple with manual review, add AI analysis when you need it
|
|
70
71
|
|
|
71
72
|
## Workflows
|
|
@@ -89,7 +90,8 @@ Compared to giving feedback in chat, this feels like moving from a machete to a
|
|
|
89
90
|
|
|
90
91
|
**Tips:**
|
|
91
92
|
- Stage previous changes in git, then only review new modifications in the next round
|
|
92
|
-
-
|
|
93
|
+
- By default local mode reviews unstaged changes and untracked files; adjust the range with `--scope` (or the web UI scope selector) — e.g. `branch..untracked` to cover the whole branch (see [Local review scope](#local-review-scope))
|
|
94
|
+
|
|
93
95
|
### 2. Meta-Review: Judging AI Suggestions
|
|
94
96
|
|
|
95
97
|
**When to use:** You're not going to read every line of code. Let AI be your reader.
|
|
@@ -203,7 +205,10 @@ pair-review --local [path]
|
|
|
203
205
|
| `-d`, `--debug` | Enable verbose debug logging for troubleshooting |
|
|
204
206
|
| `-h`, `--help` | Show help message with full CLI documentation |
|
|
205
207
|
| `-l`, `--local [path]` | Review local uncommitted changes. Optional path defaults to current directory |
|
|
208
|
+
| `--scope <start>..<end>` | **Local mode only.** Set the diff range a local review covers. Stops (in order): `branch`, `staged`, `unstaged`, `untracked`. Six valid ranges (contiguous, must include `unstaged`); default `unstaged..untracked`. `branch..*` diffs from the merge-base with the base branch. See [Local review scope](#local-review-scope). |
|
|
209
|
+
| `--base <branch>` | **Local mode only.** With a `branch..*` scope, override base-branch auto-detection. Errors if used without a branch-start scope. |
|
|
206
210
|
| `--model <name>` | Override the AI model for any provider. Model availability depends on provider configuration. |
|
|
211
|
+
| `--provider <name>` | Override the AI provider. Applies to headless modes (`--ai-draft` / `--ai-review`) **and** to browser-driven auto-analysis (`--ai`), where it overrides the repo/app default the browser would otherwise use — including across single-port delegation to an already-running server. Defaults to the repo/app default provider (`claude`). Pair with `--model` when the model belongs to a non-default provider (e.g. `--provider codex --model gpt-5.5`). |
|
|
207
212
|
| `--register` | Register `pair-review://` URL scheme handler (macOS only) |
|
|
208
213
|
| `--unregister` | Unregister `pair-review://` URL scheme handler (macOS only) |
|
|
209
214
|
| `--command <cmd>` | Custom CLI command for `--register` (default: `npx @in-the-loop-labs/pair-review`) |
|
|
@@ -216,8 +221,10 @@ pair-review 123 # Review PR #123 in current repo
|
|
|
216
221
|
pair-review https://github.com/owner/repo/pull/456
|
|
217
222
|
pair-review --local # Review uncommitted local changes
|
|
218
223
|
pair-review 123 --ai # Auto-run AI analysis
|
|
224
|
+
pair-review 123 --ai --provider codex # Auto-run analysis in the browser with a specific provider
|
|
219
225
|
pair-review --list-councils # List saved councils and their handles
|
|
220
226
|
pair-review 123 --ai-draft --council security-review # Headless draft with a council
|
|
227
|
+
pair-review 123 --ai-draft --provider codex --model gpt-5.5 # Headless draft with a specific provider + model
|
|
221
228
|
pair-review --local --ai --council security-review # Local review with a council
|
|
222
229
|
pair-review --local --headless # Analyze local changes, print a summary, exit
|
|
223
230
|
pair-review --local --headless --json # Analyze local changes, emit JSON, exit
|
|
@@ -240,6 +247,44 @@ pair-review --register --command "node bin/pair-review.js" # Custom command
|
|
|
240
247
|
> web UI's default **Analyze** action, so `--council`/`--model` are optional when
|
|
241
248
|
> a repo default is configured.
|
|
242
249
|
|
|
250
|
+
### Local review scope
|
|
251
|
+
|
|
252
|
+
`--scope <start>..<end>` sets which changes a **local** review covers (it has
|
|
253
|
+
no effect on PR reviews). The scope walks four ordered stops — `branch` →
|
|
254
|
+
`staged` → `unstaged` → `untracked` — and a range must be contiguous and always
|
|
255
|
+
include `unstaged`, since the AI models read files from the working tree and the
|
|
256
|
+
diff must cover that state. That leaves six valid ranges:
|
|
257
|
+
|
|
258
|
+
| Scope | Covers |
|
|
259
|
+
|-------|--------|
|
|
260
|
+
| `branch..unstaged` | All tracked changes since the base branch (committed + staged + unstaged); no new files |
|
|
261
|
+
| `branch..untracked` | Everything since the base branch, including new files |
|
|
262
|
+
| `staged..unstaged` | Staged changes plus working-tree edits vs `HEAD`; no new files |
|
|
263
|
+
| `staged..untracked` | Staged changes, working-tree edits, and new files |
|
|
264
|
+
| `unstaged..unstaged` | Only unstaged working-tree edits (staged changes treated as already reviewed) |
|
|
265
|
+
| `unstaged..untracked` | Unstaged working-tree edits plus new files (**default**) |
|
|
266
|
+
|
|
267
|
+
A scope ending at `untracked` includes new (untracked) files automatically — no
|
|
268
|
+
`git add -N` needed. `branch..*` diffs from the merge-base with the base branch.
|
|
269
|
+
|
|
270
|
+
`--base <branch>` overrides base-branch auto-detection for a `branch..*` scope.
|
|
271
|
+
Detection order when `--base` is omitted: Graphite stack state (only when
|
|
272
|
+
`enable_graphite: true` is set in `~/.pair-review/config.json`) → GitHub PR base →
|
|
273
|
+
`origin`'s default branch → `main`/`master`. `--base` errors if used without a
|
|
274
|
+
branch-start scope.
|
|
275
|
+
|
|
276
|
+
Both flags are **local-mode only**: they error when given a PR argument and do
|
|
277
|
+
**not** imply `--local`. An explicit `--scope` is persisted on the review
|
|
278
|
+
session, so opening the web UI for that review later shows the same scope.
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Review everything on this branch since it diverged from the trunk, plus new files
|
|
282
|
+
pair-review --local --scope branch..untracked
|
|
283
|
+
|
|
284
|
+
# Same, but against an explicit base branch (stacked branches / non-default trunk)
|
|
285
|
+
pair-review --local --scope branch..untracked --base develop
|
|
286
|
+
```
|
|
287
|
+
|
|
243
288
|
### Headless analysis mode
|
|
244
289
|
|
|
245
290
|
`--headless` runs an AI analysis (single provider or council), stores the results
|
|
@@ -427,7 +472,7 @@ Nested objects (like `chat`, `providers`, `chat_providers`, `monorepos`) are dee
|
|
|
427
472
|
|
|
428
473
|
### Alternate Git Hosts
|
|
429
474
|
|
|
430
|
-
pair-review can review pull requests on self-hosted Git platforms that expose a GitHub-compatible REST API, configured per-repository via `repos["owner/repo"].api_host` and related keys. See [docs/alt-host.md](docs/alt-host.md) for the full configuration guide.
|
|
475
|
+
pair-review can review pull requests on self-hosted Git platforms that expose a GitHub-compatible REST API, configured per-repository via `repos["owner/repo"].api_host` and related keys. By default an `api_host` repo is treated as living exclusively on the alt host; set `exclusive: false` on the repo entry to mark it as **dual-host**, where some PRs live on `github.com` and others on the alt host and pair-review resolves the host per PR. See [docs/alt-host.md](docs/alt-host.md) for the full configuration guide.
|
|
431
476
|
|
|
432
477
|
### Environment Variables
|
|
433
478
|
|
|
@@ -437,17 +482,20 @@ pair-review supports several environment variables for customizing behavior:
|
|
|
437
482
|
|----------|-------------|---------|
|
|
438
483
|
| `GITHUB_TOKEN` | GitHub Personal Access Token (takes precedence over config file) | - |
|
|
439
484
|
| `PAIR_REVIEW_CLAUDE_CMD` | Custom command to invoke Claude CLI | `claude` |
|
|
440
|
-
| `
|
|
485
|
+
| `PAIR_REVIEW_ANTIGRAVITY_CMD` | Custom command to invoke Antigravity CLI | `agy` |
|
|
441
486
|
| `PAIR_REVIEW_CODEX_CMD` | Custom command to invoke Codex CLI | `codex` |
|
|
442
487
|
| `PAIR_REVIEW_COPILOT_CMD` | Custom command to invoke Copilot CLI | `copilot` |
|
|
443
488
|
| `PAIR_REVIEW_OPENCODE_CMD` | Custom command to invoke OpenCode CLI | `opencode` |
|
|
444
489
|
| `PAIR_REVIEW_CURSOR_AGENT_CMD` | Custom command to invoke Cursor Agent CLI | `agent` |
|
|
445
490
|
| `PAIR_REVIEW_PI_CMD` | Custom command to invoke Pi CLI | `pi` |
|
|
446
491
|
| `PAIR_REVIEW_MODEL` | Override the AI model to use (same as `--model` flag) | Provider default |
|
|
492
|
+
| `PAIR_REVIEW_PROVIDER` | Override the AI provider to use (same as `--provider` flag) | `claude` |
|
|
447
493
|
|
|
448
494
|
**Note:** `GITHUB_TOKEN` is the standard environment variable used by many GitHub tools (gh CLI, GitHub Actions, etc.). When set, it takes precedence over the `github_token` field in the config file.
|
|
449
495
|
|
|
450
|
-
**Note:** The `--model` CLI flag is shorthand for setting `PAIR_REVIEW_MODEL`. If both are specified, the CLI flag takes precedence.
|
|
496
|
+
**Note:** The `--model` CLI flag is shorthand for setting `PAIR_REVIEW_MODEL`, and `--provider` is shorthand for `PAIR_REVIEW_PROVIDER`. If both the flag and env var are specified, the CLI flag takes precedence. The `--provider` flag selects which provider runs the analysis; `--model` selects the model within that provider, so set both when the model belongs to a non-default provider (e.g. `--provider codex --model gpt-5.5`). The override outranks saved repository settings (`CLI/env > repo settings`); if the repo's default is a Review Council, an active `--provider`/`--model` override forces the single-provider path instead.
|
|
497
|
+
|
|
498
|
+
**Delegation caveat:** With single-port mode (the default `single_port: true`), a new invocation delegates to an already-running server. On that path the `--provider`/`--model` override is carried to the running server only alongside browser auto-analysis (`--ai`). Delegating without `--ai` (just opening the review) does not seed the override into the other process's manual analysis dialog — use `--ai`, or a headless mode, to pin the provider on the delegated path.
|
|
451
499
|
|
|
452
500
|
These variables are useful when:
|
|
453
501
|
- Your CLI tools are installed in a non-standard location
|
|
@@ -466,12 +514,15 @@ PAIR_REVIEW_CLAUDE_CMD="/usr/local/bin/claude" pair-review 123
|
|
|
466
514
|
# Use a wrapper command (supports multi-word commands)
|
|
467
515
|
PAIR_REVIEW_CLAUDE_CMD="devx claude" pair-review 123
|
|
468
516
|
|
|
469
|
-
# Use
|
|
470
|
-
|
|
517
|
+
# Use a custom path for the Antigravity CLI
|
|
518
|
+
PAIR_REVIEW_ANTIGRAVITY_CMD="/usr/local/bin/agy" pair-review --local
|
|
471
519
|
|
|
472
520
|
# Force a specific model for this review
|
|
473
521
|
PAIR_REVIEW_MODEL="opus" pair-review 123
|
|
474
522
|
|
|
523
|
+
# Force a specific provider + model for a headless review
|
|
524
|
+
PAIR_REVIEW_PROVIDER="codex" PAIR_REVIEW_MODEL="gpt-5.5" pair-review 123 --ai-draft
|
|
525
|
+
|
|
475
526
|
# Combine multiple settings
|
|
476
527
|
PAIR_REVIEW_CLAUDE_CMD="/opt/claude/bin/claude" PAIR_REVIEW_MODEL="haiku" pair-review 123
|
|
477
528
|
```
|
|
@@ -491,7 +542,7 @@ Create a Personal Access Token (PAT) with these scopes:
|
|
|
491
542
|
pair-review integrates with AI providers via their CLI tools:
|
|
492
543
|
|
|
493
544
|
- **Claude**: Uses Claude Code CLI
|
|
494
|
-
- **
|
|
545
|
+
- **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
546
|
- **Codex**: Uses Codex CLI
|
|
496
547
|
- **GitHub Copilot**: Uses Copilot CLI
|
|
497
548
|
- **OpenCode**: Uses OpenCode CLI (requires model configuration)
|
|
@@ -502,7 +553,7 @@ You can select your preferred provider and model in the repository settings UI.
|
|
|
502
553
|
|
|
503
554
|
#### Built-in vs. Configurable Providers
|
|
504
555
|
|
|
505
|
-
Most providers (Claude,
|
|
556
|
+
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
557
|
|
|
507
558
|
#### Configuring Custom Models
|
|
508
559
|
|
|
@@ -648,7 +699,7 @@ The key difference from Review Council is consolidation order: Advanced consolid
|
|
|
648
699
|
|
|
649
700
|
- **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
701
|
- **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,
|
|
702
|
+
- **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
703
|
|
|
653
704
|
### Chat
|
|
654
705
|
|
|
@@ -683,7 +734,7 @@ Configure your preferred models in `providers.pi.models` — see [AI Provider Co
|
|
|
683
734
|
}
|
|
684
735
|
```
|
|
685
736
|
|
|
686
|
-
Available chat provider IDs: `pi`, `claude`, `codex`, `copilot-acp`, `
|
|
737
|
+
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
738
|
|
|
688
739
|
**Keyboard shortcut:** Press `p` then `c` to toggle the chat panel.
|
|
689
740
|
|
|
@@ -791,7 +842,10 @@ Templates typically include `{description}` to render the suggestion body.
|
|
|
791
842
|
|
|
792
843
|
### Local Mode
|
|
793
844
|
|
|
794
|
-
Review
|
|
845
|
+
Review your uncommitted changes before creating a PR — by default the unstaged
|
|
846
|
+
working-tree edits plus untracked files, with the range adjustable via `--scope`
|
|
847
|
+
(or the web UI scope selector), e.g. `branch..untracked` to cover the whole
|
|
848
|
+
branch (see [Local review scope](#local-review-scope)):
|
|
795
849
|
|
|
796
850
|
```bash
|
|
797
851
|
pair-review --local
|
|
@@ -806,11 +860,28 @@ Perfect for:
|
|
|
806
860
|
|
|
807
861
|
## Claude Code Plugins
|
|
808
862
|
|
|
809
|
-
pair-review provides
|
|
863
|
+
pair-review provides three [Claude Code plugins](https://code.claude.com/docs/en/plugins) that bring AI-powered code review directly into Claude Code.
|
|
864
|
+
|
|
865
|
+
### pair-loop — Agent-Orchestrated Review Loop
|
|
866
|
+
|
|
867
|
+
An implement→review→fix loop with pair-review as the review oracle. The agent runs multi-model council reviews through the headless CLI, triages the findings (fixing, dismissing with reasons, or asking you), applies fixes, and repeats with narrowing instructions until a final review returns no blockers. Every round persists to pair-review, and when the server is running the agent writes its triage back — so you can open the web UI, inspect every round, and see exactly what was fixed or dismissed and why. No MCP required.
|
|
868
|
+
|
|
869
|
+
**Install via Marketplace:**
|
|
870
|
+
|
|
871
|
+
```
|
|
872
|
+
/plugin marketplace add in-the-loop-labs/pair-review
|
|
873
|
+
/plugin install pair-review@pair-loop
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
**Available Skills:**
|
|
877
|
+
|
|
878
|
+
| Skill | Description |
|
|
879
|
+
|-------|-------------|
|
|
880
|
+
| `/pair-loop:loop` | Implement, review with pair-review councils, triage, fix, and repeat until clean |
|
|
810
881
|
|
|
811
882
|
### code-critic — Standalone Analysis
|
|
812
883
|
|
|
813
|
-
AI-powered code review analysis that works without any server or MCP dependency. Install this plugin for three-level AI analysis
|
|
884
|
+
AI-powered code review analysis that works without any server or MCP dependency. Install this plugin for three-level AI analysis directly in your coding agent.
|
|
814
885
|
|
|
815
886
|
**Install via Marketplace:**
|
|
816
887
|
|
|
@@ -824,7 +895,8 @@ AI-powered code review analysis that works without any server or MCP dependency.
|
|
|
824
895
|
| Skill | Description |
|
|
825
896
|
|-------|-------------|
|
|
826
897
|
| `/code-critic:analyze` | Run three-level AI analysis using Task agents directly (standalone, no server needed) |
|
|
827
|
-
|
|
898
|
+
|
|
899
|
+
Looking for an implement-review-fix loop? That's the [pair-loop plugin](#pair-loop--agent-orchestrated-review-loop) above, which replaced the former `/code-critic:loop` skill.
|
|
828
900
|
|
|
829
901
|
These skills work standalone. If the pair-review MCP server happens to be available (from the pair-review plugin), `code-critic:analyze` will use it for prompts and push results to the web UI — but it's entirely optional.
|
|
830
902
|
|
|
@@ -857,10 +929,12 @@ If you prefer not to use the marketplace, load plugins directly from an npm-inst
|
|
|
857
929
|
|
|
858
930
|
```bash
|
|
859
931
|
# From a local clone
|
|
932
|
+
claude --plugin-dir ./path/to/pair-review/plugin-pair-loop
|
|
860
933
|
claude --plugin-dir ./path/to/pair-review/plugin-code-critic
|
|
861
934
|
claude --plugin-dir ./path/to/pair-review/plugin
|
|
862
935
|
|
|
863
936
|
# From a globally installed npm package
|
|
937
|
+
claude --plugin-dir "$(npm root -g)/@in-the-loop-labs/pair-review/plugin-pair-loop"
|
|
864
938
|
claude --plugin-dir "$(npm root -g)/@in-the-loop-labs/pair-review/plugin-code-critic"
|
|
865
939
|
claude --plugin-dir "$(npm root -g)/@in-the-loop-labs/pair-review/plugin"
|
|
866
940
|
```
|
|
@@ -880,6 +954,7 @@ To pre-configure plugins for all contributors on a repository, add this to your
|
|
|
880
954
|
}
|
|
881
955
|
},
|
|
882
956
|
"enabledPlugins": {
|
|
957
|
+
"pair-review@pair-loop": true,
|
|
883
958
|
"pair-review@code-critic": true,
|
|
884
959
|
"pair-review@pair-review": true
|
|
885
960
|
}
|
|
@@ -959,8 +1034,8 @@ These commands update your MCP configuration in `~/.claude/settings.json` (user-
|
|
|
959
1034
|
|
|
960
1035
|
### Prerequisites
|
|
961
1036
|
|
|
962
|
-
- Node.js
|
|
963
|
-
- [pnpm](https://pnpm.io/)
|
|
1037
|
+
- Node.js 22.0.0 or higher
|
|
1038
|
+
- [pnpm](https://pnpm.io/) 11.x
|
|
964
1039
|
- Git
|
|
965
1040
|
|
|
966
1041
|
### 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.1.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": {
|
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
"public/",
|
|
15
15
|
"plugin/",
|
|
16
16
|
"plugin-code-critic/",
|
|
17
|
+
"plugin-pair-loop/",
|
|
17
18
|
"config.managed.json",
|
|
18
19
|
"README.md",
|
|
19
20
|
"LICENSE"
|
|
20
21
|
],
|
|
21
22
|
"engines": {
|
|
22
|
-
"node": ">=
|
|
23
|
+
"node": ">=22.0.0"
|
|
23
24
|
},
|
|
25
|
+
"packageManager": "pnpm@11.9.0",
|
|
24
26
|
"scripts": {
|
|
25
27
|
"start": "node src/server.js",
|
|
26
28
|
"dev": "node bin/pair-review.js",
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
"cursor",
|
|
48
50
|
"copilot",
|
|
49
51
|
"codex",
|
|
50
|
-
"
|
|
52
|
+
"antigravity"
|
|
51
53
|
],
|
|
52
54
|
"author": "Tim Perkins",
|
|
53
55
|
"license": "Apache-2.0",
|
|
@@ -63,7 +65,7 @@
|
|
|
63
65
|
"@agentclientprotocol/sdk": "^0.14.1",
|
|
64
66
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
65
67
|
"@octokit/rest": "^19.0.11",
|
|
66
|
-
"better-sqlite3": "^11.
|
|
68
|
+
"better-sqlite3": "^12.11.1",
|
|
67
69
|
"express": "^4.18.2",
|
|
68
70
|
"glob": "^13.0.6",
|
|
69
71
|
"markdown-it": "^13.0.2",
|
|
@@ -83,10 +85,5 @@
|
|
|
83
85
|
"jsdom": "^29.0.1",
|
|
84
86
|
"supertest": "^7.1.4",
|
|
85
87
|
"vitest": "^4.0.16"
|
|
86
|
-
},
|
|
87
|
-
"pnpm": {
|
|
88
|
-
"onlyBuiltDependencies": [
|
|
89
|
-
"better-sqlite3"
|
|
90
|
-
]
|
|
91
88
|
}
|
|
92
89
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pair-review",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-critic",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "AI-powered code review analysis — Run three-level AI analysis
|
|
3
|
+
"version": "4.1.0",
|
|
4
|
+
"description": "AI-powered code review analysis — Run three-level AI analysis directly in your coding agent. Works standalone, no server required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "in-the-loop-labs",
|
|
7
7
|
"email": "tjwp@inthelooplabs.com"
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
"code-review",
|
|
14
14
|
"ai",
|
|
15
15
|
"analysis",
|
|
16
|
-
"code-critic"
|
|
17
|
-
"critic-loop"
|
|
16
|
+
"code-critic"
|
|
18
17
|
]
|
|
19
18
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pair-loop",
|
|
3
|
+
"version": "4.1.0",
|
|
4
|
+
"description": "Agent-orchestrated review loop — implement, review with pair-review multi-model councils via the headless CLI, triage, fix, and repeat until a final review returns no blockers. MCP-independent.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "in-the-loop-labs",
|
|
7
|
+
"email": "tjwp@inthelooplabs.com"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/in-the-loop-labs/pair-review",
|
|
10
|
+
"repository": "https://github.com/in-the-loop-labs/pair-review",
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"code-review",
|
|
14
|
+
"ai",
|
|
15
|
+
"review-loop",
|
|
16
|
+
"council",
|
|
17
|
+
"feedback-loop"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: loop
|
|
3
|
+
description: >
|
|
4
|
+
Drive an implement→review→fix loop with pair-review as the review oracle:
|
|
5
|
+
run multi-model council reviews, triage the findings, apply fixes, and
|
|
6
|
+
repeat with narrowing instructions until a final review returns no
|
|
7
|
+
blockers. Use when the user says "pair loop", "review loop",
|
|
8
|
+
"loop until clean", "review this with pair-review and fix what it finds",
|
|
9
|
+
or wants iterative development reviewed by pair-review councils.
|
|
10
|
+
arguments:
|
|
11
|
+
council:
|
|
12
|
+
description: "Council handle (name, name-slug, or id prefix) to review with. Defaults to the repo's default council when omitted."
|
|
13
|
+
required: false
|
|
14
|
+
maxRounds:
|
|
15
|
+
description: "Budget for review rounds. On reaching it without convergence, stop and report honestly."
|
|
16
|
+
required: false
|
|
17
|
+
default: 5
|
|
18
|
+
instructions:
|
|
19
|
+
description: "Extra review instructions applied to every round (e.g. 'focus on security')"
|
|
20
|
+
required: false
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# pair-loop
|
|
24
|
+
|
|
25
|
+
You are a thin orchestrator. pair-review does the heavy lifting — multi-level,
|
|
26
|
+
multi-model council reviews with persistence and a web UI. Your job is
|
|
27
|
+
judgment: what to send for review, how to triage what comes back, what to
|
|
28
|
+
fix, when to ask the user, and when to stop.
|
|
29
|
+
|
|
30
|
+
Reviews run through **one execution mode: the headless CLI**. It is
|
|
31
|
+
synchronous (no polling), emits JSON, and needs no server. The HTTP API is
|
|
32
|
+
used for exactly one optional thing: writing triage back so a running web UI
|
|
33
|
+
shows reality (see Write triage back).
|
|
34
|
+
|
|
35
|
+
Any text in the user's request beyond the named arguments is the
|
|
36
|
+
**objective** — what to build or change. If there is no objective, the loop
|
|
37
|
+
runs over the changes already in the working tree.
|
|
38
|
+
|
|
39
|
+
## Hard rules
|
|
40
|
+
|
|
41
|
+
These are not judgment calls:
|
|
42
|
+
|
|
43
|
+
1. **Never report "clean" or "ready" unless a full-quality council round,
|
|
44
|
+
instructed to report only merge-blockers, came back with an empty finding
|
|
45
|
+
list.** A cheaper or broader round cannot grant the final verdict.
|
|
46
|
+
2. **Never silently stop.** If the loop is not converging or hits
|
|
47
|
+
`maxRounds`, say so, list the outstanding findings, and (when a server is
|
|
48
|
+
up) link the review URL.
|
|
49
|
+
3. **Sequence strictly: fix → write triage back → then run the next round.**
|
|
50
|
+
4. **One review at a time.** Never run two reviews concurrently for the same
|
|
51
|
+
repo.
|
|
52
|
+
5. **Do not commit or push.** Leave changes in the working tree. New
|
|
53
|
+
(untracked) files are included automatically whenever the review scope ends
|
|
54
|
+
at `untracked` — which the default scope (`unstaged..untracked`) does — so
|
|
55
|
+
no `git add -N` (intent-to-add) is needed.
|
|
56
|
+
6. **Report failures faithfully.** A failed run (`"ok": false`, non-zero
|
|
57
|
+
exit) is reported as what it is — never papered over as "no findings".
|
|
58
|
+
|
|
59
|
+
## Phase: Setup
|
|
60
|
+
|
|
61
|
+
1. `git rev-parse --show-toplevel` → `REPO_ROOT`. All commands run from here.
|
|
62
|
+
2. Create the round log at `REPO_ROOT/.pair-loop/round-log.md` (mkdir -p; add
|
|
63
|
+
`.pair-loop/` to `.gitignore` if missing). Record: objective, arguments,
|
|
64
|
+
start time. Append to this file after every phase — it is your resume
|
|
65
|
+
state if the session compacts.
|
|
66
|
+
3. If a `council` argument was given, confirm the handle exists:
|
|
67
|
+
`pair-review --list-councils` prints the saved councils. If `pair-review`
|
|
68
|
+
is not on PATH, use `npx -y @in-the-loop-labs/pair-review` for every CLI
|
|
69
|
+
call. An unknown handle → show the available names and ask the user.
|
|
70
|
+
4. If an objective was given and the working tree has no relevant changes
|
|
71
|
+
yet, implement the objective first (directly or via a Task agent — your
|
|
72
|
+
call based on size). Verify with `git status --short` that changes exist
|
|
73
|
+
before the first review round; if none, tell the user and stop.
|
|
74
|
+
|
|
75
|
+
## Running a review round
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pair-review --local --headless --json --council <handle> [--scope <range>] --instructions "<round instructions>"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- Run from `REPO_ROOT`. `--local` with no path uses the current directory.
|
|
82
|
+
- Omit `--council` to use the repo's default council.
|
|
83
|
+
- **Scope (`--scope`).** By default a local review covers only uncommitted
|
|
84
|
+
working-tree changes plus new files (`unstaged..untracked`). That is right
|
|
85
|
+
only while all the work under review stays uncommitted. If the work under
|
|
86
|
+
review already spans commits on a branch, a clean working tree would review as
|
|
87
|
+
**empty** — pass
|
|
88
|
+
`--scope branch..untracked` so every round covers the whole branch
|
|
89
|
+
(everything since the base branch) plus the working tree and new files. Add
|
|
90
|
+
`--base <branch>` when the base branch is not auto-detected (a non-default
|
|
91
|
+
trunk, or a stacked branch). `--scope`/`--base` are local-mode only.
|
|
92
|
+
- **Be patient — council reviews routinely take 15–40 minutes.** Individual
|
|
93
|
+
council voices have their own internal timeouts; trust the CLI to finish.
|
|
94
|
+
Run the command in the background (or with a timeout of at least 45
|
|
95
|
+
minutes) and wait for it to exit. Do not abandon or kill it early; do not
|
|
96
|
+
start a second review while one is running (hard rule 4).
|
|
97
|
+
- stdout is exactly one JSON document (logs go to stderr):
|
|
98
|
+
- success: `{"ok": true, "mode": "local", "run": {...}, "suggestions":
|
|
99
|
+
[...], "count": N}` — exit code 0. **Zero suggestions is still
|
|
100
|
+
success.** Record `run.id` (the runId) and `run.review_id` (the
|
|
101
|
+
reviewId, used for write-back and the review URL).
|
|
102
|
+
- failure: `{"ok": false, "error": {"message": ...}}`, exit code 1.
|
|
103
|
+
|
|
104
|
+
### Round instructions
|
|
105
|
+
|
|
106
|
+
Build `--instructions` per round from, in order: this standing line —
|
|
107
|
+
"Do not report praise findings; report only issues." — then the user's
|
|
108
|
+
`instructions` argument, the objective, and this round's directive (see
|
|
109
|
+
Convergence judgment). On rounds after the first, add: "Earlier review
|
|
110
|
+
rounds already reported issues that have since been fixed or dismissed;
|
|
111
|
+
report only issues present in the current code."
|
|
112
|
+
|
|
113
|
+
### Suggestion fields
|
|
114
|
+
|
|
115
|
+
Each entry in `suggestions`: `id`, `file`, `line_start`, `line_end`, `type`
|
|
116
|
+
(`bug|improvement|suggestion|design|performance|security|code-style` —
|
|
117
|
+
praise is instructed away; ignore it if it appears anyway), `severity`
|
|
118
|
+
(`critical|medium|minor`), `title`, `body`, `reasoning`, `ai_confidence`
|
|
119
|
+
(0–1).
|
|
120
|
+
|
|
121
|
+
## Triage
|
|
122
|
+
|
|
123
|
+
For each finding, decide one of:
|
|
124
|
+
|
|
125
|
+
- **Fix** — it is real and in scope. Goes on this round's fix list.
|
|
126
|
+
- **Dismiss** — false positive, out of scope, or explicitly contrary to the
|
|
127
|
+
user's stated intent. Requires a stated reason in the round log.
|
|
128
|
+
- **Ask the user** — mandatory stop, not a preference, when any of:
|
|
129
|
+
(a) you would dismiss a **critical**-severity finding;
|
|
130
|
+
(b) the fix would change behavior beyond the stated objective;
|
|
131
|
+
(c) the same finding has survived two fix rounds;
|
|
132
|
+
(d) findings conflict with the user's explicit instructions.
|
|
133
|
+
Batch the questions, ask once per round at most, record the answers in the
|
|
134
|
+
round log so later rounds do not re-ask.
|
|
135
|
+
|
|
136
|
+
Dedup against your round log: a finding you already dismissed in an earlier
|
|
137
|
+
round, reappearing unchanged, keeps its dismissal (re-dismiss it in
|
|
138
|
+
write-back) — unless a fix touched that code since, in which case judge it
|
|
139
|
+
fresh.
|
|
140
|
+
|
|
141
|
+
## Fix
|
|
142
|
+
|
|
143
|
+
Apply the fixes (directly or via a Task agent for large batches — your
|
|
144
|
+
call). Run the project's relevant tests for the changed code. A fix that
|
|
145
|
+
breaks tests is not a fix.
|
|
146
|
+
|
|
147
|
+
## Write triage back (optional, needs the server)
|
|
148
|
+
|
|
149
|
+
If the pair-review server is running, keep its UI truthful. Probe once per
|
|
150
|
+
loop: `curl -s http://localhost:7247/health` → it is pair-review iff the
|
|
151
|
+
response has `"service": "pair-review"`. (7247 is the default and is right
|
|
152
|
+
most of the time; if it isn't pair-review, check `port` in
|
|
153
|
+
`REPO_ROOT/.pair-review/config.json` then `~/.pair-review/config.json`, then
|
|
154
|
+
give up on write-back for this loop.)
|
|
155
|
+
|
|
156
|
+
When the server is up, after fixing/dismissing, for each handled finding:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
curl -s -X POST http://localhost:<port>/api/reviews/<run.review_id>/suggestions/<id>/status \
|
|
160
|
+
-H 'Content-Type: application/json' -d '{"status": "dismissed"}'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
- Only `"dismissed"` and `"active"` are accepted; `"adopted"` is reserved
|
|
164
|
+
for the human's adopt flow — never send it.
|
|
165
|
+
- Newer servers accept a `reason` field on this endpoint (check
|
|
166
|
+
`http://localhost:<port>/api.md?reviewId=<reviewId>` if unsure); when
|
|
167
|
+
supported, include it: `"Fixed in loop round <n>: ..."` or
|
|
168
|
+
`"Dismissed: <why>"`. Otherwise the fixed-vs-rejected distinction lives in
|
|
169
|
+
your round log and final report.
|
|
170
|
+
- The human may be triaging in the same UI: before fixing, you may re-check
|
|
171
|
+
`GET /api/reviews/<reviewId>/suggestions` and skip findings whose status
|
|
172
|
+
is no longer `active` — the human got there first. Do not resurrect
|
|
173
|
+
anything the human dismissed.
|
|
174
|
+
- The review URL for your report: `http://localhost:<port>/local/<reviewId>`.
|
|
175
|
+
Note: the UI does not live-update during CLI runs; results appear on
|
|
176
|
+
refresh.
|
|
177
|
+
|
|
178
|
+
No server → skip this entirely; triage lives in the round log and final
|
|
179
|
+
report, and the persisted run history is visible whenever the user next
|
|
180
|
+
starts the server.
|
|
181
|
+
|
|
182
|
+
## Convergence judgment
|
|
183
|
+
|
|
184
|
+
You decide each round's directive and whether to continue. Principles:
|
|
185
|
+
|
|
186
|
+
- **Round 1**: broad. Directive like: "Review these changes thoroughly.
|
|
187
|
+
Objective: <objective>."
|
|
188
|
+
- **Escalate** back to a broad directive whenever the previous round
|
|
189
|
+
produced any `critical` finding.
|
|
190
|
+
- **Narrow** as findings shrink. The gate directive:
|
|
191
|
+
"Final review. Only report blockers — issues that must be fixed before
|
|
192
|
+
merging. Is this ready to merge?"
|
|
193
|
+
- **The gate uses the same council.** The round that says "merge it" must be
|
|
194
|
+
just as rigorous as the rounds that found problems. Do not substitute a
|
|
195
|
+
cheaper configuration for gate rounds.
|
|
196
|
+
- **Continue vs stop**: another round is worth it when the last one caused
|
|
197
|
+
substantial fixes. When rounds churn the same ground without progress,
|
|
198
|
+
stop and ask the user instead of grinding the budget.
|
|
199
|
+
- The loop **ends successfully** when a gate round returns **no blockers** —
|
|
200
|
+
an empty finding list (per hard rule 1). Read the run's `summary` for the
|
|
201
|
+
reviewer's own answer to the ready-to-merge question and quote it in your
|
|
202
|
+
report.
|
|
203
|
+
|
|
204
|
+
## PR mode
|
|
205
|
+
|
|
206
|
+
The loop also works against a GitHub PR:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pair-review <pr-number-or-url> --headless --json [--council <handle>] [--instructions "..."]
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This self-bootstraps the PR (fetches metadata, prepares a review worktree)
|
|
213
|
+
and requires a GitHub token in `~/.pair-review/config.json`. The review's
|
|
214
|
+
checkout lives in a pair-review worktree — your fixes belong in whatever
|
|
215
|
+
checkout the user asked you to work in; do not edit the pool worktree.
|
|
216
|
+
Write-back and the review URL work the same way via `run.review_id`.
|
|
217
|
+
`--scope`/`--base` do not apply here — they are local-mode only; a PR review
|
|
218
|
+
always covers the PR's full diff.
|
|
219
|
+
|
|
220
|
+
## Reporting
|
|
221
|
+
|
|
222
|
+
Every loop ends with a report containing:
|
|
223
|
+
|
|
224
|
+
1. **Verdict** — converged (quote the final run's summary) or not converged
|
|
225
|
+
(why, and what remains).
|
|
226
|
+
2. **Rounds table** — per round: directive, findings found, fixed, dismissed,
|
|
227
|
+
asked.
|
|
228
|
+
3. **Triage detail** — every dismissed finding with its reason; every fix in
|
|
229
|
+
one line each.
|
|
230
|
+
4. **The review URL** when a server is up, so the user can inspect
|
|
231
|
+
everything in the web UI.
|
|
232
|
+
5. Working-tree status reminder: changes are uncommitted, tests run and
|
|
233
|
+
their result.
|
|
@@ -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);
|