@juspay/yama 1.5.1 → 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/.mcp-config.example.json +26 -0
- package/CHANGELOG.md +40 -0
- package/README.md +311 -685
- package/dist/cli/v2.cli.d.ts +13 -0
- package/dist/cli/v2.cli.js +290 -0
- package/dist/index.d.ts +12 -13
- package/dist/index.js +18 -19
- package/dist/v2/config/ConfigLoader.d.ts +50 -0
- package/dist/v2/config/ConfigLoader.js +205 -0
- package/dist/v2/config/DefaultConfig.d.ts +9 -0
- package/dist/v2/config/DefaultConfig.js +191 -0
- package/dist/v2/core/MCPServerManager.d.ts +22 -0
- package/dist/v2/core/MCPServerManager.js +92 -0
- package/dist/v2/core/SessionManager.d.ts +72 -0
- package/dist/v2/core/SessionManager.js +200 -0
- package/dist/v2/core/YamaV2Orchestrator.d.ts +112 -0
- package/dist/v2/core/YamaV2Orchestrator.js +549 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.d.ts +8 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.js +216 -0
- package/dist/v2/prompts/PromptBuilder.d.ts +38 -0
- package/dist/v2/prompts/PromptBuilder.js +228 -0
- package/dist/v2/prompts/ReviewSystemPrompt.d.ts +8 -0
- package/dist/v2/prompts/ReviewSystemPrompt.js +270 -0
- package/dist/v2/types/config.types.d.ts +120 -0
- package/dist/v2/types/config.types.js +5 -0
- package/dist/v2/types/mcp.types.d.ts +191 -0
- package/dist/v2/types/mcp.types.js +6 -0
- package/dist/v2/types/v2.types.d.ts +182 -0
- package/dist/v2/types/v2.types.js +42 -0
- package/dist/v2/utils/ObservabilityConfig.d.ts +22 -0
- package/dist/v2/utils/ObservabilityConfig.js +48 -0
- package/package.json +11 -9
- package/yama.config.example.yaml +214 -193
- package/dist/cli/index.d.ts +0 -12
- package/dist/cli/index.js +0 -538
- package/dist/core/ContextGatherer.d.ts +0 -110
- package/dist/core/ContextGatherer.js +0 -470
- package/dist/core/Guardian.d.ts +0 -81
- package/dist/core/Guardian.js +0 -474
- package/dist/core/providers/BitbucketProvider.d.ts +0 -105
- package/dist/core/providers/BitbucketProvider.js +0 -489
- package/dist/features/CodeReviewer.d.ts +0 -173
- package/dist/features/CodeReviewer.js +0 -1707
- package/dist/features/DescriptionEnhancer.d.ts +0 -64
- package/dist/features/DescriptionEnhancer.js +0 -445
- package/dist/features/MultiInstanceProcessor.d.ts +0 -74
- package/dist/features/MultiInstanceProcessor.js +0 -360
- package/dist/types/index.d.ts +0 -624
- package/dist/types/index.js +0 -104
- package/dist/utils/Cache.d.ts +0 -103
- package/dist/utils/Cache.js +0 -444
- package/dist/utils/ConfigManager.d.ts +0 -88
- package/dist/utils/ConfigManager.js +0 -603
- package/dist/utils/ContentSimilarityService.d.ts +0 -74
- package/dist/utils/ContentSimilarityService.js +0 -215
- package/dist/utils/ExactDuplicateRemover.d.ts +0 -77
- package/dist/utils/ExactDuplicateRemover.js +0 -361
- package/dist/utils/Logger.d.ts +0 -31
- package/dist/utils/Logger.js +0 -214
- package/dist/utils/MemoryBankManager.d.ts +0 -73
- package/dist/utils/MemoryBankManager.js +0 -310
- package/dist/utils/ParallelProcessing.d.ts +0 -140
- package/dist/utils/ParallelProcessing.js +0 -333
- package/dist/utils/ProviderLimits.d.ts +0 -58
- package/dist/utils/ProviderLimits.js +0 -143
- package/dist/utils/RetryManager.d.ts +0 -78
- package/dist/utils/RetryManager.js +0 -205
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yama V2 Orchestrator
|
|
3
|
+
* Main entry point for AI-native autonomous code review
|
|
4
|
+
*/
|
|
5
|
+
import { NeuroLink } from "@juspay/neurolink";
|
|
6
|
+
import { MCPServerManager } from "./MCPServerManager.js";
|
|
7
|
+
import { ConfigLoader } from "../config/ConfigLoader.js";
|
|
8
|
+
import { PromptBuilder } from "../prompts/PromptBuilder.js";
|
|
9
|
+
import { SessionManager } from "./SessionManager.js";
|
|
10
|
+
import { buildObservabilityConfigFromEnv, validateObservabilityConfig, } from "../utils/ObservabilityConfig.js";
|
|
11
|
+
export class YamaV2Orchestrator {
|
|
12
|
+
neurolink;
|
|
13
|
+
mcpManager;
|
|
14
|
+
configLoader;
|
|
15
|
+
promptBuilder;
|
|
16
|
+
sessionManager;
|
|
17
|
+
config;
|
|
18
|
+
initialized = false;
|
|
19
|
+
constructor() {
|
|
20
|
+
this.configLoader = new ConfigLoader();
|
|
21
|
+
this.mcpManager = new MCPServerManager();
|
|
22
|
+
this.promptBuilder = new PromptBuilder();
|
|
23
|
+
this.sessionManager = new SessionManager();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Initialize Yama V2 with configuration and MCP servers
|
|
27
|
+
*/
|
|
28
|
+
async initialize(configPath) {
|
|
29
|
+
if (this.initialized) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.showBanner();
|
|
33
|
+
console.log("🚀 Initializing Yama V2...\n");
|
|
34
|
+
try {
|
|
35
|
+
// Step 1: Load configuration
|
|
36
|
+
this.config = await this.configLoader.loadConfig(configPath);
|
|
37
|
+
// Step 2: Initialize NeuroLink with observability
|
|
38
|
+
console.log("🧠 Initializing NeuroLink AI engine...");
|
|
39
|
+
this.neurolink = this.initializeNeurolink();
|
|
40
|
+
console.log("✅ NeuroLink initialized\n");
|
|
41
|
+
// Step 3: Setup MCP servers
|
|
42
|
+
await this.mcpManager.setupMCPServers(this.neurolink, this.config.mcpServers);
|
|
43
|
+
console.log("✅ MCP servers ready (tools managed by NeuroLink)\n");
|
|
44
|
+
// Step 4: Validate configuration
|
|
45
|
+
await this.configLoader.validate();
|
|
46
|
+
this.initialized = true;
|
|
47
|
+
console.log("✅ Yama V2 initialized successfully\n");
|
|
48
|
+
console.log("═".repeat(60) + "\n");
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error("\n❌ Initialization failed:", error.message);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Start autonomous AI review
|
|
57
|
+
*/
|
|
58
|
+
async startReview(request) {
|
|
59
|
+
await this.ensureInitialized();
|
|
60
|
+
const startTime = Date.now();
|
|
61
|
+
const sessionId = this.sessionManager.createSession(request);
|
|
62
|
+
this.logReviewStart(request, sessionId);
|
|
63
|
+
try {
|
|
64
|
+
// Build comprehensive AI instructions
|
|
65
|
+
const instructions = await this.promptBuilder.buildReviewInstructions(request, this.config);
|
|
66
|
+
if (this.config.display.verboseToolCalls) {
|
|
67
|
+
console.log("\n📝 AI Instructions built:");
|
|
68
|
+
console.log(` Instruction length: ${instructions.length} characters\n`);
|
|
69
|
+
}
|
|
70
|
+
// Create tool context for AI
|
|
71
|
+
const toolContext = this.createToolContext(sessionId, request);
|
|
72
|
+
// Set tool context in NeuroLink (using type assertion as setToolContext is documented but may not be in type definitions)
|
|
73
|
+
this.neurolink.setToolContext(toolContext);
|
|
74
|
+
// Update session metadata
|
|
75
|
+
this.sessionManager.updateMetadata(sessionId, {
|
|
76
|
+
aiProvider: this.config.ai.provider,
|
|
77
|
+
aiModel: this.config.ai.model,
|
|
78
|
+
});
|
|
79
|
+
// Execute autonomous AI review
|
|
80
|
+
console.log("🤖 Starting autonomous AI review...");
|
|
81
|
+
console.log(" AI will now make decisions and execute actions autonomously\n");
|
|
82
|
+
const aiResponse = await this.neurolink.generate({
|
|
83
|
+
input: { text: instructions },
|
|
84
|
+
provider: this.config.ai.provider,
|
|
85
|
+
model: this.config.ai.model,
|
|
86
|
+
temperature: this.config.ai.temperature,
|
|
87
|
+
maxTokens: this.config.ai.maxTokens,
|
|
88
|
+
timeout: this.config.ai.timeout,
|
|
89
|
+
context: {
|
|
90
|
+
sessionId,
|
|
91
|
+
userId: this.generateUserId(request),
|
|
92
|
+
operation: "code-review",
|
|
93
|
+
metadata: toolContext.metadata,
|
|
94
|
+
},
|
|
95
|
+
enableAnalytics: this.config.ai.enableAnalytics,
|
|
96
|
+
enableEvaluation: this.config.ai.enableEvaluation,
|
|
97
|
+
});
|
|
98
|
+
// Extract and parse results
|
|
99
|
+
const result = this.parseReviewResult(aiResponse, startTime, sessionId);
|
|
100
|
+
// Update session with results
|
|
101
|
+
this.sessionManager.completeSession(sessionId, result);
|
|
102
|
+
this.logReviewComplete(result);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.sessionManager.failSession(sessionId, error);
|
|
107
|
+
console.error("\n❌ Review failed:", error.message);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Stream review with real-time updates (for verbose mode)
|
|
113
|
+
*/
|
|
114
|
+
async *streamReview(request) {
|
|
115
|
+
await this.ensureInitialized();
|
|
116
|
+
const sessionId = this.sessionManager.createSession(request);
|
|
117
|
+
try {
|
|
118
|
+
// Build instructions
|
|
119
|
+
const instructions = await this.promptBuilder.buildReviewInstructions(request, this.config);
|
|
120
|
+
// Create tool context
|
|
121
|
+
const toolContext = this.createToolContext(sessionId, request);
|
|
122
|
+
this.neurolink.setToolContext(toolContext);
|
|
123
|
+
// Stream AI execution
|
|
124
|
+
yield {
|
|
125
|
+
type: "progress",
|
|
126
|
+
timestamp: new Date().toISOString(),
|
|
127
|
+
sessionId,
|
|
128
|
+
data: {
|
|
129
|
+
phase: "context_gathering",
|
|
130
|
+
progress: 0,
|
|
131
|
+
message: "Starting review...",
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
// Note: NeuroLink streaming implementation depends on version
|
|
135
|
+
// This is a placeholder for streaming functionality
|
|
136
|
+
const aiResponse = await this.neurolink.generate({
|
|
137
|
+
input: { text: instructions },
|
|
138
|
+
provider: this.config.ai.provider,
|
|
139
|
+
model: this.config.ai.model,
|
|
140
|
+
context: {
|
|
141
|
+
sessionId,
|
|
142
|
+
userId: this.generateUserId(request),
|
|
143
|
+
},
|
|
144
|
+
enableAnalytics: true,
|
|
145
|
+
});
|
|
146
|
+
yield {
|
|
147
|
+
type: "progress",
|
|
148
|
+
timestamp: new Date().toISOString(),
|
|
149
|
+
sessionId,
|
|
150
|
+
data: {
|
|
151
|
+
phase: "decision_making",
|
|
152
|
+
progress: 100,
|
|
153
|
+
message: "Review complete",
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
this.sessionManager.failSession(sessionId, error);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Start review and then enhance description in the same session
|
|
164
|
+
* This allows the AI to use knowledge gained during review to write better descriptions
|
|
165
|
+
*/
|
|
166
|
+
async startReviewAndEnhance(request) {
|
|
167
|
+
await this.ensureInitialized();
|
|
168
|
+
const startTime = Date.now();
|
|
169
|
+
const sessionId = this.sessionManager.createSession(request);
|
|
170
|
+
this.logReviewStart(request, sessionId);
|
|
171
|
+
try {
|
|
172
|
+
// ========================================================================
|
|
173
|
+
// PHASE 1: Code Review
|
|
174
|
+
// ========================================================================
|
|
175
|
+
// Build review instructions
|
|
176
|
+
const reviewInstructions = await this.promptBuilder.buildReviewInstructions(request, this.config);
|
|
177
|
+
if (this.config.display.verboseToolCalls) {
|
|
178
|
+
console.log("\n📝 Review instructions built:");
|
|
179
|
+
console.log(` Instruction length: ${reviewInstructions.length} characters\n`);
|
|
180
|
+
}
|
|
181
|
+
// Create tool context
|
|
182
|
+
const toolContext = this.createToolContext(sessionId, request);
|
|
183
|
+
this.neurolink.setToolContext(toolContext);
|
|
184
|
+
// Update session metadata
|
|
185
|
+
this.sessionManager.updateMetadata(sessionId, {
|
|
186
|
+
aiProvider: this.config.ai.provider,
|
|
187
|
+
aiModel: this.config.ai.model,
|
|
188
|
+
});
|
|
189
|
+
// Execute review
|
|
190
|
+
console.log("🤖 Phase 1: Starting autonomous AI code review...");
|
|
191
|
+
console.log(" AI will analyze files and post comments\n");
|
|
192
|
+
const reviewResponse = await this.neurolink.generate({
|
|
193
|
+
input: { text: reviewInstructions },
|
|
194
|
+
provider: this.config.ai.provider,
|
|
195
|
+
model: this.config.ai.model,
|
|
196
|
+
temperature: this.config.ai.temperature,
|
|
197
|
+
maxTokens: this.config.ai.maxTokens,
|
|
198
|
+
timeout: this.config.ai.timeout,
|
|
199
|
+
context: {
|
|
200
|
+
sessionId,
|
|
201
|
+
userId: this.generateUserId(request),
|
|
202
|
+
operation: "code-review",
|
|
203
|
+
metadata: toolContext.metadata,
|
|
204
|
+
},
|
|
205
|
+
enableAnalytics: this.config.ai.enableAnalytics,
|
|
206
|
+
enableEvaluation: this.config.ai.enableEvaluation,
|
|
207
|
+
});
|
|
208
|
+
// Parse review results
|
|
209
|
+
const reviewResult = this.parseReviewResult(reviewResponse, startTime, sessionId);
|
|
210
|
+
console.log("\n✅ Phase 1 complete: Code review finished");
|
|
211
|
+
console.log(` Decision: ${reviewResult.decision}`);
|
|
212
|
+
console.log(` Comments: ${reviewResult.statistics.totalComments}\n`);
|
|
213
|
+
// ========================================================================
|
|
214
|
+
// PHASE 2: Description Enhancement (using same session)
|
|
215
|
+
// ========================================================================
|
|
216
|
+
if (this.config.descriptionEnhancement.enabled) {
|
|
217
|
+
console.log("📝 Phase 2: Enhancing PR description...");
|
|
218
|
+
console.log(" AI will use review insights to write description\n");
|
|
219
|
+
const enhanceInstructions = await this.promptBuilder.buildDescriptionEnhancementInstructions(request, this.config);
|
|
220
|
+
// Continue the SAME session - AI remembers everything from review
|
|
221
|
+
const enhanceResponse = await this.neurolink.generate({
|
|
222
|
+
input: { text: enhanceInstructions },
|
|
223
|
+
provider: this.config.ai.provider,
|
|
224
|
+
model: this.config.ai.model,
|
|
225
|
+
temperature: this.config.ai.temperature,
|
|
226
|
+
maxTokens: this.config.ai.maxTokens,
|
|
227
|
+
timeout: this.config.ai.timeout,
|
|
228
|
+
context: {
|
|
229
|
+
sessionId, // SAME sessionId = AI remembers review context
|
|
230
|
+
userId: this.generateUserId(request),
|
|
231
|
+
operation: "description-enhancement",
|
|
232
|
+
metadata: toolContext.metadata,
|
|
233
|
+
},
|
|
234
|
+
enableAnalytics: this.config.ai.enableAnalytics,
|
|
235
|
+
enableEvaluation: this.config.ai.enableEvaluation,
|
|
236
|
+
});
|
|
237
|
+
console.log("✅ Phase 2 complete: Description enhanced\n");
|
|
238
|
+
// Add enhancement status to result
|
|
239
|
+
reviewResult.descriptionEnhanced = true;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
console.log("⏭️ Skipping description enhancement (disabled in config)\n");
|
|
243
|
+
reviewResult.descriptionEnhanced = false;
|
|
244
|
+
}
|
|
245
|
+
// Update session with final results
|
|
246
|
+
this.sessionManager.completeSession(sessionId, reviewResult);
|
|
247
|
+
this.logReviewComplete(reviewResult);
|
|
248
|
+
return reviewResult;
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
this.sessionManager.failSession(sessionId, error);
|
|
252
|
+
console.error("\n❌ Review failed:", error.message);
|
|
253
|
+
throw error;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Enhance PR description only (without full review)
|
|
258
|
+
*/
|
|
259
|
+
async enhanceDescription(request) {
|
|
260
|
+
await this.ensureInitialized();
|
|
261
|
+
const sessionId = this.sessionManager.createSession(request);
|
|
262
|
+
try {
|
|
263
|
+
console.log("\n📝 Enhancing PR description...\n");
|
|
264
|
+
const instructions = await this.promptBuilder.buildDescriptionEnhancementInstructions(request, this.config);
|
|
265
|
+
const toolContext = this.createToolContext(sessionId, request);
|
|
266
|
+
this.neurolink.setToolContext(toolContext);
|
|
267
|
+
const aiResponse = await this.neurolink.generate({
|
|
268
|
+
input: { text: instructions },
|
|
269
|
+
provider: this.config.ai.provider,
|
|
270
|
+
model: this.config.ai.model,
|
|
271
|
+
context: {
|
|
272
|
+
sessionId,
|
|
273
|
+
userId: this.generateUserId(request),
|
|
274
|
+
operation: "description-enhancement",
|
|
275
|
+
},
|
|
276
|
+
enableAnalytics: true,
|
|
277
|
+
});
|
|
278
|
+
console.log("✅ Description enhanced successfully\n");
|
|
279
|
+
return {
|
|
280
|
+
success: true,
|
|
281
|
+
enhanced: true,
|
|
282
|
+
sessionId,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
this.sessionManager.failSession(sessionId, error);
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Get session information
|
|
292
|
+
*/
|
|
293
|
+
getSession(sessionId) {
|
|
294
|
+
return this.sessionManager.getSession(sessionId);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get session statistics
|
|
298
|
+
*/
|
|
299
|
+
getSessionStats(sessionId) {
|
|
300
|
+
return this.sessionManager.getSessionStats(sessionId);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Export session data
|
|
304
|
+
*/
|
|
305
|
+
exportSession(sessionId) {
|
|
306
|
+
return this.sessionManager.exportSession(sessionId);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Create tool context for AI
|
|
310
|
+
*/
|
|
311
|
+
createToolContext(sessionId, request) {
|
|
312
|
+
return {
|
|
313
|
+
sessionId,
|
|
314
|
+
workspace: request.workspace,
|
|
315
|
+
repository: request.repository,
|
|
316
|
+
pullRequestId: request.pullRequestId,
|
|
317
|
+
branch: request.branch,
|
|
318
|
+
dryRun: request.dryRun || false,
|
|
319
|
+
metadata: {
|
|
320
|
+
yamaVersion: "2.0.0",
|
|
321
|
+
startTime: new Date().toISOString(),
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Parse AI response into structured review result
|
|
327
|
+
*/
|
|
328
|
+
parseReviewResult(aiResponse, startTime, sessionId) {
|
|
329
|
+
const session = this.sessionManager.getSession(sessionId);
|
|
330
|
+
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
331
|
+
// Extract decision from AI response or tool calls
|
|
332
|
+
const decision = this.extractDecision(aiResponse, session);
|
|
333
|
+
// Calculate statistics from session tool calls
|
|
334
|
+
const statistics = this.calculateStatistics(session);
|
|
335
|
+
return {
|
|
336
|
+
prId: session.request.pullRequestId || 0,
|
|
337
|
+
decision,
|
|
338
|
+
statistics,
|
|
339
|
+
summary: this.extractSummary(aiResponse),
|
|
340
|
+
duration,
|
|
341
|
+
tokenUsage: {
|
|
342
|
+
input: aiResponse.usage?.inputTokens || 0,
|
|
343
|
+
output: aiResponse.usage?.outputTokens || 0,
|
|
344
|
+
total: aiResponse.usage?.totalTokens || 0,
|
|
345
|
+
},
|
|
346
|
+
costEstimate: this.calculateCost(aiResponse.usage),
|
|
347
|
+
sessionId,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Extract decision from AI response
|
|
352
|
+
*/
|
|
353
|
+
extractDecision(aiResponse, session) {
|
|
354
|
+
// Check if AI called approve_pull_request or request_changes
|
|
355
|
+
const toolCalls = session.toolCalls || [];
|
|
356
|
+
const approveCall = toolCalls.find((tc) => tc.toolName === "approve_pull_request");
|
|
357
|
+
const requestChangesCall = toolCalls.find((tc) => tc.toolName === "request_changes");
|
|
358
|
+
if (approveCall) {
|
|
359
|
+
return "APPROVED";
|
|
360
|
+
}
|
|
361
|
+
if (requestChangesCall) {
|
|
362
|
+
return "BLOCKED";
|
|
363
|
+
}
|
|
364
|
+
// Default to changes requested if unclear
|
|
365
|
+
return "CHANGES_REQUESTED";
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Calculate statistics from session
|
|
369
|
+
*/
|
|
370
|
+
calculateStatistics(session) {
|
|
371
|
+
const toolCalls = session.toolCalls || [];
|
|
372
|
+
// Count file diffs read
|
|
373
|
+
const filesReviewed = toolCalls.filter((tc) => tc.toolName === "get_pull_request_diff").length;
|
|
374
|
+
// Try to extract issue counts from comments
|
|
375
|
+
const commentCalls = toolCalls.filter((tc) => tc.toolName === "add_comment");
|
|
376
|
+
const issuesFound = this.extractIssueCountsFromComments(commentCalls);
|
|
377
|
+
return {
|
|
378
|
+
filesReviewed,
|
|
379
|
+
issuesFound,
|
|
380
|
+
requirementCoverage: 0, // Would need to parse from Jira comparison
|
|
381
|
+
codeQualityScore: 0, // Would need AI to provide this
|
|
382
|
+
toolCallsMade: toolCalls.length,
|
|
383
|
+
cacheHits: 0,
|
|
384
|
+
totalComments: commentCalls.length,
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Extract issue counts from comment tool calls
|
|
389
|
+
*/
|
|
390
|
+
extractIssueCountsFromComments(commentCalls) {
|
|
391
|
+
const counts = {
|
|
392
|
+
critical: 0,
|
|
393
|
+
major: 0,
|
|
394
|
+
minor: 0,
|
|
395
|
+
suggestions: 0,
|
|
396
|
+
};
|
|
397
|
+
commentCalls.forEach((call) => {
|
|
398
|
+
const text = call.args?.comment_text || "";
|
|
399
|
+
if (text.includes("🔒 CRITICAL") || text.includes("CRITICAL:")) {
|
|
400
|
+
counts.critical++;
|
|
401
|
+
}
|
|
402
|
+
else if (text.includes("⚠️ MAJOR") || text.includes("MAJOR:")) {
|
|
403
|
+
counts.major++;
|
|
404
|
+
}
|
|
405
|
+
else if (text.includes("💡 MINOR") || text.includes("MINOR:")) {
|
|
406
|
+
counts.minor++;
|
|
407
|
+
}
|
|
408
|
+
else if (text.includes("💬 SUGGESTION") ||
|
|
409
|
+
text.includes("SUGGESTION:")) {
|
|
410
|
+
counts.suggestions++;
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
return counts;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Extract summary from AI response
|
|
417
|
+
*/
|
|
418
|
+
extractSummary(aiResponse) {
|
|
419
|
+
return aiResponse.content || aiResponse.text || "Review completed";
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Calculate cost estimate from token usage
|
|
423
|
+
*/
|
|
424
|
+
calculateCost(usage) {
|
|
425
|
+
if (!usage) {
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
// Rough estimates (update with actual pricing)
|
|
429
|
+
const inputCostPer1M = 0.25; // $0.25 per 1M input tokens (Gemini 2.0 Flash)
|
|
430
|
+
const outputCostPer1M = 1.0; // $1.00 per 1M output tokens
|
|
431
|
+
const inputCost = (usage.inputTokens / 1_000_000) * inputCostPer1M;
|
|
432
|
+
const outputCost = (usage.outputTokens / 1_000_000) * outputCostPer1M;
|
|
433
|
+
return Number((inputCost + outputCost).toFixed(4));
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Generate userId for NeuroLink context from repository and branch/PR
|
|
437
|
+
*/
|
|
438
|
+
generateUserId(request) {
|
|
439
|
+
const repo = request.repository;
|
|
440
|
+
const identifier = request.branch || `pr-${request.pullRequestId}`;
|
|
441
|
+
return `${repo}-${identifier}`;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Initialize NeuroLink with observability configuration
|
|
445
|
+
*/
|
|
446
|
+
initializeNeurolink() {
|
|
447
|
+
try {
|
|
448
|
+
const observabilityConfig = buildObservabilityConfigFromEnv();
|
|
449
|
+
const neurolinkConfig = {
|
|
450
|
+
conversationMemory: this.config.ai.conversationMemory,
|
|
451
|
+
};
|
|
452
|
+
if (observabilityConfig) {
|
|
453
|
+
// Validate observability config
|
|
454
|
+
if (!validateObservabilityConfig(observabilityConfig)) {
|
|
455
|
+
throw new Error("Invalid observability configuration");
|
|
456
|
+
}
|
|
457
|
+
neurolinkConfig.observability = observabilityConfig;
|
|
458
|
+
console.log(" 📊 Observability enabled (Langfuse tracing active)");
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
console.log(" 📊 Observability not configured (set LANGFUSE_* env vars to enable)");
|
|
462
|
+
}
|
|
463
|
+
const neurolink = new NeuroLink(neurolinkConfig);
|
|
464
|
+
return neurolink;
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
console.error("❌ Failed to initialize NeuroLink:", error.message);
|
|
468
|
+
throw new Error(`NeuroLink initialization failed: ${error}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Ensure orchestrator is initialized
|
|
473
|
+
*/
|
|
474
|
+
async ensureInitialized() {
|
|
475
|
+
if (!this.initialized) {
|
|
476
|
+
await this.initialize();
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Show Yama V2 banner
|
|
481
|
+
*/
|
|
482
|
+
showBanner() {
|
|
483
|
+
if (!this.config?.display?.showBanner) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
console.log("\n" + "═".repeat(60));
|
|
487
|
+
console.log(`
|
|
488
|
+
⚔️ YAMA V2 - AI-Native Code Review Guardian
|
|
489
|
+
|
|
490
|
+
Version: 2.0.0
|
|
491
|
+
Mode: Autonomous AI-Powered Review
|
|
492
|
+
Powered by: NeuroLink + MCP Tools
|
|
493
|
+
`);
|
|
494
|
+
console.log("═".repeat(60) + "\n");
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Log review start
|
|
498
|
+
*/
|
|
499
|
+
logReviewStart(request, sessionId) {
|
|
500
|
+
console.log("\n" + "─".repeat(60));
|
|
501
|
+
console.log(`📋 Review Session Started`);
|
|
502
|
+
console.log("─".repeat(60));
|
|
503
|
+
console.log(` Session ID: ${sessionId}`);
|
|
504
|
+
console.log(` Workspace: ${request.workspace}`);
|
|
505
|
+
console.log(` Repository: ${request.repository}`);
|
|
506
|
+
console.log(` PR: ${request.pullRequestId || request.branch}`);
|
|
507
|
+
console.log(` Mode: ${request.dryRun ? "🔵 DRY RUN" : "🔴 LIVE"}`);
|
|
508
|
+
console.log("─".repeat(60) + "\n");
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Log review completion
|
|
512
|
+
*/
|
|
513
|
+
logReviewComplete(result) {
|
|
514
|
+
console.log("\n" + "═".repeat(60));
|
|
515
|
+
console.log(`✅ Review Completed Successfully`);
|
|
516
|
+
console.log("═".repeat(60));
|
|
517
|
+
console.log(` Decision: ${this.formatDecision(result.decision)}`);
|
|
518
|
+
console.log(` Duration: ${result.duration}s`);
|
|
519
|
+
console.log(` Files Reviewed: ${result.statistics.filesReviewed}`);
|
|
520
|
+
console.log(` Issues Found:`);
|
|
521
|
+
console.log(` 🔒 CRITICAL: ${result.statistics.issuesFound.critical}`);
|
|
522
|
+
console.log(` ⚠️ MAJOR: ${result.statistics.issuesFound.major}`);
|
|
523
|
+
console.log(` 💡 MINOR: ${result.statistics.issuesFound.minor}`);
|
|
524
|
+
console.log(` 💬 SUGGESTIONS: ${result.statistics.issuesFound.suggestions}`);
|
|
525
|
+
console.log(` Token Usage: ${result.tokenUsage.total.toLocaleString()}`);
|
|
526
|
+
console.log(` Cost Estimate: $${result.costEstimate.toFixed(4)}`);
|
|
527
|
+
console.log("═".repeat(60) + "\n");
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Format decision for display
|
|
531
|
+
*/
|
|
532
|
+
formatDecision(decision) {
|
|
533
|
+
switch (decision) {
|
|
534
|
+
case "APPROVED":
|
|
535
|
+
return "✅ APPROVED";
|
|
536
|
+
case "BLOCKED":
|
|
537
|
+
return "🚫 BLOCKED";
|
|
538
|
+
case "CHANGES_REQUESTED":
|
|
539
|
+
return "⚠️ CHANGES REQUESTED";
|
|
540
|
+
default:
|
|
541
|
+
return decision;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// Export factory function
|
|
546
|
+
export function createYamaV2() {
|
|
547
|
+
return new YamaV2Orchestrator();
|
|
548
|
+
}
|
|
549
|
+
//# sourceMappingURL=YamaV2Orchestrator.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Enhancement System Prompt
|
|
3
|
+
* Generic, project-agnostic instructions for PR description enhancement
|
|
4
|
+
* Project-specific sections and requirements come from config
|
|
5
|
+
*/
|
|
6
|
+
export declare const ENHANCEMENT_SYSTEM_PROMPT = "\n<yama-enhancement-system>\n <identity>\n <role>Technical Documentation Writer</role>\n <focus>Complete PR descriptions with comprehensive, accurate information</focus>\n </identity>\n\n <core-rules>\n <rule priority=\"CRITICAL\" id=\"complete-all-sections\">\n <title>Complete All Required Sections</title>\n <description>\n Fill every required section defined in project configuration.\n For sections that don't apply: explain why with \"Not applicable because {reason}\".\n Never leave sections empty or use generic \"N/A\".\n </description>\n </rule>\n\n <rule priority=\"CRITICAL\" id=\"extract-from-code\">\n <title>Extract Information from Code Changes</title>\n <description>\n Analyze the diff to find configuration changes, API modifications, dependencies.\n Use search_code() to find patterns in the codebase.\n Document what actually changed, not assumptions.\n </description>\n </rule>\n\n <rule priority=\"MAJOR\" id=\"structured-output\">\n <title>Follow Section Structure</title>\n <description>\n Use exact section headers from configuration.\n Maintain consistent formatting.\n Use markdown for readability.\n </description>\n </rule>\n\n <rule priority=\"MAJOR\" id=\"clean-output\">\n <title>Clean Output Only</title>\n <description>\n Return ONLY the enhanced PR description content.\n Do NOT include meta-commentary like \"Here is...\" or explanations.\n Start directly with the enhanced content.\n </description>\n </rule>\n\n <rule priority=\"MAJOR\" id=\"preserve-existing\">\n <title>Preserve User Content When Possible</title>\n <description>\n If preserveContent is enabled, merge existing description with enhancements.\n Don't overwrite manually written sections unless improving them.\n </description>\n </rule>\n </core-rules>\n\n <workflow>\n <phase name=\"analysis\">\n <step>Read PR diff to understand all changes</step>\n <step>Use search_code() to find configuration patterns</step>\n <step>Identify files modified, APIs changed, dependencies added</step>\n <step>Extract information for each required section</step>\n </phase>\n\n <phase name=\"extraction\">\n <step>For each required section from config:</step>\n <step>- Extract relevant information from diff and codebase</step>\n <step>- Use search_code() if patterns need to be found</step>\n <step>- If not applicable: write clear reason why</step>\n </phase>\n\n <phase name=\"composition\">\n <step>Build description with all sections in order</step>\n <step>Verify completeness against config requirements</step>\n <step>Format as clean markdown</step>\n <step>Ensure no meta-commentary included</step>\n </phase>\n\n <phase name=\"update\">\n <step>Call update_pull_request() with enhanced description</step>\n </phase>\n </workflow>\n\n <tools>\n <tool name=\"get_pull_request\">\n <purpose>Get current PR description and context</purpose>\n <usage>Read existing description to preserve user content</usage>\n </tool>\n\n <tool name=\"get_pull_request_diff\">\n <purpose>Analyze code changes to extract information</purpose>\n <usage>Find what files changed, what was modified</usage>\n </tool>\n\n <tool name=\"search_code\">\n <purpose>Find patterns, configurations, similar implementations</purpose>\n <examples>\n <example>Search for configuration getters to find config keys</example>\n <example>Search for API endpoint definitions</example>\n <example>Search for test file patterns</example>\n <example>Search for environment variable usage</example>\n <example>Search for database migration patterns</example>\n </examples>\n </tool>\n\n <tool name=\"list_directory_content\">\n <purpose>Understand project structure</purpose>\n <usage>Find related files, understand organization</usage>\n </tool>\n\n <tool name=\"get_file_content\">\n <purpose>Read specific files for context</purpose>\n <usage>Read config files, package.json, migration files</usage>\n </tool>\n\n <tool name=\"update_pull_request\">\n <purpose>Update PR description with enhanced content</purpose>\n <parameters>\n <param name=\"description\">Enhanced markdown description</param>\n </parameters>\n </tool>\n </tools>\n\n <section-completion-guide>\n <guideline>For applicable sections: Be specific and detailed</guideline>\n <guideline>For non-applicable sections: Write \"Not applicable for this PR because {specific reason}\"</guideline>\n <guideline>Never use generic \"N/A\" without explanation</guideline>\n <guideline>Link changes to business/technical value</guideline>\n <guideline>Include file references where relevant (e.g., \"Modified src/auth/Login.tsx\")</guideline>\n <guideline>Use lists and checkboxes for better readability</guideline>\n </section-completion-guide>\n\n <extraction-strategies>\n <strategy name=\"configuration-changes\">\n <description>How to find and document configuration changes</description>\n <steps>\n <step>Search diff for configuration file changes (config.yaml, .env.example, etc.)</step>\n <step>Use search_code() to find configuration getters in code</step>\n <step>Document key names and their purpose</step>\n <step>Explain impact of configuration changes</step>\n </steps>\n </strategy>\n\n <strategy name=\"api-modifications\">\n <description>How to identify API changes</description>\n <steps>\n <step>Look for route definitions, endpoint handlers in diff</step>\n <step>Search for API client calls, fetch/axios usage</step>\n <step>Document endpoints added/modified/removed</step>\n <step>Note request/response format changes</step>\n </steps>\n </strategy>\n\n <strategy name=\"database-changes\">\n <description>How to find database alterations</description>\n <steps>\n <step>Look for migration files in diff</step>\n <step>Search for schema definitions, model changes</step>\n <step>Document table/column changes</step>\n <step>Note any data migration requirements</step>\n </steps>\n </strategy>\n\n <strategy name=\"dependency-changes\">\n <description>How to document library updates</description>\n <steps>\n <step>Check package.json, requirements.txt, etc. in diff</step>\n <step>Document added/updated/removed dependencies</step>\n <step>Note version changes and breaking changes</step>\n <step>Explain why dependency was added/updated</step>\n </steps>\n </strategy>\n\n <strategy name=\"testing-coverage\">\n <description>How to document testing</description>\n <steps>\n <step>Look for test files in diff (*.test.*, *.spec.*)</step>\n <step>Document test scenarios covered</step>\n <step>Note integration/unit/e2e tests added</step>\n <step>Create testing checklist for reviewers</step>\n </steps>\n </strategy>\n </extraction-strategies>\n\n <output-format>\n <requirement>Return enhanced description as clean markdown</requirement>\n <requirement>No meta-commentary or wrapper text</requirement>\n <requirement>Start directly with section headers</requirement>\n <requirement>Use consistent formatting throughout</requirement>\n <requirement>Follow section order from configuration</requirement>\n </output-format>\n\n <formatting-guidelines>\n <guideline>Use ## for section headers</guideline>\n <guideline>Use - or * for bulleted lists</guideline>\n <guideline>Use - [ ] for checkboxes in test cases</guideline>\n <guideline>Use `code` for inline code references</guideline>\n <guideline>Use ```language for code blocks</guideline>\n <guideline>Use **bold** for emphasis on important items</guideline>\n <guideline>Use tables for structured data when appropriate</guideline>\n </formatting-guidelines>\n\n <anti-patterns>\n <dont>Start with \"Here is the enhanced description...\"</dont>\n <dont>Include explanatory wrapper text</dont>\n <dont>Use generic \"N/A\" without explanation</dont>\n <dont>Skip sections even if they seem not applicable</dont>\n <dont>Make assumptions - verify with code search</dont>\n <dont>Copy code changes verbatim - summarize meaningfully</dont>\n </anti-patterns>\n</yama-enhancement-system>\n";
|
|
7
|
+
export default ENHANCEMENT_SYSTEM_PROMPT;
|
|
8
|
+
//# sourceMappingURL=EnhancementSystemPrompt.d.ts.map
|