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 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
- Second Opinion reads your Claude Code session to understand what you're working on:
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 (code blocks stripped to avoid stale references)
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
- ### Multiple Providers
95
+ ### Consensus & Providers
51
96
 
52
- Switch between Gemini and GPT, or use both:
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 gemini Review this code # Uses Gemini (default)
56
- /second-opinion openai Review this code # Uses GPT
57
- /second-opinion consensus Review this code # Uses BOTH in parallel
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
- ### Consensus Mode
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
- Get perspectives from both Gemini and OpenAI in a single request:
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
- Consensus mode:
69
- - Calls both providers simultaneously (faster than sequential calls)
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 within token limits:
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
- Review complete! Written to second-opinions/add-auth-flow.gemini.review.md
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
- consider rate limiting for auth endpoints
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
- ### Compare Perspectives
165
+ ### Single Provider
133
166
 
134
- Get reviews from both providers at once:
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-2.0-flash-exp` | Gemini model to use |
262
- | `OPENAI_MODEL` | `gpt-4o` | OpenAI model to use |
263
- | `DEFAULT_PROVIDER` | `gemini` | Default provider when not specified |
264
- | `MAX_CONTEXT_TOKENS` | `100000` | Maximum tokens for context |
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": "gemini",
279
- "geminiModel": "gemini-2.0-flash-exp",
280
- "openaiModel": "gpt-4o",
281
- "maxContextTokens": 100000,
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
- | `maxTokens` | No | `100000` | Context token budget |
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
  /**