second-opinion-mcp 0.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 +323 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.js +84 -0
- package/dist/context/bundler.d.ts +51 -0
- package/dist/context/bundler.js +481 -0
- package/dist/context/bundler.test.d.ts +1 -0
- package/dist/context/bundler.test.js +275 -0
- package/dist/context/git.d.ts +21 -0
- package/dist/context/git.js +102 -0
- package/dist/context/imports.d.ts +43 -0
- package/dist/context/imports.js +197 -0
- package/dist/context/imports.test.d.ts +1 -0
- package/dist/context/imports.test.js +147 -0
- package/dist/context/index.d.ts +6 -0
- package/dist/context/index.js +6 -0
- package/dist/context/session.d.ts +35 -0
- package/dist/context/session.js +317 -0
- package/dist/context/tests.d.ts +13 -0
- package/dist/context/tests.js +83 -0
- package/dist/context/types.d.ts +16 -0
- package/dist/context/types.js +100 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/output/writer.d.ts +34 -0
- package/dist/output/writer.js +162 -0
- package/dist/output/writer.test.d.ts +1 -0
- package/dist/output/writer.test.js +175 -0
- package/dist/providers/base.d.ts +24 -0
- package/dist/providers/base.js +77 -0
- package/dist/providers/base.test.d.ts +1 -0
- package/dist/providers/base.test.js +91 -0
- package/dist/providers/gemini.d.ts +8 -0
- package/dist/providers/gemini.js +43 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +31 -0
- package/dist/providers/openai.d.ts +8 -0
- package/dist/providers/openai.js +39 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +181 -0
- package/dist/test-utils.d.ts +71 -0
- package/dist/test-utils.js +136 -0
- package/dist/tools/review.d.ts +76 -0
- package/dist/tools/review.js +199 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/tokens.d.ts +26 -0
- package/dist/utils/tokens.js +27 -0
- package/package.json +61 -0
- package/scripts/install-config.js +51 -0
- package/second-opinion.skill.md +34 -0
- package/templates/second-opinion.md +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# Second Opinion
|
|
2
|
+
|
|
3
|
+
Get code reviews and feedback from Gemini or GPT while working in Claude Code.
|
|
4
|
+
|
|
5
|
+
Second Opinion is an MCP server that automatically collects context from your Claude Code session—files you've read, edited, and their dependencies—and sends it to another LLM for review. No copy-pasting, no context switching.
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Add to Claude Code (one command)
|
|
11
|
+
claude mcp add second-opinion \
|
|
12
|
+
-e GEMINI_API_KEY="$(cat ~/.secrets/gemini-key)" \
|
|
13
|
+
-- npx second-opinion-mcp
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then in Claude Code:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
/second-opinion
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
That's it. The review appears in `second-opinions/`.
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
### Automatic Context Collection
|
|
27
|
+
|
|
28
|
+
Second Opinion reads your Claude Code session to understand what you're working on:
|
|
29
|
+
|
|
30
|
+
- **Session files** — Files you read, edited, or created
|
|
31
|
+
- **Conversation** — What you asked Claude to do (code blocks stripped to avoid stale references)
|
|
32
|
+
- **Dependencies** — Files imported by your modified code
|
|
33
|
+
- **Dependents** — Files that import your modified code
|
|
34
|
+
- **Tests** — Test files related to your changes
|
|
35
|
+
- **Types** — TypeScript/JSDoc type definitions
|
|
36
|
+
|
|
37
|
+
### Custom Tasks
|
|
38
|
+
|
|
39
|
+
Don't just get code reviews—ask for anything:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
/second-opinion Evaluate the error handling strategy across this codebase
|
|
43
|
+
|
|
44
|
+
/second-opinion Write user documentation for the API changes
|
|
45
|
+
|
|
46
|
+
/second-opinion openai Identify potential performance bottlenecks
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Multiple Providers
|
|
50
|
+
|
|
51
|
+
Switch between Gemini and GPT:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
/second-opinion gemini Review this code # Uses Gemini (default)
|
|
55
|
+
/second-opinion openai Review this code # Uses GPT
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Smart Token Budgeting
|
|
59
|
+
|
|
60
|
+
Context is prioritized to fit within token limits:
|
|
61
|
+
|
|
62
|
+
1. Explicitly included files (highest priority)
|
|
63
|
+
2. Session files (what you worked on)
|
|
64
|
+
3. Git changes
|
|
65
|
+
4. Dependencies
|
|
66
|
+
5. Dependents
|
|
67
|
+
6. Tests
|
|
68
|
+
7. Type definitions
|
|
69
|
+
|
|
70
|
+
Files that don't fit are listed in the output so you know what was omitted.
|
|
71
|
+
|
|
72
|
+
### Include Additional Files
|
|
73
|
+
|
|
74
|
+
Reference files outside your session:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/second-opinion The previous review at reviews/initial.md has been addressed. Verify the fixes.
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Examples
|
|
81
|
+
|
|
82
|
+
### Basic Code Review
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
> /second-opinion
|
|
86
|
+
|
|
87
|
+
Review complete! Written to second-opinions/add-auth-flow.gemini.review.md
|
|
88
|
+
- Analyzed 14 files (52,000 tokens)
|
|
89
|
+
- Key findings: Missing input validation in login handler,
|
|
90
|
+
consider rate limiting for auth endpoints
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Security Audit
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
> /second-opinion openai Audit this code for security vulnerabilities.
|
|
97
|
+
Focus on authentication, input validation, and data exposure.
|
|
98
|
+
|
|
99
|
+
Analysis complete! Written to second-opinions/add-auth-flow.openai.security-audit.md
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Architecture Review
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
> /second-opinion Evaluate the architecture of this feature.
|
|
106
|
+
Is the separation of concerns appropriate? Are there any circular dependencies?
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Documentation Generation
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
> /second-opinion Write API documentation for the changes made in this session.
|
|
113
|
+
Include request/response examples.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Compare Perspectives
|
|
117
|
+
|
|
118
|
+
Get reviews from both providers:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
> /second-opinion gemini Review this implementation
|
|
122
|
+
> /second-opinion openai Review this implementation
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Security
|
|
126
|
+
|
|
127
|
+
Second Opinion implements multiple layers of protection:
|
|
128
|
+
|
|
129
|
+
### What Data Is Sent
|
|
130
|
+
|
|
131
|
+
When you use Second Opinion, the following data may be sent to the external LLM (Gemini or OpenAI):
|
|
132
|
+
|
|
133
|
+
- **File contents**: Source code from your project and any explicitly included files
|
|
134
|
+
- **Conversation context**: A summary of your Claude Code session (what you asked, not your full chat history)
|
|
135
|
+
- **File metadata**: File paths relative to your project
|
|
136
|
+
|
|
137
|
+
The tool does NOT send:
|
|
138
|
+
- Your API keys
|
|
139
|
+
- System files or shell history
|
|
140
|
+
- Files blocked by sensitive path patterns
|
|
141
|
+
|
|
142
|
+
### Sensitive Path Blocking
|
|
143
|
+
|
|
144
|
+
The following paths are always blocked, even when explicitly requested:
|
|
145
|
+
|
|
146
|
+
- SSH keys and config (`~/.ssh/`)
|
|
147
|
+
- AWS credentials (`~/.aws/`)
|
|
148
|
+
- GPG keys (`~/.gnupg/`)
|
|
149
|
+
- Cloud configs (`~/.config/gcloud/`, `~/.kube/`)
|
|
150
|
+
- Git internals (`/.git/`)
|
|
151
|
+
- Auth files (`.netrc`, `.npmrc`, `.pypirc`)
|
|
152
|
+
- Private keys (`*.pem`, `*.key`, `id_rsa`, `id_ed25519`)
|
|
153
|
+
- Service account credentials
|
|
154
|
+
- Environment files (`.env`, `.env.local`, `.env.production`)
|
|
155
|
+
- Terraform secrets (`.tfvars`, `terraform.tfstate`)
|
|
156
|
+
- Kubernetes secrets (`secret.yaml`, `secret.yml`)
|
|
157
|
+
- Shell history (`.bash_history`, `.zsh_history`)
|
|
158
|
+
|
|
159
|
+
### External File Protection
|
|
160
|
+
|
|
161
|
+
By default, files outside your project directory are blocked. If you need to include external files, you must explicitly set `allowExternalFiles: true`. This prevents accidental exfiltration of files from other projects or system locations.
|
|
162
|
+
|
|
163
|
+
### Symlink Protection
|
|
164
|
+
|
|
165
|
+
All paths are resolved via `realpathSync()` before reading. A symlink pointing to `~/.ssh/id_rsa` will be blocked even if it lives inside your project.
|
|
166
|
+
|
|
167
|
+
### Output Directory Validation
|
|
168
|
+
|
|
169
|
+
Reviews are only written within your project directory. Path traversal attempts (e.g., `../../../etc/passwd`) are rejected.
|
|
170
|
+
|
|
171
|
+
### Egress Audit Trail
|
|
172
|
+
|
|
173
|
+
Every review creates a companion `.egress.json` file that records:
|
|
174
|
+
- Exactly which files were sent to the external LLM
|
|
175
|
+
- Which files were blocked and why
|
|
176
|
+
- Timestamp and provider information
|
|
177
|
+
|
|
178
|
+
This allows you to audit what data left your system.
|
|
179
|
+
|
|
180
|
+
### API Key Safety
|
|
181
|
+
|
|
182
|
+
Never paste API keys directly in the terminal—they get saved to shell history. Instead:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# Read from a file
|
|
186
|
+
export GEMINI_API_KEY=$(cat ~/.secrets/gemini-key)
|
|
187
|
+
|
|
188
|
+
# Use a password manager
|
|
189
|
+
export OPENAI_API_KEY=$(op read "op://Private/OpenAI/api-key")
|
|
190
|
+
|
|
191
|
+
# Set via MCP config
|
|
192
|
+
claude mcp add second-opinion \
|
|
193
|
+
-e GEMINI_API_KEY="$(cat ~/.secrets/gemini-key)" \
|
|
194
|
+
-- npx second-opinion-mcp
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Never paste API keys directly in Claude Code chat.** Keys in chat messages could be logged or sent to external providers.
|
|
198
|
+
|
|
199
|
+
## Configuration
|
|
200
|
+
|
|
201
|
+
### Environment Variables
|
|
202
|
+
|
|
203
|
+
| Variable | Default | Description |
|
|
204
|
+
|----------|---------|-------------|
|
|
205
|
+
| `GEMINI_API_KEY` | — | API key for Google Gemini |
|
|
206
|
+
| `OPENAI_API_KEY` | — | API key for OpenAI |
|
|
207
|
+
| `GEMINI_MODEL` | `gemini-2.0-flash-exp` | Gemini model to use |
|
|
208
|
+
| `OPENAI_MODEL` | `gpt-4o` | OpenAI model to use |
|
|
209
|
+
| `DEFAULT_PROVIDER` | `gemini` | Default provider when not specified |
|
|
210
|
+
| `MAX_CONTEXT_TOKENS` | `100000` | Maximum tokens for context |
|
|
211
|
+
| `REVIEWS_DIR` | `second-opinions` | Output directory (relative to project) |
|
|
212
|
+
|
|
213
|
+
### Config File
|
|
214
|
+
|
|
215
|
+
Create `~/.config/second-opinion/config.json`:
|
|
216
|
+
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"geminiApiKey": "your-key",
|
|
220
|
+
"openaiApiKey": "your-key",
|
|
221
|
+
"defaultProvider": "gemini",
|
|
222
|
+
"geminiModel": "gemini-2.0-flash-exp",
|
|
223
|
+
"openaiModel": "gpt-4o",
|
|
224
|
+
"maxContextTokens": 100000,
|
|
225
|
+
"reviewsDir": "second-opinions"
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Environment variables take precedence over the config file.
|
|
230
|
+
|
|
231
|
+
### Custom Review Instructions
|
|
232
|
+
|
|
233
|
+
Create `~/.config/second-opinion/second-opinion.md` for global instructions, or `second-opinion.md` in your project root for project-specific instructions:
|
|
234
|
+
|
|
235
|
+
```markdown
|
|
236
|
+
# Review Instructions
|
|
237
|
+
|
|
238
|
+
Focus on:
|
|
239
|
+
- Security vulnerabilities (OWASP Top 10)
|
|
240
|
+
- Performance implications
|
|
241
|
+
- Error handling completeness
|
|
242
|
+
- Test coverage gaps
|
|
243
|
+
|
|
244
|
+
Our stack: TypeScript, React, PostgreSQL
|
|
245
|
+
Coding standards: Airbnb style guide
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Tool Parameters
|
|
249
|
+
|
|
250
|
+
When calling the MCP tool directly:
|
|
251
|
+
|
|
252
|
+
| Parameter | Required | Default | Description |
|
|
253
|
+
|-----------|----------|---------|-------------|
|
|
254
|
+
| `provider` | Yes | — | `"gemini"` or `"openai"` |
|
|
255
|
+
| `projectPath` | Yes | — | Absolute path to project |
|
|
256
|
+
| `task` | No | — | Custom prompt (defaults to code review) |
|
|
257
|
+
| `sessionId` | No | latest | Claude Code session ID |
|
|
258
|
+
| `sessionName` | No | auto | Name for output file |
|
|
259
|
+
| `includeFiles` | No | — | Additional files/folders to include |
|
|
260
|
+
| `allowExternalFiles` | No | `false` | Allow files outside project (required for external paths in includeFiles) |
|
|
261
|
+
| `dryRun` | No | `false` | Preview what would be sent without calling external API |
|
|
262
|
+
| `includeConversation` | No | `true` | Include conversation context |
|
|
263
|
+
| `includeDependencies` | No | `true` | Include imported files |
|
|
264
|
+
| `includeDependents` | No | `true` | Include importing files |
|
|
265
|
+
| `includeTests` | No | `true` | Include test files |
|
|
266
|
+
| `includeTypes` | No | `true` | Include type definitions |
|
|
267
|
+
| `maxTokens` | No | `100000` | Context token budget |
|
|
268
|
+
| `focusAreas` | No | — | Specific areas to focus on |
|
|
269
|
+
|
|
270
|
+
## How It Works
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
274
|
+
│ Claude Code │
|
|
275
|
+
│ │
|
|
276
|
+
│ You: "Add user authentication" │
|
|
277
|
+
│ Claude: [reads files, writes code, runs tests] │
|
|
278
|
+
│ You: "/second-opinion" │
|
|
279
|
+
│ │
|
|
280
|
+
└─────────────────────┬───────────────────────────────────────────┘
|
|
281
|
+
│
|
|
282
|
+
▼
|
|
283
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
284
|
+
│ Second Opinion MCP │
|
|
285
|
+
│ │
|
|
286
|
+
│ 1. Parse Claude Code session logs │
|
|
287
|
+
│ 2. Collect files read/written + their content │
|
|
288
|
+
│ 3. Resolve dependencies and dependents │
|
|
289
|
+
│ 4. Find related tests and types │
|
|
290
|
+
│ 5. Bundle within token budget │
|
|
291
|
+
│ 6. Send to Gemini/GPT │
|
|
292
|
+
│ 7. Write response to second-opinions/ │
|
|
293
|
+
│ │
|
|
294
|
+
└─────────────────────┬───────────────────────────────────────────┘
|
|
295
|
+
│
|
|
296
|
+
▼
|
|
297
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
298
|
+
│ second-opinions/add-auth.gemini.review.md │
|
|
299
|
+
│ │
|
|
300
|
+
│ # Code Review - add-auth │
|
|
301
|
+
│ **Provider:** gemini │
|
|
302
|
+
│ │
|
|
303
|
+
│ ## Summary │
|
|
304
|
+
│ The authentication implementation is solid... │
|
|
305
|
+
│ │
|
|
306
|
+
│ ## Critical Issues │
|
|
307
|
+
│ - Missing rate limiting on login endpoint │
|
|
308
|
+
│ │
|
|
309
|
+
│ ## Suggestions │
|
|
310
|
+
│ - Consider adding refresh token rotation │
|
|
311
|
+
│ │
|
|
312
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Requirements
|
|
316
|
+
|
|
317
|
+
- Node.js 18+
|
|
318
|
+
- Claude Code CLI
|
|
319
|
+
- At least one API key (Gemini or OpenAI)
|
|
320
|
+
|
|
321
|
+
## License
|
|
322
|
+
|
|
323
|
+
MIT
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const ConfigSchema: z.ZodObject<{
|
|
3
|
+
geminiApiKey: z.ZodOptional<z.ZodString>;
|
|
4
|
+
openaiApiKey: z.ZodOptional<z.ZodString>;
|
|
5
|
+
defaultProvider: z.ZodDefault<z.ZodEnum<["gemini", "openai"]>>;
|
|
6
|
+
geminiModel: z.ZodDefault<z.ZodString>;
|
|
7
|
+
openaiModel: z.ZodDefault<z.ZodString>;
|
|
8
|
+
maxContextTokens: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
reviewsDir: z.ZodDefault<z.ZodString>;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
defaultProvider: "gemini" | "openai";
|
|
12
|
+
geminiModel: string;
|
|
13
|
+
openaiModel: string;
|
|
14
|
+
maxContextTokens: number;
|
|
15
|
+
reviewsDir: string;
|
|
16
|
+
geminiApiKey?: string | undefined;
|
|
17
|
+
openaiApiKey?: string | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
geminiApiKey?: string | undefined;
|
|
20
|
+
openaiApiKey?: string | undefined;
|
|
21
|
+
defaultProvider?: "gemini" | "openai" | undefined;
|
|
22
|
+
geminiModel?: string | undefined;
|
|
23
|
+
openaiModel?: string | undefined;
|
|
24
|
+
maxContextTokens?: number | undefined;
|
|
25
|
+
reviewsDir?: string | undefined;
|
|
26
|
+
}>;
|
|
27
|
+
export type Config = z.infer<typeof ConfigSchema>;
|
|
28
|
+
export declare function getConfigDir(): string;
|
|
29
|
+
export declare function getClaudeProjectsDir(): string;
|
|
30
|
+
export declare function loadConfig(): Config;
|
|
31
|
+
export declare function loadReviewInstructions(projectPath?: string): string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
export const ConfigSchema = z.object({
|
|
6
|
+
geminiApiKey: z.string().optional(),
|
|
7
|
+
openaiApiKey: z.string().optional(),
|
|
8
|
+
defaultProvider: z.enum(["gemini", "openai"]).default("gemini"),
|
|
9
|
+
geminiModel: z.string().default("gemini-3-flash-preview"),
|
|
10
|
+
openaiModel: z.string().default("gpt-5.2"),
|
|
11
|
+
maxContextTokens: z.number().default(100000),
|
|
12
|
+
reviewsDir: z.string().default("second-opinions"),
|
|
13
|
+
});
|
|
14
|
+
export function getConfigDir() {
|
|
15
|
+
return path.join(os.homedir(), ".config", "second-opinion");
|
|
16
|
+
}
|
|
17
|
+
export function getClaudeProjectsDir() {
|
|
18
|
+
return path.join(os.homedir(), ".claude", "projects");
|
|
19
|
+
}
|
|
20
|
+
export function loadConfig() {
|
|
21
|
+
const configDir = getConfigDir();
|
|
22
|
+
const configPath = path.join(configDir, "config.json");
|
|
23
|
+
let fileConfig = {};
|
|
24
|
+
if (fs.existsSync(configPath)) {
|
|
25
|
+
try {
|
|
26
|
+
fileConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error(`Warning: Invalid JSON in config file ${configPath}. Using defaults.`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const config = ConfigSchema.parse({
|
|
33
|
+
geminiApiKey: process.env.GEMINI_API_KEY || fileConfig.geminiApiKey,
|
|
34
|
+
openaiApiKey: process.env.OPENAI_API_KEY || fileConfig.openaiApiKey,
|
|
35
|
+
defaultProvider: process.env.DEFAULT_PROVIDER || fileConfig.defaultProvider,
|
|
36
|
+
geminiModel: process.env.GEMINI_MODEL || fileConfig.geminiModel,
|
|
37
|
+
openaiModel: process.env.OPENAI_MODEL || fileConfig.openaiModel,
|
|
38
|
+
maxContextTokens: process.env.MAX_CONTEXT_TOKENS
|
|
39
|
+
? parseInt(process.env.MAX_CONTEXT_TOKENS)
|
|
40
|
+
: fileConfig.maxContextTokens,
|
|
41
|
+
reviewsDir: process.env.REVIEWS_DIR || fileConfig.reviewsDir,
|
|
42
|
+
});
|
|
43
|
+
return config;
|
|
44
|
+
}
|
|
45
|
+
export function loadReviewInstructions(projectPath) {
|
|
46
|
+
const configDir = getConfigDir();
|
|
47
|
+
// Check project-local first
|
|
48
|
+
if (projectPath) {
|
|
49
|
+
const projectInstructions = path.join(projectPath, "second-opinion.md");
|
|
50
|
+
if (fs.existsSync(projectInstructions)) {
|
|
51
|
+
return fs.readFileSync(projectInstructions, "utf-8");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Fall back to global
|
|
55
|
+
const globalInstructions = path.join(configDir, "second-opinion.md");
|
|
56
|
+
if (fs.existsSync(globalInstructions)) {
|
|
57
|
+
return fs.readFileSync(globalInstructions, "utf-8");
|
|
58
|
+
}
|
|
59
|
+
// Default instructions
|
|
60
|
+
return `# Code Review Instructions
|
|
61
|
+
|
|
62
|
+
You are a code reviewer providing a second opinion on code changes.
|
|
63
|
+
|
|
64
|
+
## Your Role
|
|
65
|
+
- Review the code changes objectively
|
|
66
|
+
- Identify potential issues, bugs, or improvements
|
|
67
|
+
- Be constructive and specific in your feedback
|
|
68
|
+
- Consider security, performance, and maintainability
|
|
69
|
+
|
|
70
|
+
## Review Focus
|
|
71
|
+
- Security vulnerabilities and best practices
|
|
72
|
+
- Performance considerations
|
|
73
|
+
- Code clarity and maintainability
|
|
74
|
+
- Error handling and edge cases
|
|
75
|
+
- Testing coverage
|
|
76
|
+
|
|
77
|
+
## Output Format
|
|
78
|
+
Structure your review with:
|
|
79
|
+
1. **Summary** (2-3 sentences overview)
|
|
80
|
+
2. **Critical Issues** (if any - things that must be fixed)
|
|
81
|
+
3. **Suggestions** (improvements that would be nice)
|
|
82
|
+
4. **What's Done Well** (positive feedback)
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface BlockedFile {
|
|
2
|
+
path: string;
|
|
3
|
+
reason: "sensitive_path" | "outside_project_requires_allowExternalFiles";
|
|
4
|
+
}
|
|
5
|
+
export interface BundleOptions {
|
|
6
|
+
projectPath: string;
|
|
7
|
+
sessionId?: string;
|
|
8
|
+
includeConversation?: boolean;
|
|
9
|
+
includeDependencies?: boolean;
|
|
10
|
+
includeDependents?: boolean;
|
|
11
|
+
includeTests?: boolean;
|
|
12
|
+
includeTypes?: boolean;
|
|
13
|
+
includeFiles?: string[];
|
|
14
|
+
allowExternalFiles?: boolean;
|
|
15
|
+
maxTokens?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface FileEntry {
|
|
18
|
+
path: string;
|
|
19
|
+
content: string;
|
|
20
|
+
category: "session" | "git" | "dependency" | "dependent" | "test" | "type" | "explicit";
|
|
21
|
+
tokenEstimate: number;
|
|
22
|
+
}
|
|
23
|
+
export interface OmittedFile {
|
|
24
|
+
path: string;
|
|
25
|
+
category: FileEntry["category"];
|
|
26
|
+
tokenEstimate: number;
|
|
27
|
+
reason: "budget_exceeded" | "outside_project" | "sensitive_path" | "outside_project_requires_allowExternalFiles";
|
|
28
|
+
}
|
|
29
|
+
export interface ContextBundle {
|
|
30
|
+
conversationContext: string;
|
|
31
|
+
files: FileEntry[];
|
|
32
|
+
omittedFiles: OmittedFile[];
|
|
33
|
+
totalTokens: number;
|
|
34
|
+
categories: {
|
|
35
|
+
session: number;
|
|
36
|
+
git: number;
|
|
37
|
+
dependency: number;
|
|
38
|
+
dependent: number;
|
|
39
|
+
test: number;
|
|
40
|
+
type: number;
|
|
41
|
+
explicit: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Collect and bundle all context for review
|
|
46
|
+
*/
|
|
47
|
+
export declare function bundleContext(options: BundleOptions): Promise<ContextBundle>;
|
|
48
|
+
/**
|
|
49
|
+
* Format the bundle as markdown for the reviewer
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatBundleAsMarkdown(bundle: ContextBundle, projectPath: string): string;
|