bitbucket-gemini-action 1.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/.claude/settings.local.json +8 -0
- package/.prettierrc +8 -0
- package/CLAUDE.md +150 -0
- package/README.md +375 -0
- package/bitbucket-pipelines.yml +95 -0
- package/bun.lock +227 -0
- package/dist/prepare.js +7111 -0
- package/examples/bitbucket-pipelines-full.yml +157 -0
- package/examples/bitbucket-pipelines-minimal.yml +22 -0
- package/package.json +33 -0
- package/src/bitbucket/api/client.ts +406 -0
- package/src/bitbucket/context.ts +196 -0
- package/src/bitbucket/data/fetcher.ts +195 -0
- package/src/bitbucket/data/formatter.ts +221 -0
- package/src/bitbucket/operations/comments.ts +236 -0
- package/src/bitbucket/types.ts +262 -0
- package/src/bitbucket/validation/permissions.ts +154 -0
- package/src/bitbucket/validation/trigger.ts +175 -0
- package/src/entrypoints/execute.ts +349 -0
- package/src/entrypoints/prepare.ts +216 -0
- package/src/gemini/client.ts +263 -0
- package/src/gemini/presets.ts +2130 -0
- package/src/gemini/prompts.ts +331 -0
- package/src/gemini/tools.ts +226 -0
- package/src/index.ts +71 -0
- package/src/modes/agent/index.ts +119 -0
- package/src/modes/registry.ts +118 -0
- package/src/modes/tag/index.ts +172 -0
- package/src/modes/types.ts +95 -0
- package/src/utils/env.ts +190 -0
- package/src/utils/retry.ts +149 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Prompt Templates
|
|
3
|
+
* System prompts and prompt builders for code review
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
combinePresets,
|
|
8
|
+
getPreset,
|
|
9
|
+
getAllPresetKeys,
|
|
10
|
+
type ReviewPreset,
|
|
11
|
+
} from "./presets.js";
|
|
12
|
+
|
|
13
|
+
export const CODE_REVIEW_SYSTEM_PROMPT = `You are an expert code reviewer assistant integrated with Bitbucket. Your role is to provide helpful, actionable feedback on pull requests.
|
|
14
|
+
|
|
15
|
+
## Your Capabilities
|
|
16
|
+
- Analyze code changes for bugs, security issues, and best practices
|
|
17
|
+
- Suggest improvements for code quality and maintainability
|
|
18
|
+
- Identify potential performance issues
|
|
19
|
+
- Check for proper error handling
|
|
20
|
+
- Verify code follows project conventions
|
|
21
|
+
|
|
22
|
+
## Guidelines
|
|
23
|
+
1. **Be Constructive**: Focus on helping improve the code, not criticizing the author
|
|
24
|
+
2. **Be Specific**: Point to exact lines and provide concrete suggestions
|
|
25
|
+
3. **Be Concise**: Keep comments focused and actionable
|
|
26
|
+
4. **Prioritize**: Focus on important issues first (bugs > security > style)
|
|
27
|
+
5. **Explain Why**: Always explain the reasoning behind suggestions
|
|
28
|
+
6. **Suggest Alternatives**: When pointing out issues, suggest better approaches
|
|
29
|
+
|
|
30
|
+
## Comment Format
|
|
31
|
+
- Use markdown for formatting
|
|
32
|
+
- For code suggestions, use fenced code blocks with language hints
|
|
33
|
+
- Keep inline comments short (1-3 sentences)
|
|
34
|
+
- Use the general PR comment for summaries and overall feedback
|
|
35
|
+
|
|
36
|
+
## What to Look For
|
|
37
|
+
- Logic errors and potential bugs
|
|
38
|
+
- Security vulnerabilities (SQL injection, XSS, etc.)
|
|
39
|
+
- Null/undefined handling
|
|
40
|
+
- Error handling completeness
|
|
41
|
+
- Type safety issues
|
|
42
|
+
- Performance concerns
|
|
43
|
+
- Code duplication
|
|
44
|
+
- Naming clarity
|
|
45
|
+
- Documentation gaps (for public APIs)
|
|
46
|
+
|
|
47
|
+
## What NOT to Do
|
|
48
|
+
- Don't nitpick style issues covered by linters
|
|
49
|
+
- Don't request changes for subjective preferences
|
|
50
|
+
- Don't leave comments without actionable suggestions
|
|
51
|
+
- Don't focus on trivial issues when there are bigger concerns`;
|
|
52
|
+
|
|
53
|
+
export const TAG_MODE_SYSTEM_PROMPT = `${CODE_REVIEW_SYSTEM_PROMPT}
|
|
54
|
+
|
|
55
|
+
## Tag Mode Behavior
|
|
56
|
+
You were triggered by a user mentioning @gemini in a comment. Respond directly to their question or request. Focus your response on what they specifically asked for.
|
|
57
|
+
|
|
58
|
+
If they asked a question, answer it.
|
|
59
|
+
If they requested a review, review the relevant code.
|
|
60
|
+
If they asked for clarification, provide context.`;
|
|
61
|
+
|
|
62
|
+
export const AGENT_MODE_SYSTEM_PROMPT = `${CODE_REVIEW_SYSTEM_PROMPT}
|
|
63
|
+
|
|
64
|
+
## Agent Mode Behavior
|
|
65
|
+
You are running in automated mode, triggered by a pipeline or automation. Execute the provided prompt completely and thoroughly. Be systematic and cover all aspects mentioned in the prompt.`;
|
|
66
|
+
|
|
67
|
+
export interface PRContext {
|
|
68
|
+
title: string;
|
|
69
|
+
description: string;
|
|
70
|
+
author: string;
|
|
71
|
+
sourceBranch: string;
|
|
72
|
+
targetBranch: string;
|
|
73
|
+
files: Array<{
|
|
74
|
+
path: string;
|
|
75
|
+
status: string;
|
|
76
|
+
additions: number;
|
|
77
|
+
deletions: number;
|
|
78
|
+
}>;
|
|
79
|
+
diff: string;
|
|
80
|
+
comments?: Array<{
|
|
81
|
+
author: string;
|
|
82
|
+
content: string;
|
|
83
|
+
path?: string;
|
|
84
|
+
line?: number;
|
|
85
|
+
createdAt: string;
|
|
86
|
+
}>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function buildPRReviewPrompt(context: PRContext): string {
|
|
90
|
+
const filesSummary = context.files
|
|
91
|
+
.map(
|
|
92
|
+
(f) =>
|
|
93
|
+
`- ${f.path} (${f.status}): +${f.additions}/-${f.deletions}`
|
|
94
|
+
)
|
|
95
|
+
.join("\n");
|
|
96
|
+
|
|
97
|
+
return `## Pull Request Review Request
|
|
98
|
+
|
|
99
|
+
### PR Details
|
|
100
|
+
- **Title**: ${context.title}
|
|
101
|
+
- **Author**: ${context.author}
|
|
102
|
+
- **Branch**: ${context.sourceBranch} → ${context.targetBranch}
|
|
103
|
+
|
|
104
|
+
### Description
|
|
105
|
+
${context.description || "_No description provided_"}
|
|
106
|
+
|
|
107
|
+
### Changed Files
|
|
108
|
+
${filesSummary}
|
|
109
|
+
|
|
110
|
+
### Diff
|
|
111
|
+
\`\`\`diff
|
|
112
|
+
${context.diff}
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
${context.comments?.length ? `### Existing Comments\n${formatComments(context.comments)}` : ""}
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
Please review this pull request and provide feedback. Focus on:
|
|
120
|
+
1. Any bugs or logic errors
|
|
121
|
+
2. Security concerns
|
|
122
|
+
3. Code quality improvements
|
|
123
|
+
4. Missing error handling
|
|
124
|
+
5. Performance considerations
|
|
125
|
+
|
|
126
|
+
Use the available tools to leave inline comments on specific lines and a summary comment for overall feedback.`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function buildTagModePrompt(
|
|
130
|
+
context: PRContext,
|
|
131
|
+
userMessage: string,
|
|
132
|
+
mentionAuthor: string
|
|
133
|
+
): string {
|
|
134
|
+
const filesSummary = context.files
|
|
135
|
+
.map(
|
|
136
|
+
(f) =>
|
|
137
|
+
`- ${f.path} (${f.status}): +${f.additions}/-${f.deletions}`
|
|
138
|
+
)
|
|
139
|
+
.join("\n");
|
|
140
|
+
|
|
141
|
+
return `## User Request
|
|
142
|
+
|
|
143
|
+
**@${mentionAuthor}** mentioned you with the following message:
|
|
144
|
+
|
|
145
|
+
> ${userMessage}
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### PR Context
|
|
150
|
+
- **Title**: ${context.title}
|
|
151
|
+
- **Author**: ${context.author}
|
|
152
|
+
- **Branch**: ${context.sourceBranch} → ${context.targetBranch}
|
|
153
|
+
|
|
154
|
+
### Description
|
|
155
|
+
${context.description || "_No description provided_"}
|
|
156
|
+
|
|
157
|
+
### Changed Files
|
|
158
|
+
${filesSummary}
|
|
159
|
+
|
|
160
|
+
### Diff
|
|
161
|
+
\`\`\`diff
|
|
162
|
+
${context.diff}
|
|
163
|
+
\`\`\`
|
|
164
|
+
|
|
165
|
+
${context.comments?.length ? `### Recent Comments\n${formatComments(context.comments)}` : ""}
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
Please respond to the user's request. Address their specific question or concern.`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function buildAgentModePrompt(
|
|
173
|
+
context: PRContext,
|
|
174
|
+
agentPrompt: string
|
|
175
|
+
): string {
|
|
176
|
+
const filesSummary = context.files
|
|
177
|
+
.map(
|
|
178
|
+
(f) =>
|
|
179
|
+
`- ${f.path} (${f.status}): +${f.additions}/-${f.deletions}`
|
|
180
|
+
)
|
|
181
|
+
.join("\n");
|
|
182
|
+
|
|
183
|
+
return `## Automated Task
|
|
184
|
+
|
|
185
|
+
Execute the following task:
|
|
186
|
+
|
|
187
|
+
${agentPrompt}
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
### PR Context
|
|
192
|
+
- **Title**: ${context.title}
|
|
193
|
+
- **Author**: ${context.author}
|
|
194
|
+
- **Branch**: ${context.sourceBranch} → ${context.targetBranch}
|
|
195
|
+
|
|
196
|
+
### Description
|
|
197
|
+
${context.description || "_No description provided_"}
|
|
198
|
+
|
|
199
|
+
### Changed Files
|
|
200
|
+
${filesSummary}
|
|
201
|
+
|
|
202
|
+
### Diff
|
|
203
|
+
\`\`\`diff
|
|
204
|
+
${context.diff}
|
|
205
|
+
\`\`\`
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
Complete the requested task using the available tools.`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function formatComments(
|
|
213
|
+
comments: NonNullable<PRContext["comments"]>
|
|
214
|
+
): string {
|
|
215
|
+
return comments
|
|
216
|
+
.map((c) => {
|
|
217
|
+
const location = c.path
|
|
218
|
+
? `on \`${c.path}${c.line ? `:${c.line}` : ""}\``
|
|
219
|
+
: "";
|
|
220
|
+
return `- **${c.author}** ${location} (${c.createdAt}):\n > ${c.content.split("\n").join("\n > ")}`;
|
|
221
|
+
})
|
|
222
|
+
.join("\n\n");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Build system prompt with presets
|
|
227
|
+
*/
|
|
228
|
+
export function buildSystemPromptWithPresets(
|
|
229
|
+
baseSystemPrompt: string,
|
|
230
|
+
presetKeys: string[],
|
|
231
|
+
customPrompt?: string
|
|
232
|
+
): string {
|
|
233
|
+
// If no presets or custom prompt, return base
|
|
234
|
+
if (presetKeys.length === 0 && !customPrompt) {
|
|
235
|
+
return baseSystemPrompt;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Validate preset keys
|
|
239
|
+
const validKeys = getAllPresetKeys();
|
|
240
|
+
const invalidKeys = presetKeys.filter((key) => !validKeys.includes(key));
|
|
241
|
+
if (invalidKeys.length > 0) {
|
|
242
|
+
console.warn(`⚠️ Unknown preset keys: ${invalidKeys.join(", ")}`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const validPresetKeys = presetKeys.filter((key) => validKeys.includes(key));
|
|
246
|
+
|
|
247
|
+
// Combine presets
|
|
248
|
+
const presetPrompt = combinePresets(validPresetKeys, customPrompt);
|
|
249
|
+
|
|
250
|
+
if (!presetPrompt) {
|
|
251
|
+
return baseSystemPrompt;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return `${baseSystemPrompt}
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
${presetPrompt}`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Build prompt with presets for PR review
|
|
263
|
+
*/
|
|
264
|
+
export function buildPRReviewPromptWithPresets(
|
|
265
|
+
context: PRContext,
|
|
266
|
+
presetKeys: string[],
|
|
267
|
+
customPrompt?: string
|
|
268
|
+
): string {
|
|
269
|
+
const basePrompt = buildPRReviewPrompt(context);
|
|
270
|
+
const presetPrompt = combinePresets(presetKeys, customPrompt);
|
|
271
|
+
|
|
272
|
+
if (!presetPrompt) {
|
|
273
|
+
return basePrompt;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return `${basePrompt}
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
${presetPrompt}`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Build agent mode prompt with presets
|
|
285
|
+
*/
|
|
286
|
+
export function buildAgentModePromptWithPresets(
|
|
287
|
+
context: PRContext,
|
|
288
|
+
agentPrompt: string,
|
|
289
|
+
presetKeys: string[],
|
|
290
|
+
customPrompt?: string
|
|
291
|
+
): string {
|
|
292
|
+
const basePrompt = buildAgentModePrompt(context, agentPrompt);
|
|
293
|
+
const presetPrompt = combinePresets(presetKeys, customPrompt);
|
|
294
|
+
|
|
295
|
+
if (!presetPrompt) {
|
|
296
|
+
return basePrompt;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return `${basePrompt}
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
${presetPrompt}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Build tag mode prompt with presets
|
|
308
|
+
*/
|
|
309
|
+
export function buildTagModePromptWithPresets(
|
|
310
|
+
context: PRContext,
|
|
311
|
+
userMessage: string,
|
|
312
|
+
mentionAuthor: string,
|
|
313
|
+
presetKeys: string[],
|
|
314
|
+
customPrompt?: string
|
|
315
|
+
): string {
|
|
316
|
+
const basePrompt = buildTagModePrompt(context, userMessage, mentionAuthor);
|
|
317
|
+
const presetPrompt = combinePresets(presetKeys, customPrompt);
|
|
318
|
+
|
|
319
|
+
if (!presetPrompt) {
|
|
320
|
+
return basePrompt;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return `${basePrompt}
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
${presetPrompt}`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Re-export preset utilities for convenience
|
|
331
|
+
export { combinePresets, getPreset, getAllPresetKeys, type ReviewPreset };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Function Calling Tools Definition
|
|
3
|
+
* Tools for code review and PR interaction
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { SchemaType, type FunctionDeclaration } from "@google/generative-ai";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Tool for creating inline comments on specific code lines
|
|
10
|
+
*/
|
|
11
|
+
export const createInlineCommentTool: FunctionDeclaration = {
|
|
12
|
+
name: "create_inline_comment",
|
|
13
|
+
description:
|
|
14
|
+
"Create an inline comment on a specific line of code in the pull request. Use this to provide targeted feedback on specific code changes.",
|
|
15
|
+
parameters: {
|
|
16
|
+
type: SchemaType.OBJECT,
|
|
17
|
+
properties: {
|
|
18
|
+
path: {
|
|
19
|
+
type: SchemaType.STRING,
|
|
20
|
+
description: "The file path relative to the repository root",
|
|
21
|
+
},
|
|
22
|
+
line: {
|
|
23
|
+
type: SchemaType.NUMBER,
|
|
24
|
+
description: "The line number to comment on (1-indexed)",
|
|
25
|
+
},
|
|
26
|
+
content: {
|
|
27
|
+
type: SchemaType.STRING,
|
|
28
|
+
description:
|
|
29
|
+
"The comment content in markdown format. Be specific and actionable.",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
required: ["path", "line", "content"],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Tool for creating a general PR comment
|
|
38
|
+
*/
|
|
39
|
+
export const createPRCommentTool: FunctionDeclaration = {
|
|
40
|
+
name: "create_pr_comment",
|
|
41
|
+
description:
|
|
42
|
+
"Create a general comment on the pull request. Use this for overall feedback, summaries, or questions that are not specific to a particular line of code.",
|
|
43
|
+
parameters: {
|
|
44
|
+
type: SchemaType.OBJECT,
|
|
45
|
+
properties: {
|
|
46
|
+
content: {
|
|
47
|
+
type: SchemaType.STRING,
|
|
48
|
+
description:
|
|
49
|
+
"The comment content in markdown format. Structure your feedback clearly.",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ["content"],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Tool for updating the tracking comment
|
|
58
|
+
*/
|
|
59
|
+
export const updateTrackingCommentTool: FunctionDeclaration = {
|
|
60
|
+
name: "update_tracking_comment",
|
|
61
|
+
description:
|
|
62
|
+
"Update the AI assistant's tracking comment to show progress or final results.",
|
|
63
|
+
parameters: {
|
|
64
|
+
type: SchemaType.OBJECT,
|
|
65
|
+
properties: {
|
|
66
|
+
content: {
|
|
67
|
+
type: SchemaType.STRING,
|
|
68
|
+
description: "The updated content for the tracking comment in markdown",
|
|
69
|
+
},
|
|
70
|
+
status: {
|
|
71
|
+
type: SchemaType.STRING,
|
|
72
|
+
enum: ["in_progress", "completed", "failed"],
|
|
73
|
+
description: "The current status of the review",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
required: ["content", "status"],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Tool for reading file contents
|
|
82
|
+
*/
|
|
83
|
+
export const readFileTool: FunctionDeclaration = {
|
|
84
|
+
name: "read_file",
|
|
85
|
+
description:
|
|
86
|
+
"Read the contents of a file from the repository. Use this to understand the full context of code changes.",
|
|
87
|
+
parameters: {
|
|
88
|
+
type: SchemaType.OBJECT,
|
|
89
|
+
properties: {
|
|
90
|
+
path: {
|
|
91
|
+
type: SchemaType.STRING,
|
|
92
|
+
description: "The file path relative to the repository root",
|
|
93
|
+
},
|
|
94
|
+
startLine: {
|
|
95
|
+
type: SchemaType.NUMBER,
|
|
96
|
+
description: "Optional starting line number (1-indexed)",
|
|
97
|
+
},
|
|
98
|
+
endLine: {
|
|
99
|
+
type: SchemaType.NUMBER,
|
|
100
|
+
description: "Optional ending line number (1-indexed)",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
required: ["path"],
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Tool for searching code in the repository
|
|
109
|
+
*/
|
|
110
|
+
export const searchCodeTool: FunctionDeclaration = {
|
|
111
|
+
name: "search_code",
|
|
112
|
+
description:
|
|
113
|
+
"Search for code patterns or text in the repository. Use this to find related code, usages, or definitions.",
|
|
114
|
+
parameters: {
|
|
115
|
+
type: SchemaType.OBJECT,
|
|
116
|
+
properties: {
|
|
117
|
+
query: {
|
|
118
|
+
type: SchemaType.STRING,
|
|
119
|
+
description: "The search query (supports regex)",
|
|
120
|
+
},
|
|
121
|
+
filePattern: {
|
|
122
|
+
type: SchemaType.STRING,
|
|
123
|
+
description: "Optional glob pattern to filter files (e.g., '*.ts')",
|
|
124
|
+
},
|
|
125
|
+
maxResults: {
|
|
126
|
+
type: SchemaType.NUMBER,
|
|
127
|
+
description: "Maximum number of results to return (default: 20)",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
required: ["query"],
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Tool for getting pipeline/CI status
|
|
136
|
+
*/
|
|
137
|
+
export const getPipelineStatusTool: FunctionDeclaration = {
|
|
138
|
+
name: "get_pipeline_status",
|
|
139
|
+
description:
|
|
140
|
+
"Get the current status of the pipeline builds for this PR or branch.",
|
|
141
|
+
parameters: {
|
|
142
|
+
type: SchemaType.OBJECT,
|
|
143
|
+
properties: {
|
|
144
|
+
includeHistory: {
|
|
145
|
+
type: SchemaType.BOOLEAN,
|
|
146
|
+
description: "Include recent build history (default: false)",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
required: [],
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Tool for suggesting code fixes
|
|
155
|
+
*/
|
|
156
|
+
export const suggestFixTool: FunctionDeclaration = {
|
|
157
|
+
name: "suggest_fix",
|
|
158
|
+
description:
|
|
159
|
+
"Suggest a code fix for a specific issue. This creates a code suggestion that can be applied directly.",
|
|
160
|
+
parameters: {
|
|
161
|
+
type: SchemaType.OBJECT,
|
|
162
|
+
properties: {
|
|
163
|
+
path: {
|
|
164
|
+
type: SchemaType.STRING,
|
|
165
|
+
description: "The file path relative to the repository root",
|
|
166
|
+
},
|
|
167
|
+
startLine: {
|
|
168
|
+
type: SchemaType.NUMBER,
|
|
169
|
+
description: "The starting line number of the code to replace",
|
|
170
|
+
},
|
|
171
|
+
endLine: {
|
|
172
|
+
type: SchemaType.NUMBER,
|
|
173
|
+
description: "The ending line number of the code to replace",
|
|
174
|
+
},
|
|
175
|
+
suggestedCode: {
|
|
176
|
+
type: SchemaType.STRING,
|
|
177
|
+
description: "The suggested replacement code",
|
|
178
|
+
},
|
|
179
|
+
explanation: {
|
|
180
|
+
type: SchemaType.STRING,
|
|
181
|
+
description: "Brief explanation of why this change is suggested",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
required: ["path", "startLine", "endLine", "suggestedCode", "explanation"],
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* All available tools for code review
|
|
190
|
+
*/
|
|
191
|
+
export const codeReviewTools: FunctionDeclaration[] = [
|
|
192
|
+
createInlineCommentTool,
|
|
193
|
+
createPRCommentTool,
|
|
194
|
+
updateTrackingCommentTool,
|
|
195
|
+
readFileTool,
|
|
196
|
+
searchCodeTool,
|
|
197
|
+
getPipelineStatusTool,
|
|
198
|
+
suggestFixTool,
|
|
199
|
+
];
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Subset of tools for lightweight reviews
|
|
203
|
+
*/
|
|
204
|
+
export const lightweightReviewTools: FunctionDeclaration[] = [
|
|
205
|
+
createInlineCommentTool,
|
|
206
|
+
createPRCommentTool,
|
|
207
|
+
readFileTool,
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get tools based on mode
|
|
212
|
+
*/
|
|
213
|
+
export function getToolsForMode(
|
|
214
|
+
mode: "full" | "lightweight" | "agent"
|
|
215
|
+
): FunctionDeclaration[] {
|
|
216
|
+
switch (mode) {
|
|
217
|
+
case "full":
|
|
218
|
+
return codeReviewTools;
|
|
219
|
+
case "lightweight":
|
|
220
|
+
return lightweightReviewTools;
|
|
221
|
+
case "agent":
|
|
222
|
+
return codeReviewTools;
|
|
223
|
+
default:
|
|
224
|
+
return lightweightReviewTools;
|
|
225
|
+
}
|
|
226
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bitbucket Gemini Action
|
|
3
|
+
* Main entry point for the action
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { prepare } from "./entrypoints/prepare.js";
|
|
7
|
+
export { execute } from "./entrypoints/execute.js";
|
|
8
|
+
|
|
9
|
+
// Bitbucket API
|
|
10
|
+
export { BitbucketClient } from "./bitbucket/api/client.js";
|
|
11
|
+
export { parseBitbucketContext } from "./bitbucket/context.js";
|
|
12
|
+
export type {
|
|
13
|
+
BitbucketPullRequest,
|
|
14
|
+
BitbucketComment,
|
|
15
|
+
BitbucketUser,
|
|
16
|
+
ParsedBitbucketContext,
|
|
17
|
+
} from "./bitbucket/types.js";
|
|
18
|
+
|
|
19
|
+
// Gemini API
|
|
20
|
+
export { GeminiClient } from "./gemini/client.js";
|
|
21
|
+
export { codeReviewTools, getToolsForMode } from "./gemini/tools.js";
|
|
22
|
+
export {
|
|
23
|
+
buildPRReviewPrompt,
|
|
24
|
+
buildTagModePrompt,
|
|
25
|
+
buildAgentModePrompt,
|
|
26
|
+
} from "./gemini/prompts.js";
|
|
27
|
+
|
|
28
|
+
// Modes
|
|
29
|
+
export { detectMode, getMode, shouldAnyModeTrigger } from "./modes/registry.js";
|
|
30
|
+
export type { Mode, ModeName, ModeContext } from "./modes/types.js";
|
|
31
|
+
|
|
32
|
+
// Utils
|
|
33
|
+
export { getEnvConfig, getGeminiApiKey } from "./utils/env.js";
|
|
34
|
+
export { withRetry } from "./utils/retry.js";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run the full action (prepare + execute)
|
|
38
|
+
*/
|
|
39
|
+
export async function run(): Promise<void> {
|
|
40
|
+
const { prepare } = await import("./entrypoints/prepare.js");
|
|
41
|
+
const { execute } = await import("./entrypoints/execute.js");
|
|
42
|
+
|
|
43
|
+
const prepareResult = await prepare();
|
|
44
|
+
|
|
45
|
+
if (!prepareResult.success) {
|
|
46
|
+
console.error("Prepare phase failed:", prepareResult.error);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!prepareResult.shouldContinue) {
|
|
51
|
+
console.log("No action needed:", prepareResult.error || "No trigger");
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const executeResult = await execute();
|
|
56
|
+
|
|
57
|
+
if (!executeResult.success) {
|
|
58
|
+
console.error("Execute phase failed:", executeResult.error);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log("Action completed successfully");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Run if executed directly
|
|
66
|
+
if (import.meta.main) {
|
|
67
|
+
run().catch((error) => {
|
|
68
|
+
console.error("Fatal error:", error);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|
|
71
|
+
}
|