@maxanatsko/gemini-mcp-tool 2.0.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/LICENSE +25 -0
- package/README.md +230 -0
- package/dist/constants.d.ts +153 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +150 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +188 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/ask-gemini.tool.d.ts +3 -0
- package/dist/tools/ask-gemini.tool.d.ts.map +1 -0
- package/dist/tools/ask-gemini.tool.js +74 -0
- package/dist/tools/ask-gemini.tool.js.map +1 -0
- package/dist/tools/brainstorm.tool.d.ts +3 -0
- package/dist/tools/brainstorm.tool.d.ts.map +1 -0
- package/dist/tools/brainstorm.tool.js +202 -0
- package/dist/tools/brainstorm.tool.js.map +1 -0
- package/dist/tools/fetch-chunk.tool.d.ts +3 -0
- package/dist/tools/fetch-chunk.tool.d.ts.map +1 -0
- package/dist/tools/fetch-chunk.tool.js +62 -0
- package/dist/tools/fetch-chunk.tool.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +11 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +25 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +80 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/review-code.tool.d.ts +3 -0
- package/dist/tools/review-code.tool.d.ts.map +1 -0
- package/dist/tools/review-code.tool.js +186 -0
- package/dist/tools/review-code.tool.js.map +1 -0
- package/dist/tools/simple-tools.d.ts +4 -0
- package/dist/tools/simple-tools.d.ts.map +1 -0
- package/dist/tools/simple-tools.js +32 -0
- package/dist/tools/simple-tools.js.map +1 -0
- package/dist/tools/test-tool.example.d.ts +13 -0
- package/dist/tools/test-tool.example.d.ts.map +1 -0
- package/dist/tools/test-tool.example.js +32 -0
- package/dist/tools/test-tool.example.js.map +1 -0
- package/dist/tools/timeout-test.tool.d.ts +3 -0
- package/dist/tools/timeout-test.tool.d.ts.map +1 -0
- package/dist/tools/timeout-test.tool.js +32 -0
- package/dist/tools/timeout-test.tool.js.map +1 -0
- package/dist/utils/askGeminiSessionManager.d.ts +57 -0
- package/dist/utils/askGeminiSessionManager.d.ts.map +1 -0
- package/dist/utils/askGeminiSessionManager.js +110 -0
- package/dist/utils/askGeminiSessionManager.js.map +1 -0
- package/dist/utils/brainstormSessionManager.d.ts +67 -0
- package/dist/utils/brainstormSessionManager.d.ts.map +1 -0
- package/dist/utils/brainstormSessionManager.js +165 -0
- package/dist/utils/brainstormSessionManager.js.map +1 -0
- package/dist/utils/changeModeChunker.d.ts +11 -0
- package/dist/utils/changeModeChunker.d.ts.map +1 -0
- package/dist/utils/changeModeChunker.js +89 -0
- package/dist/utils/changeModeChunker.js.map +1 -0
- package/dist/utils/changeModeParser.d.ts +15 -0
- package/dist/utils/changeModeParser.d.ts.map +1 -0
- package/dist/utils/changeModeParser.js +67 -0
- package/dist/utils/changeModeParser.js.map +1 -0
- package/dist/utils/changeModeTranslator.d.ts +8 -0
- package/dist/utils/changeModeTranslator.d.ts.map +1 -0
- package/dist/utils/changeModeTranslator.js +70 -0
- package/dist/utils/changeModeTranslator.js.map +1 -0
- package/dist/utils/chunkCache.d.ts +22 -0
- package/dist/utils/chunkCache.d.ts.map +1 -0
- package/dist/utils/chunkCache.js +161 -0
- package/dist/utils/chunkCache.js.map +1 -0
- package/dist/utils/commandExecutor.d.ts +2 -0
- package/dist/utils/commandExecutor.d.ts.map +1 -0
- package/dist/utils/commandExecutor.js +74 -0
- package/dist/utils/commandExecutor.js.map +1 -0
- package/dist/utils/geminiExecutor.d.ts +3 -0
- package/dist/utils/geminiExecutor.d.ts.map +1 -0
- package/dist/utils/geminiExecutor.js +170 -0
- package/dist/utils/geminiExecutor.js.map +1 -0
- package/dist/utils/gitStateDetector.d.ts +31 -0
- package/dist/utils/gitStateDetector.d.ts.map +1 -0
- package/dist/utils/gitStateDetector.js +67 -0
- package/dist/utils/gitStateDetector.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +42 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/reviewFormatter.d.ts +35 -0
- package/dist/utils/reviewFormatter.d.ts.map +1 -0
- package/dist/utils/reviewFormatter.js +198 -0
- package/dist/utils/reviewFormatter.js.map +1 -0
- package/dist/utils/reviewPromptBuilder.d.ts +35 -0
- package/dist/utils/reviewPromptBuilder.d.ts.map +1 -0
- package/dist/utils/reviewPromptBuilder.js +146 -0
- package/dist/utils/reviewPromptBuilder.js.map +1 -0
- package/dist/utils/reviewResponseParser.d.ts +20 -0
- package/dist/utils/reviewResponseParser.d.ts.map +1 -0
- package/dist/utils/reviewResponseParser.js +149 -0
- package/dist/utils/reviewResponseParser.js.map +1 -0
- package/dist/utils/reviewSessionCache.d.ts +81 -0
- package/dist/utils/reviewSessionCache.d.ts.map +1 -0
- package/dist/utils/reviewSessionCache.js +220 -0
- package/dist/utils/reviewSessionCache.js.map +1 -0
- package/dist/utils/reviewSessionManager.d.ts +52 -0
- package/dist/utils/reviewSessionManager.d.ts.map +1 -0
- package/dist/utils/reviewSessionManager.js +65 -0
- package/dist/utils/reviewSessionManager.js.map +1 -0
- package/dist/utils/sessionManager.d.ts +94 -0
- package/dist/utils/sessionManager.d.ts.map +1 -0
- package/dist/utils/sessionManager.js +374 -0
- package/dist/utils/sessionManager.js.map +1 -0
- package/dist/utils/sessionSchemas.d.ts +126 -0
- package/dist/utils/sessionSchemas.d.ts.map +1 -0
- package/dist/utils/sessionSchemas.js +2 -0
- package/dist/utils/sessionSchemas.js.map +1 -0
- package/dist/utils/timeoutManager.d.ts +2 -0
- package/dist/utils/timeoutManager.d.ts.map +1 -0
- package/dist/utils/timeoutManager.js +2 -0
- package/dist/utils/timeoutManager.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CodeReviewSession, ReviewRound, ReviewComment } from './reviewSessionCache.js';
|
|
2
|
+
export interface ReviewFormatterConfig {
|
|
3
|
+
session: CodeReviewSession;
|
|
4
|
+
currentRound: ReviewRound;
|
|
5
|
+
newComments: ReviewComment[];
|
|
6
|
+
showHistory: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Formats the review response for Claude
|
|
10
|
+
* @param config Formatter configuration
|
|
11
|
+
* @returns Formatted markdown string
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatReviewResponse(config: ReviewFormatterConfig): string;
|
|
14
|
+
/**
|
|
15
|
+
* Groups comments by file pattern
|
|
16
|
+
* @param comments Array of comments
|
|
17
|
+
* @returns Map of file patterns to comments
|
|
18
|
+
*/
|
|
19
|
+
export declare function groupCommentsByFile(comments: ReviewComment[]): Map<string, ReviewComment[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Formats a message when session expires or is not found
|
|
22
|
+
* @param sessionId The session ID that was requested
|
|
23
|
+
* @param currentGitBranch Current git branch
|
|
24
|
+
* @param currentGitCommit Current git commit hash
|
|
25
|
+
* @returns Formatted error message
|
|
26
|
+
*/
|
|
27
|
+
export declare function formatSessionNotFound(sessionId: string, currentGitBranch: string, currentGitCommit: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Formats a warning message for git state changes
|
|
30
|
+
* @param reason The reason for the warning
|
|
31
|
+
* @param continuing Whether the session is continuing anyway
|
|
32
|
+
* @returns Formatted warning string
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatGitStateWarning(reason: string, continuing: boolean): string;
|
|
35
|
+
//# sourceMappingURL=reviewFormatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewFormatter.d.ts","sourceRoot":"","sources":["../../src/utils/reviewFormatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,iBAAiB,CAAC;IAC3B,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CA+B1E;AAyDD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAgB3F;AA8ED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAeR;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,MAAM,CAUjF"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { REVIEW } from '../constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Formats the review response for Claude
|
|
4
|
+
* @param config Formatter configuration
|
|
5
|
+
* @returns Formatted markdown string
|
|
6
|
+
*/
|
|
7
|
+
export function formatReviewResponse(config) {
|
|
8
|
+
const { session, currentRound, newComments, showHistory } = config;
|
|
9
|
+
let output = `# Code Review - Round ${currentRound.roundNumber}\n\n`;
|
|
10
|
+
// Session info
|
|
11
|
+
output += `**Session:** \`${session.sessionId}\`\n`;
|
|
12
|
+
output += `**Branch:** ${session.currentGitState.branch} @ ${session.currentGitState.commitHash.slice(0, 8)}\n`;
|
|
13
|
+
output += `**Files Reviewed:** ${currentRound.filesReviewed.length}\n\n`;
|
|
14
|
+
// Summary
|
|
15
|
+
output += formatSummary(newComments);
|
|
16
|
+
// Comments by file
|
|
17
|
+
if (newComments.length > 0) {
|
|
18
|
+
output += `## Issues Found\n\n`;
|
|
19
|
+
output += formatCommentsByFile(newComments);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
output += `## Result\n\n`;
|
|
23
|
+
output += `✅ No new issues found in this round.\n\n`;
|
|
24
|
+
}
|
|
25
|
+
// Session continuation instructions
|
|
26
|
+
output += formatContinuationInstructions(session);
|
|
27
|
+
// History summary if enabled
|
|
28
|
+
if (showHistory && session.rounds.length > 1) {
|
|
29
|
+
output += formatHistorySummary(session);
|
|
30
|
+
}
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Formats the summary section with severity counts
|
|
35
|
+
* @param comments Array of comments to summarize
|
|
36
|
+
* @returns Formatted summary string
|
|
37
|
+
*/
|
|
38
|
+
function formatSummary(comments) {
|
|
39
|
+
const criticalCount = comments.filter(c => c.severity === 'critical').length;
|
|
40
|
+
const importantCount = comments.filter(c => c.severity === 'important').length;
|
|
41
|
+
const suggestionCount = comments.filter(c => c.severity === 'suggestion').length;
|
|
42
|
+
const questionCount = comments.filter(c => c.severity === 'question').length;
|
|
43
|
+
let summary = `## Summary\n`;
|
|
44
|
+
summary += `- 🔴 Critical: ${criticalCount}\n`;
|
|
45
|
+
summary += `- 🟠 Important: ${importantCount}\n`;
|
|
46
|
+
summary += `- 🟡 Suggestions: ${suggestionCount}\n`;
|
|
47
|
+
summary += `- 💬 Questions: ${questionCount}\n`;
|
|
48
|
+
summary += `- **Total:** ${comments.length} issues\n\n`;
|
|
49
|
+
return summary;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Formats comments grouped by file
|
|
53
|
+
* @param comments Array of comments to format
|
|
54
|
+
* @returns Formatted comments string
|
|
55
|
+
*/
|
|
56
|
+
function formatCommentsByFile(comments) {
|
|
57
|
+
const commentsByFile = groupCommentsByFile(comments);
|
|
58
|
+
let output = '';
|
|
59
|
+
for (const [file, fileComments] of commentsByFile) {
|
|
60
|
+
output += `### ${file}\n\n`;
|
|
61
|
+
fileComments.forEach((comment, idx) => {
|
|
62
|
+
const severityEmoji = REVIEW.SEVERITY_EMOJI[comment.severity] || '📌';
|
|
63
|
+
output += `#### ${severityEmoji} Issue ${idx + 1}\n`;
|
|
64
|
+
output += `**Comment ID:** \`${comment.id}\`\n`;
|
|
65
|
+
if (comment.lineRange) {
|
|
66
|
+
if (comment.lineRange.start === comment.lineRange.end) {
|
|
67
|
+
output += `**Line:** ${comment.lineRange.start}\n`;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
output += `**Lines:** ${comment.lineRange.start}-${comment.lineRange.end}\n`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
output += `\n${comment.comment}\n\n`;
|
|
74
|
+
output += `---\n\n`;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return output;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Groups comments by file pattern
|
|
81
|
+
* @param comments Array of comments
|
|
82
|
+
* @returns Map of file patterns to comments
|
|
83
|
+
*/
|
|
84
|
+
export function groupCommentsByFile(comments) {
|
|
85
|
+
const groups = new Map();
|
|
86
|
+
// Sort comments by severity first
|
|
87
|
+
const sortedComments = [...comments].sort((a, b) => {
|
|
88
|
+
const severityOrder = { critical: 0, important: 1, suggestion: 2, question: 3 };
|
|
89
|
+
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
90
|
+
});
|
|
91
|
+
sortedComments.forEach(comment => {
|
|
92
|
+
const existing = groups.get(comment.filePattern) || [];
|
|
93
|
+
existing.push(comment);
|
|
94
|
+
groups.set(comment.filePattern, existing);
|
|
95
|
+
});
|
|
96
|
+
return groups;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Formats continuation instructions
|
|
100
|
+
* @param session The current session
|
|
101
|
+
* @returns Formatted instructions string
|
|
102
|
+
*/
|
|
103
|
+
function formatContinuationInstructions(session) {
|
|
104
|
+
const expiryDate = new Date(session.lastAccessedAt + REVIEW.SESSION.TTL);
|
|
105
|
+
let instructions = `## Continue Review\n\n`;
|
|
106
|
+
instructions += `To continue this review session:\n`;
|
|
107
|
+
instructions += `1. Make your code changes based on the feedback above\n`;
|
|
108
|
+
instructions += `2. Call the \`review-code\` tool again (session auto-detected from git state)\n`;
|
|
109
|
+
instructions += `3. Optionally provide \`commentDecisions\` to track which issues you've addressed\n\n`;
|
|
110
|
+
instructions += `**Example:**\n`;
|
|
111
|
+
instructions += '```json\n';
|
|
112
|
+
instructions += `{
|
|
113
|
+
"name": "review-code",
|
|
114
|
+
"arguments": {
|
|
115
|
+
"prompt": "I've fixed the security issues. Please review.",
|
|
116
|
+
"commentDecisions": [
|
|
117
|
+
{"commentId": "cmt-xxx", "decision": "accepted", "notes": "Fixed"}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
`;
|
|
122
|
+
instructions += '```\n\n';
|
|
123
|
+
instructions += `**Session Details:**\n`;
|
|
124
|
+
instructions += `- Session ID: \`${session.sessionId}\`\n`;
|
|
125
|
+
instructions += `- Expires: ${expiryDate.toLocaleString()}\n`;
|
|
126
|
+
instructions += `- Git State: ${session.currentGitState.branch} @ ${session.currentGitState.commitHash.slice(0, 8)}\n\n`;
|
|
127
|
+
return instructions;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Formats the history summary
|
|
131
|
+
* @param session The current session
|
|
132
|
+
* @returns Formatted history string
|
|
133
|
+
*/
|
|
134
|
+
function formatHistorySummary(session) {
|
|
135
|
+
let history = `## Review History\n\n`;
|
|
136
|
+
// Exclude current (last) round from history summary
|
|
137
|
+
const previousRounds = session.rounds.slice(0, -1);
|
|
138
|
+
if (previousRounds.length === 0) {
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
history += `| Round | Files | Issues | Resolved | Pending |\n`;
|
|
142
|
+
history += `|-------|-------|--------|----------|----------|\n`;
|
|
143
|
+
previousRounds.forEach(round => {
|
|
144
|
+
const resolved = round.commentsGenerated.filter(c => c.status !== 'pending').length;
|
|
145
|
+
const pending = round.commentsGenerated.filter(c => c.status === 'pending').length;
|
|
146
|
+
history += `| ${round.roundNumber} | ${round.filesReviewed.length} | ${round.commentsGenerated.length} | ${resolved} | ${pending} |\n`;
|
|
147
|
+
});
|
|
148
|
+
history += `\n`;
|
|
149
|
+
// Overall statistics
|
|
150
|
+
const totalResolved = session.allComments.filter(c => c.status !== 'pending').length;
|
|
151
|
+
const totalPending = session.allComments.filter(c => c.status === 'pending').length;
|
|
152
|
+
history += `**Overall Progress:**\n`;
|
|
153
|
+
history += `- Total Issues: ${session.allComments.length}\n`;
|
|
154
|
+
history += `- Resolved: ${totalResolved}\n`;
|
|
155
|
+
history += `- Pending: ${totalPending}\n`;
|
|
156
|
+
history += `- Files Tracked: ${session.filesTracked.length}\n\n`;
|
|
157
|
+
return history;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Formats a message when session expires or is not found
|
|
161
|
+
* @param sessionId The session ID that was requested
|
|
162
|
+
* @param currentGitBranch Current git branch
|
|
163
|
+
* @param currentGitCommit Current git commit hash
|
|
164
|
+
* @returns Formatted error message
|
|
165
|
+
*/
|
|
166
|
+
export function formatSessionNotFound(sessionId, currentGitBranch, currentGitCommit) {
|
|
167
|
+
return `⚠️ **Session Not Found or Expired**
|
|
168
|
+
|
|
169
|
+
The review session \`${sessionId}\` was not found or has expired.
|
|
170
|
+
|
|
171
|
+
**Current Git State:**
|
|
172
|
+
- Branch: ${currentGitBranch}
|
|
173
|
+
- Commit: ${currentGitCommit.slice(0, 8)}
|
|
174
|
+
|
|
175
|
+
**Available Options:**
|
|
176
|
+
1. Start a new review session with \`forceNewSession: true\`
|
|
177
|
+
2. Wait if you just made a commit (session auto-creates based on current git state)
|
|
178
|
+
|
|
179
|
+
**Note:** Review sessions expire after 60 minutes of inactivity.
|
|
180
|
+
`;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Formats a warning message for git state changes
|
|
184
|
+
* @param reason The reason for the warning
|
|
185
|
+
* @param continuing Whether the session is continuing anyway
|
|
186
|
+
* @returns Formatted warning string
|
|
187
|
+
*/
|
|
188
|
+
export function formatGitStateWarning(reason, continuing) {
|
|
189
|
+
let warning = `⚠️ **Git State Changed**\n\n${reason}\n\n`;
|
|
190
|
+
if (continuing) {
|
|
191
|
+
warning += `Continuing with existing session. Use \`forceNewSession: true\` to start fresh.\n`;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
warning += `Creating new session for current git state.\n`;
|
|
195
|
+
}
|
|
196
|
+
return warning;
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=reviewFormatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewFormatter.js","sourceRoot":"","sources":["../../src/utils/reviewFormatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CodeReviewSession } from './reviewSessionCache.js';
|
|
2
|
+
import { GitState } from './gitStateDetector.js';
|
|
3
|
+
export interface ReviewPromptConfig {
|
|
4
|
+
userPrompt: string;
|
|
5
|
+
session: CodeReviewSession;
|
|
6
|
+
files?: string[];
|
|
7
|
+
reviewType: string;
|
|
8
|
+
includeHistory: boolean;
|
|
9
|
+
currentGitState: GitState;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Builds a context-aware review prompt for Gemini
|
|
13
|
+
* @param config Review prompt configuration
|
|
14
|
+
* @returns Formatted prompt string
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildReviewPrompt(config: ReviewPromptConfig): string;
|
|
17
|
+
/**
|
|
18
|
+
* Returns review instructions based on review type
|
|
19
|
+
* @param reviewType The type of review to perform
|
|
20
|
+
* @returns Formatted instructions string
|
|
21
|
+
*/
|
|
22
|
+
export declare function getReviewTypeInstructions(reviewType: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Formats previous review rounds for context inclusion
|
|
25
|
+
* @param session The current review session
|
|
26
|
+
* @returns Formatted history string
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatPreviousRounds(session: CodeReviewSession): string;
|
|
29
|
+
/**
|
|
30
|
+
* Extracts file patterns from a prompt containing @ references
|
|
31
|
+
* @param prompt The prompt to parse
|
|
32
|
+
* @returns Array of file patterns
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractFilesFromPrompt(prompt: string): string[];
|
|
35
|
+
//# sourceMappingURL=reviewPromptBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewPromptBuilder.d.ts","sourceRoot":"","sources":["../../src/utils/reviewPromptBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAe,MAAM,yBAAyB,CAAC;AACzE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,QAAQ,CAAC;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CA6CpE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA2CpE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CA8CvE;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAU/D"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { REVIEW } from '../constants.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a context-aware review prompt for Gemini
|
|
4
|
+
* @param config Review prompt configuration
|
|
5
|
+
* @returns Formatted prompt string
|
|
6
|
+
*/
|
|
7
|
+
export function buildReviewPrompt(config) {
|
|
8
|
+
const { userPrompt, session, files, reviewType, includeHistory, currentGitState } = config;
|
|
9
|
+
// Build file references with @ syntax
|
|
10
|
+
const fileRefs = files?.map(f => `@${f}`).join(' ') || '';
|
|
11
|
+
let prompt = `# CODE REVIEW SESSION (Round ${session.totalRounds + 1})
|
|
12
|
+
|
|
13
|
+
## Review Context
|
|
14
|
+
- Session ID: ${session.sessionId}
|
|
15
|
+
- Branch: ${currentGitState.branch}
|
|
16
|
+
- Commit: ${currentGitState.commitHash.slice(0, 8)}
|
|
17
|
+
- Review Type: ${reviewType}
|
|
18
|
+
- Files: ${files?.length || 'all tracked'} ${files ? 'specified' : 'files'}
|
|
19
|
+
|
|
20
|
+
## Review Instructions
|
|
21
|
+
${getReviewTypeInstructions(reviewType)}
|
|
22
|
+
|
|
23
|
+
## Output Format
|
|
24
|
+
For each issue found, use this EXACT format:
|
|
25
|
+
|
|
26
|
+
**[SEVERITY: critical|important|suggestion|question]**
|
|
27
|
+
**File:** {filename}
|
|
28
|
+
**Lines:** {start}-{end} (if applicable, otherwise write "N/A")
|
|
29
|
+
**Issue:** {brief title}
|
|
30
|
+
**Details:** {explanation}
|
|
31
|
+
**Recommendation:** {suggested fix or action}
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
`;
|
|
36
|
+
// Include conversation history if requested and exists
|
|
37
|
+
if (includeHistory && session.rounds.length > 0) {
|
|
38
|
+
prompt += formatPreviousRounds(session);
|
|
39
|
+
}
|
|
40
|
+
// Add user's current request
|
|
41
|
+
prompt += `\n## Current Review Request\n`;
|
|
42
|
+
if (fileRefs) {
|
|
43
|
+
prompt += `${fileRefs}\n\n`;
|
|
44
|
+
}
|
|
45
|
+
prompt += `${userPrompt}\n`;
|
|
46
|
+
return prompt;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns review instructions based on review type
|
|
50
|
+
* @param reviewType The type of review to perform
|
|
51
|
+
* @returns Formatted instructions string
|
|
52
|
+
*/
|
|
53
|
+
export function getReviewTypeInstructions(reviewType) {
|
|
54
|
+
const instructions = {
|
|
55
|
+
security: `Focus on:
|
|
56
|
+
- Input validation and sanitization
|
|
57
|
+
- Authentication and authorization flaws
|
|
58
|
+
- SQL injection, XSS, CSRF vulnerabilities
|
|
59
|
+
- Secrets exposure (hardcoded credentials, API keys)
|
|
60
|
+
- Dependency vulnerabilities
|
|
61
|
+
- Insecure cryptography and data handling`,
|
|
62
|
+
performance: `Focus on:
|
|
63
|
+
- Algorithm complexity (O(n) analysis)
|
|
64
|
+
- Unnecessary loops or computations
|
|
65
|
+
- Memory leaks and inefficient data structures
|
|
66
|
+
- Database query optimization (N+1 queries, missing indexes)
|
|
67
|
+
- Caching opportunities
|
|
68
|
+
- Resource cleanup and connection pooling`,
|
|
69
|
+
quality: `Focus on:
|
|
70
|
+
- Code clarity and readability
|
|
71
|
+
- Naming conventions and consistency
|
|
72
|
+
- DRY violations (repeated code)
|
|
73
|
+
- Error handling and edge cases
|
|
74
|
+
- Test coverage gaps
|
|
75
|
+
- Documentation completeness`,
|
|
76
|
+
architecture: `Focus on:
|
|
77
|
+
- Design patterns and principles (SOLID, DRY, KISS)
|
|
78
|
+
- Module coupling and cohesion
|
|
79
|
+
- Scalability concerns
|
|
80
|
+
- API design and contracts
|
|
81
|
+
- Separation of concerns
|
|
82
|
+
- Technical debt and maintainability`,
|
|
83
|
+
general: `Perform a comprehensive review covering:
|
|
84
|
+
- Security vulnerabilities
|
|
85
|
+
- Performance bottlenecks
|
|
86
|
+
- Code quality issues
|
|
87
|
+
- Architectural concerns
|
|
88
|
+
Prioritize critical and important issues.`
|
|
89
|
+
};
|
|
90
|
+
return instructions[reviewType] || instructions.general;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Formats previous review rounds for context inclusion
|
|
94
|
+
* @param session The current review session
|
|
95
|
+
* @returns Formatted history string
|
|
96
|
+
*/
|
|
97
|
+
export function formatPreviousRounds(session) {
|
|
98
|
+
let historyText = `\n## Previous Review Rounds\n`;
|
|
99
|
+
// Get last N rounds based on constant
|
|
100
|
+
const recentRounds = session.rounds.slice(-REVIEW.MAX_HISTORY_ROUNDS);
|
|
101
|
+
for (const round of recentRounds) {
|
|
102
|
+
historyText += `\n### Round ${round.roundNumber} (${new Date(round.timestamp).toLocaleString()})\n`;
|
|
103
|
+
historyText += `**User Request:** ${round.userPrompt}\n`;
|
|
104
|
+
historyText += `**Issues Found:** ${round.commentsGenerated.length}\n`;
|
|
105
|
+
// Include resolved/modified comments
|
|
106
|
+
const resolvedComments = round.commentsGenerated.filter(c => c.status !== 'pending');
|
|
107
|
+
if (resolvedComments.length > 0) {
|
|
108
|
+
historyText += `\n**Resolved Issues:**\n`;
|
|
109
|
+
resolvedComments.forEach(c => {
|
|
110
|
+
const statusEmoji = c.status === 'accepted' ? '✅' : c.status === 'rejected' ? '❌' : '📝';
|
|
111
|
+
historyText += `- [${statusEmoji} ${c.status.toUpperCase()}] ${c.filePattern}: ${c.comment.split('\n')[0]}`;
|
|
112
|
+
if (c.resolution) {
|
|
113
|
+
historyText += ` - ${c.resolution}`;
|
|
114
|
+
}
|
|
115
|
+
historyText += '\n';
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Show pending critical/important issues
|
|
119
|
+
const pendingImportant = round.commentsGenerated.filter(c => c.status === 'pending' && (c.severity === 'critical' || c.severity === 'important'));
|
|
120
|
+
if (pendingImportant.length > 0) {
|
|
121
|
+
historyText += `\n**Still Pending (Critical/Important):**\n`;
|
|
122
|
+
pendingImportant.forEach(c => {
|
|
123
|
+
const emoji = REVIEW.SEVERITY_EMOJI[c.severity];
|
|
124
|
+
historyText += `- ${emoji} ${c.filePattern}: ${c.comment.split('\n')[0]}\n`;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
historyText += `\n**Total Issues Across All Rounds:** ${session.allComments.length}\n`;
|
|
129
|
+
historyText += `**Files Reviewed:** ${session.filesTracked.length}\n\n`;
|
|
130
|
+
return historyText;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Extracts file patterns from a prompt containing @ references
|
|
134
|
+
* @param prompt The prompt to parse
|
|
135
|
+
* @returns Array of file patterns
|
|
136
|
+
*/
|
|
137
|
+
export function extractFilesFromPrompt(prompt) {
|
|
138
|
+
const filePattern = /@([^\s]+)/g;
|
|
139
|
+
const matches = [];
|
|
140
|
+
let match;
|
|
141
|
+
while ((match = filePattern.exec(prompt)) !== null) {
|
|
142
|
+
matches.push(match[1]);
|
|
143
|
+
}
|
|
144
|
+
return matches;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=reviewPromptBuilder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewPromptBuilder.js","sourceRoot":"","sources":["../../src/utils/reviewPromptBuilder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ReviewComment } from './reviewSessionCache.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses Gemini's review response into structured ReviewComment objects
|
|
4
|
+
* @param geminiResponse The raw response from Gemini
|
|
5
|
+
* @param roundNumber The current review round number
|
|
6
|
+
* @returns Array of parsed review comments
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseReviewResponse(geminiResponse: string, roundNumber: number): ReviewComment[];
|
|
9
|
+
/**
|
|
10
|
+
* Generates a unique comment ID using cryptographically secure UUID
|
|
11
|
+
* @returns Comment ID string
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateCommentId(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Validates parsed comments for completeness
|
|
16
|
+
* @param comments Array of comments to validate
|
|
17
|
+
* @returns Array of valid comments
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateComments(comments: ReviewComment[]): ReviewComment[];
|
|
20
|
+
//# sourceMappingURL=reviewResponseParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewResponseParser.d.ts","sourceRoot":"","sources":["../../src/utils/reviewResponseParser.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,aAAa,EAAE,CA+DjB;AAiBD;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAqDD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAe3E"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { Logger } from './logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parses Gemini's review response into structured ReviewComment objects
|
|
5
|
+
* @param geminiResponse The raw response from Gemini
|
|
6
|
+
* @param roundNumber The current review round number
|
|
7
|
+
* @returns Array of parsed review comments
|
|
8
|
+
*/
|
|
9
|
+
export function parseReviewResponse(geminiResponse, roundNumber) {
|
|
10
|
+
const comments = [];
|
|
11
|
+
try {
|
|
12
|
+
// Pattern to match review comments with more flexible formatting
|
|
13
|
+
// Handles variations in whitespace, asterisk count, and casing that LLMs might produce
|
|
14
|
+
// More robust: \*{2,3} allows 2-3 asterisks, \s+ for flexible spacing, case-insensitive matching
|
|
15
|
+
const commentPattern = /\*{2,3}\s*\[\s*SEVERITY\s*:\s*(critical|important|suggestion|question)\s*\]\s*\*{2,3}\s+\*{2,3}\s*File\s*:\s*\*{2,3}\s*([^\n]+?)\s+\*{2,3}\s*Lines\s*:\s*\*{2,3}\s*([^\n]+?)\s+\*{2,3}\s*Issue\s*:\s*\*{2,3}\s*([^\n]+?)\s+\*{2,3}\s*Details\s*:\s*\*{2,3}\s*([\s\S]+?)\s+\*{2,3}\s*Recommendation\s*:\s*\*{2,3}\s*([\s\S]+?)(?=\n\s*\*{2,3}\s*\[\s*SEVERITY|---|$)/gi;
|
|
16
|
+
let match;
|
|
17
|
+
let matchCount = 0;
|
|
18
|
+
while ((match = commentPattern.exec(geminiResponse)) !== null) {
|
|
19
|
+
matchCount++;
|
|
20
|
+
const [_, severity, file, lines, issue, details, recommendation] = match;
|
|
21
|
+
// Parse line range
|
|
22
|
+
let lineRange;
|
|
23
|
+
const linesStr = lines.trim();
|
|
24
|
+
if (linesStr !== 'N/A' && linesStr.toLowerCase() !== 'n/a') {
|
|
25
|
+
const lineMatch = linesStr.match(/(\d+)-(\d+)/);
|
|
26
|
+
if (lineMatch) {
|
|
27
|
+
lineRange = {
|
|
28
|
+
start: parseInt(lineMatch[1], 10),
|
|
29
|
+
end: parseInt(lineMatch[2], 10)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Single line number
|
|
34
|
+
const singleLine = parseInt(linesStr, 10);
|
|
35
|
+
if (!isNaN(singleLine)) {
|
|
36
|
+
lineRange = { start: singleLine, end: singleLine };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const comment = {
|
|
41
|
+
id: generateCommentId(),
|
|
42
|
+
filePattern: file.trim(),
|
|
43
|
+
lineRange,
|
|
44
|
+
severity: severity.trim().toLowerCase(),
|
|
45
|
+
comment: formatCommentText(issue.trim(), details.trim(), recommendation.trim()),
|
|
46
|
+
roundGenerated: roundNumber,
|
|
47
|
+
status: 'pending'
|
|
48
|
+
};
|
|
49
|
+
comments.push(comment);
|
|
50
|
+
}
|
|
51
|
+
Logger.debug(`Parsed ${matchCount} review comments from Gemini response`);
|
|
52
|
+
// If no structured comments found, try fallback parsing
|
|
53
|
+
if (comments.length === 0) {
|
|
54
|
+
Logger.debug('No structured comments found, attempting fallback parsing');
|
|
55
|
+
const fallbackComments = fallbackParse(geminiResponse, roundNumber);
|
|
56
|
+
comments.push(...fallbackComments);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
Logger.error(`Error parsing review response: ${error}`);
|
|
61
|
+
// Return empty array on parse error - caller can handle
|
|
62
|
+
}
|
|
63
|
+
return comments;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Formats the comment text from parsed components
|
|
67
|
+
* @param issue Brief issue title
|
|
68
|
+
* @param details Detailed explanation
|
|
69
|
+
* @param recommendation Suggested fix
|
|
70
|
+
* @returns Formatted comment string
|
|
71
|
+
*/
|
|
72
|
+
function formatCommentText(issue, details, recommendation) {
|
|
73
|
+
return `${issue}
|
|
74
|
+
|
|
75
|
+
${details}
|
|
76
|
+
|
|
77
|
+
**Recommendation:** ${recommendation}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Generates a unique comment ID using cryptographically secure UUID
|
|
81
|
+
* @returns Comment ID string
|
|
82
|
+
*/
|
|
83
|
+
export function generateCommentId() {
|
|
84
|
+
return `cmt-${randomUUID()}`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Fallback parser for when structured format isn't followed
|
|
88
|
+
* Attempts to extract any review-like content
|
|
89
|
+
* @param geminiResponse The raw response
|
|
90
|
+
* @param roundNumber The round number
|
|
91
|
+
* @returns Array of comments (may be empty or contain unstructured feedback)
|
|
92
|
+
*/
|
|
93
|
+
function fallbackParse(geminiResponse, roundNumber) {
|
|
94
|
+
const comments = [];
|
|
95
|
+
// Look for common issue indicators
|
|
96
|
+
const issuePatterns = [
|
|
97
|
+
/(?:issue|problem|concern|warning|error):\s*(.+?)(?:\n\n|$)/gi,
|
|
98
|
+
/(?:critical|important|security|vulnerability):\s*(.+?)(?:\n\n|$)/gi,
|
|
99
|
+
/(?:recommendation|suggestion|fix):\s*(.+?)(?:\n\n|$)/gi,
|
|
100
|
+
];
|
|
101
|
+
let foundAny = false;
|
|
102
|
+
for (const pattern of issuePatterns) {
|
|
103
|
+
let match;
|
|
104
|
+
while ((match = pattern.exec(geminiResponse)) !== null) {
|
|
105
|
+
foundAny = true;
|
|
106
|
+
const comment = {
|
|
107
|
+
id: generateCommentId(),
|
|
108
|
+
filePattern: 'Unknown',
|
|
109
|
+
severity: 'suggestion',
|
|
110
|
+
comment: match[1].trim(),
|
|
111
|
+
roundGenerated: roundNumber,
|
|
112
|
+
status: 'pending'
|
|
113
|
+
};
|
|
114
|
+
comments.push(comment);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// If still nothing found and response has substantial content, create a general comment
|
|
118
|
+
if (!foundAny && geminiResponse.trim().length > 50) {
|
|
119
|
+
Logger.debug('Creating general unstructured comment from response');
|
|
120
|
+
comments.push({
|
|
121
|
+
id: generateCommentId(),
|
|
122
|
+
filePattern: 'General',
|
|
123
|
+
severity: 'question',
|
|
124
|
+
comment: geminiResponse.trim(),
|
|
125
|
+
roundGenerated: roundNumber,
|
|
126
|
+
status: 'pending'
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return comments;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validates parsed comments for completeness
|
|
133
|
+
* @param comments Array of comments to validate
|
|
134
|
+
* @returns Array of valid comments
|
|
135
|
+
*/
|
|
136
|
+
export function validateComments(comments) {
|
|
137
|
+
return comments.filter(comment => {
|
|
138
|
+
const isValid = comment.id &&
|
|
139
|
+
comment.filePattern &&
|
|
140
|
+
comment.severity &&
|
|
141
|
+
comment.comment &&
|
|
142
|
+
comment.comment.trim().length > 0;
|
|
143
|
+
if (!isValid) {
|
|
144
|
+
Logger.debug(`Filtered out invalid comment: ${JSON.stringify(comment)}`);
|
|
145
|
+
}
|
|
146
|
+
return isValid;
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=reviewResponseParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reviewResponseParser.js","sourceRoot":"","sources":["../../src/utils/reviewResponseParser.ts"],"names":[],"mappings":"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"}
|