@j0hanz/code-review-analyst-mcp 1.7.1 ā 1.7.2
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/dist/lib/tool-factory.d.ts +1 -0
- package/dist/lib/tool-factory.js +34 -16
- package/dist/prompts/index.js +7 -7
- package/dist/tools/analyze-complexity.js +17 -4
- package/dist/tools/analyze-pr-impact.js +17 -3
- package/dist/tools/detect-api-breaking.js +16 -4
- package/dist/tools/generate-review-summary.js +16 -3
- package/dist/tools/generate-test-plan.js +15 -4
- package/dist/tools/inspect-code-quality.js +24 -5
- package/dist/tools/suggest-search-replace.js +16 -4
- package/package.json +1 -1
|
@@ -123,6 +123,7 @@ export declare class ToolExecutionRunner<TInput extends object, TResult extends
|
|
|
123
123
|
reportProgress: (payload: ProgressPayload) => Promise<void>;
|
|
124
124
|
statusReporter: TaskStatusReporter;
|
|
125
125
|
}, signal?: AbortSignal | undefined);
|
|
126
|
+
private handleInternalLog;
|
|
126
127
|
setResponseSchemaOverride(responseSchema: Record<string, unknown>): void;
|
|
127
128
|
setDiffSlotSnapshot(diffSlotSnapshot: DiffSlot | undefined): void;
|
|
128
129
|
private updateStatusMessage;
|
package/dist/lib/tool-factory.js
CHANGED
|
@@ -241,7 +241,7 @@ function normalizeProgressContext(context) {
|
|
|
241
241
|
return `${compact.slice(0, 77)}...`;
|
|
242
242
|
}
|
|
243
243
|
function formatProgressStep(toolName, context, metadata) {
|
|
244
|
-
return `${toolName}: ${context}
|
|
244
|
+
return `${toolName}: ${context} ⢠${metadata}`;
|
|
245
245
|
}
|
|
246
246
|
function formatProgressCompletion(toolName, context, outcome) {
|
|
247
247
|
return `š ${toolName}: ${context} ⢠${outcome}`;
|
|
@@ -276,7 +276,7 @@ async function reportProgressCompletionUpdate(reportProgress, toolName, context,
|
|
|
276
276
|
}
|
|
277
277
|
async function reportSchemaRetryProgressBestEffort(reportProgress, toolName, context, retryCount, maxRetries) {
|
|
278
278
|
try {
|
|
279
|
-
await reportProgressStepUpdate(reportProgress, toolName, context, STEP_VALIDATING_RESPONSE, `
|
|
279
|
+
await reportProgressStepUpdate(reportProgress, toolName, context, STEP_VALIDATING_RESPONSE, `Schema repair in progress (attempt ${retryCount}/${maxRetries})...`);
|
|
280
280
|
}
|
|
281
281
|
catch {
|
|
282
282
|
// Progress updates are best-effort and must not interrupt retries.
|
|
@@ -370,10 +370,28 @@ export class ToolExecutionRunner {
|
|
|
370
370
|
this.config = config;
|
|
371
371
|
this.signal = signal;
|
|
372
372
|
this.responseSchema = getCachedGeminiResponseSchema(config);
|
|
373
|
-
this.onLog = dependencies.onLog;
|
|
374
373
|
this.reportProgress = dependencies.reportProgress;
|
|
375
374
|
this.statusReporter = dependencies.statusReporter;
|
|
376
375
|
this.progressContext = DEFAULT_PROGRESS_CONTEXT;
|
|
376
|
+
this.onLog = async (level, data) => {
|
|
377
|
+
try {
|
|
378
|
+
await dependencies.onLog(level, data);
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
// Ignore logging failures
|
|
382
|
+
}
|
|
383
|
+
await this.handleInternalLog(data);
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
async handleInternalLog(data) {
|
|
387
|
+
const record = asObjectRecord(data);
|
|
388
|
+
if (record.event === 'gemini_retry') {
|
|
389
|
+
const details = asObjectRecord(record.details);
|
|
390
|
+
const { attempt } = details;
|
|
391
|
+
const msg = `Network error. Retrying (attempt ${String(attempt)})...`;
|
|
392
|
+
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_CALLING_MODEL, msg);
|
|
393
|
+
await this.updateStatusMessage(msg);
|
|
394
|
+
}
|
|
377
395
|
}
|
|
378
396
|
setResponseSchemaOverride(responseSchema) {
|
|
379
397
|
this.responseSchema = responseSchema;
|
|
@@ -440,8 +458,8 @@ export class ToolExecutionRunner {
|
|
|
440
458
|
try {
|
|
441
459
|
const raw = await generateStructuredJson(createGenerationRequest(this.config, { systemInstruction, prompt: retryPrompt }, this.responseSchema, this.onLog, this.signal));
|
|
442
460
|
if (attempt === 0) {
|
|
443
|
-
await this.updateStatusMessage('
|
|
444
|
-
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_VALIDATING_RESPONSE, '
|
|
461
|
+
await this.updateStatusMessage('Verifying output structure...');
|
|
462
|
+
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_VALIDATING_RESPONSE, 'Verifying output structure...');
|
|
445
463
|
}
|
|
446
464
|
parsed = this.config.resultSchema.parse(raw);
|
|
447
465
|
break;
|
|
@@ -476,23 +494,23 @@ export class ToolExecutionRunner {
|
|
|
476
494
|
const ctx = {
|
|
477
495
|
diffSlot: this.hasSnapshot ? this.diffSlotSnapshot : getDiff(),
|
|
478
496
|
};
|
|
479
|
-
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_STARTING, '
|
|
480
|
-
await this.updateStatusMessage('
|
|
481
|
-
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_VALIDATING, '
|
|
482
|
-
await this.updateStatusMessage('
|
|
497
|
+
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_STARTING, 'Initializing...');
|
|
498
|
+
await this.updateStatusMessage('Initializing...');
|
|
499
|
+
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_VALIDATING, 'Validating request parameters...');
|
|
500
|
+
await this.updateStatusMessage('Validating request parameters...');
|
|
483
501
|
const validationError = await this.executeValidation(inputRecord, ctx);
|
|
484
502
|
if (validationError) {
|
|
485
503
|
return validationError;
|
|
486
504
|
}
|
|
487
|
-
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_BUILDING_PROMPT, '
|
|
488
|
-
await this.updateStatusMessage('
|
|
505
|
+
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_BUILDING_PROMPT, 'Constructing analysis context...');
|
|
506
|
+
await this.updateStatusMessage('Constructing analysis context...');
|
|
489
507
|
const promptParts = this.config.buildPrompt(inputRecord, ctx);
|
|
490
508
|
const { prompt, systemInstruction } = promptParts;
|
|
491
|
-
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_CALLING_MODEL, '
|
|
492
|
-
await this.updateStatusMessage('
|
|
509
|
+
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_CALLING_MODEL, 'Querying Gemini model...');
|
|
510
|
+
await this.updateStatusMessage('Querying Gemini model...');
|
|
493
511
|
const parsed = await this.executeModelCall(systemInstruction, prompt);
|
|
494
|
-
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_FINALIZING, '
|
|
495
|
-
await this.updateStatusMessage('
|
|
512
|
+
await reportProgressStepUpdate(this.reportProgress, this.config.name, this.progressContext, STEP_FINALIZING, 'Processing results...');
|
|
513
|
+
await this.updateStatusMessage('Processing results...');
|
|
496
514
|
const finalResult = (this.config.transformResult
|
|
497
515
|
? this.config.transformResult(inputRecord, parsed, ctx)
|
|
498
516
|
: parsed);
|
|
@@ -503,7 +521,7 @@ export class ToolExecutionRunner {
|
|
|
503
521
|
const ageMs = Date.now() - new Date(ctx.diffSlot.generatedAt).getTime();
|
|
504
522
|
if (ageMs > diffStaleWarningMs.get()) {
|
|
505
523
|
const ageMinutes = Math.round(ageMs / 60_000);
|
|
506
|
-
const warning = `\n\
|
|
524
|
+
const warning = `\n\nWarning: The analyzed diff is over ${ageMinutes} minutes old. If you have made recent changes, please run generate_diff again.`;
|
|
507
525
|
textContent = textContent ? textContent + warning : warning;
|
|
508
526
|
}
|
|
509
527
|
}
|
package/dist/prompts/index.js
CHANGED
|
@@ -17,13 +17,13 @@ const TOOLS = getToolContractNames();
|
|
|
17
17
|
const TOOL_DESCRIPTION_TEXT = 'Select tool for review guide.';
|
|
18
18
|
const FOCUS_DESCRIPTION_TEXT = 'Select focus area.';
|
|
19
19
|
const FOCUS_AREA_GUIDES = {
|
|
20
|
-
security: 'Focus: Injection
|
|
21
|
-
correctness: 'Focus: Logic
|
|
22
|
-
performance: 'Focus:
|
|
23
|
-
regressions: 'Focus: Behavior
|
|
24
|
-
tests: 'Focus:
|
|
25
|
-
maintainability: 'Focus:
|
|
26
|
-
concurrency: 'Focus:
|
|
20
|
+
security: 'Focus: Injection, Auth, Crypto, OWASP.',
|
|
21
|
+
correctness: 'Focus: Logic, Edge Cases, Types.',
|
|
22
|
+
performance: 'Focus: Complexity, Memory, Latency.',
|
|
23
|
+
regressions: 'Focus: Behavior Changes, Breaking APIs.',
|
|
24
|
+
tests: 'Focus: Coverage, Error Paths.',
|
|
25
|
+
maintainability: 'Focus: Complexity, Readability, Patterns.',
|
|
26
|
+
concurrency: 'Focus: Races, Deadlocks, Atomicity.',
|
|
27
27
|
};
|
|
28
28
|
function isFocusArea(value) {
|
|
29
29
|
return INSPECTION_FOCUS_AREAS.includes(value);
|
|
@@ -3,11 +3,23 @@ import { registerStructuredToolTask } from '../lib/tool-factory.js';
|
|
|
3
3
|
import { AnalyzeComplexityInputSchema } from '../schemas/inputs.js';
|
|
4
4
|
import { AnalyzeComplexityResultSchema } from '../schemas/outputs.js';
|
|
5
5
|
const SYSTEM_INSTRUCTION = `
|
|
6
|
+
<role>
|
|
6
7
|
Algorithm Complexity Analyst.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
You are an expert in Big-O analysis and performance optimization.
|
|
9
|
+
</role>
|
|
10
|
+
|
|
11
|
+
<task>
|
|
12
|
+
Analyze the time and space complexity of the code changes:
|
|
13
|
+
- Compare new complexity vs. original implementation.
|
|
14
|
+
- Detect performance degradation (regression).
|
|
15
|
+
- Identify bottlenecks (nested loops, recursion, allocations).
|
|
16
|
+
</task>
|
|
17
|
+
|
|
18
|
+
<constraints>
|
|
19
|
+
- Focus on the changed code paths.
|
|
20
|
+
- Flag degradation only if complexity class worsens (e.g., O(n) -> O(n^2)).
|
|
21
|
+
- Return valid JSON matching the schema.
|
|
22
|
+
</constraints>
|
|
11
23
|
`;
|
|
12
24
|
const TOOL_CONTRACT = requireToolContract('analyze_time_space_complexity');
|
|
13
25
|
export function registerAnalyzeComplexityTool(server) {
|
|
@@ -23,6 +35,7 @@ export function registerAnalyzeComplexityTool(server) {
|
|
|
23
35
|
maxOutputTokens: TOOL_CONTRACT.maxOutputTokens,
|
|
24
36
|
...buildStructuredToolRuntimeOptions(TOOL_CONTRACT),
|
|
25
37
|
requiresDiff: true,
|
|
38
|
+
progressContext: (input) => input.language ?? 'auto-detect',
|
|
26
39
|
formatOutcome: (result) => result.isDegradation
|
|
27
40
|
? 'Performance degradation detected'
|
|
28
41
|
: 'No degradation',
|
|
@@ -4,10 +4,24 @@ import { registerStructuredToolTask } from '../lib/tool-factory.js';
|
|
|
4
4
|
import { AnalyzePrImpactInputSchema } from '../schemas/inputs.js';
|
|
5
5
|
import { PrImpactResultSchema } from '../schemas/outputs.js';
|
|
6
6
|
const SYSTEM_INSTRUCTION = `
|
|
7
|
+
<role>
|
|
7
8
|
Technical Change Analyst.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
You are a strict, objective auditor of code changes.
|
|
10
|
+
</role>
|
|
11
|
+
|
|
12
|
+
<task>
|
|
13
|
+
Analyze the unified diff to assess:
|
|
14
|
+
- Severity (low/medium/high/critical)
|
|
15
|
+
- Risk categories (security, stability, etc.)
|
|
16
|
+
- Breaking changes (API, contract, schema)
|
|
17
|
+
- Rollback complexity
|
|
18
|
+
</task>
|
|
19
|
+
|
|
20
|
+
<constraints>
|
|
21
|
+
- Base analysis ONLY on the provided diff. No external inference.
|
|
22
|
+
- Ignore formatting/style changes unless they affect logic.
|
|
23
|
+
- Return valid JSON matching the schema.
|
|
24
|
+
</constraints>
|
|
11
25
|
`;
|
|
12
26
|
const TOOL_CONTRACT = requireToolContract('analyze_pr_impact');
|
|
13
27
|
function formatLanguageSegment(language) {
|
|
@@ -3,11 +3,22 @@ import { registerStructuredToolTask } from '../lib/tool-factory.js';
|
|
|
3
3
|
import { DetectApiBreakingInputSchema } from '../schemas/inputs.js';
|
|
4
4
|
import { DetectApiBreakingResultSchema } from '../schemas/outputs.js';
|
|
5
5
|
const SYSTEM_INSTRUCTION = `
|
|
6
|
+
<role>
|
|
6
7
|
API Compatibility Analyst.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
You are a strict guardian of public interfaces and contracts.
|
|
9
|
+
</role>
|
|
10
|
+
|
|
11
|
+
<task>
|
|
12
|
+
Detect breaking changes in public APIs, interfaces, or schemas:
|
|
13
|
+
- Identify changes that require consumer code modification.
|
|
14
|
+
- classify the nature, impact, and mitigation for each break.
|
|
15
|
+
</task>
|
|
16
|
+
|
|
17
|
+
<constraints>
|
|
18
|
+
- Definition: Breaking change = backwards-incompatible modification.
|
|
19
|
+
- Ignore internal/private APIs unless exported.
|
|
20
|
+
- Return valid JSON matching the schema.
|
|
21
|
+
</constraints>
|
|
11
22
|
`;
|
|
12
23
|
const TOOL_CONTRACT = requireToolContract('detect_api_breaking_changes');
|
|
13
24
|
export function registerDetectApiBreakingTool(server) {
|
|
@@ -23,6 +34,7 @@ export function registerDetectApiBreakingTool(server) {
|
|
|
23
34
|
maxOutputTokens: TOOL_CONTRACT.maxOutputTokens,
|
|
24
35
|
...buildStructuredToolRuntimeOptions(TOOL_CONTRACT),
|
|
25
36
|
requiresDiff: true,
|
|
37
|
+
progressContext: (input) => input.language ?? 'auto-detect',
|
|
26
38
|
formatOutcome: (result) => `${result.breakingChanges.length} breaking change(s) found`,
|
|
27
39
|
formatOutput: (result) => result.hasBreakingChanges
|
|
28
40
|
? `${result.breakingChanges.length} breaking changes found.`
|
|
@@ -7,10 +7,23 @@ const ReviewSummaryModelSchema = ReviewSummaryResultSchema.omit({
|
|
|
7
7
|
});
|
|
8
8
|
const TOOL_CONTRACT = requireToolContract('generate_review_summary');
|
|
9
9
|
const SYSTEM_INSTRUCTION = `
|
|
10
|
+
<role>
|
|
10
11
|
Senior Code Reviewer.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
You are a pragmatic engineer focused on stability and maintainability.
|
|
13
|
+
</role>
|
|
14
|
+
|
|
15
|
+
<task>
|
|
16
|
+
Summarize the pull request based on the diff:
|
|
17
|
+
- Assess overall risk (low/medium/high).
|
|
18
|
+
- Highlight key logic/behavior changes.
|
|
19
|
+
- Recommend action: merge, squash, or block.
|
|
20
|
+
</task>
|
|
21
|
+
|
|
22
|
+
<constraints>
|
|
23
|
+
- Focus on logic and behavior; ignore style, formatting, and typos.
|
|
24
|
+
- Be concise and actionable.
|
|
25
|
+
- Return valid JSON matching the schema.
|
|
26
|
+
</constraints>
|
|
14
27
|
`;
|
|
15
28
|
function formatLanguageSegment(language) {
|
|
16
29
|
return language ? `\nLanguage: ${language}` : '';
|
|
@@ -4,11 +4,22 @@ import { registerStructuredToolTask } from '../lib/tool-factory.js';
|
|
|
4
4
|
import { GenerateTestPlanInputSchema } from '../schemas/inputs.js';
|
|
5
5
|
import { TestPlanResultSchema } from '../schemas/outputs.js';
|
|
6
6
|
const SYSTEM_INSTRUCTION = `
|
|
7
|
+
<role>
|
|
7
8
|
QA Automation Architect.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
You are an expert in test strategy and coverage analysis.
|
|
10
|
+
</role>
|
|
11
|
+
|
|
12
|
+
<task>
|
|
13
|
+
Generate a prioritized test plan for the provided diff:
|
|
14
|
+
- Focus on negative cases, edge cases, and boundary conditions.
|
|
15
|
+
- Target branch coverage and integration points.
|
|
16
|
+
</task>
|
|
17
|
+
|
|
18
|
+
<constraints>
|
|
19
|
+
- Focus on observable behavior changes.
|
|
20
|
+
- Ignore internal refactors that do not affect contract.
|
|
21
|
+
- Return valid JSON matching the schema.
|
|
22
|
+
</constraints>
|
|
12
23
|
`;
|
|
13
24
|
const TOOL_CONTRACT = requireToolContract('generate_test_plan');
|
|
14
25
|
function formatOptionalLine(label, value) {
|
|
@@ -5,11 +5,24 @@ import { InspectCodeQualityInputSchema } from '../schemas/inputs.js';
|
|
|
5
5
|
import { CodeQualityOutputSchema, CodeQualityResultSchema, } from '../schemas/outputs.js';
|
|
6
6
|
const DEFAULT_FOCUS_AREAS = 'General';
|
|
7
7
|
const SYSTEM_INSTRUCTION = `
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
<role>
|
|
9
|
+
Principal Engineer.
|
|
10
|
+
You are an expert in code quality, security, and performance.
|
|
11
|
+
</role>
|
|
12
|
+
|
|
13
|
+
<task>
|
|
14
|
+
Perform a deep code review of the provided diff:
|
|
15
|
+
- Identify bugs, security vulnerabilities, and performance issues.
|
|
16
|
+
- Assess maintainability and clarity.
|
|
17
|
+
- Provide contextual insights if full file content is available.
|
|
18
|
+
</task>
|
|
19
|
+
|
|
20
|
+
<constraints>
|
|
21
|
+
- Ignore style/formatting/whitespace changes.
|
|
22
|
+
- Prioritize correctness and failure modes over opinionated patterns.
|
|
23
|
+
- Findings must be actionable and specific to the diff.
|
|
24
|
+
- Return valid JSON matching the schema.
|
|
25
|
+
</constraints>
|
|
13
26
|
`;
|
|
14
27
|
const TOOL_CONTRACT = requireToolContract('inspect_code_quality');
|
|
15
28
|
function formatOptionalLine(label, value) {
|
|
@@ -32,6 +45,12 @@ export function registerInspectCodeQualityTool(server) {
|
|
|
32
45
|
maxOutputTokens: TOOL_CONTRACT.maxOutputTokens,
|
|
33
46
|
...buildStructuredToolRuntimeOptions(TOOL_CONTRACT),
|
|
34
47
|
requiresDiff: true,
|
|
48
|
+
progressContext: (input) => {
|
|
49
|
+
const focus = input.focusAreas
|
|
50
|
+
? `[${input.focusAreas.join(',')}]`
|
|
51
|
+
: 'general';
|
|
52
|
+
return `${input.repository} ${focus}`;
|
|
53
|
+
},
|
|
35
54
|
formatOutcome: (result) => `${result.findings.length} findings, risk: ${result.overallRisk}`,
|
|
36
55
|
formatOutput: (result) => {
|
|
37
56
|
const count = result.findings.length;
|
|
@@ -4,11 +4,23 @@ import { registerStructuredToolTask } from '../lib/tool-factory.js';
|
|
|
4
4
|
import { SuggestSearchReplaceInputSchema } from '../schemas/inputs.js';
|
|
5
5
|
import { SearchReplaceResultSchema } from '../schemas/outputs.js';
|
|
6
6
|
const SYSTEM_INSTRUCTION = `
|
|
7
|
+
<role>
|
|
7
8
|
Code Remediation Expert.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
You are a precise tool for generating automated code fixes.
|
|
10
|
+
</role>
|
|
11
|
+
|
|
12
|
+
<task>
|
|
13
|
+
Generate minimal search-and-replace blocks to fix the described issue:
|
|
14
|
+
- 'search' block must match the existing code EXACTLY (including whitespace).
|
|
15
|
+
- 'replace' block must be syntactically correct and follow local style.
|
|
16
|
+
</task>
|
|
17
|
+
|
|
18
|
+
<constraints>
|
|
19
|
+
- Verbatim match required: preserve all indentation and newlines in 'search'.
|
|
20
|
+
- Do not include surrounding code unless necessary for uniqueness.
|
|
21
|
+
- If exact match is ambiguous, return an empty block list.
|
|
22
|
+
- Return valid JSON matching the schema.
|
|
23
|
+
</constraints>
|
|
12
24
|
`;
|
|
13
25
|
const TOOL_CONTRACT = requireToolContract('suggest_search_replace');
|
|
14
26
|
function formatPatchCount(count) {
|