@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.
Files changed (79) hide show
  1. package/.mcp-config.example.json +26 -0
  2. package/CHANGELOG.md +46 -0
  3. package/README.md +311 -685
  4. package/dist/cli/v2.cli.d.ts +13 -0
  5. package/dist/cli/v2.cli.js +359 -0
  6. package/dist/index.d.ts +12 -13
  7. package/dist/index.js +18 -19
  8. package/dist/v2/config/ConfigLoader.d.ts +50 -0
  9. package/dist/v2/config/ConfigLoader.js +205 -0
  10. package/dist/v2/config/DefaultConfig.d.ts +9 -0
  11. package/dist/v2/config/DefaultConfig.js +187 -0
  12. package/dist/v2/core/LearningOrchestrator.d.ts +65 -0
  13. package/dist/v2/core/LearningOrchestrator.js +499 -0
  14. package/dist/v2/core/MCPServerManager.d.ts +22 -0
  15. package/dist/v2/core/MCPServerManager.js +100 -0
  16. package/dist/v2/core/SessionManager.d.ts +72 -0
  17. package/dist/v2/core/SessionManager.js +200 -0
  18. package/dist/v2/core/YamaV2Orchestrator.d.ts +112 -0
  19. package/dist/v2/core/YamaV2Orchestrator.js +549 -0
  20. package/dist/v2/learning/FeedbackExtractor.d.ts +46 -0
  21. package/dist/v2/learning/FeedbackExtractor.js +237 -0
  22. package/dist/v2/learning/KnowledgeBaseManager.d.ts +91 -0
  23. package/dist/v2/learning/KnowledgeBaseManager.js +475 -0
  24. package/dist/v2/learning/types.d.ts +121 -0
  25. package/dist/v2/learning/types.js +15 -0
  26. package/dist/v2/prompts/EnhancementSystemPrompt.d.ts +8 -0
  27. package/dist/v2/prompts/EnhancementSystemPrompt.js +216 -0
  28. package/dist/v2/prompts/LangfusePromptManager.d.ts +48 -0
  29. package/dist/v2/prompts/LangfusePromptManager.js +144 -0
  30. package/dist/v2/prompts/LearningSystemPrompt.d.ts +11 -0
  31. package/dist/v2/prompts/LearningSystemPrompt.js +180 -0
  32. package/dist/v2/prompts/PromptBuilder.d.ts +45 -0
  33. package/dist/v2/prompts/PromptBuilder.js +257 -0
  34. package/dist/v2/prompts/ReviewSystemPrompt.d.ts +8 -0
  35. package/dist/v2/prompts/ReviewSystemPrompt.js +270 -0
  36. package/dist/v2/types/config.types.d.ts +141 -0
  37. package/dist/v2/types/config.types.js +5 -0
  38. package/dist/v2/types/mcp.types.d.ts +191 -0
  39. package/dist/v2/types/mcp.types.js +6 -0
  40. package/dist/v2/types/v2.types.d.ts +182 -0
  41. package/dist/v2/types/v2.types.js +42 -0
  42. package/dist/v2/utils/ObservabilityConfig.d.ts +22 -0
  43. package/dist/v2/utils/ObservabilityConfig.js +48 -0
  44. package/package.json +16 -10
  45. package/yama.config.example.yaml +259 -204
  46. package/dist/cli/index.d.ts +0 -12
  47. package/dist/cli/index.js +0 -538
  48. package/dist/core/ContextGatherer.d.ts +0 -110
  49. package/dist/core/ContextGatherer.js +0 -470
  50. package/dist/core/Guardian.d.ts +0 -81
  51. package/dist/core/Guardian.js +0 -480
  52. package/dist/core/providers/BitbucketProvider.d.ts +0 -105
  53. package/dist/core/providers/BitbucketProvider.js +0 -489
  54. package/dist/features/CodeReviewer.d.ts +0 -173
  55. package/dist/features/CodeReviewer.js +0 -1707
  56. package/dist/features/DescriptionEnhancer.d.ts +0 -70
  57. package/dist/features/DescriptionEnhancer.js +0 -511
  58. package/dist/features/MultiInstanceProcessor.d.ts +0 -74
  59. package/dist/features/MultiInstanceProcessor.js +0 -360
  60. package/dist/types/index.d.ts +0 -624
  61. package/dist/types/index.js +0 -104
  62. package/dist/utils/Cache.d.ts +0 -103
  63. package/dist/utils/Cache.js +0 -444
  64. package/dist/utils/ConfigManager.d.ts +0 -88
  65. package/dist/utils/ConfigManager.js +0 -602
  66. package/dist/utils/ContentSimilarityService.d.ts +0 -74
  67. package/dist/utils/ContentSimilarityService.js +0 -215
  68. package/dist/utils/ExactDuplicateRemover.d.ts +0 -77
  69. package/dist/utils/ExactDuplicateRemover.js +0 -361
  70. package/dist/utils/Logger.d.ts +0 -31
  71. package/dist/utils/Logger.js +0 -214
  72. package/dist/utils/MemoryBankManager.d.ts +0 -73
  73. package/dist/utils/MemoryBankManager.js +0 -310
  74. package/dist/utils/ParallelProcessing.d.ts +0 -140
  75. package/dist/utils/ParallelProcessing.js +0 -333
  76. package/dist/utils/ProviderLimits.d.ts +0 -58
  77. package/dist/utils/ProviderLimits.js +0 -143
  78. package/dist/utils/RetryManager.d.ts +0 -78
  79. package/dist/utils/RetryManager.js +0 -205
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Feedback Extractor
3
+ * Identifies AI comments and developer replies from PR comments
4
+ */
5
+ /**
6
+ * Text patterns that identify AI-generated comments
7
+ */
8
+ const AI_TEXT_PATTERNS = [
9
+ "🔒 CRITICAL",
10
+ "⚠️ MAJOR",
11
+ "💡 MINOR",
12
+ "💬 SUGGESTION",
13
+ "**Issue**:",
14
+ "**Impact**:",
15
+ "**Fix**:",
16
+ "Yama Review",
17
+ "_Review powered by Yama_",
18
+ "🔒 SECURITY",
19
+ "⚡ PERFORMANCE",
20
+ ];
21
+ export class FeedbackExtractor {
22
+ config;
23
+ constructor(config) {
24
+ this.config = config;
25
+ }
26
+ /**
27
+ * Check if a comment is from AI based on author and text patterns
28
+ */
29
+ isAIComment(comment) {
30
+ // Check author name patterns
31
+ const authorMatch = this.config.aiAuthorPatterns.some((pattern) => comment.author.name.toLowerCase().includes(pattern.toLowerCase()));
32
+ if (authorMatch) {
33
+ return true;
34
+ }
35
+ // Check text patterns
36
+ const textMatch = AI_TEXT_PATTERNS.some((pattern) => comment.text.includes(pattern));
37
+ return textMatch;
38
+ }
39
+ /**
40
+ * Check if a comment is a developer reply (not from AI)
41
+ */
42
+ isDeveloperComment(comment) {
43
+ return !this.isAIComment(comment);
44
+ }
45
+ /**
46
+ * Extract AI comment + developer reply pairs from a list of comments
47
+ */
48
+ extractCommentPairs(comments) {
49
+ const pairs = [];
50
+ // Group comments by parent (for threaded comments)
51
+ const commentsByParent = new Map();
52
+ const commentById = new Map();
53
+ for (const comment of comments) {
54
+ commentById.set(comment.id, comment);
55
+ const parentKey = comment.parentId;
56
+ if (!commentsByParent.has(parentKey)) {
57
+ commentsByParent.set(parentKey, []);
58
+ }
59
+ commentsByParent.get(parentKey).push(comment);
60
+ }
61
+ // Find AI comments with developer replies
62
+ for (const comment of comments) {
63
+ if (this.isAIComment(comment)) {
64
+ // Look for developer replies to this AI comment
65
+ const replies = commentsByParent.get(comment.id) || [];
66
+ const developerReplies = replies.filter((r) => this.isDeveloperComment(r));
67
+ for (const reply of developerReplies) {
68
+ pairs.push({
69
+ aiComment: comment,
70
+ developerReply: reply,
71
+ filePath: comment.filePath || reply.filePath,
72
+ });
73
+ }
74
+ }
75
+ }
76
+ // Also check for inline comment threads (adjacent comments on same file/line)
77
+ const inlinePairs = this.findInlineCommentPairs(comments);
78
+ pairs.push(...inlinePairs);
79
+ return pairs;
80
+ }
81
+ /**
82
+ * Find comment pairs based on file/line proximity (for inline comments)
83
+ */
84
+ findInlineCommentPairs(comments) {
85
+ const pairs = [];
86
+ const processedIds = new Set();
87
+ // Group by file path and line
88
+ const byLocation = new Map();
89
+ for (const comment of comments) {
90
+ if (comment.filePath && comment.lineNumber) {
91
+ const key = `${comment.filePath}:${comment.lineNumber}`;
92
+ if (!byLocation.has(key)) {
93
+ byLocation.set(key, []);
94
+ }
95
+ byLocation.get(key).push(comment);
96
+ }
97
+ }
98
+ // Find AI + developer pairs at same location
99
+ for (const [, locationComments] of byLocation) {
100
+ // Sort by timestamp
101
+ locationComments.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
102
+ for (let i = 0; i < locationComments.length; i++) {
103
+ const aiComment = locationComments[i];
104
+ if (!this.isAIComment(aiComment) || processedIds.has(aiComment.id)) {
105
+ continue;
106
+ }
107
+ // Look for developer reply after this AI comment
108
+ for (let j = i + 1; j < locationComments.length; j++) {
109
+ const reply = locationComments[j];
110
+ if (this.isDeveloperComment(reply) && !processedIds.has(reply.id)) {
111
+ pairs.push({
112
+ aiComment,
113
+ developerReply: reply,
114
+ filePath: aiComment.filePath,
115
+ });
116
+ processedIds.add(aiComment.id);
117
+ processedIds.add(reply.id);
118
+ break;
119
+ }
120
+ }
121
+ }
122
+ }
123
+ return pairs;
124
+ }
125
+ /**
126
+ * Convert raw Bitbucket API comment to PRComment
127
+ */
128
+ convertBitbucketComment(rawComment) {
129
+ const author = rawComment.author;
130
+ const user = rawComment.user;
131
+ const authorData = author || user;
132
+ // Handle text extraction - Bitbucket Cloud uses content.raw
133
+ let text = rawComment.text;
134
+ if (!text) {
135
+ const content = rawComment.content;
136
+ if (typeof content === "string") {
137
+ text = content;
138
+ }
139
+ else if (content && typeof content === "object" && "raw" in content) {
140
+ text = content.raw;
141
+ }
142
+ }
143
+ return {
144
+ id: rawComment.id ||
145
+ parseInt(String(rawComment.id), 10) ||
146
+ Date.now(),
147
+ text: text || "",
148
+ author: {
149
+ name: authorData?.name ||
150
+ authorData?.username ||
151
+ authorData?.slug ||
152
+ "Unknown",
153
+ displayName: authorData?.displayName,
154
+ email: authorData?.emailAddress,
155
+ },
156
+ createdAt: rawComment.createdDate ||
157
+ rawComment.created_on ||
158
+ new Date().toISOString(),
159
+ filePath: rawComment.path ||
160
+ rawComment.inline?.path,
161
+ lineNumber: rawComment.line ||
162
+ rawComment.inline?.to,
163
+ parentId: rawComment.parent?.id || undefined,
164
+ };
165
+ }
166
+ /**
167
+ * Extract meaningful feedback from a comment pair
168
+ * Determines if the developer reply contains actionable feedback
169
+ */
170
+ hasActionableFeedback(pair) {
171
+ const reply = pair.developerReply.text.toLowerCase();
172
+ // Skip very short replies (likely just acknowledgments like "ok" or "done")
173
+ if (reply.length < 20) {
174
+ return false;
175
+ }
176
+ // Skip pure acknowledgments
177
+ const acknowledgments = [
178
+ "fixed",
179
+ "done",
180
+ "ok",
181
+ "thanks",
182
+ "thank you",
183
+ "will do",
184
+ "good catch",
185
+ "nice catch",
186
+ "👍",
187
+ "✅",
188
+ ];
189
+ const isJustAcknowledgment = acknowledgments.some((ack) => reply.trim() === ack || reply.trim() === ack + ".");
190
+ if (isJustAcknowledgment) {
191
+ return false;
192
+ }
193
+ // Look for indicators of substantive feedback
194
+ const feedbackIndicators = [
195
+ "actually",
196
+ "but",
197
+ "however",
198
+ "not necessary",
199
+ "don't need",
200
+ "intentional",
201
+ "by design",
202
+ "we prefer",
203
+ "our convention",
204
+ "team decision",
205
+ "won't fix",
206
+ "false positive",
207
+ "not an issue",
208
+ "this is fine",
209
+ "this is okay",
210
+ "that's expected",
211
+ "that's intentional",
212
+ "on purpose",
213
+ "should also",
214
+ "you missed",
215
+ "also check",
216
+ "what about",
217
+ "consider",
218
+ ];
219
+ return feedbackIndicators.some((indicator) => reply.includes(indicator));
220
+ }
221
+ /**
222
+ * Get statistics about comment analysis
223
+ */
224
+ getCommentStats(comments) {
225
+ const pairs = this.extractCommentPairs(comments);
226
+ const actionablePairs = pairs.filter((p) => this.hasActionableFeedback(p));
227
+ return {
228
+ total: comments.length,
229
+ aiComments: comments.filter((c) => this.isAIComment(c)).length,
230
+ developerComments: comments.filter((c) => this.isDeveloperComment(c))
231
+ .length,
232
+ pairs: pairs.length,
233
+ actionablePairs: actionablePairs.length,
234
+ };
235
+ }
236
+ }
237
+ //# sourceMappingURL=FeedbackExtractor.js.map
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Knowledge Base Manager
3
+ * Handles reading, writing, and parsing the knowledge base markdown file
4
+ */
5
+ import { KnowledgeBaseConfig } from "../types/config.types.js";
6
+ import { KnowledgeBase, ExtractedLearning } from "./types.js";
7
+ export declare class KnowledgeBaseManager {
8
+ private config;
9
+ private projectRoot;
10
+ constructor(config: KnowledgeBaseConfig, projectRoot?: string);
11
+ /**
12
+ * Get the full path to the knowledge base file
13
+ */
14
+ getFilePath(): string;
15
+ /**
16
+ * Check if knowledge base file exists
17
+ */
18
+ exists(): boolean;
19
+ /**
20
+ * Load and parse the knowledge base file
21
+ */
22
+ load(): Promise<KnowledgeBase>;
23
+ /**
24
+ * Append new learnings to the knowledge base
25
+ * Returns count of learnings actually added (excludes duplicates)
26
+ */
27
+ append(learnings: ExtractedLearning[]): Promise<number>;
28
+ /**
29
+ * Write the knowledge base back to file
30
+ */
31
+ write(kb: KnowledgeBase): Promise<void>;
32
+ /**
33
+ * Write raw markdown content directly to file
34
+ * Used by summarization to write AI-generated consolidated content
35
+ */
36
+ writeRaw(content: string): Promise<void>;
37
+ /**
38
+ * Create a new knowledge base file from template
39
+ */
40
+ create(): Promise<void>;
41
+ /**
42
+ * Get knowledge base content formatted for AI prompt injection
43
+ */
44
+ getForPrompt(): Promise<string | null>;
45
+ /**
46
+ * Get count of learnings in the knowledge base
47
+ */
48
+ getLearningCount(): Promise<number>;
49
+ /**
50
+ * Check if summarization is needed based on entry count
51
+ */
52
+ needsSummarization(): Promise<boolean>;
53
+ /**
54
+ * Commit the knowledge base file to git
55
+ * Uses execFile with argument arrays to prevent command injection
56
+ */
57
+ commit(prId: number, learningsAdded: number): Promise<void>;
58
+ /**
59
+ * Generate a hash for deduplication
60
+ */
61
+ generateLearningId(learning: string): string;
62
+ /**
63
+ * Create an empty knowledge base structure
64
+ */
65
+ private createEmptyKnowledgeBase;
66
+ /**
67
+ * Check if a learning already exists in the knowledge base
68
+ */
69
+ private isDuplicate;
70
+ /**
71
+ * Check if two learnings are similar (simple similarity check)
72
+ */
73
+ private isSimilar;
74
+ /**
75
+ * Parse markdown content into structured knowledge base
76
+ */
77
+ private parseMarkdown;
78
+ /**
79
+ * Convert category section name back to category enum
80
+ */
81
+ private categoryFromSectionName;
82
+ /**
83
+ * Convert knowledge base structure to markdown
84
+ */
85
+ private toMarkdown;
86
+ /**
87
+ * Get description text for empty sections
88
+ */
89
+ private getSectionDescription;
90
+ }
91
+ //# sourceMappingURL=KnowledgeBaseManager.d.ts.map