cc-reviewer 1.5.3 → 1.7.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.
@@ -0,0 +1,35 @@
1
+ # Ask Codex
2
+
3
+ Ask OpenAI Codex CLI for help as a peer engineer.
4
+
5
+ ## Arguments
6
+ - `$ARGUMENTS` - Your question or request
7
+
8
+ ## Codex Strengths
9
+ - **Correctness**: Logic errors, edge cases, bugs
10
+ - **Performance**: Efficiency, complexity analysis
11
+ - **Security**: Vulnerability detection
12
+
13
+ ## Tool Invocation
14
+
15
+ Call `ask_codex` with:
16
+
17
+ ```json
18
+ {
19
+ "workingDir": "<current directory>",
20
+ "prompt": "<your question or request from $ARGUMENTS>",
21
+ "taskType": "<infer from request: plan|debug|explain|question|fix|explore|general>",
22
+ "relevantFiles": ["<files related to the question>"],
23
+ "context": "<any error messages or prior analysis>"
24
+ }
25
+ ```
26
+
27
+ ## After Receiving Response
28
+
29
+ 1. **Read the answer** and key points
30
+ 2. **Check file references** — verify they exist
31
+ 3. **Evaluate suggested actions** — do they make sense?
32
+ 4. **Apply your judgment** — you may disagree
33
+ 5. **Act on the suggestions** or ask follow-up questions
34
+
35
+ $ARGUMENTS
@@ -0,0 +1,35 @@
1
+ # Ask Gemini
2
+
3
+ Ask Google Gemini CLI for help as a peer engineer.
4
+
5
+ ## Arguments
6
+ - `$ARGUMENTS` - Your question or request
7
+
8
+ ## Gemini Strengths
9
+ - **Architecture**: Design patterns, structure
10
+ - **Scalability**: Load handling, bottlenecks
11
+ - **Maintainability**: Code clarity, tech debt
12
+
13
+ ## Tool Invocation
14
+
15
+ Call `ask_gemini` with:
16
+
17
+ ```json
18
+ {
19
+ "workingDir": "<current directory>",
20
+ "prompt": "<your question or request from $ARGUMENTS>",
21
+ "taskType": "<infer from request: plan|debug|explain|question|fix|explore|general>",
22
+ "relevantFiles": ["<files related to the question>"],
23
+ "context": "<any error messages or prior analysis>"
24
+ }
25
+ ```
26
+
27
+ ## After Receiving Response
28
+
29
+ 1. **Read the answer** and key points
30
+ 2. **Check file references** — verify they exist
31
+ 3. **Evaluate suggested actions** — do they make sense?
32
+ 4. **Apply your judgment** — you may disagree
33
+ 5. **Act on the suggestions** or ask follow-up questions
34
+
35
+ $ARGUMENTS
@@ -0,0 +1,38 @@
1
+ # Ask Multi
2
+
3
+ Ask both Codex and Gemini for help in parallel — get multiple perspectives.
4
+
5
+ ## Arguments
6
+ - `$ARGUMENTS` - Your question or request
7
+
8
+ ## When to Use
9
+
10
+ Use `/ask-multi` when you want perspectives from both models on the same question.
11
+
12
+ ## Tool Invocation
13
+
14
+ Call `ask_multi` with:
15
+
16
+ ```json
17
+ {
18
+ "workingDir": "<current directory>",
19
+ "prompt": "<your question or request from $ARGUMENTS>",
20
+ "taskType": "<infer from request: plan|debug|explain|question|fix|explore|general>",
21
+ "relevantFiles": ["<files related to the question>"],
22
+ "context": "<any error messages or prior analysis>"
23
+ }
24
+ ```
25
+
26
+ ## After Receiving Responses
27
+
28
+ You will receive separate responses from each model.
29
+
30
+ ### Synthesize
31
+
32
+ 1. **Find agreements** — both models say the same thing (higher confidence)
33
+ 2. **Identify conflicts** — they disagree (YOU decide who's right)
34
+ 3. **Note unique insights** — findings only one model provided
35
+ 4. **Verify file references** — check they exist
36
+ 5. **Make YOUR recommendation** — don't just relay, apply judgment
37
+
38
+ $ARGUMENTS
@@ -5,8 +5,8 @@
5
5
  * Makes it easy to add new models (Ollama, Azure, etc.) without
6
6
  * changing the core orchestration logic.
7
7
  */
8
- import { ReviewOutput } from '../schema.js';
9
- import { FocusArea, OutputType, ReasoningEffort } from '../types.js';
8
+ import { ReviewOutput, PeerOutput } from '../schema.js';
9
+ import { FocusArea, OutputType, ReasoningEffort, TaskType } from '../types.js';
10
10
  export interface ReviewerCapabilities {
11
11
  /** Display name for this reviewer */
12
12
  name: string;
@@ -43,6 +43,24 @@ export interface ReviewRequest {
43
43
  /** Expert role configuration (optional override) */
44
44
  expertRole?: ExpertRole;
45
45
  }
46
+ export interface PeerRequest {
47
+ /** Working directory containing the code */
48
+ workingDir: string;
49
+ /** The question or request from CC */
50
+ prompt: string;
51
+ /** Hint about the type of task */
52
+ taskType?: TaskType;
53
+ /** Files the peer should focus on */
54
+ relevantFiles?: string[];
55
+ /** Additional context (error messages, prior analysis) */
56
+ context?: string;
57
+ /** Areas to focus on */
58
+ focusAreas?: FocusArea[];
59
+ /** Custom instructions from the user */
60
+ customPrompt?: string;
61
+ /** Reasoning effort level (for models that support it) */
62
+ reasoningEffort?: ReasoningEffort;
63
+ }
46
64
  export interface ExpertRole {
47
65
  name: string;
48
66
  description: string;
@@ -74,6 +92,20 @@ export interface ReviewError {
74
92
  message: string;
75
93
  details?: Record<string, unknown>;
76
94
  }
95
+ export interface PeerSuccess {
96
+ success: true;
97
+ output: PeerOutput;
98
+ rawOutput?: string;
99
+ executionTimeMs: number;
100
+ }
101
+ export interface PeerFailure {
102
+ success: false;
103
+ error: ReviewError;
104
+ suggestion?: string;
105
+ rawOutput?: string;
106
+ executionTimeMs: number;
107
+ }
108
+ export type PeerResult = PeerSuccess | PeerFailure;
77
109
  /**
78
110
  * Base interface that all reviewer adapters must implement.
79
111
  * This allows easy addition of new AI CLIs without changing orchestration logic.
@@ -87,6 +119,8 @@ export interface ReviewerAdapter {
87
119
  isAvailable(): Promise<boolean>;
88
120
  /** Run a review and return structured output */
89
121
  runReview(request: ReviewRequest): Promise<ReviewResult>;
122
+ /** Run a general-purpose peer request and return structured output */
123
+ runPeerRequest(request: PeerRequest): Promise<PeerResult>;
90
124
  /**
91
125
  * Optional: Run peer review of another model's output
92
126
  * Future capability - not currently implemented by any adapter
@@ -4,13 +4,15 @@
4
4
  * Implements the ReviewerAdapter interface for OpenAI's Codex CLI.
5
5
  * Specializes in correctness, edge cases, and performance analysis.
6
6
  */
7
- import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult } from './base.js';
7
+ import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult, PeerRequest, PeerResult } from './base.js';
8
8
  export declare class CodexAdapter implements ReviewerAdapter {
9
9
  readonly id = "codex";
10
10
  getCapabilities(): ReviewerCapabilities;
11
11
  isAvailable(): Promise<boolean>;
12
12
  runReview(request: ReviewRequest): Promise<ReviewResult>;
13
13
  private runWithRetry;
14
+ runPeerRequest(request: PeerRequest): Promise<PeerResult>;
15
+ private runPeerWithRetry;
14
16
  private runCli;
15
17
  private categorizeError;
16
18
  private getSuggestion;
@@ -9,8 +9,8 @@ import { existsSync, writeFileSync, unlinkSync, mkdtempSync } from 'fs';
9
9
  import { tmpdir } from 'os';
10
10
  import { join } from 'path';
11
11
  import { registerAdapter, } from './base.js';
12
- import { parseReviewOutput, parseLegacyMarkdownOutput, getReviewOutputJsonSchema } from '../schema.js';
13
- import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
12
+ import { parseReviewOutput, parseLegacyMarkdownOutput, getReviewOutputJsonSchema, getPeerOutputJsonSchema, parsePeerOutput } from '../schema.js';
13
+ import { buildSimpleHandoff, buildHandoffPrompt, buildPeerPrompt, selectRole, } from '../handoff.js';
14
14
  // =============================================================================
15
15
  // CONFIGURATION
16
16
  // =============================================================================
@@ -89,7 +89,7 @@ export class CodexAdapter {
89
89
  (previousOutput ? `\nPrevious output (for reference):\n${previousOutput.slice(0, 500)}...` : '');
90
90
  }
91
91
  // Run the CLI
92
- const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high');
92
+ const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high', getReviewOutputJsonSchema);
93
93
  // Handle CLI errors
94
94
  if (result.exitCode !== 0) {
95
95
  const error = this.categorizeError(result.stderr);
@@ -214,14 +214,105 @@ export class CodexAdapter {
214
214
  };
215
215
  }
216
216
  }
217
- runCli(prompt, workingDir, reasoningEffort) {
217
+ async runPeerRequest(request) {
218
+ const startTime = Date.now();
219
+ if (!existsSync(request.workingDir)) {
220
+ return {
221
+ success: false,
222
+ error: {
223
+ type: 'cli_error',
224
+ message: `Working directory does not exist: ${request.workingDir}`,
225
+ },
226
+ suggestion: 'Check that the working directory path is correct',
227
+ executionTimeMs: Date.now() - startTime,
228
+ };
229
+ }
230
+ return this.runPeerWithRetry(request, 0, startTime);
231
+ }
232
+ async runPeerWithRetry(request, attempt, startTime, previousError, previousOutput) {
233
+ try {
234
+ let prompt = buildPeerPrompt({
235
+ workingDir: request.workingDir,
236
+ prompt: request.prompt,
237
+ taskType: request.taskType,
238
+ relevantFiles: request.relevantFiles,
239
+ context: request.context,
240
+ focusAreas: request.focusAreas,
241
+ customInstructions: request.customPrompt,
242
+ outputFormat: 'json',
243
+ });
244
+ if (attempt > 0) {
245
+ prompt += `\n\n---\n\n# RETRY ATTEMPT ${attempt + 1}\n\n` +
246
+ `Previous output had issues: ${previousError}\n` +
247
+ `Please fix these issues and provide valid JSON output.\n` +
248
+ (previousOutput ? `\nPrevious output (for reference):\n${previousOutput.slice(0, 500)}...` : '');
249
+ }
250
+ const result = await this.runCli(prompt, request.workingDir, request.reasoningEffort || 'high', getPeerOutputJsonSchema);
251
+ if (result.exitCode !== 0) {
252
+ const error = this.categorizeError(result.stderr);
253
+ return {
254
+ success: false,
255
+ error,
256
+ suggestion: this.getSuggestion(error),
257
+ rawOutput: result.stderr,
258
+ executionTimeMs: Date.now() - startTime,
259
+ };
260
+ }
261
+ if (result.truncated) {
262
+ return {
263
+ success: false,
264
+ error: { type: 'cli_error', message: 'Output exceeded maximum buffer size (1MB)' },
265
+ suggestion: 'Try a more focused request',
266
+ executionTimeMs: Date.now() - startTime,
267
+ };
268
+ }
269
+ const output = parsePeerOutput(result.stdout);
270
+ if (!output) {
271
+ if (attempt < MAX_RETRIES) {
272
+ return this.runPeerWithRetry(request, attempt + 1, startTime, 'Output did not match expected JSON schema', result.stdout);
273
+ }
274
+ return {
275
+ success: false,
276
+ error: { type: 'parse_error', message: 'Failed to parse peer output after retries',
277
+ details: { rawOutput: result.stdout.slice(0, 1000) } },
278
+ suggestion: 'The model may not be following the output format.',
279
+ rawOutput: result.stdout,
280
+ executionTimeMs: Date.now() - startTime,
281
+ };
282
+ }
283
+ return {
284
+ success: true,
285
+ output,
286
+ rawOutput: result.stdout,
287
+ executionTimeMs: Date.now() - startTime,
288
+ };
289
+ }
290
+ catch (error) {
291
+ const err = error;
292
+ if (err.code === 'ENOENT') {
293
+ return { success: false, error: { type: 'cli_not_found', message: 'Codex CLI not found' },
294
+ suggestion: 'Install with: npm install -g @openai/codex', executionTimeMs: Date.now() - startTime };
295
+ }
296
+ if (err.message === 'TIMEOUT') {
297
+ return { success: false, error: { type: 'timeout', message: 'No output for 2 minutes' },
298
+ suggestion: 'Try a simpler request', executionTimeMs: Date.now() - startTime };
299
+ }
300
+ if (err.message === 'MAX_TIMEOUT') {
301
+ return { success: false, error: { type: 'timeout', message: 'Task exceeded 60 minute maximum' },
302
+ suggestion: 'Try a smaller scope', executionTimeMs: Date.now() - startTime };
303
+ }
304
+ return { success: false, error: { type: 'cli_error', message: err.message },
305
+ executionTimeMs: Date.now() - startTime };
306
+ }
307
+ }
308
+ runCli(prompt, workingDir, reasoningEffort, schemaGetter) {
218
309
  return new Promise((resolve, reject) => {
219
310
  // Create temp schema file for structured output
220
311
  let schemaFile = null;
221
312
  try {
222
313
  const tempDir = mkdtempSync(join(tmpdir(), 'codex-schema-'));
223
314
  schemaFile = join(tempDir, 'schema.json');
224
- const schema = getReviewOutputJsonSchema();
315
+ const schema = schemaGetter();
225
316
  writeFileSync(schemaFile, JSON.stringify(schema, null, 2), 'utf-8');
226
317
  }
227
318
  catch (err) {
@@ -4,13 +4,15 @@
4
4
  * Implements the ReviewerAdapter interface for Google's Gemini CLI.
5
5
  * Specializes in architecture, design patterns, and large-context analysis.
6
6
  */
7
- import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult } from './base.js';
7
+ import { ReviewerAdapter, ReviewerCapabilities, ReviewRequest, ReviewResult, PeerRequest, PeerResult } from './base.js';
8
8
  export declare class GeminiAdapter implements ReviewerAdapter {
9
9
  readonly id = "gemini";
10
10
  getCapabilities(): ReviewerCapabilities;
11
11
  isAvailable(): Promise<boolean>;
12
12
  runReview(request: ReviewRequest): Promise<ReviewResult>;
13
13
  private runWithRetry;
14
+ runPeerRequest(request: PeerRequest): Promise<PeerResult>;
15
+ private runPeerWithRetry;
14
16
  private runCli;
15
17
  private categorizeError;
16
18
  private getSuggestion;
@@ -7,8 +7,8 @@
7
7
  import { spawn } from 'child_process';
8
8
  import { existsSync } from 'fs';
9
9
  import { registerAdapter, } from './base.js';
10
- import { parseReviewOutput, parseLegacyMarkdownOutput } from '../schema.js';
11
- import { buildSimpleHandoff, buildHandoffPrompt, selectRole, } from '../handoff.js';
10
+ import { parseReviewOutput, parseLegacyMarkdownOutput, parsePeerOutput } from '../schema.js';
11
+ import { buildSimpleHandoff, buildHandoffPrompt, buildPeerPrompt, selectRole, } from '../handoff.js';
12
12
  // =============================================================================
13
13
  // CONFIGURATION
14
14
  // =============================================================================
@@ -212,6 +212,92 @@ export class GeminiAdapter {
212
212
  };
213
213
  }
214
214
  }
215
+ async runPeerRequest(request) {
216
+ const startTime = Date.now();
217
+ if (!existsSync(request.workingDir)) {
218
+ return {
219
+ success: false,
220
+ error: { type: 'cli_error', message: `Working directory does not exist: ${request.workingDir}` },
221
+ suggestion: 'Check that the working directory path is correct',
222
+ executionTimeMs: Date.now() - startTime,
223
+ };
224
+ }
225
+ return this.runPeerWithRetry(request, 0, startTime);
226
+ }
227
+ async runPeerWithRetry(request, attempt, startTime, previousError, previousOutput) {
228
+ try {
229
+ let prompt = buildPeerPrompt({
230
+ workingDir: request.workingDir,
231
+ prompt: request.prompt,
232
+ taskType: request.taskType,
233
+ relevantFiles: request.relevantFiles,
234
+ context: request.context,
235
+ focusAreas: request.focusAreas,
236
+ customInstructions: request.customPrompt,
237
+ outputFormat: 'json',
238
+ });
239
+ if (attempt > 0) {
240
+ prompt += `\n\n---\n\n# RETRY ATTEMPT ${attempt + 1}\n\n` +
241
+ `Previous output had issues: ${previousError}\n` +
242
+ `Please fix these issues and provide valid JSON output.\n` +
243
+ (previousOutput ? `\nPrevious output (for reference):\n${previousOutput.slice(0, 500)}...` : '');
244
+ }
245
+ const result = await this.runCli(prompt, request.workingDir);
246
+ if (result.exitCode !== 0) {
247
+ const error = this.categorizeError(result.stderr);
248
+ return {
249
+ success: false, error,
250
+ suggestion: this.getSuggestion(error),
251
+ rawOutput: result.stderr,
252
+ executionTimeMs: Date.now() - startTime,
253
+ };
254
+ }
255
+ if (result.truncated) {
256
+ return {
257
+ success: false,
258
+ error: { type: 'cli_error', message: 'Output exceeded maximum buffer size (1MB)' },
259
+ suggestion: 'Try a more focused request',
260
+ executionTimeMs: Date.now() - startTime,
261
+ };
262
+ }
263
+ const output = parsePeerOutput(result.stdout);
264
+ if (!output) {
265
+ if (attempt < MAX_RETRIES) {
266
+ return this.runPeerWithRetry(request, attempt + 1, startTime, 'Output did not match expected JSON schema', result.stdout);
267
+ }
268
+ return {
269
+ success: false,
270
+ error: { type: 'parse_error', message: 'Failed to parse peer output after retries',
271
+ details: { rawOutput: result.stdout.slice(0, 1000) } },
272
+ suggestion: 'The model may not be following the output format.',
273
+ rawOutput: result.stdout,
274
+ executionTimeMs: Date.now() - startTime,
275
+ };
276
+ }
277
+ return {
278
+ success: true, output,
279
+ rawOutput: result.stdout,
280
+ executionTimeMs: Date.now() - startTime,
281
+ };
282
+ }
283
+ catch (error) {
284
+ const err = error;
285
+ if (err.code === 'ENOENT') {
286
+ return { success: false, error: { type: 'cli_not_found', message: 'Gemini CLI not found' },
287
+ suggestion: 'Install with: npm install -g @google/gemini-cli', executionTimeMs: Date.now() - startTime };
288
+ }
289
+ if (err.message === 'TIMEOUT') {
290
+ return { success: false, error: { type: 'timeout', message: 'No output for 10 minutes' },
291
+ suggestion: 'Try a simpler request', executionTimeMs: Date.now() - startTime };
292
+ }
293
+ if (err.message === 'MAX_TIMEOUT') {
294
+ return { success: false, error: { type: 'timeout', message: 'Task exceeded 60 minute maximum' },
295
+ suggestion: 'Try a smaller scope', executionTimeMs: Date.now() - startTime };
296
+ }
297
+ return { success: false, error: { type: 'cli_error', message: err.message },
298
+ executionTimeMs: Date.now() - startTime };
299
+ }
300
+ }
215
301
  runCli(prompt, workingDir) {
216
302
  return new Promise((resolve, reject) => {
217
303
  // Gemini CLI uses --yolo for auto-approval, prompt passed via stdin
package/dist/handoff.d.ts CHANGED
@@ -230,3 +230,18 @@ export declare function buildSimpleHandoff(workingDir: string, ccOutput: string,
230
230
  * CC should call this to add its specific concerns
231
231
  */
232
232
  export declare function enhanceHandoff(handoff: Handoff, uncertainties?: Uncertainty[], questions?: Question[], decisions?: Decision[]): Handoff;
233
+ export interface PeerPromptOptions {
234
+ workingDir: string;
235
+ prompt: string;
236
+ taskType?: string;
237
+ relevantFiles?: string[];
238
+ context?: string;
239
+ focusAreas?: FocusArea[];
240
+ customInstructions?: string;
241
+ outputFormat: 'json';
242
+ }
243
+ /**
244
+ * Build a prompt for general-purpose peer assistance (not review).
245
+ * The peer acts as a collaborative coworker, not a critic.
246
+ */
247
+ export declare function buildPeerPrompt(options: PeerPromptOptions): string;
package/dist/handoff.js CHANGED
@@ -476,3 +476,125 @@ export function enhanceHandoff(handoff, uncertainties, questions, decisions) {
476
476
  decisions: decisions || handoff.decisions,
477
477
  };
478
478
  }
479
+ /**
480
+ * Build a prompt for general-purpose peer assistance (not review).
481
+ * The peer acts as a collaborative coworker, not a critic.
482
+ */
483
+ export function buildPeerPrompt(options) {
484
+ const { workingDir, prompt, taskType, relevantFiles, context, focusAreas, customInstructions } = options;
485
+ // Select role based on focus areas (reuse existing role selection)
486
+ const role = selectRole(focusAreas);
487
+ const sections = [];
488
+ // SECTION 1: ROLE (adapted from review role)
489
+ sections.push(`# ROLE: ${role.name} — Peer Engineer
490
+
491
+ ${role.systemPrompt}
492
+
493
+ You are acting as a collaborative peer engineer, NOT a reviewer.
494
+ Your job is to help Claude Code (CC) with whatever it needs:
495
+ planning, debugging, explaining, fixing, exploring, or answering questions.
496
+ Be direct, specific, and actionable.`);
497
+ // SECTION 2: TASK
498
+ const taskLabel = taskType ? ` [${taskType.toUpperCase()}]` : '';
499
+ sections.push(`
500
+ ---
501
+
502
+ # YOUR TASK${taskLabel}
503
+
504
+ **Working Directory:** \`${workingDir}\`
505
+
506
+ **CC's Request:**
507
+ ${prompt}
508
+ ${context ? `\n**Additional Context:**\n${context}` : ''}`);
509
+ // SECTION 3: RELEVANT FILES
510
+ if (relevantFiles && relevantFiles.length > 0) {
511
+ sections.push(`
512
+ ---
513
+
514
+ # RELEVANT FILES
515
+
516
+ CC suggests focusing on these files:
517
+ ${relevantFiles.map(f => `- \`${f}\``).join('\n')}
518
+
519
+ Read these files to understand the context. Also explore related files if needed.`);
520
+ }
521
+ // SECTION 4: FOCUS AREAS
522
+ if (focusAreas && focusAreas.length > 0) {
523
+ sections.push(`
524
+ ---
525
+
526
+ # FOCUS AREAS
527
+
528
+ Prioritize these aspects: ${focusAreas.join(', ')}`);
529
+ }
530
+ // SECTION 5: CUSTOM INSTRUCTIONS
531
+ if (customInstructions) {
532
+ sections.push(`
533
+ ---
534
+
535
+ # ADDITIONAL INSTRUCTIONS
536
+
537
+ ${customInstructions}`);
538
+ }
539
+ // SECTION 6: HOW TO WORK
540
+ sections.push(`
541
+ ---
542
+
543
+ # HOW TO WORK
544
+
545
+ 1. Read the relevant files in the working directory
546
+ 2. Use \`git log --oneline -10\` and \`git diff\` if useful
547
+ 3. Think through the problem step by step
548
+ 4. Provide a clear, actionable answer
549
+ 5. Reference specific files and line numbers
550
+ 6. Suggest concrete next steps`);
551
+ // SECTION 7: OUTPUT FORMAT
552
+ sections.push(`
553
+ ---
554
+
555
+ # OUTPUT FORMAT
556
+
557
+ Respond with valid JSON:
558
+
559
+ \`\`\`json
560
+ {
561
+ "responder": "<your-name>",
562
+ "answer": "Your detailed response in markdown",
563
+ "confidence": 0.0-1.0,
564
+ "key_points": ["Point 1", "Point 2"],
565
+ "suggested_actions": [
566
+ {
567
+ "action": "What to do",
568
+ "priority": "high|medium|low",
569
+ "file": "path/to/file.ts",
570
+ "rationale": "Why"
571
+ }
572
+ ],
573
+ "file_references": [
574
+ {
575
+ "path": "path/to/file.ts",
576
+ "lines": "10-25",
577
+ "relevance": "Why this file matters"
578
+ }
579
+ ],
580
+ "alternatives": [
581
+ {
582
+ "topic": "The decision point",
583
+ "current_approach": "What exists now",
584
+ "alternative": "Different approach",
585
+ "tradeoffs": { "pros": ["..."], "cons": ["..."] },
586
+ "recommendation": "strongly_prefer|consider|situational|informational"
587
+ }
588
+ ],
589
+ "execution_notes": "Any notes about your process"
590
+ }
591
+ \`\`\`
592
+
593
+ **Rules:**
594
+ - Read files before making claims
595
+ - Reference specific file paths and line numbers
596
+ - Be concrete and actionable — no vague suggestions
597
+ - Confidence reflects how sure YOU are about your answer
598
+ - Include alternatives when there are meaningful tradeoffs`);
599
+ return sections.join('\n');
600
+ }
package/dist/index.js CHANGED
@@ -19,6 +19,8 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
19
19
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
20
20
  import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
21
21
  import { handleCodexReview, handleGeminiReview, handleMultiReview, ReviewInputSchema, TOOL_DEFINITIONS } from './tools/feedback.js';
22
+ import { handleAskCodex, handleAskGemini, handleAskMulti, PEER_TOOL_DEFINITIONS, } from './tools/peer.js';
23
+ import { PeerInputSchema } from './schema.js';
22
24
  import { logCliStatus } from './cli/check.js';
23
25
  import { installCommands } from './commands.js';
24
26
  // Handle --setup flag: install commands and exit
@@ -51,6 +53,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
51
53
  TOOL_DEFINITIONS.codex_review,
52
54
  TOOL_DEFINITIONS.gemini_review,
53
55
  TOOL_DEFINITIONS.multi_review,
56
+ PEER_TOOL_DEFINITIONS.ask_codex,
57
+ PEER_TOOL_DEFINITIONS.ask_gemini,
58
+ PEER_TOOL_DEFINITIONS.ask_multi,
54
59
  ],
55
60
  };
56
61
  });
@@ -71,6 +76,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
71
76
  const input = ReviewInputSchema.parse(args);
72
77
  return await handleMultiReview(input);
73
78
  }
79
+ case 'ask_codex': {
80
+ const input = PeerInputSchema.parse(args);
81
+ return await handleAskCodex(input);
82
+ }
83
+ case 'ask_gemini': {
84
+ const input = PeerInputSchema.parse(args);
85
+ return await handleAskGemini(input);
86
+ }
87
+ case 'ask_multi': {
88
+ const input = PeerInputSchema.parse(args);
89
+ return await handleAskMulti(input);
90
+ }
74
91
  default:
75
92
  return {
76
93
  content: [{