second-opinion-mcp 0.4.0 → 0.5.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 +79 -100
- package/dist/config.js +14 -1
- package/dist/context/bundler.d.ts +39 -1
- package/dist/context/bundler.js +431 -219
- package/dist/context/git.d.ts +17 -1
- package/dist/context/git.js +84 -1
- package/dist/output/consensus-formatter.js +28 -7
- package/dist/providers/base.d.ts +6 -1
- package/dist/providers/base.js +22 -16
- package/dist/providers/consensus.d.ts +1 -2
- package/dist/providers/consensus.js +5 -26
- package/dist/tools/review.js +2 -1
- package/dist/utils/tokens.d.ts +8 -0
- package/dist/utils/tokens.js +8 -0
- package/package.json +1 -1
- package/second-opinion.skill.md +9 -8
- package/templates/second-opinion.md +17 -1
package/README.md
CHANGED
|
@@ -21,14 +21,59 @@ Then in Claude Code:
|
|
|
21
21
|
|
|
22
22
|
That's it. The review appears in `second-opinions/`.
|
|
23
23
|
|
|
24
|
+
## How It Works
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
28
|
+
│ Claude Code │
|
|
29
|
+
│ │
|
|
30
|
+
│ You: "Add user authentication" │
|
|
31
|
+
│ Claude: [reads files, writes code, runs tests] │
|
|
32
|
+
│ You: "/second-opinion" │
|
|
33
|
+
│ │
|
|
34
|
+
└─────────────────────┬───────────────────────────────────────────┘
|
|
35
|
+
│
|
|
36
|
+
▼
|
|
37
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
38
|
+
│ Second Opinion MCP │
|
|
39
|
+
│ │
|
|
40
|
+
│ 1. Parse Claude Code session logs │
|
|
41
|
+
│ 2. Collect files read/written + their content │
|
|
42
|
+
│ 3. Resolve dependencies and dependents │
|
|
43
|
+
│ 4. Find related tests and types │
|
|
44
|
+
│ 5. Collect branch diff (feature branch vs base) │
|
|
45
|
+
│ 6. Bundle within token budget │
|
|
46
|
+
│ 7. Send to Gemini + GPT (consensus mode) │
|
|
47
|
+
│ 8. Write response to second-opinions/ │
|
|
48
|
+
│ │
|
|
49
|
+
└─────────────────────┬───────────────────────────────────────────┘
|
|
50
|
+
│
|
|
51
|
+
▼
|
|
52
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
53
|
+
│ second-opinions/add-auth.consensus.review.md │
|
|
54
|
+
│ │
|
|
55
|
+
│ # Consensus Code Review │
|
|
56
|
+
│ │
|
|
57
|
+
│ ## Synthesis │
|
|
58
|
+
│ [Claude merges both perspectives with full context] │
|
|
59
|
+
│ │
|
|
60
|
+
│ ## Gemini's Review │
|
|
61
|
+
│ [BLOCKING] Missing rate limiting on login endpoint │
|
|
62
|
+
│ │
|
|
63
|
+
│ ## OpenAI's Review │
|
|
64
|
+
│ [SUGGESTION] Consider adding refresh token rotation │
|
|
65
|
+
│ │
|
|
66
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
67
|
+
```
|
|
68
|
+
|
|
24
69
|
## Features
|
|
25
70
|
|
|
26
71
|
### Automatic Context Collection
|
|
27
72
|
|
|
28
|
-
|
|
73
|
+
Your session is the context. Second Opinion reads it automatically:
|
|
29
74
|
|
|
30
75
|
- **Session files** — Files you read, edited, or created
|
|
31
|
-
- **Conversation** — What you asked Claude to do
|
|
76
|
+
- **Conversation** — What you asked Claude to do
|
|
32
77
|
- **Dependencies** — Files imported by your modified code
|
|
33
78
|
- **Dependents** — Files that import your modified code
|
|
34
79
|
- **Tests** — Test files related to your changes
|
|
@@ -47,43 +92,31 @@ Don't just get code reviews—ask for anything:
|
|
|
47
92
|
/second-opinion openai Identify potential performance bottlenecks
|
|
48
93
|
```
|
|
49
94
|
|
|
50
|
-
###
|
|
95
|
+
### Consensus & Providers
|
|
51
96
|
|
|
52
|
-
|
|
97
|
+
By default, Second Opinion calls both Gemini and OpenAI in parallel. Claude then synthesizes the findings using its full session context—merging agreements, surfacing unique insights, and resolving disagreements.
|
|
53
98
|
|
|
54
99
|
```
|
|
55
|
-
/second-opinion
|
|
56
|
-
/second-opinion
|
|
57
|
-
/second-opinion
|
|
100
|
+
/second-opinion # Consensus (default) — both providers
|
|
101
|
+
/second-opinion gemini Review this # Gemini only
|
|
102
|
+
/second-opinion openai Review this # GPT only
|
|
58
103
|
```
|
|
59
104
|
|
|
60
|
-
|
|
105
|
+
Consensus mode:
|
|
106
|
+
- Calls both providers simultaneously
|
|
107
|
+
- Claude synthesizes findings using the unified review framework
|
|
108
|
+
- **Smart fallback**: if only one API key is configured, uses that single provider
|
|
61
109
|
|
|
62
|
-
|
|
110
|
+
### Diff-Scoped Reviews
|
|
63
111
|
|
|
64
|
-
|
|
65
|
-
/second-opinion consensus
|
|
66
|
-
```
|
|
112
|
+
On feature branches, Second Opinion automatically includes the git diff (branch vs base). Reviewers distinguish issues introduced by your changes from pre-existing issues in the codebase:
|
|
67
113
|
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
- Returns combined output with each model's perspective
|
|
71
|
-
- Highlights areas of agreement and differences
|
|
72
|
-
- Requires both `GEMINI_API_KEY` and `OPENAI_API_KEY` to be configured
|
|
114
|
+
- **Findings** — Issues in the diff (your changes)
|
|
115
|
+
- **Pre-existing Issues** — Legitimate issues NOT introduced by this change (lower priority)
|
|
73
116
|
|
|
74
117
|
### Smart Token Budgeting
|
|
75
118
|
|
|
76
|
-
Context is prioritized to fit
|
|
77
|
-
|
|
78
|
-
1. Explicitly included files (highest priority)
|
|
79
|
-
2. Session files (what you worked on)
|
|
80
|
-
3. Git changes
|
|
81
|
-
4. Dependencies
|
|
82
|
-
5. Dependents
|
|
83
|
-
6. Tests
|
|
84
|
-
7. Type definitions
|
|
85
|
-
|
|
86
|
-
Files that don't fit are listed in the output so you know what was omitted.
|
|
119
|
+
Context is prioritized by category: explicitly included files first, then session files, git changes, dependencies, dependents, tests, and type definitions. Unused budget spills over to later categories. Files that don't fit are listed so you know what was omitted.
|
|
87
120
|
|
|
88
121
|
### Include Additional Files
|
|
89
122
|
|
|
@@ -100,10 +133,10 @@ Reference files outside your session:
|
|
|
100
133
|
```
|
|
101
134
|
> /second-opinion
|
|
102
135
|
|
|
103
|
-
|
|
136
|
+
Consensus review complete! Written to second-opinions/add-auth-flow.consensus.review.md
|
|
104
137
|
- Analyzed 14 files (52,000 tokens)
|
|
105
|
-
- Key findings: Missing input validation in login handler,
|
|
106
|
-
|
|
138
|
+
- Key findings: [BLOCKING] Missing input validation in login handler,
|
|
139
|
+
[IMPORTANT] Consider rate limiting for auth endpoints
|
|
107
140
|
```
|
|
108
141
|
|
|
109
142
|
### Security Audit
|
|
@@ -129,21 +162,9 @@ Analysis complete! Written to second-opinions/add-auth-flow.openai.security-audi
|
|
|
129
162
|
Include request/response examples.
|
|
130
163
|
```
|
|
131
164
|
|
|
132
|
-
###
|
|
165
|
+
### Single Provider
|
|
133
166
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
```
|
|
137
|
-
> /second-opinion consensus Review this implementation
|
|
138
|
-
|
|
139
|
-
Consensus review complete! Written to second-opinions/auth-flow.consensus.review.md
|
|
140
|
-
- Both models analyzed 14 files
|
|
141
|
-
- Agreement: Both flagged the missing null check on line 42
|
|
142
|
-
- Gemini highlighted: Performance concern with nested loops
|
|
143
|
-
- OpenAI highlighted: Inconsistent error message formats
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Or separately:
|
|
167
|
+
When you want one model's perspective:
|
|
147
168
|
|
|
148
169
|
```
|
|
149
170
|
> /second-opinion gemini Review this implementation
|
|
@@ -258,10 +279,11 @@ claude mcp add second-opinion \
|
|
|
258
279
|
|----------|---------|-------------|
|
|
259
280
|
| `GEMINI_API_KEY` | — | API key for Google Gemini |
|
|
260
281
|
| `OPENAI_API_KEY` | — | API key for OpenAI |
|
|
261
|
-
| `GEMINI_MODEL` | `gemini-
|
|
262
|
-
| `OPENAI_MODEL` | `gpt-
|
|
263
|
-
| `DEFAULT_PROVIDER` | `
|
|
264
|
-
| `MAX_CONTEXT_TOKENS` | `
|
|
282
|
+
| `GEMINI_MODEL` | `gemini-3-flash-preview` | Gemini model to use |
|
|
283
|
+
| `OPENAI_MODEL` | `gpt-5.2` | OpenAI model to use |
|
|
284
|
+
| `DEFAULT_PROVIDER` | `consensus` | Default provider (`gemini`, `openai`, or `consensus`) |
|
|
285
|
+
| `MAX_CONTEXT_TOKENS` | `200000` | Maximum tokens for context |
|
|
286
|
+
| `MAX_OUTPUT_TOKENS` | `32768` | Maximum tokens for reviewer's response |
|
|
265
287
|
| `TEMPERATURE` | `0.3` | Default LLM temperature (0-1) |
|
|
266
288
|
| `RATE_LIMIT_WINDOW_MS` | `60000` | Rate limit window (1 minute) |
|
|
267
289
|
| `RATE_LIMIT_MAX_REQUESTS` | `10` | Max requests per window |
|
|
@@ -275,10 +297,11 @@ Create `~/.config/second-opinion/config.json`:
|
|
|
275
297
|
{
|
|
276
298
|
"geminiApiKey": "your-key",
|
|
277
299
|
"openaiApiKey": "your-key",
|
|
278
|
-
"defaultProvider": "
|
|
279
|
-
"geminiModel": "gemini-
|
|
280
|
-
"openaiModel": "gpt-
|
|
281
|
-
"maxContextTokens":
|
|
300
|
+
"defaultProvider": "consensus",
|
|
301
|
+
"geminiModel": "gemini-3-flash-preview",
|
|
302
|
+
"openaiModel": "gpt-5.2",
|
|
303
|
+
"maxContextTokens": 200000,
|
|
304
|
+
"maxOutputTokens": 32768,
|
|
282
305
|
"temperature": 0.3,
|
|
283
306
|
"rateLimitWindowMs": 60000,
|
|
284
307
|
"rateLimitMaxRequests": 10,
|
|
@@ -311,7 +334,7 @@ When calling the MCP tool directly:
|
|
|
311
334
|
|
|
312
335
|
| Parameter | Required | Default | Description |
|
|
313
336
|
|-----------|----------|---------|-------------|
|
|
314
|
-
| `provider` | Yes | — | `"gemini"`, `"openai"`, or `"consensus"` |
|
|
337
|
+
| `provider` | Yes | — | `"gemini"`, `"openai"`, or `"consensus"` (falls back to single provider if only one key configured) |
|
|
315
338
|
| `projectPath` | Yes | — | Absolute path to project |
|
|
316
339
|
| `task` | No | — | Custom prompt (defaults to code review) |
|
|
317
340
|
| `sessionId` | No | latest | Claude Code session ID |
|
|
@@ -324,55 +347,11 @@ When calling the MCP tool directly:
|
|
|
324
347
|
| `includeDependents` | No | `true` | Include importing files |
|
|
325
348
|
| `includeTests` | No | `true` | Include test files |
|
|
326
349
|
| `includeTypes` | No | `true` | Include type definitions |
|
|
327
|
-
| `
|
|
350
|
+
| `maxInputTokens` | No | `200000` | Context token budget |
|
|
351
|
+
| `maxOutputTokens` | No | `32768` | Max tokens for reviewer's response |
|
|
328
352
|
| `temperature` | No | `0.3` | LLM temperature (0-1) |
|
|
329
353
|
| `focusAreas` | No | — | Specific areas to focus on |
|
|
330
354
|
|
|
331
|
-
## How It Works
|
|
332
|
-
|
|
333
|
-
```
|
|
334
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
335
|
-
│ Claude Code │
|
|
336
|
-
│ │
|
|
337
|
-
│ You: "Add user authentication" │
|
|
338
|
-
│ Claude: [reads files, writes code, runs tests] │
|
|
339
|
-
│ You: "/second-opinion" │
|
|
340
|
-
│ │
|
|
341
|
-
└─────────────────────┬───────────────────────────────────────────┘
|
|
342
|
-
│
|
|
343
|
-
▼
|
|
344
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
345
|
-
│ Second Opinion MCP │
|
|
346
|
-
│ │
|
|
347
|
-
│ 1. Parse Claude Code session logs │
|
|
348
|
-
│ 2. Collect files read/written + their content │
|
|
349
|
-
│ 3. Resolve dependencies and dependents │
|
|
350
|
-
│ 4. Find related tests and types │
|
|
351
|
-
│ 5. Bundle within token budget │
|
|
352
|
-
│ 6. Send to Gemini/GPT │
|
|
353
|
-
│ 7. Write response to second-opinions/ │
|
|
354
|
-
│ │
|
|
355
|
-
└─────────────────────┬───────────────────────────────────────────┘
|
|
356
|
-
│
|
|
357
|
-
▼
|
|
358
|
-
┌─────────────────────────────────────────────────────────────────┐
|
|
359
|
-
│ second-opinions/add-auth.gemini.review.md │
|
|
360
|
-
│ │
|
|
361
|
-
│ # Code Review - add-auth │
|
|
362
|
-
│ **Provider:** gemini │
|
|
363
|
-
│ │
|
|
364
|
-
│ ## Summary │
|
|
365
|
-
│ The authentication implementation is solid... │
|
|
366
|
-
│ │
|
|
367
|
-
│ ## Critical Issues │
|
|
368
|
-
│ - Missing rate limiting on login endpoint │
|
|
369
|
-
│ │
|
|
370
|
-
│ ## Suggestions │
|
|
371
|
-
│ - Consider adding refresh token rotation │
|
|
372
|
-
│ │
|
|
373
|
-
└─────────────────────────────────────────────────────────────────┘
|
|
374
|
-
```
|
|
375
|
-
|
|
376
355
|
## Requirements
|
|
377
356
|
|
|
378
357
|
- Node.js 18+
|
package/dist/config.js
CHANGED
|
@@ -34,7 +34,7 @@ export function loadConfig() {
|
|
|
34
34
|
fileConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
35
35
|
}
|
|
36
36
|
catch (error) {
|
|
37
|
-
console.error(`Warning: Invalid JSON in config file ${configPath}. Using defaults
|
|
37
|
+
console.error(`Warning: Invalid JSON in config file ${configPath}. Using defaults.`, error instanceof Error ? error.message : String(error));
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
const config = ConfigSchema.parse({
|
|
@@ -95,6 +95,12 @@ Work through these phases in order:
|
|
|
95
95
|
### Phase 3: Detailed Analysis
|
|
96
96
|
- Correctness, security, performance, error handling, edge cases
|
|
97
97
|
|
|
98
|
+
When a branch diff is provided:
|
|
99
|
+
- Primary focus: code that appears in the diff (new/changed lines)
|
|
100
|
+
- Use the diff to determine if an issue is newly introduced or pre-existing
|
|
101
|
+
- Findings section = only issues in the diff
|
|
102
|
+
- Pre-existing Issues section = legitimate issues NOT in the diff
|
|
103
|
+
|
|
98
104
|
### Phase 4: Self-Interrogation
|
|
99
105
|
For each finding: form it as a question, search the code for evidence, then:
|
|
100
106
|
- Confirmed → include as a finding with evidence
|
|
@@ -119,11 +125,18 @@ Brief overall assessment.
|
|
|
119
125
|
|
|
120
126
|
### Findings
|
|
121
127
|
Ordered by severity, every finding grounded in specific code.
|
|
128
|
+
When a branch diff is provided, only include issues introduced by the diff.
|
|
129
|
+
|
|
130
|
+
### Pre-existing Issues
|
|
131
|
+
(Include only when a branch diff is provided and pre-existing issues are found.)
|
|
132
|
+
Issues found in reviewed files that were NOT introduced by this change.
|
|
133
|
+
Same severity labels and evidence requirements as Findings.
|
|
122
134
|
|
|
123
135
|
### Questions
|
|
124
136
|
Findings that couldn't be fully grounded.
|
|
125
137
|
|
|
126
138
|
### Upstream/Downstream Opportunities
|
|
139
|
+
Architectural suggestions beyond the current change.
|
|
127
140
|
- **What/Where** · **Why** · **Risk Level**: Safe / Worth Investigating / Bold
|
|
128
141
|
|
|
129
142
|
### What's Done Well
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BudgetCategory } from "../utils/tokens.js";
|
|
1
2
|
export interface BlockedFile {
|
|
2
3
|
path: string;
|
|
3
4
|
reason: "sensitive_path" | "outside_project_requires_allowExternalFiles";
|
|
@@ -48,6 +49,8 @@ export interface ContextBundle {
|
|
|
48
49
|
commentsCount: number;
|
|
49
50
|
reviewsCount: number;
|
|
50
51
|
};
|
|
52
|
+
/** Unified diff of branch changes (from base branch), kept separate from file markdown */
|
|
53
|
+
branchDiff?: string;
|
|
51
54
|
files: FileEntry[];
|
|
52
55
|
omittedFiles: OmittedFile[];
|
|
53
56
|
totalTokens: number;
|
|
@@ -74,8 +77,43 @@ export interface ContextBundle {
|
|
|
74
77
|
message: string;
|
|
75
78
|
};
|
|
76
79
|
}
|
|
80
|
+
export interface CandidateFile {
|
|
81
|
+
path: string;
|
|
82
|
+
content: string;
|
|
83
|
+
category: FileEntry["category"];
|
|
84
|
+
tokenEstimate: number;
|
|
85
|
+
annotation?: string;
|
|
86
|
+
redactionCount: number;
|
|
87
|
+
redactedTypes: string[];
|
|
88
|
+
}
|
|
89
|
+
export interface CategoryCandidates {
|
|
90
|
+
category: BudgetCategory;
|
|
91
|
+
files: CandidateFile[];
|
|
92
|
+
totalDemand: number;
|
|
93
|
+
}
|
|
94
|
+
export interface AllocationResult {
|
|
95
|
+
included: FileEntry[];
|
|
96
|
+
omitted: OmittedFile[];
|
|
97
|
+
categoryTokens: Record<BudgetCategory, number>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Two-pass budget allocator.
|
|
101
|
+
*
|
|
102
|
+
* If total demand <= filePool, include everything (common case, fixes the original bug).
|
|
103
|
+
* If total demand > filePool, redistribute surplus from low-demand categories to high-demand ones.
|
|
104
|
+
*
|
|
105
|
+
* Within each category:
|
|
106
|
+
* - explicit/session: preserve insertion order (user intent)
|
|
107
|
+
* - all others: sort smallest first (maximize file count)
|
|
108
|
+
*/
|
|
109
|
+
export declare function allocateBudget(candidates: CategoryCandidates[], filePool: number, budgetWeights: Record<BudgetCategory, number>, priorityOrder: BudgetCategory[]): AllocationResult;
|
|
77
110
|
/**
|
|
78
|
-
* Collect and bundle all context for review
|
|
111
|
+
* Collect and bundle all context for review.
|
|
112
|
+
*
|
|
113
|
+
* Two-pass architecture:
|
|
114
|
+
* Pass 1 (Collection): Gather all candidate files per category without committing budget.
|
|
115
|
+
* Deduplication: Assign each file to its highest-priority category.
|
|
116
|
+
* Pass 2 (Allocation): Distribute the file pool across categories via allocateBudget().
|
|
79
117
|
*/
|
|
80
118
|
export declare function bundleContext(options: BundleOptions): Promise<ContextBundle>;
|
|
81
119
|
/**
|