@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
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enhanced Configuration Manager for Yama
|
|
3
|
-
* Handles configuration loading, validation, and merging from multiple sources
|
|
4
|
-
*/
|
|
5
|
-
import { GuardianConfig } from "../types/index.js";
|
|
6
|
-
export declare class ConfigManager {
|
|
7
|
-
private config;
|
|
8
|
-
private configPaths;
|
|
9
|
-
/**
|
|
10
|
-
* Default configuration
|
|
11
|
-
*/
|
|
12
|
-
private static readonly DEFAULT_CONFIG;
|
|
13
|
-
constructor();
|
|
14
|
-
/**
|
|
15
|
-
* Setup configuration file search paths
|
|
16
|
-
*/
|
|
17
|
-
private setupConfigPaths;
|
|
18
|
-
/**
|
|
19
|
-
* Load configuration from files and environment
|
|
20
|
-
*/
|
|
21
|
-
loadConfig(configPath?: string): Promise<GuardianConfig>;
|
|
22
|
-
/**
|
|
23
|
-
* Load configuration from a specific file
|
|
24
|
-
*/
|
|
25
|
-
private loadConfigFile;
|
|
26
|
-
/**
|
|
27
|
-
* Apply provider-aware token limits using shared utility
|
|
28
|
-
*/
|
|
29
|
-
private applyProviderTokenLimits;
|
|
30
|
-
/**
|
|
31
|
-
* Apply environment variable overrides
|
|
32
|
-
*/
|
|
33
|
-
private applyEnvironmentOverrides;
|
|
34
|
-
/**
|
|
35
|
-
* Validate configuration
|
|
36
|
-
*/
|
|
37
|
-
private validateConfig;
|
|
38
|
-
/**
|
|
39
|
-
* Merge two configuration objects deeply
|
|
40
|
-
*/
|
|
41
|
-
private mergeConfigs;
|
|
42
|
-
/**
|
|
43
|
-
* Deep merge utility
|
|
44
|
-
*/
|
|
45
|
-
private deepMerge;
|
|
46
|
-
/**
|
|
47
|
-
* Deep clone utility
|
|
48
|
-
*/
|
|
49
|
-
private deepClone;
|
|
50
|
-
/**
|
|
51
|
-
* Get current configuration
|
|
52
|
-
*/
|
|
53
|
-
getConfig(): GuardianConfig;
|
|
54
|
-
/**
|
|
55
|
-
* Create default configuration file
|
|
56
|
-
*/
|
|
57
|
-
createDefaultConfig(outputPath?: string): Promise<string>;
|
|
58
|
-
/**
|
|
59
|
-
* Add helpful comments to configuration file
|
|
60
|
-
*/
|
|
61
|
-
private addConfigComments;
|
|
62
|
-
/**
|
|
63
|
-
* Validate specific configuration section
|
|
64
|
-
*/
|
|
65
|
-
validateSection(section: keyof GuardianConfig, config: any): boolean;
|
|
66
|
-
private validateProviders;
|
|
67
|
-
private validateFeatures;
|
|
68
|
-
private validateCache;
|
|
69
|
-
/**
|
|
70
|
-
* Get configuration schema for validation
|
|
71
|
-
*/
|
|
72
|
-
getSchema(): any;
|
|
73
|
-
/**
|
|
74
|
-
* Find the first available configuration file
|
|
75
|
-
*/
|
|
76
|
-
private findConfigFile;
|
|
77
|
-
/**
|
|
78
|
-
* Watch configuration file for changes and reload automatically
|
|
79
|
-
*/
|
|
80
|
-
watchConfig(callback?: (config: GuardianConfig) => void): () => void;
|
|
81
|
-
/**
|
|
82
|
-
* Enable hot-reload for configuration
|
|
83
|
-
*/
|
|
84
|
-
enableHotReload(callback?: (config: GuardianConfig) => void): () => void;
|
|
85
|
-
}
|
|
86
|
-
export declare const configManager: ConfigManager;
|
|
87
|
-
export declare function createConfigManager(): ConfigManager;
|
|
88
|
-
//# sourceMappingURL=ConfigManager.d.ts.map
|
|
@@ -1,603 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Enhanced Configuration Manager for Yama
|
|
3
|
-
* Handles configuration loading, validation, and merging from multiple sources
|
|
4
|
-
*/
|
|
5
|
-
import fs from "fs";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import yaml from "yaml";
|
|
8
|
-
import { homedir } from "os";
|
|
9
|
-
import { ConfigurationError } from "../types/index.js";
|
|
10
|
-
import { logger } from "./Logger.js";
|
|
11
|
-
import { validateProviderTokenLimit } from "./ProviderLimits.js";
|
|
12
|
-
export class ConfigManager {
|
|
13
|
-
config = null;
|
|
14
|
-
configPaths = [];
|
|
15
|
-
/**
|
|
16
|
-
* Default configuration
|
|
17
|
-
*/
|
|
18
|
-
static DEFAULT_CONFIG = {
|
|
19
|
-
providers: {
|
|
20
|
-
ai: {
|
|
21
|
-
provider: "auto",
|
|
22
|
-
enableFallback: true,
|
|
23
|
-
enableAnalytics: false,
|
|
24
|
-
enableEvaluation: false,
|
|
25
|
-
timeout: "10m",
|
|
26
|
-
retryAttempts: 3,
|
|
27
|
-
temperature: 0.3,
|
|
28
|
-
maxTokens: 65000,
|
|
29
|
-
},
|
|
30
|
-
git: {
|
|
31
|
-
platform: "bitbucket",
|
|
32
|
-
credentials: {
|
|
33
|
-
username: process.env.BITBUCKET_USERNAME || "",
|
|
34
|
-
token: process.env.BITBUCKET_TOKEN || "",
|
|
35
|
-
baseUrl: process.env.BITBUCKET_BASE_URL ||
|
|
36
|
-
"https://your-bitbucket-server.com",
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
features: {
|
|
41
|
-
codeReview: {
|
|
42
|
-
enabled: true,
|
|
43
|
-
severityLevels: ["CRITICAL", "MAJOR", "MINOR", "SUGGESTION"],
|
|
44
|
-
categories: [
|
|
45
|
-
"security",
|
|
46
|
-
"performance",
|
|
47
|
-
"maintainability",
|
|
48
|
-
"functionality",
|
|
49
|
-
"error_handling",
|
|
50
|
-
"testing",
|
|
51
|
-
],
|
|
52
|
-
excludePatterns: ["*.lock", "*.svg", "*.min.js", "*.map"],
|
|
53
|
-
contextLines: 3,
|
|
54
|
-
systemPrompt: "You are an Expert Security Code Reviewer for enterprise applications. Your role is to:\n\n🔒 SECURITY FIRST: Prioritize security vulnerabilities and data protection\n⚡ PERFORMANCE AWARE: Identify performance bottlenecks and optimization opportunities\n🏗️ QUALITY FOCUSED: Ensure maintainable, readable, and robust code\n🛡️ ERROR RESILIENT: Verify comprehensive error handling and edge cases\n\nYou provide actionable, educational feedback with specific examples and solutions.\nFocus on critical issues that could impact production systems.",
|
|
55
|
-
focusAreas: [
|
|
56
|
-
"🔒 Security Analysis (CRITICAL PRIORITY)",
|
|
57
|
-
"⚡ Performance Review",
|
|
58
|
-
"🏗️ Code Quality",
|
|
59
|
-
"🧪 Testing & Error Handling",
|
|
60
|
-
],
|
|
61
|
-
},
|
|
62
|
-
descriptionEnhancement: {
|
|
63
|
-
enabled: true,
|
|
64
|
-
preserveContent: true,
|
|
65
|
-
requiredSections: [
|
|
66
|
-
{
|
|
67
|
-
key: "changelog",
|
|
68
|
-
name: "Changelog (Modules Modified)",
|
|
69
|
-
required: true,
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
key: "testcases",
|
|
73
|
-
name: "Test Cases (What to be tested)",
|
|
74
|
-
required: true,
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
key: "config_changes",
|
|
78
|
-
name: "CAC Config Or Service Config Changes",
|
|
79
|
-
required: true,
|
|
80
|
-
},
|
|
81
|
-
],
|
|
82
|
-
autoFormat: true,
|
|
83
|
-
systemPrompt: "You are an Expert Technical Writer specializing in pull request documentation. Your role is to:\n\n📝 CLARITY FIRST: Create clear, comprehensive PR descriptions that help reviewers understand the changes\n🎥 STORY TELLING: Explain the 'why' behind changes, not just the 'what'\n📈 STRUCTURED: Follow consistent formatting with required sections\n🔗 CONTEXTUAL: Link changes to business value and technical rationale\n\nCRITICAL INSTRUCTION: Return ONLY the enhanced PR description content as clean markdown. Do NOT include any meta-commentary, explanations about what you're doing, or introductory text like \"I will enhance...\" or \"Here is the enhanced description:\". \n\nOutput the enhanced description directly without any wrapper text or explanations.",
|
|
84
|
-
outputTemplate: "# PR Title Enhancement (if needed)\n\n## Summary\n[Clear overview of what this PR accomplishes]\n\n## Changes Made\n[Specific technical changes - be precise]\n\n## Testing\n[How the changes were tested]\n\n## Impact\n[Business/technical impact and considerations]\n\n## Additional Notes\n[Any deployment notes, follow-ups, or special considerations]",
|
|
85
|
-
enhancementInstructions: 'Return ONLY the enhanced PR description as clean markdown. Do NOT include any explanatory text, meta-commentary, or phrases like "Here is the enhanced description:" or "I will enhance...".\n\nStart directly with the enhanced description content using this structure:',
|
|
86
|
-
},
|
|
87
|
-
securityScan: {
|
|
88
|
-
enabled: true,
|
|
89
|
-
level: "strict",
|
|
90
|
-
scanTypes: ["secrets", "vulnerabilities", "dependencies"],
|
|
91
|
-
},
|
|
92
|
-
analytics: {
|
|
93
|
-
enabled: true,
|
|
94
|
-
trackMetrics: true,
|
|
95
|
-
exportFormat: "json",
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
cache: {
|
|
99
|
-
enabled: true,
|
|
100
|
-
ttl: "1h",
|
|
101
|
-
maxSize: "100MB",
|
|
102
|
-
storage: "memory",
|
|
103
|
-
},
|
|
104
|
-
performance: {
|
|
105
|
-
batch: {
|
|
106
|
-
enabled: true,
|
|
107
|
-
maxConcurrent: 5,
|
|
108
|
-
delayBetween: "1s",
|
|
109
|
-
},
|
|
110
|
-
optimization: {
|
|
111
|
-
reuseConnections: true,
|
|
112
|
-
compressRequests: true,
|
|
113
|
-
enableHttp2: true,
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
rules: {
|
|
117
|
-
security: [
|
|
118
|
-
{
|
|
119
|
-
name: "No hardcoded secrets",
|
|
120
|
-
pattern: "(password|secret|key|token)\\s*[=:]\\s*['\"][^'\"]{8,}['\"]",
|
|
121
|
-
severity: "CRITICAL",
|
|
122
|
-
message: "Hardcoded secrets detected",
|
|
123
|
-
suggestion: "Use environment variables or secure configuration management",
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
performance: [
|
|
127
|
-
{
|
|
128
|
-
name: "Avoid N+1 queries",
|
|
129
|
-
pattern: "for.*\\.(find|get|query|select)",
|
|
130
|
-
severity: "MAJOR",
|
|
131
|
-
message: "Potential N+1 query pattern detected",
|
|
132
|
-
suggestion: "Consider using batch queries or joins",
|
|
133
|
-
},
|
|
134
|
-
],
|
|
135
|
-
},
|
|
136
|
-
reporting: {
|
|
137
|
-
formats: ["markdown", "json"],
|
|
138
|
-
includeAnalytics: true,
|
|
139
|
-
includeMetrics: true,
|
|
140
|
-
},
|
|
141
|
-
monitoring: {
|
|
142
|
-
enabled: true,
|
|
143
|
-
metrics: ["performance", "cache", "api_calls"],
|
|
144
|
-
exportFormat: "json",
|
|
145
|
-
interval: "5m",
|
|
146
|
-
},
|
|
147
|
-
memoryBank: {
|
|
148
|
-
enabled: true,
|
|
149
|
-
path: "memory-bank",
|
|
150
|
-
fallbackPaths: ["docs/memory-bank", ".memory-bank"],
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
constructor() {
|
|
154
|
-
this.setupConfigPaths();
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Setup configuration file search paths
|
|
158
|
-
*/
|
|
159
|
-
setupConfigPaths() {
|
|
160
|
-
const cwd = process.cwd();
|
|
161
|
-
const homeDir = homedir();
|
|
162
|
-
this.configPaths = [
|
|
163
|
-
path.join(cwd, "yama.config.yaml"),
|
|
164
|
-
path.join(cwd, "yama.config.yml"),
|
|
165
|
-
path.join(cwd, "yama.config.json"),
|
|
166
|
-
path.join(cwd, ".yama.yaml"),
|
|
167
|
-
path.join(cwd, ".yama.yml"),
|
|
168
|
-
path.join(cwd, ".yama.json"),
|
|
169
|
-
path.join(homeDir, ".yama", "config.yaml"),
|
|
170
|
-
path.join(homeDir, ".yama", "config.yml"),
|
|
171
|
-
path.join(homeDir, ".yama", "config.json"),
|
|
172
|
-
path.join(homeDir, ".config", "yama", "config.yaml"),
|
|
173
|
-
path.join(homeDir, ".config", "yama", "config.yml"),
|
|
174
|
-
path.join(homeDir, ".config", "yama", "config.json"),
|
|
175
|
-
];
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Load configuration from files and environment
|
|
179
|
-
*/
|
|
180
|
-
async loadConfig(configPath) {
|
|
181
|
-
if (this.config) {
|
|
182
|
-
return this.config;
|
|
183
|
-
}
|
|
184
|
-
logger.debug("Loading Yama configuration...");
|
|
185
|
-
// Start with default config
|
|
186
|
-
let config = this.deepClone(ConfigManager.DEFAULT_CONFIG);
|
|
187
|
-
// If specific config path provided, use only that
|
|
188
|
-
if (configPath) {
|
|
189
|
-
if (!fs.existsSync(configPath)) {
|
|
190
|
-
throw new ConfigurationError(`Configuration file not found: ${configPath}`);
|
|
191
|
-
}
|
|
192
|
-
const fileConfig = await this.loadConfigFile(configPath);
|
|
193
|
-
config = this.mergeConfigs(config, fileConfig);
|
|
194
|
-
logger.debug(`Loaded configuration from: ${configPath}`);
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
// Search for config files in predefined paths
|
|
198
|
-
for (const configFilePath of this.configPaths) {
|
|
199
|
-
if (fs.existsSync(configFilePath)) {
|
|
200
|
-
try {
|
|
201
|
-
const fileConfig = await this.loadConfigFile(configFilePath);
|
|
202
|
-
config = this.mergeConfigs(config, fileConfig);
|
|
203
|
-
logger.debug(`Loaded configuration from: ${configFilePath}`);
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
catch (error) {
|
|
207
|
-
logger.warn(`Failed to load config from ${configFilePath}:`, error);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
// Override with environment variables
|
|
213
|
-
config = this.applyEnvironmentOverrides(config);
|
|
214
|
-
// Apply provider-aware token limits
|
|
215
|
-
config = this.applyProviderTokenLimits(config);
|
|
216
|
-
// Validate configuration
|
|
217
|
-
this.validateConfig(config);
|
|
218
|
-
this.config = config;
|
|
219
|
-
logger.debug("Configuration loaded successfully");
|
|
220
|
-
return config;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Load configuration from a specific file
|
|
224
|
-
*/
|
|
225
|
-
async loadConfigFile(filePath) {
|
|
226
|
-
try {
|
|
227
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
228
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
229
|
-
switch (ext) {
|
|
230
|
-
case ".yaml":
|
|
231
|
-
case ".yml":
|
|
232
|
-
return yaml.parse(content);
|
|
233
|
-
case ".json":
|
|
234
|
-
return JSON.parse(content);
|
|
235
|
-
default:
|
|
236
|
-
throw new ConfigurationError(`Unsupported config file format: ${ext}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
catch (error) {
|
|
240
|
-
throw new ConfigurationError(`Failed to parse config file ${filePath}: ${error.message}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Apply provider-aware token limits using shared utility
|
|
245
|
-
*/
|
|
246
|
-
applyProviderTokenLimits(config) {
|
|
247
|
-
const provider = config.providers.ai.provider || 'auto';
|
|
248
|
-
const configuredTokens = config.providers.ai.maxTokens;
|
|
249
|
-
// Use the shared utility to validate and adjust token limits
|
|
250
|
-
const validatedTokens = validateProviderTokenLimit(provider, configuredTokens, false // Use standard limits for configuration
|
|
251
|
-
);
|
|
252
|
-
config.providers.ai.maxTokens = validatedTokens;
|
|
253
|
-
return config;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Apply environment variable overrides
|
|
257
|
-
*/
|
|
258
|
-
applyEnvironmentOverrides(config) {
|
|
259
|
-
const env = process.env;
|
|
260
|
-
// AI Provider overrides
|
|
261
|
-
if (env.AI_PROVIDER) {
|
|
262
|
-
config.providers.ai.provider = env.AI_PROVIDER;
|
|
263
|
-
}
|
|
264
|
-
if (env.AI_MODEL) {
|
|
265
|
-
config.providers.ai.model = env.AI_MODEL;
|
|
266
|
-
}
|
|
267
|
-
if (env.AI_TIMEOUT) {
|
|
268
|
-
config.providers.ai.timeout = env.AI_TIMEOUT;
|
|
269
|
-
}
|
|
270
|
-
if (env.AI_TEMPERATURE) {
|
|
271
|
-
config.providers.ai.temperature = parseFloat(env.AI_TEMPERATURE);
|
|
272
|
-
}
|
|
273
|
-
if (env.AI_MAX_TOKENS) {
|
|
274
|
-
config.providers.ai.maxTokens = parseInt(env.AI_MAX_TOKENS);
|
|
275
|
-
}
|
|
276
|
-
// Git Provider overrides
|
|
277
|
-
if (env.BITBUCKET_USERNAME) {
|
|
278
|
-
config.providers.git.credentials.username = env.BITBUCKET_USERNAME;
|
|
279
|
-
}
|
|
280
|
-
if (env.BITBUCKET_TOKEN) {
|
|
281
|
-
config.providers.git.credentials.token = env.BITBUCKET_TOKEN;
|
|
282
|
-
}
|
|
283
|
-
if (env.BITBUCKET_BASE_URL) {
|
|
284
|
-
config.providers.git.credentials.baseUrl = env.BITBUCKET_BASE_URL;
|
|
285
|
-
}
|
|
286
|
-
// Feature toggles
|
|
287
|
-
if (env.ENABLE_CODE_REVIEW !== undefined) {
|
|
288
|
-
config.features.codeReview.enabled = env.ENABLE_CODE_REVIEW === "true";
|
|
289
|
-
}
|
|
290
|
-
if (env.ENABLE_DESCRIPTION_ENHANCEMENT !== undefined) {
|
|
291
|
-
config.features.descriptionEnhancement.enabled =
|
|
292
|
-
env.ENABLE_DESCRIPTION_ENHANCEMENT === "true";
|
|
293
|
-
}
|
|
294
|
-
if (env.ENABLE_SECURITY_SCAN !== undefined) {
|
|
295
|
-
config.features.securityScan.enabled =
|
|
296
|
-
env.ENABLE_SECURITY_SCAN === "true";
|
|
297
|
-
}
|
|
298
|
-
if (env.ENABLE_ANALYTICS !== undefined) {
|
|
299
|
-
config.features.analytics.enabled = env.ENABLE_ANALYTICS === "true";
|
|
300
|
-
}
|
|
301
|
-
// Cache configuration
|
|
302
|
-
if (env.CACHE_ENABLED !== undefined) {
|
|
303
|
-
config.cache.enabled = env.CACHE_ENABLED === "true";
|
|
304
|
-
}
|
|
305
|
-
if (env.CACHE_TTL) {
|
|
306
|
-
config.cache.ttl = env.CACHE_TTL;
|
|
307
|
-
}
|
|
308
|
-
if (env.CACHE_STORAGE) {
|
|
309
|
-
config.cache.storage = env.CACHE_STORAGE;
|
|
310
|
-
}
|
|
311
|
-
// Debug mode
|
|
312
|
-
if (env.GUARDIAN_DEBUG === "true") {
|
|
313
|
-
logger.setLevel("debug");
|
|
314
|
-
logger.setVerbose(true);
|
|
315
|
-
}
|
|
316
|
-
logger.debug("Applied environment variable overrides");
|
|
317
|
-
return config;
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Validate configuration
|
|
321
|
-
*/
|
|
322
|
-
validateConfig(config) {
|
|
323
|
-
const errors = [];
|
|
324
|
-
// Validate AI provider credentials
|
|
325
|
-
if (!config.providers.ai.provider) {
|
|
326
|
-
errors.push("AI provider must be specified");
|
|
327
|
-
}
|
|
328
|
-
// Validate Git provider credentials
|
|
329
|
-
if (!config.providers.git.credentials.username) {
|
|
330
|
-
errors.push("Git username must be specified");
|
|
331
|
-
}
|
|
332
|
-
if (!config.providers.git.credentials.token) {
|
|
333
|
-
errors.push("Git token must be specified");
|
|
334
|
-
}
|
|
335
|
-
// Validate enabled features have required configuration
|
|
336
|
-
if (config.features.codeReview.enabled) {
|
|
337
|
-
if (!config.features.codeReview.severityLevels?.length) {
|
|
338
|
-
errors.push("Code review severity levels must be specified when enabled");
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
if (config.features.descriptionEnhancement.enabled) {
|
|
342
|
-
if (!config.features.descriptionEnhancement.requiredSections?.length) {
|
|
343
|
-
errors.push("Description enhancement required sections must be specified when enabled");
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
// Validate cache configuration
|
|
347
|
-
if (config.cache?.enabled) {
|
|
348
|
-
if (!config.cache.storage) {
|
|
349
|
-
errors.push("Cache storage type must be specified when cache is enabled");
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (errors.length > 0) {
|
|
353
|
-
throw new ConfigurationError(`Configuration validation failed:\n${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
354
|
-
}
|
|
355
|
-
logger.debug("Configuration validation passed");
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Merge two configuration objects deeply
|
|
359
|
-
*/
|
|
360
|
-
mergeConfigs(base, override) {
|
|
361
|
-
return this.deepMerge(base, override);
|
|
362
|
-
}
|
|
363
|
-
/**
|
|
364
|
-
* Deep merge utility
|
|
365
|
-
*/
|
|
366
|
-
deepMerge(target, source) {
|
|
367
|
-
const result = { ...target };
|
|
368
|
-
for (const key in source) {
|
|
369
|
-
if (source[key] !== null &&
|
|
370
|
-
typeof source[key] === "object" &&
|
|
371
|
-
!Array.isArray(source[key])) {
|
|
372
|
-
result[key] = this.deepMerge(target[key] || {}, source[key]);
|
|
373
|
-
}
|
|
374
|
-
else {
|
|
375
|
-
result[key] = source[key];
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return result;
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Deep clone utility
|
|
382
|
-
*/
|
|
383
|
-
deepClone(obj) {
|
|
384
|
-
return JSON.parse(JSON.stringify(obj));
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Get current configuration
|
|
388
|
-
*/
|
|
389
|
-
getConfig() {
|
|
390
|
-
if (!this.config) {
|
|
391
|
-
throw new ConfigurationError("Configuration not loaded. Call loadConfig() first.");
|
|
392
|
-
}
|
|
393
|
-
return this.config;
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Create default configuration file
|
|
397
|
-
*/
|
|
398
|
-
async createDefaultConfig(outputPath) {
|
|
399
|
-
const defaultPath = outputPath || path.join(process.cwd(), "yama.config.yaml");
|
|
400
|
-
const configContent = yaml.stringify(ConfigManager.DEFAULT_CONFIG, {
|
|
401
|
-
indent: 2,
|
|
402
|
-
lineWidth: 100,
|
|
403
|
-
});
|
|
404
|
-
// Add comments to make the config file more user-friendly
|
|
405
|
-
const commentedConfig = this.addConfigComments(configContent);
|
|
406
|
-
fs.writeFileSync(defaultPath, commentedConfig, "utf8");
|
|
407
|
-
logger.info(`Default configuration created at: ${defaultPath}`);
|
|
408
|
-
return defaultPath;
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Add helpful comments to configuration file
|
|
412
|
-
*/
|
|
413
|
-
addConfigComments(content) {
|
|
414
|
-
const header = `# Yama Configuration
|
|
415
|
-
# This file configures all aspects of Yama behavior
|
|
416
|
-
# For more information, visit: https://github.com/juspay/yama
|
|
417
|
-
|
|
418
|
-
`;
|
|
419
|
-
const sections = [
|
|
420
|
-
"# AI Provider Configuration",
|
|
421
|
-
"# Git Platform Configuration",
|
|
422
|
-
"# Feature Configuration",
|
|
423
|
-
"# Cache Configuration",
|
|
424
|
-
"# Performance Configuration",
|
|
425
|
-
"# Custom Rules Configuration",
|
|
426
|
-
"# Reporting Configuration",
|
|
427
|
-
];
|
|
428
|
-
let commented = header + content;
|
|
429
|
-
// Add section comments (this is a simplified approach)
|
|
430
|
-
sections.forEach((section) => {
|
|
431
|
-
const key = section.split(" ")[1].toLowerCase();
|
|
432
|
-
commented = commented.replace(new RegExp(`^(${key}:)`, "m"), `${section}\n$1`);
|
|
433
|
-
});
|
|
434
|
-
return commented;
|
|
435
|
-
}
|
|
436
|
-
/**
|
|
437
|
-
* Validate specific configuration section
|
|
438
|
-
*/
|
|
439
|
-
validateSection(section, config) {
|
|
440
|
-
try {
|
|
441
|
-
switch (section) {
|
|
442
|
-
case "providers":
|
|
443
|
-
return this.validateProviders(config);
|
|
444
|
-
case "features":
|
|
445
|
-
return this.validateFeatures(config);
|
|
446
|
-
case "cache":
|
|
447
|
-
return this.validateCache(config);
|
|
448
|
-
default:
|
|
449
|
-
return true;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
catch (error) {
|
|
453
|
-
logger.error(`Validation failed for section ${String(section)}:`, error);
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
validateProviders(providers) {
|
|
458
|
-
return !!(providers?.ai?.provider &&
|
|
459
|
-
providers?.git?.credentials?.username &&
|
|
460
|
-
providers?.git?.credentials?.token);
|
|
461
|
-
}
|
|
462
|
-
validateFeatures(features) {
|
|
463
|
-
return !!(features && typeof features === "object");
|
|
464
|
-
}
|
|
465
|
-
validateCache(cacheConfig) {
|
|
466
|
-
if (!cacheConfig?.enabled) {
|
|
467
|
-
return true;
|
|
468
|
-
}
|
|
469
|
-
return !!(cacheConfig.storage && cacheConfig.ttl);
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* Get configuration schema for validation
|
|
473
|
-
*/
|
|
474
|
-
getSchema() {
|
|
475
|
-
return {
|
|
476
|
-
type: "object",
|
|
477
|
-
required: ["providers", "features"],
|
|
478
|
-
properties: {
|
|
479
|
-
providers: {
|
|
480
|
-
type: "object",
|
|
481
|
-
required: ["ai", "git"],
|
|
482
|
-
properties: {
|
|
483
|
-
ai: {
|
|
484
|
-
type: "object",
|
|
485
|
-
required: ["provider"],
|
|
486
|
-
properties: {
|
|
487
|
-
provider: { type: "string" },
|
|
488
|
-
model: { type: "string" },
|
|
489
|
-
enableFallback: { type: "boolean" },
|
|
490
|
-
enableAnalytics: { type: "boolean" },
|
|
491
|
-
timeout: { type: ["string", "number"] },
|
|
492
|
-
temperature: { type: "number", minimum: 0, maximum: 2 },
|
|
493
|
-
maxTokens: { type: "number", minimum: 1 },
|
|
494
|
-
},
|
|
495
|
-
},
|
|
496
|
-
git: {
|
|
497
|
-
type: "object",
|
|
498
|
-
required: ["platform", "credentials"],
|
|
499
|
-
properties: {
|
|
500
|
-
platform: {
|
|
501
|
-
type: "string",
|
|
502
|
-
enum: ["bitbucket", "github", "gitlab", "azure-devops"],
|
|
503
|
-
},
|
|
504
|
-
credentials: {
|
|
505
|
-
type: "object",
|
|
506
|
-
required: ["username", "token"],
|
|
507
|
-
properties: {
|
|
508
|
-
username: { type: "string" },
|
|
509
|
-
token: { type: "string" },
|
|
510
|
-
baseUrl: { type: "string" },
|
|
511
|
-
},
|
|
512
|
-
},
|
|
513
|
-
},
|
|
514
|
-
},
|
|
515
|
-
},
|
|
516
|
-
},
|
|
517
|
-
features: {
|
|
518
|
-
type: "object",
|
|
519
|
-
required: ["codeReview", "descriptionEnhancement"],
|
|
520
|
-
properties: {
|
|
521
|
-
codeReview: {
|
|
522
|
-
type: "object",
|
|
523
|
-
required: ["enabled"],
|
|
524
|
-
properties: {
|
|
525
|
-
enabled: { type: "boolean" },
|
|
526
|
-
severityLevels: { type: "array", items: { type: "string" } },
|
|
527
|
-
categories: { type: "array", items: { type: "string" } },
|
|
528
|
-
excludePatterns: { type: "array", items: { type: "string" } },
|
|
529
|
-
},
|
|
530
|
-
},
|
|
531
|
-
descriptionEnhancement: {
|
|
532
|
-
type: "object",
|
|
533
|
-
required: ["enabled"],
|
|
534
|
-
properties: {
|
|
535
|
-
enabled: { type: "boolean" },
|
|
536
|
-
preserveContent: { type: "boolean" },
|
|
537
|
-
requiredSections: { type: "array" },
|
|
538
|
-
autoFormat: { type: "boolean" },
|
|
539
|
-
},
|
|
540
|
-
},
|
|
541
|
-
},
|
|
542
|
-
},
|
|
543
|
-
},
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* Find the first available configuration file
|
|
548
|
-
*/
|
|
549
|
-
findConfigFile() {
|
|
550
|
-
for (const configPath of this.configPaths) {
|
|
551
|
-
if (fs.existsSync(configPath)) {
|
|
552
|
-
return configPath;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Watch configuration file for changes and reload automatically
|
|
559
|
-
*/
|
|
560
|
-
watchConfig(callback) {
|
|
561
|
-
if (!this.configPaths.length) {
|
|
562
|
-
logger.warn("No configuration file found to watch");
|
|
563
|
-
return () => { };
|
|
564
|
-
}
|
|
565
|
-
const configPath = this.findConfigFile();
|
|
566
|
-
if (!configPath) {
|
|
567
|
-
logger.warn("No configuration file found to watch");
|
|
568
|
-
return () => { };
|
|
569
|
-
}
|
|
570
|
-
logger.debug(`Watching configuration file: ${configPath}`);
|
|
571
|
-
fs.watchFile(configPath, { interval: 1000 }, async () => {
|
|
572
|
-
try {
|
|
573
|
-
logger.info("Configuration file changed, reloading...");
|
|
574
|
-
const newConfig = await this.loadConfig();
|
|
575
|
-
if (callback) {
|
|
576
|
-
callback(newConfig);
|
|
577
|
-
}
|
|
578
|
-
logger.success("Configuration reloaded successfully");
|
|
579
|
-
}
|
|
580
|
-
catch (error) {
|
|
581
|
-
logger.error(`Failed to reload configuration: ${error.message}`);
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
// Return cleanup function
|
|
585
|
-
return () => {
|
|
586
|
-
fs.unwatchFile(configPath);
|
|
587
|
-
logger.debug("Stopped watching configuration file");
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
/**
|
|
591
|
-
* Enable hot-reload for configuration
|
|
592
|
-
*/
|
|
593
|
-
enableHotReload(callback) {
|
|
594
|
-
return this.watchConfig(callback);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
// Export singleton instance
|
|
598
|
-
export const configManager = new ConfigManager();
|
|
599
|
-
// Export factory function
|
|
600
|
-
export function createConfigManager() {
|
|
601
|
-
return new ConfigManager();
|
|
602
|
-
}
|
|
603
|
-
//# sourceMappingURL=ConfigManager.js.map
|