@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.
Files changed (67) hide show
  1. package/.mcp-config.example.json +26 -0
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +311 -685
  4. package/dist/cli/v2.cli.d.ts +13 -0
  5. package/dist/cli/v2.cli.js +290 -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 +191 -0
  12. package/dist/v2/core/MCPServerManager.d.ts +22 -0
  13. package/dist/v2/core/MCPServerManager.js +92 -0
  14. package/dist/v2/core/SessionManager.d.ts +72 -0
  15. package/dist/v2/core/SessionManager.js +200 -0
  16. package/dist/v2/core/YamaV2Orchestrator.d.ts +112 -0
  17. package/dist/v2/core/YamaV2Orchestrator.js +549 -0
  18. package/dist/v2/prompts/EnhancementSystemPrompt.d.ts +8 -0
  19. package/dist/v2/prompts/EnhancementSystemPrompt.js +216 -0
  20. package/dist/v2/prompts/PromptBuilder.d.ts +38 -0
  21. package/dist/v2/prompts/PromptBuilder.js +228 -0
  22. package/dist/v2/prompts/ReviewSystemPrompt.d.ts +8 -0
  23. package/dist/v2/prompts/ReviewSystemPrompt.js +270 -0
  24. package/dist/v2/types/config.types.d.ts +120 -0
  25. package/dist/v2/types/config.types.js +5 -0
  26. package/dist/v2/types/mcp.types.d.ts +191 -0
  27. package/dist/v2/types/mcp.types.js +6 -0
  28. package/dist/v2/types/v2.types.d.ts +182 -0
  29. package/dist/v2/types/v2.types.js +42 -0
  30. package/dist/v2/utils/ObservabilityConfig.d.ts +22 -0
  31. package/dist/v2/utils/ObservabilityConfig.js +48 -0
  32. package/package.json +11 -9
  33. package/yama.config.example.yaml +214 -193
  34. package/dist/cli/index.d.ts +0 -12
  35. package/dist/cli/index.js +0 -538
  36. package/dist/core/ContextGatherer.d.ts +0 -110
  37. package/dist/core/ContextGatherer.js +0 -470
  38. package/dist/core/Guardian.d.ts +0 -81
  39. package/dist/core/Guardian.js +0 -474
  40. package/dist/core/providers/BitbucketProvider.d.ts +0 -105
  41. package/dist/core/providers/BitbucketProvider.js +0 -489
  42. package/dist/features/CodeReviewer.d.ts +0 -173
  43. package/dist/features/CodeReviewer.js +0 -1707
  44. package/dist/features/DescriptionEnhancer.d.ts +0 -64
  45. package/dist/features/DescriptionEnhancer.js +0 -445
  46. package/dist/features/MultiInstanceProcessor.d.ts +0 -74
  47. package/dist/features/MultiInstanceProcessor.js +0 -360
  48. package/dist/types/index.d.ts +0 -624
  49. package/dist/types/index.js +0 -104
  50. package/dist/utils/Cache.d.ts +0 -103
  51. package/dist/utils/Cache.js +0 -444
  52. package/dist/utils/ConfigManager.d.ts +0 -88
  53. package/dist/utils/ConfigManager.js +0 -603
  54. package/dist/utils/ContentSimilarityService.d.ts +0 -74
  55. package/dist/utils/ContentSimilarityService.js +0 -215
  56. package/dist/utils/ExactDuplicateRemover.d.ts +0 -77
  57. package/dist/utils/ExactDuplicateRemover.js +0 -361
  58. package/dist/utils/Logger.d.ts +0 -31
  59. package/dist/utils/Logger.js +0 -214
  60. package/dist/utils/MemoryBankManager.d.ts +0 -73
  61. package/dist/utils/MemoryBankManager.js +0 -310
  62. package/dist/utils/ParallelProcessing.d.ts +0 -140
  63. package/dist/utils/ParallelProcessing.js +0 -333
  64. package/dist/utils/ProviderLimits.d.ts +0 -58
  65. package/dist/utils/ProviderLimits.js +0 -143
  66. package/dist/utils/RetryManager.d.ts +0 -78
  67. package/dist/utils/RetryManager.js +0 -205
@@ -1,310 +0,0 @@
1
- /**
2
- * Memory Bank Manager - Handles configurable memory bank operations
3
- * Provides abstraction for memory bank file access with fallback support
4
- */
5
- import { ConfigurationError } from "../types/index.js";
6
- import { logger } from "./Logger.js";
7
- import { cache, Cache } from "./Cache.js";
8
- export class MemoryBankManager {
9
- config;
10
- bitbucketProvider;
11
- constructor(config, bitbucketProvider) {
12
- this.config = config;
13
- this.bitbucketProvider = bitbucketProvider;
14
- this.validateConfig();
15
- }
16
- /**
17
- * Get memory bank files from the configured path with fallback support
18
- */
19
- async getMemoryBankFiles(identifier, forceRefresh = false) {
20
- if (!this.config.enabled) {
21
- logger.debug("Memory bank is disabled in configuration");
22
- return {
23
- files: [],
24
- resolvedPath: "",
25
- filesProcessed: 0,
26
- fallbackUsed: false,
27
- };
28
- }
29
- const cacheKey = Cache.keys.memoryBankFiles(identifier.workspace, identifier.repository, identifier.branch || "main", this.config.path);
30
- if (!forceRefresh && cache.has(cacheKey)) {
31
- logger.debug("Using cached memory bank files");
32
- return cache.get(cacheKey);
33
- }
34
- logger.debug(`Gathering memory bank files from configured paths...`);
35
- // Try primary path first
36
- const primaryResult = await this.tryGetFilesFromPath(identifier, this.config.path);
37
- if (primaryResult.files.length > 0) {
38
- const result = {
39
- ...primaryResult,
40
- resolvedPath: this.config.path,
41
- fallbackUsed: false,
42
- };
43
- // Cache the result
44
- cache.set(cacheKey, result, 7200); // 2 hours
45
- return result;
46
- }
47
- // Try fallback paths if primary path failed
48
- if (this.config.fallbackPaths && this.config.fallbackPaths.length > 0) {
49
- logger.debug(`Primary path '${this.config.path}' not found, trying fallback paths...`);
50
- for (const fallbackPath of this.config.fallbackPaths) {
51
- logger.debug(`Trying fallback path: ${fallbackPath}`);
52
- const fallbackResult = await this.tryGetFilesFromPath(identifier, fallbackPath);
53
- if (fallbackResult.files.length > 0) {
54
- logger.info(`Memory bank found at fallback path: ${fallbackPath} (${fallbackResult.files.length} files)`);
55
- const result = {
56
- ...fallbackResult,
57
- resolvedPath: fallbackPath,
58
- fallbackUsed: true,
59
- };
60
- // Cache the result
61
- cache.set(cacheKey, result, 7200); // 2 hours
62
- return result;
63
- }
64
- }
65
- }
66
- // No memory bank found anywhere
67
- logger.debug(`No memory bank found in primary path '${this.config.path}' or fallback paths`);
68
- const emptyResult = {
69
- files: [],
70
- resolvedPath: "",
71
- filesProcessed: 0,
72
- fallbackUsed: false,
73
- };
74
- // Cache empty result for shorter time to allow for quick retry
75
- cache.set(cacheKey, emptyResult, 1800); // 30 minutes
76
- return emptyResult;
77
- }
78
- /**
79
- * Try to get files from a specific path
80
- */
81
- async tryGetFilesFromPath(identifier, path) {
82
- try {
83
- // Get directory listing
84
- const directoryFiles = await this.bitbucketProvider.listDirectoryContent(identifier.workspace, identifier.repository, path, identifier.branch || "main");
85
- if (!directoryFiles.length) {
86
- logger.debug(`No files found in directory: ${path}`);
87
- return { files: [], filesProcessed: 0 };
88
- }
89
- // Filter to only files (not directories)
90
- const files = directoryFiles.filter((f) => f.type === "file");
91
- logger.debug(`Found ${files.length} files in ${path}`);
92
- // Get content of each file
93
- const memoryBankFiles = [];
94
- for (const file of files) {
95
- try {
96
- const content = await this.bitbucketProvider.getFileContent(identifier.workspace, identifier.repository, `${path}/${file.name}`, identifier.branch || "main");
97
- memoryBankFiles.push({
98
- name: file.name,
99
- content,
100
- path: `${path}/${file.name}`,
101
- });
102
- logger.debug(`✓ Loaded content for: ${file.name}`);
103
- }
104
- catch (error) {
105
- logger.debug(`Could not read file ${file.name}: ${error.message}`);
106
- // Continue with other files even if one fails
107
- }
108
- }
109
- return {
110
- files: memoryBankFiles,
111
- filesProcessed: memoryBankFiles.length,
112
- };
113
- }
114
- catch (error) {
115
- logger.debug(`Failed to access path '${path}': ${error.message}`);
116
- return { files: [], filesProcessed: 0 };
117
- }
118
- }
119
- /**
120
- * Get the effective memory bank path (resolved after fallback logic)
121
- */
122
- async getEffectiveMemoryBankPath(identifier) {
123
- const result = await this.getMemoryBankFiles(identifier);
124
- return result.resolvedPath || null;
125
- }
126
- /**
127
- * Check if memory bank exists at any configured path
128
- */
129
- async hasMemoryBank(identifier) {
130
- const result = await this.getMemoryBankFiles(identifier);
131
- return result.files.length > 0;
132
- }
133
- /**
134
- * Get memory bank configuration
135
- */
136
- getConfig() {
137
- return { ...this.config };
138
- }
139
- /**
140
- * Update memory bank configuration
141
- */
142
- updateConfig(newConfig) {
143
- this.config = { ...this.config, ...newConfig };
144
- this.validateConfig();
145
- logger.debug("Memory bank configuration updated");
146
- }
147
- /**
148
- * Validates that a path is safe for use as a relative path
149
- * Protects against path traversal attacks including encoded variants
150
- */
151
- static isSafeRelativePath(path) {
152
- if (!path || typeof path !== "string") {
153
- return false;
154
- }
155
- // Reject empty or whitespace-only paths
156
- if (path.trim().length === 0) {
157
- return false;
158
- }
159
- // Reject excessively long paths
160
- if (path.length > 1000) {
161
- return false;
162
- }
163
- // Reject absolute paths (Unix-style)
164
- if (path.startsWith("/")) {
165
- return false;
166
- }
167
- // Reject absolute paths (Windows-style)
168
- if (/^[a-zA-Z]:/.test(path)) {
169
- return false;
170
- }
171
- // Reject UNC paths (Windows network paths)
172
- if (path.startsWith("\\\\") || path.startsWith("//")) {
173
- return false;
174
- }
175
- // Decode URL-encoded characters to catch encoded traversal attempts
176
- let decodedPath = path;
177
- try {
178
- // Multiple rounds of decoding to catch double-encoded attacks
179
- for (let i = 0; i < 3; i++) {
180
- const previousPath = decodedPath;
181
- decodedPath = decodeURIComponent(decodedPath);
182
- if (decodedPath === previousPath) {
183
- break; // No more decoding needed
184
- }
185
- }
186
- }
187
- catch {
188
- // If decoding fails, treat as suspicious
189
- return false;
190
- }
191
- // Normalize Unicode characters
192
- decodedPath = decodedPath.normalize("NFC");
193
- // Check for null bytes (can be used to bypass filters)
194
- if (decodedPath.includes("\0") || decodedPath.includes("%00")) {
195
- return false;
196
- }
197
- // Normalize path separators to forward slashes
198
- const normalizedPath = decodedPath.replace(/\\/g, "/");
199
- // Check for path traversal sequences after normalization
200
- if (normalizedPath.includes("../") || normalizedPath.includes("/..")) {
201
- return false;
202
- }
203
- // Check for path traversal at the beginning or end
204
- if (normalizedPath.startsWith("..") || normalizedPath.endsWith("..")) {
205
- return false;
206
- }
207
- // Check for hidden traversal patterns
208
- if (normalizedPath.includes("./..") || normalizedPath.includes("../.")) {
209
- return false;
210
- }
211
- // Split path into segments and validate each
212
- const segments = normalizedPath.split("/").filter(segment => segment.length > 0);
213
- for (const segment of segments) {
214
- // Reject any segment that is exactly ".."
215
- if (segment === "..") {
216
- return false;
217
- }
218
- // Reject segments that contain ".." anywhere
219
- if (segment.includes("..")) {
220
- return false;
221
- }
222
- // Allow segments that start with a single dot (like .memory-bank, .config)
223
- // but reject multiple dots or suspicious patterns
224
- if (segment.startsWith(".") && segment !== ".") {
225
- // Allow single dot followed by alphanumeric/dash/underscore
226
- if (!/^\.[\w-]+$/.test(segment)) {
227
- return false;
228
- }
229
- }
230
- // Reject segments with control characters
231
- // Check for control characters (0x00-0x1F and 0x7F)
232
- for (let i = 0; i < segment.length; i++) {
233
- const charCode = segment.charCodeAt(i);
234
- if ((charCode >= 0 && charCode <= 31) || charCode === 127) {
235
- return false;
236
- }
237
- }
238
- }
239
- // Additional check: ensure the resolved path doesn't escape the base
240
- // This is a final safety check using path resolution logic
241
- const pathParts = segments.filter(part => part !== ".");
242
- let depth = 0;
243
- for (const part of pathParts) {
244
- if (part === "..") {
245
- depth--;
246
- if (depth < 0) {
247
- return false; // Would escape the base directory
248
- }
249
- }
250
- else {
251
- depth++;
252
- }
253
- }
254
- return true;
255
- }
256
- /**
257
- * Validate memory bank configuration
258
- */
259
- validateConfig() {
260
- if (!this.config.path) {
261
- throw new ConfigurationError("Memory bank path must be specified when memory bank is enabled");
262
- }
263
- if (!MemoryBankManager.isSafeRelativePath(this.config.path)) {
264
- throw new ConfigurationError(`Memory bank path is unsafe or contains path traversal: ${this.config.path}`);
265
- }
266
- // Validate fallback paths
267
- if (this.config.fallbackPaths) {
268
- for (const fallbackPath of this.config.fallbackPaths) {
269
- if (!MemoryBankManager.isSafeRelativePath(fallbackPath)) {
270
- throw new ConfigurationError(`Memory bank fallback path is unsafe or contains path traversal: ${fallbackPath}`);
271
- }
272
- }
273
- }
274
- logger.debug("Memory bank configuration validated successfully");
275
- }
276
- /**
277
- * Clear memory bank cache for a specific repository
278
- */
279
- clearCache(identifier) {
280
- const patterns = [
281
- `memory-bank:${identifier.workspace}:${identifier.repository}:*`,
282
- `project-context:${identifier.workspace}:${identifier.repository}:*`,
283
- ];
284
- patterns.forEach((pattern) => {
285
- cache.invalidatePattern(pattern);
286
- });
287
- logger.debug(`Memory bank cache cleared for ${identifier.workspace}/${identifier.repository}`);
288
- }
289
- /**
290
- * Get memory bank statistics
291
- */
292
- async getStats(identifier) {
293
- const result = await this.getMemoryBankFiles(identifier);
294
- const cacheStats = cache.stats();
295
- return {
296
- enabled: this.config.enabled,
297
- primaryPath: this.config.path,
298
- fallbackPaths: this.config.fallbackPaths || [],
299
- hasMemoryBank: result.files.length > 0,
300
- resolvedPath: result.resolvedPath || null,
301
- fileCount: result.files.length,
302
- cacheHits: cacheStats.hits,
303
- };
304
- }
305
- }
306
- // Export factory function
307
- export function createMemoryBankManager(config, bitbucketProvider) {
308
- return new MemoryBankManager(config, bitbucketProvider);
309
- }
310
- //# sourceMappingURL=MemoryBankManager.js.map
@@ -1,140 +0,0 @@
1
- /**
2
- * Parallel Processing Utilities for Batch Processing
3
- * Provides concurrency control and resource management for parallel batch execution
4
- */
5
- import { SemaphoreInterface, TokenBudgetManagerInterface } from "../types/index.js";
6
- /**
7
- * Semaphore for controlling concurrent access to resources
8
- * Limits the number of concurrent operations that can run simultaneously
9
- */
10
- export declare class Semaphore implements SemaphoreInterface {
11
- private permits;
12
- private waiting;
13
- constructor(permits: number);
14
- /**
15
- * Acquire a permit from the semaphore
16
- * If no permits are available, the caller will wait until one becomes available
17
- */
18
- acquire(): Promise<void>;
19
- /**
20
- * Release a permit back to the semaphore
21
- * This will allow waiting operations to proceed
22
- */
23
- release(): void;
24
- /**
25
- * Get the number of available permits
26
- */
27
- getAvailablePermits(): number;
28
- /**
29
- * Get the number of operations waiting for permits
30
- */
31
- getWaitingCount(): number;
32
- /**
33
- * Get semaphore status for debugging
34
- */
35
- getStatus(): {
36
- available: number;
37
- waiting: number;
38
- };
39
- }
40
- /**
41
- * Token Budget Manager for controlling AI token usage across parallel batches
42
- * Ensures that the total token usage doesn't exceed the configured limits
43
- */
44
- export declare class TokenBudgetManager implements TokenBudgetManagerInterface {
45
- private totalBudget;
46
- private usedTokens;
47
- private batchAllocations;
48
- private reservedTokens;
49
- private preAllocationMode;
50
- private preAllocatedBatches;
51
- private batchStates;
52
- constructor(totalBudget: number);
53
- /**
54
- * Allocate tokens for a specific batch
55
- * Returns true if allocation was successful, false if insufficient budget
56
- */
57
- allocateForBatch(batchIndex: number, estimatedTokens: number): boolean;
58
- /**
59
- * Release tokens allocated to a batch
60
- * This should be called when a batch completes (successfully or with error)
61
- */
62
- releaseBatch(batchIndex: number): void;
63
- /**
64
- * Get the available token budget (not yet allocated or used)
65
- */
66
- getAvailableBudget(): number;
67
- /**
68
- * Get the total token budget
69
- */
70
- getTotalBudget(): number;
71
- /**
72
- * Get the number of tokens actually used (completed batches)
73
- */
74
- getUsedTokens(): number;
75
- /**
76
- * Get the number of tokens reserved (allocated but not yet used)
77
- */
78
- getReservedTokens(): number;
79
- /**
80
- * Get the number of active batch allocations
81
- */
82
- getActiveBatches(): number;
83
- /**
84
- * Get detailed budget status for monitoring
85
- */
86
- getBudgetStatus(): {
87
- total: number;
88
- used: number;
89
- reserved: number;
90
- available: number;
91
- activeBatches: number;
92
- utilizationPercent: number;
93
- };
94
- /**
95
- * Reset the budget manager (for testing or reuse)
96
- */
97
- reset(): void;
98
- /**
99
- * Pre-allocate tokens for all batches upfront
100
- * This ensures all batches have guaranteed token allocation before processing starts
101
- */
102
- preAllocateAllBatches(allocations: Map<number, number>): boolean;
103
- /**
104
- * Mark a batch as failed and handle cleanup
105
- */
106
- markBatchFailed(batchIndex: number, error?: string): void;
107
- /**
108
- * Get the current state of a batch
109
- */
110
- getBatchState(batchIndex: number): "pending" | "processing" | "completed" | "failed" | undefined;
111
- /**
112
- * Check if pre-allocation mode is active
113
- */
114
- isPreAllocationMode(): boolean;
115
- /**
116
- * Get all batch states for debugging
117
- */
118
- getAllBatchStates(): Map<number, "pending" | "processing" | "completed" | "failed">;
119
- /**
120
- * Disable pre-allocation mode and clean up
121
- */
122
- disablePreAllocationMode(): void;
123
- /**
124
- * Update the total budget (useful for dynamic adjustment)
125
- */
126
- updateBudget(newBudget: number): void;
127
- }
128
- /**
129
- * Factory function to create a Semaphore with validation
130
- */
131
- export declare function createSemaphore(permits: number): Semaphore;
132
- /**
133
- * Factory function to create a TokenBudgetManager with validation
134
- */
135
- export declare function createTokenBudgetManager(totalBudget: number): TokenBudgetManager;
136
- /**
137
- * Utility function to calculate optimal concurrency based on available resources
138
- */
139
- export declare function calculateOptimalConcurrency(totalBatches: number, maxConcurrent: number, averageTokensPerBatch: number, totalTokenBudget: number): number;
140
- //# sourceMappingURL=ParallelProcessing.d.ts.map