@juspay/yama 1.6.0 → 2.1.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 +46 -0
- package/README.md +311 -685
- package/dist/cli/v2.cli.d.ts +13 -0
- package/dist/cli/v2.cli.js +359 -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 +187 -0
- package/dist/v2/core/LearningOrchestrator.d.ts +65 -0
- package/dist/v2/core/LearningOrchestrator.js +499 -0
- package/dist/v2/core/MCPServerManager.d.ts +22 -0
- package/dist/v2/core/MCPServerManager.js +100 -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/learning/FeedbackExtractor.d.ts +46 -0
- package/dist/v2/learning/FeedbackExtractor.js +237 -0
- package/dist/v2/learning/KnowledgeBaseManager.d.ts +91 -0
- package/dist/v2/learning/KnowledgeBaseManager.js +475 -0
- package/dist/v2/learning/types.d.ts +121 -0
- package/dist/v2/learning/types.js +15 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.d.ts +8 -0
- package/dist/v2/prompts/EnhancementSystemPrompt.js +216 -0
- package/dist/v2/prompts/LangfusePromptManager.d.ts +48 -0
- package/dist/v2/prompts/LangfusePromptManager.js +144 -0
- package/dist/v2/prompts/LearningSystemPrompt.d.ts +11 -0
- package/dist/v2/prompts/LearningSystemPrompt.js +180 -0
- package/dist/v2/prompts/PromptBuilder.d.ts +45 -0
- package/dist/v2/prompts/PromptBuilder.js +257 -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 +141 -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 +16 -10
- package/yama.config.example.yaml +259 -204
- 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 -480
- 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 -70
- package/dist/features/DescriptionEnhancer.js +0 -511
- 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 -602
- 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,475 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge Base Manager
|
|
3
|
+
* Handles reading, writing, and parsing the knowledge base markdown file
|
|
4
|
+
*/
|
|
5
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { dirname, join } from "path";
|
|
8
|
+
import { createHash } from "crypto";
|
|
9
|
+
import { execFile } from "child_process";
|
|
10
|
+
import { promisify } from "util";
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
import { CATEGORY_SECTION_NAMES, } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Template for a new knowledge base file
|
|
15
|
+
*/
|
|
16
|
+
const KNOWLEDGE_BASE_TEMPLATE = `# Project Knowledge Base
|
|
17
|
+
> Learned patterns, preferences, and guidelines from team feedback
|
|
18
|
+
|
|
19
|
+
## Metadata
|
|
20
|
+
- Last Updated: {{TIMESTAMP}}
|
|
21
|
+
- Total Learnings: 0
|
|
22
|
+
- Last Summarization: N/A
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## False Positives (Don't Flag These)
|
|
27
|
+
|
|
28
|
+
Things AI incorrectly flagged as issues. Avoid repeating these mistakes.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Style Preferences (Team Conventions)
|
|
33
|
+
|
|
34
|
+
Project-specific coding conventions that differ from general best practices.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Missed Issues (Should Have Flagged)
|
|
39
|
+
|
|
40
|
+
Patterns AI missed that should be caught in future reviews.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Context & Domain Knowledge
|
|
45
|
+
|
|
46
|
+
Project-specific context AI needs for accurate reviews.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Enhancement Guidelines
|
|
51
|
+
|
|
52
|
+
How AI should provide suggestions for this project.
|
|
53
|
+
|
|
54
|
+
`;
|
|
55
|
+
export class KnowledgeBaseManager {
|
|
56
|
+
config;
|
|
57
|
+
projectRoot;
|
|
58
|
+
constructor(config, projectRoot) {
|
|
59
|
+
this.config = config;
|
|
60
|
+
this.projectRoot = projectRoot || process.cwd();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the full path to the knowledge base file
|
|
64
|
+
*/
|
|
65
|
+
getFilePath() {
|
|
66
|
+
return join(this.projectRoot, this.config.path);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Check if knowledge base file exists
|
|
70
|
+
*/
|
|
71
|
+
exists() {
|
|
72
|
+
return existsSync(this.getFilePath());
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Load and parse the knowledge base file
|
|
76
|
+
*/
|
|
77
|
+
async load() {
|
|
78
|
+
if (!this.exists()) {
|
|
79
|
+
return this.createEmptyKnowledgeBase();
|
|
80
|
+
}
|
|
81
|
+
const content = await readFile(this.getFilePath(), "utf-8");
|
|
82
|
+
return this.parseMarkdown(content);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Append new learnings to the knowledge base
|
|
86
|
+
* Returns count of learnings actually added (excludes duplicates)
|
|
87
|
+
*/
|
|
88
|
+
async append(learnings) {
|
|
89
|
+
const kb = await this.load();
|
|
90
|
+
let addedCount = 0;
|
|
91
|
+
for (const learning of learnings) {
|
|
92
|
+
// Check for duplicates
|
|
93
|
+
if (this.isDuplicate(kb, learning)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// Get or create section
|
|
97
|
+
let section = kb.sections.get(learning.category);
|
|
98
|
+
if (!section) {
|
|
99
|
+
section = {
|
|
100
|
+
category: learning.category,
|
|
101
|
+
subcategories: new Map(),
|
|
102
|
+
};
|
|
103
|
+
kb.sections.set(learning.category, section);
|
|
104
|
+
}
|
|
105
|
+
// Get or create subcategory
|
|
106
|
+
const subcatKey = learning.subcategory || "General";
|
|
107
|
+
let learningsList = section.subcategories.get(subcatKey);
|
|
108
|
+
if (!learningsList) {
|
|
109
|
+
learningsList = [];
|
|
110
|
+
section.subcategories.set(subcatKey, learningsList);
|
|
111
|
+
}
|
|
112
|
+
// Add the learning
|
|
113
|
+
learningsList.push(learning.learning);
|
|
114
|
+
addedCount++;
|
|
115
|
+
}
|
|
116
|
+
// Update metadata
|
|
117
|
+
kb.metadata.lastUpdated = new Date().toISOString();
|
|
118
|
+
kb.metadata.totalLearnings += addedCount;
|
|
119
|
+
// Write back
|
|
120
|
+
await this.write(kb);
|
|
121
|
+
return addedCount;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Write the knowledge base back to file
|
|
125
|
+
*/
|
|
126
|
+
async write(kb) {
|
|
127
|
+
const content = this.toMarkdown(kb);
|
|
128
|
+
const filePath = this.getFilePath();
|
|
129
|
+
const dir = dirname(filePath);
|
|
130
|
+
// Ensure directory exists
|
|
131
|
+
if (!existsSync(dir)) {
|
|
132
|
+
await mkdir(dir, { recursive: true });
|
|
133
|
+
}
|
|
134
|
+
await writeFile(filePath, content, "utf-8");
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Write raw markdown content directly to file
|
|
138
|
+
* Used by summarization to write AI-generated consolidated content
|
|
139
|
+
*/
|
|
140
|
+
async writeRaw(content) {
|
|
141
|
+
const filePath = this.getFilePath();
|
|
142
|
+
const dir = dirname(filePath);
|
|
143
|
+
// Ensure directory exists
|
|
144
|
+
if (!existsSync(dir)) {
|
|
145
|
+
await mkdir(dir, { recursive: true });
|
|
146
|
+
}
|
|
147
|
+
await writeFile(filePath, content, "utf-8");
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create a new knowledge base file from template
|
|
151
|
+
*/
|
|
152
|
+
async create() {
|
|
153
|
+
const content = KNOWLEDGE_BASE_TEMPLATE.replace("{{TIMESTAMP}}", new Date().toISOString());
|
|
154
|
+
const filePath = this.getFilePath();
|
|
155
|
+
const dir = dirname(filePath);
|
|
156
|
+
if (!existsSync(dir)) {
|
|
157
|
+
await mkdir(dir, { recursive: true });
|
|
158
|
+
}
|
|
159
|
+
await writeFile(filePath, content, "utf-8");
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get knowledge base content formatted for AI prompt injection
|
|
163
|
+
*/
|
|
164
|
+
async getForPrompt() {
|
|
165
|
+
if (!this.config.enabled || !this.exists()) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const content = await readFile(this.getFilePath(), "utf-8");
|
|
170
|
+
// Remove metadata section for cleaner prompt
|
|
171
|
+
const lines = content.split("\n");
|
|
172
|
+
const filteredLines = [];
|
|
173
|
+
let inMetadata = false;
|
|
174
|
+
for (const line of lines) {
|
|
175
|
+
if (line.startsWith("## Metadata")) {
|
|
176
|
+
inMetadata = true;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (inMetadata && line.startsWith("---")) {
|
|
180
|
+
inMetadata = false;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (!inMetadata) {
|
|
184
|
+
filteredLines.push(line);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return filteredLines.join("\n").trim();
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Get count of learnings in the knowledge base
|
|
195
|
+
*/
|
|
196
|
+
async getLearningCount() {
|
|
197
|
+
const kb = await this.load();
|
|
198
|
+
return kb.metadata.totalLearnings;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if summarization is needed based on entry count
|
|
202
|
+
*/
|
|
203
|
+
async needsSummarization() {
|
|
204
|
+
const count = await this.getLearningCount();
|
|
205
|
+
return count >= this.config.maxEntriesBeforeSummarization;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Commit the knowledge base file to git
|
|
209
|
+
* Uses execFile with argument arrays to prevent command injection
|
|
210
|
+
*/
|
|
211
|
+
async commit(prId, learningsAdded) {
|
|
212
|
+
const filePath = this.config.path; // Relative path for git
|
|
213
|
+
// Validate inputs to prevent injection
|
|
214
|
+
const safePrId = Math.floor(Number(prId));
|
|
215
|
+
const safeLearningsAdded = Math.floor(Number(learningsAdded));
|
|
216
|
+
if (!Number.isFinite(safePrId) || safePrId < 0) {
|
|
217
|
+
throw new Error("Invalid PR ID");
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
// Stage the file using execFile with args array (safe from injection)
|
|
221
|
+
await execFileAsync("git", ["add", filePath], { cwd: this.projectRoot });
|
|
222
|
+
// Create commit message
|
|
223
|
+
const commitMessage = `chore(yama): update knowledge base from PR #${safePrId}
|
|
224
|
+
|
|
225
|
+
Added ${safeLearningsAdded} new learning${safeLearningsAdded !== 1 ? "s" : ""}.
|
|
226
|
+
|
|
227
|
+
🤖 Generated with Yama`;
|
|
228
|
+
// Commit using execFile with args array (safe from injection)
|
|
229
|
+
await execFileAsync("git", ["commit", "-m", commitMessage], {
|
|
230
|
+
cwd: this.projectRoot,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
throw new Error(`Failed to commit knowledge base: ${error instanceof Error ? error.message : String(error)}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Generate a hash for deduplication
|
|
239
|
+
*/
|
|
240
|
+
generateLearningId(learning) {
|
|
241
|
+
return createHash("md5")
|
|
242
|
+
.update(learning.toLowerCase().trim())
|
|
243
|
+
.digest("hex")
|
|
244
|
+
.substring(0, 12);
|
|
245
|
+
}
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Private Methods
|
|
248
|
+
// ============================================================================
|
|
249
|
+
/**
|
|
250
|
+
* Create an empty knowledge base structure
|
|
251
|
+
*/
|
|
252
|
+
createEmptyKnowledgeBase() {
|
|
253
|
+
return {
|
|
254
|
+
metadata: {
|
|
255
|
+
lastUpdated: new Date().toISOString(),
|
|
256
|
+
totalLearnings: 0,
|
|
257
|
+
},
|
|
258
|
+
sections: new Map(),
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Check if a learning already exists in the knowledge base
|
|
263
|
+
*/
|
|
264
|
+
isDuplicate(kb, learning) {
|
|
265
|
+
const section = kb.sections.get(learning.category);
|
|
266
|
+
if (!section) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
const normalizedNew = learning.learning.toLowerCase().trim();
|
|
270
|
+
for (const [, learnings] of section.subcategories) {
|
|
271
|
+
for (const existing of learnings) {
|
|
272
|
+
const normalizedExisting = existing.toLowerCase().trim();
|
|
273
|
+
// Check for exact match or high similarity
|
|
274
|
+
if (normalizedExisting === normalizedNew ||
|
|
275
|
+
this.isSimilar(normalizedExisting, normalizedNew)) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Check if two learnings are similar (simple similarity check)
|
|
284
|
+
*/
|
|
285
|
+
isSimilar(a, b) {
|
|
286
|
+
// Remove common words and check overlap
|
|
287
|
+
const wordsA = new Set(a.split(/\s+/).filter((w) => w.length > 3));
|
|
288
|
+
const wordsB = new Set(b.split(/\s+/).filter((w) => w.length > 3));
|
|
289
|
+
if (wordsA.size === 0 || wordsB.size === 0) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
let overlap = 0;
|
|
293
|
+
for (const word of wordsA) {
|
|
294
|
+
if (wordsB.has(word)) {
|
|
295
|
+
overlap++;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const similarity = overlap / Math.max(wordsA.size, wordsB.size);
|
|
299
|
+
return similarity > 0.7; // 70% word overlap = similar
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Parse markdown content into structured knowledge base
|
|
303
|
+
*/
|
|
304
|
+
parseMarkdown(content) {
|
|
305
|
+
const kb = this.createEmptyKnowledgeBase();
|
|
306
|
+
const lines = content.split("\n");
|
|
307
|
+
let currentCategory = null;
|
|
308
|
+
let currentSubcategory = "General";
|
|
309
|
+
let inMetadata = false;
|
|
310
|
+
for (const line of lines) {
|
|
311
|
+
const trimmed = line.trim();
|
|
312
|
+
// Parse metadata
|
|
313
|
+
if (trimmed.startsWith("## Metadata")) {
|
|
314
|
+
inMetadata = true;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (inMetadata) {
|
|
318
|
+
if (trimmed.startsWith("---")) {
|
|
319
|
+
inMetadata = false;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (trimmed.startsWith("- Last Updated:")) {
|
|
323
|
+
kb.metadata.lastUpdated = trimmed
|
|
324
|
+
.replace("- Last Updated:", "")
|
|
325
|
+
.trim();
|
|
326
|
+
}
|
|
327
|
+
else if (trimmed.startsWith("- Total Learnings:")) {
|
|
328
|
+
kb.metadata.totalLearnings =
|
|
329
|
+
parseInt(trimmed.replace("- Total Learnings:", "").trim(), 10) || 0;
|
|
330
|
+
}
|
|
331
|
+
else if (trimmed.startsWith("- Last Summarization:")) {
|
|
332
|
+
const value = trimmed.replace("- Last Summarization:", "").trim();
|
|
333
|
+
if (value !== "N/A") {
|
|
334
|
+
kb.metadata.lastSummarization = value;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
// Parse category headers (## level)
|
|
340
|
+
if (trimmed.startsWith("## ")) {
|
|
341
|
+
const sectionName = trimmed.substring(3);
|
|
342
|
+
currentCategory = this.categoryFromSectionName(sectionName);
|
|
343
|
+
currentSubcategory = "General";
|
|
344
|
+
if (currentCategory) {
|
|
345
|
+
kb.sections.set(currentCategory, {
|
|
346
|
+
category: currentCategory,
|
|
347
|
+
subcategories: new Map(),
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
// Parse subcategory headers (### level)
|
|
353
|
+
if (trimmed.startsWith("### ")) {
|
|
354
|
+
currentSubcategory = trimmed.substring(4);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
// Parse learning entries (- bullet points)
|
|
358
|
+
if (trimmed.startsWith("- ") && currentCategory) {
|
|
359
|
+
const learning = trimmed.substring(2);
|
|
360
|
+
const section = kb.sections.get(currentCategory);
|
|
361
|
+
if (section) {
|
|
362
|
+
let learnings = section.subcategories.get(currentSubcategory);
|
|
363
|
+
if (!learnings) {
|
|
364
|
+
learnings = [];
|
|
365
|
+
section.subcategories.set(currentSubcategory, learnings);
|
|
366
|
+
}
|
|
367
|
+
learnings.push(learning);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return kb;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Convert category section name back to category enum
|
|
375
|
+
*/
|
|
376
|
+
categoryFromSectionName(name) {
|
|
377
|
+
for (const [category, sectionName] of Object.entries(CATEGORY_SECTION_NAMES)) {
|
|
378
|
+
if (name.includes(sectionName) || sectionName.includes(name)) {
|
|
379
|
+
return category;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Fallback matching
|
|
383
|
+
const lowerName = name.toLowerCase();
|
|
384
|
+
if (lowerName.includes("false positive") ||
|
|
385
|
+
lowerName.includes("don't flag")) {
|
|
386
|
+
return "false_positive";
|
|
387
|
+
}
|
|
388
|
+
if (lowerName.includes("missed") || lowerName.includes("should have")) {
|
|
389
|
+
return "missed_issue";
|
|
390
|
+
}
|
|
391
|
+
if (lowerName.includes("style") || lowerName.includes("convention")) {
|
|
392
|
+
return "style_preference";
|
|
393
|
+
}
|
|
394
|
+
if (lowerName.includes("context") || lowerName.includes("domain")) {
|
|
395
|
+
return "domain_context";
|
|
396
|
+
}
|
|
397
|
+
if (lowerName.includes("enhancement") || lowerName.includes("guideline")) {
|
|
398
|
+
return "enhancement_guideline";
|
|
399
|
+
}
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Convert knowledge base structure to markdown
|
|
404
|
+
*/
|
|
405
|
+
toMarkdown(kb) {
|
|
406
|
+
const lines = [];
|
|
407
|
+
// Header
|
|
408
|
+
lines.push("# Project Knowledge Base");
|
|
409
|
+
lines.push("> Learned patterns, preferences, and guidelines from team feedback");
|
|
410
|
+
lines.push("");
|
|
411
|
+
// Metadata
|
|
412
|
+
lines.push("## Metadata");
|
|
413
|
+
lines.push(`- Last Updated: ${kb.metadata.lastUpdated}`);
|
|
414
|
+
lines.push(`- Total Learnings: ${kb.metadata.totalLearnings}`);
|
|
415
|
+
lines.push(`- Last Summarization: ${kb.metadata.lastSummarization || "N/A"}`);
|
|
416
|
+
lines.push("");
|
|
417
|
+
lines.push("---");
|
|
418
|
+
lines.push("");
|
|
419
|
+
// Sections in order
|
|
420
|
+
const categoryOrder = [
|
|
421
|
+
"false_positive",
|
|
422
|
+
"missed_issue",
|
|
423
|
+
"style_preference",
|
|
424
|
+
"domain_context",
|
|
425
|
+
"enhancement_guideline",
|
|
426
|
+
];
|
|
427
|
+
for (const category of categoryOrder) {
|
|
428
|
+
const sectionName = CATEGORY_SECTION_NAMES[category];
|
|
429
|
+
lines.push(`## ${sectionName}`);
|
|
430
|
+
lines.push("");
|
|
431
|
+
const section = kb.sections.get(category);
|
|
432
|
+
if (section && section.subcategories.size > 0) {
|
|
433
|
+
// Sort subcategories
|
|
434
|
+
const sortedSubcats = Array.from(section.subcategories.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
435
|
+
for (const [subcategory, learnings] of sortedSubcats) {
|
|
436
|
+
if (subcategory !== "General") {
|
|
437
|
+
lines.push(`### ${subcategory}`);
|
|
438
|
+
}
|
|
439
|
+
for (const learning of learnings) {
|
|
440
|
+
lines.push(`- ${learning}`);
|
|
441
|
+
}
|
|
442
|
+
lines.push("");
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
// Add description placeholder for empty sections
|
|
447
|
+
lines.push(this.getSectionDescription(category));
|
|
448
|
+
lines.push("");
|
|
449
|
+
}
|
|
450
|
+
lines.push("---");
|
|
451
|
+
lines.push("");
|
|
452
|
+
}
|
|
453
|
+
return lines.join("\n");
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Get description text for empty sections
|
|
457
|
+
*/
|
|
458
|
+
getSectionDescription(category) {
|
|
459
|
+
switch (category) {
|
|
460
|
+
case "false_positive":
|
|
461
|
+
return "Things AI incorrectly flagged as issues. Avoid repeating these mistakes.";
|
|
462
|
+
case "missed_issue":
|
|
463
|
+
return "Patterns AI missed that should be caught in future reviews.";
|
|
464
|
+
case "style_preference":
|
|
465
|
+
return "Project-specific coding conventions that differ from general best practices.";
|
|
466
|
+
case "domain_context":
|
|
467
|
+
return "Project-specific context AI needs for accurate reviews.";
|
|
468
|
+
case "enhancement_guideline":
|
|
469
|
+
return "How AI should provide suggestions for this project.";
|
|
470
|
+
default:
|
|
471
|
+
return "";
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
//# sourceMappingURL=KnowledgeBaseManager.js.map
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learning Types
|
|
3
|
+
* Type definitions for the knowledge base and learning extraction system
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Categories for extracted learnings
|
|
7
|
+
* Maps to sections in the knowledge base file
|
|
8
|
+
*/
|
|
9
|
+
export type LearningCategory = "false_positive" | "missed_issue" | "style_preference" | "domain_context" | "enhancement_guideline";
|
|
10
|
+
/**
|
|
11
|
+
* Human-readable category names for knowledge base sections
|
|
12
|
+
*/
|
|
13
|
+
export declare const CATEGORY_SECTION_NAMES: Record<LearningCategory, string>;
|
|
14
|
+
/**
|
|
15
|
+
* A single learning extracted from PR feedback
|
|
16
|
+
*/
|
|
17
|
+
export interface ExtractedLearning {
|
|
18
|
+
/** Unique hash for deduplication */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Category of the learning */
|
|
21
|
+
category: LearningCategory;
|
|
22
|
+
/** Sub-category within the section (e.g., "Async Patterns", "Security") */
|
|
23
|
+
subcategory?: string;
|
|
24
|
+
/** The actionable, project-level guideline */
|
|
25
|
+
learning: string;
|
|
26
|
+
/** File patterns where this applies (e.g., ["services/*.ts"]) */
|
|
27
|
+
filePatterns?: string[];
|
|
28
|
+
/** Severity for missed_issue learnings */
|
|
29
|
+
severity?: string;
|
|
30
|
+
/** Source info for traceability (not displayed in KB) */
|
|
31
|
+
sourceInfo?: {
|
|
32
|
+
prId: number;
|
|
33
|
+
timestamp: string;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Metadata section of the knowledge base
|
|
38
|
+
*/
|
|
39
|
+
export interface KnowledgeBaseMetadata {
|
|
40
|
+
lastUpdated: string;
|
|
41
|
+
totalLearnings: number;
|
|
42
|
+
lastSummarization?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* A section in the knowledge base (maps to a category)
|
|
46
|
+
*/
|
|
47
|
+
export interface KnowledgeBaseSection {
|
|
48
|
+
category: LearningCategory;
|
|
49
|
+
subcategories: Map<string, string[]>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Full parsed knowledge base structure
|
|
53
|
+
*/
|
|
54
|
+
export interface KnowledgeBase {
|
|
55
|
+
metadata: KnowledgeBaseMetadata;
|
|
56
|
+
sections: Map<LearningCategory, KnowledgeBaseSection>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Request for the learn command
|
|
60
|
+
*/
|
|
61
|
+
export interface LearnRequest {
|
|
62
|
+
workspace: string;
|
|
63
|
+
repository: string;
|
|
64
|
+
pullRequestId: number;
|
|
65
|
+
dryRun?: boolean;
|
|
66
|
+
commit?: boolean;
|
|
67
|
+
summarize?: boolean;
|
|
68
|
+
outputPath?: string;
|
|
69
|
+
outputFormat?: "md" | "json";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Result from the learn command
|
|
73
|
+
*/
|
|
74
|
+
export interface LearnResult {
|
|
75
|
+
success: boolean;
|
|
76
|
+
prId: number;
|
|
77
|
+
learningsFound: number;
|
|
78
|
+
learningsAdded: number;
|
|
79
|
+
learningsDuplicate: number;
|
|
80
|
+
learnings: ExtractedLearning[];
|
|
81
|
+
knowledgeBasePath?: string;
|
|
82
|
+
committed?: boolean;
|
|
83
|
+
summarized?: boolean;
|
|
84
|
+
error?: string;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A comment from a PR
|
|
88
|
+
*/
|
|
89
|
+
export interface PRComment {
|
|
90
|
+
id: number;
|
|
91
|
+
text: string;
|
|
92
|
+
author: {
|
|
93
|
+
name: string;
|
|
94
|
+
displayName?: string;
|
|
95
|
+
email?: string;
|
|
96
|
+
};
|
|
97
|
+
createdAt: string;
|
|
98
|
+
filePath?: string;
|
|
99
|
+
lineNumber?: number;
|
|
100
|
+
parentId?: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A pair of AI comment and developer reply
|
|
104
|
+
*/
|
|
105
|
+
export interface CommentPair {
|
|
106
|
+
aiComment: PRComment;
|
|
107
|
+
developerReply: PRComment;
|
|
108
|
+
filePath?: string;
|
|
109
|
+
codeContext?: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Output format from AI learning extraction
|
|
113
|
+
*/
|
|
114
|
+
export interface AIExtractionOutput {
|
|
115
|
+
category: LearningCategory;
|
|
116
|
+
subcategory?: string;
|
|
117
|
+
learning: string;
|
|
118
|
+
filePatterns?: string[];
|
|
119
|
+
reasoning: string;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Learning Types
|
|
3
|
+
* Type definitions for the knowledge base and learning extraction system
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Human-readable category names for knowledge base sections
|
|
7
|
+
*/
|
|
8
|
+
export const CATEGORY_SECTION_NAMES = {
|
|
9
|
+
false_positive: "False Positives (Don't Flag These)",
|
|
10
|
+
missed_issue: "Missed Issues (Should Have Flagged)",
|
|
11
|
+
style_preference: "Style Preferences (Team Conventions)",
|
|
12
|
+
domain_context: "Context & Domain Knowledge",
|
|
13
|
+
enhancement_guideline: "Enhancement Guidelines",
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=types.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
|