@juspay/yama 1.1.0 โ 1.1.1
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/CHANGELOG.md +27 -1
- package/README.md +151 -119
- package/dist/cli/index.js +201 -200
- package/dist/core/ContextGatherer.d.ts +3 -3
- package/dist/core/ContextGatherer.js +145 -149
- package/dist/core/Guardian.d.ts +1 -1
- package/dist/core/Guardian.js +122 -122
- package/dist/core/providers/BitbucketProvider.d.ts +3 -3
- package/dist/core/providers/BitbucketProvider.js +129 -121
- package/dist/features/CodeReviewer.d.ts +3 -3
- package/dist/features/CodeReviewer.js +290 -221
- package/dist/features/DescriptionEnhancer.d.ts +3 -3
- package/dist/features/DescriptionEnhancer.js +115 -94
- package/dist/index.d.ts +11 -11
- package/dist/index.js +10 -48
- package/dist/types/index.d.ts +21 -21
- package/dist/types/index.js +13 -18
- package/dist/utils/Cache.d.ts +1 -1
- package/dist/utils/Cache.js +62 -68
- package/dist/utils/ConfigManager.d.ts +1 -1
- package/dist/utils/ConfigManager.js +281 -253
- package/dist/utils/Logger.d.ts +2 -2
- package/dist/utils/Logger.js +69 -67
- package/package.json +7 -6
- package/yama.config.example.yaml +28 -21
|
@@ -1,23 +1,150 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* Enhanced Configuration Manager for Yama
|
|
4
3
|
* Handles configuration loading, validation, and merging from multiple sources
|
|
5
4
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
export class ConfigManager {
|
|
12
|
+
config = null;
|
|
13
|
+
configPaths = [];
|
|
14
|
+
/**
|
|
15
|
+
* Default configuration
|
|
16
|
+
*/
|
|
17
|
+
static DEFAULT_CONFIG = {
|
|
18
|
+
providers: {
|
|
19
|
+
ai: {
|
|
20
|
+
provider: "auto",
|
|
21
|
+
enableFallback: true,
|
|
22
|
+
enableAnalytics: false,
|
|
23
|
+
enableEvaluation: false,
|
|
24
|
+
timeout: "10m",
|
|
25
|
+
retryAttempts: 3,
|
|
26
|
+
temperature: 0.3,
|
|
27
|
+
maxTokens: 1000000,
|
|
28
|
+
},
|
|
29
|
+
git: {
|
|
30
|
+
platform: "bitbucket",
|
|
31
|
+
credentials: {
|
|
32
|
+
username: process.env.BITBUCKET_USERNAME || "",
|
|
33
|
+
token: process.env.BITBUCKET_TOKEN || "",
|
|
34
|
+
baseUrl: process.env.BITBUCKET_BASE_URL ||
|
|
35
|
+
"https://your-bitbucket-server.com",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
features: {
|
|
40
|
+
codeReview: {
|
|
41
|
+
enabled: true,
|
|
42
|
+
severityLevels: ["CRITICAL", "MAJOR", "MINOR", "SUGGESTION"],
|
|
43
|
+
categories: [
|
|
44
|
+
"security",
|
|
45
|
+
"performance",
|
|
46
|
+
"maintainability",
|
|
47
|
+
"functionality",
|
|
48
|
+
"error_handling",
|
|
49
|
+
"testing",
|
|
50
|
+
],
|
|
51
|
+
excludePatterns: ["*.lock", "*.svg", "*.min.js", "*.map"],
|
|
52
|
+
contextLines: 3,
|
|
53
|
+
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.",
|
|
54
|
+
focusAreas: [
|
|
55
|
+
"๐ Security Analysis (CRITICAL PRIORITY)",
|
|
56
|
+
"โก Performance Review",
|
|
57
|
+
"๐๏ธ Code Quality",
|
|
58
|
+
"๐งช Testing & Error Handling",
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
descriptionEnhancement: {
|
|
62
|
+
enabled: true,
|
|
63
|
+
preserveContent: true,
|
|
64
|
+
requiredSections: [
|
|
65
|
+
{
|
|
66
|
+
key: "changelog",
|
|
67
|
+
name: "Changelog (Modules Modified)",
|
|
68
|
+
required: true,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: "testcases",
|
|
72
|
+
name: "Test Cases (What to be tested)",
|
|
73
|
+
required: true,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
key: "config_changes",
|
|
77
|
+
name: "CAC Config Or Service Config Changes",
|
|
78
|
+
required: true,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
autoFormat: true,
|
|
82
|
+
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.",
|
|
83
|
+
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]",
|
|
84
|
+
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:',
|
|
85
|
+
},
|
|
86
|
+
securityScan: {
|
|
87
|
+
enabled: true,
|
|
88
|
+
level: "strict",
|
|
89
|
+
scanTypes: ["secrets", "vulnerabilities", "dependencies"],
|
|
90
|
+
},
|
|
91
|
+
analytics: {
|
|
92
|
+
enabled: true,
|
|
93
|
+
trackMetrics: true,
|
|
94
|
+
exportFormat: "json",
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
cache: {
|
|
98
|
+
enabled: true,
|
|
99
|
+
ttl: "1h",
|
|
100
|
+
maxSize: "100MB",
|
|
101
|
+
storage: "memory",
|
|
102
|
+
},
|
|
103
|
+
performance: {
|
|
104
|
+
batch: {
|
|
105
|
+
enabled: true,
|
|
106
|
+
maxConcurrent: 5,
|
|
107
|
+
delayBetween: "1s",
|
|
108
|
+
},
|
|
109
|
+
optimization: {
|
|
110
|
+
reuseConnections: true,
|
|
111
|
+
compressRequests: true,
|
|
112
|
+
enableHttp2: true,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
rules: {
|
|
116
|
+
security: [
|
|
117
|
+
{
|
|
118
|
+
name: "No hardcoded secrets",
|
|
119
|
+
pattern: "(password|secret|key|token)\\s*[=:]\\s*['\"][^'\"]{8,}['\"]",
|
|
120
|
+
severity: "CRITICAL",
|
|
121
|
+
message: "Hardcoded secrets detected",
|
|
122
|
+
suggestion: "Use environment variables or secure configuration management",
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
performance: [
|
|
126
|
+
{
|
|
127
|
+
name: "Avoid N+1 queries",
|
|
128
|
+
pattern: "for.*\\.(find|get|query|select)",
|
|
129
|
+
severity: "MAJOR",
|
|
130
|
+
message: "Potential N+1 query pattern detected",
|
|
131
|
+
suggestion: "Consider using batch queries or joins",
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
reporting: {
|
|
136
|
+
formats: ["markdown", "json"],
|
|
137
|
+
includeAnalytics: true,
|
|
138
|
+
includeMetrics: true,
|
|
139
|
+
},
|
|
140
|
+
monitoring: {
|
|
141
|
+
enabled: true,
|
|
142
|
+
metrics: ["performance", "cache", "api_calls"],
|
|
143
|
+
exportFormat: "json",
|
|
144
|
+
interval: "5m",
|
|
145
|
+
},
|
|
146
|
+
};
|
|
18
147
|
constructor() {
|
|
19
|
-
this.config = null;
|
|
20
|
-
this.configPaths = [];
|
|
21
148
|
this.setupConfigPaths();
|
|
22
149
|
}
|
|
23
150
|
/**
|
|
@@ -25,20 +152,20 @@ class ConfigManager {
|
|
|
25
152
|
*/
|
|
26
153
|
setupConfigPaths() {
|
|
27
154
|
const cwd = process.cwd();
|
|
28
|
-
const homeDir =
|
|
155
|
+
const homeDir = homedir();
|
|
29
156
|
this.configPaths = [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
157
|
+
path.join(cwd, "yama.config.yaml"),
|
|
158
|
+
path.join(cwd, "yama.config.yml"),
|
|
159
|
+
path.join(cwd, "yama.config.json"),
|
|
160
|
+
path.join(cwd, ".yama.yaml"),
|
|
161
|
+
path.join(cwd, ".yama.yml"),
|
|
162
|
+
path.join(cwd, ".yama.json"),
|
|
163
|
+
path.join(homeDir, ".yama", "config.yaml"),
|
|
164
|
+
path.join(homeDir, ".yama", "config.yml"),
|
|
165
|
+
path.join(homeDir, ".yama", "config.json"),
|
|
166
|
+
path.join(homeDir, ".config", "yama", "config.yaml"),
|
|
167
|
+
path.join(homeDir, ".config", "yama", "config.yml"),
|
|
168
|
+
path.join(homeDir, ".config", "yama", "config.json"),
|
|
42
169
|
];
|
|
43
170
|
}
|
|
44
171
|
/**
|
|
@@ -48,30 +175,30 @@ class ConfigManager {
|
|
|
48
175
|
if (this.config) {
|
|
49
176
|
return this.config;
|
|
50
177
|
}
|
|
51
|
-
|
|
178
|
+
logger.debug("Loading Yama configuration...");
|
|
52
179
|
// Start with default config
|
|
53
180
|
let config = this.deepClone(ConfigManager.DEFAULT_CONFIG);
|
|
54
181
|
// If specific config path provided, use only that
|
|
55
182
|
if (configPath) {
|
|
56
|
-
if (!
|
|
57
|
-
throw new
|
|
183
|
+
if (!fs.existsSync(configPath)) {
|
|
184
|
+
throw new ConfigurationError(`Configuration file not found: ${configPath}`);
|
|
58
185
|
}
|
|
59
186
|
const fileConfig = await this.loadConfigFile(configPath);
|
|
60
187
|
config = this.mergeConfigs(config, fileConfig);
|
|
61
|
-
|
|
188
|
+
logger.debug(`Loaded configuration from: ${configPath}`);
|
|
62
189
|
}
|
|
63
190
|
else {
|
|
64
191
|
// Search for config files in predefined paths
|
|
65
192
|
for (const configFilePath of this.configPaths) {
|
|
66
|
-
if (
|
|
193
|
+
if (fs.existsSync(configFilePath)) {
|
|
67
194
|
try {
|
|
68
195
|
const fileConfig = await this.loadConfigFile(configFilePath);
|
|
69
196
|
config = this.mergeConfigs(config, fileConfig);
|
|
70
|
-
|
|
197
|
+
logger.debug(`Loaded configuration from: ${configFilePath}`);
|
|
71
198
|
break;
|
|
72
199
|
}
|
|
73
200
|
catch (error) {
|
|
74
|
-
|
|
201
|
+
logger.warn(`Failed to load config from ${configFilePath}:`, error);
|
|
75
202
|
}
|
|
76
203
|
}
|
|
77
204
|
}
|
|
@@ -81,7 +208,7 @@ class ConfigManager {
|
|
|
81
208
|
// Validate configuration
|
|
82
209
|
this.validateConfig(config);
|
|
83
210
|
this.config = config;
|
|
84
|
-
|
|
211
|
+
logger.debug("Configuration loaded successfully");
|
|
85
212
|
return config;
|
|
86
213
|
}
|
|
87
214
|
/**
|
|
@@ -89,20 +216,20 @@ class ConfigManager {
|
|
|
89
216
|
*/
|
|
90
217
|
async loadConfigFile(filePath) {
|
|
91
218
|
try {
|
|
92
|
-
const content =
|
|
93
|
-
const ext =
|
|
219
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
220
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
94
221
|
switch (ext) {
|
|
95
|
-
case
|
|
96
|
-
case
|
|
97
|
-
return
|
|
98
|
-
case
|
|
222
|
+
case ".yaml":
|
|
223
|
+
case ".yml":
|
|
224
|
+
return yaml.parse(content);
|
|
225
|
+
case ".json":
|
|
99
226
|
return JSON.parse(content);
|
|
100
227
|
default:
|
|
101
|
-
throw new
|
|
228
|
+
throw new ConfigurationError(`Unsupported config file format: ${ext}`);
|
|
102
229
|
}
|
|
103
230
|
}
|
|
104
231
|
catch (error) {
|
|
105
|
-
throw new
|
|
232
|
+
throw new ConfigurationError(`Failed to parse config file ${filePath}: ${error.message}`);
|
|
106
233
|
}
|
|
107
234
|
}
|
|
108
235
|
/**
|
|
@@ -138,20 +265,22 @@ class ConfigManager {
|
|
|
138
265
|
}
|
|
139
266
|
// Feature toggles
|
|
140
267
|
if (env.ENABLE_CODE_REVIEW !== undefined) {
|
|
141
|
-
config.features.codeReview.enabled = env.ENABLE_CODE_REVIEW ===
|
|
268
|
+
config.features.codeReview.enabled = env.ENABLE_CODE_REVIEW === "true";
|
|
142
269
|
}
|
|
143
270
|
if (env.ENABLE_DESCRIPTION_ENHANCEMENT !== undefined) {
|
|
144
|
-
config.features.descriptionEnhancement.enabled =
|
|
271
|
+
config.features.descriptionEnhancement.enabled =
|
|
272
|
+
env.ENABLE_DESCRIPTION_ENHANCEMENT === "true";
|
|
145
273
|
}
|
|
146
274
|
if (env.ENABLE_SECURITY_SCAN !== undefined) {
|
|
147
|
-
config.features.securityScan.enabled =
|
|
275
|
+
config.features.securityScan.enabled =
|
|
276
|
+
env.ENABLE_SECURITY_SCAN === "true";
|
|
148
277
|
}
|
|
149
278
|
if (env.ENABLE_ANALYTICS !== undefined) {
|
|
150
|
-
config.features.analytics.enabled = env.ENABLE_ANALYTICS ===
|
|
279
|
+
config.features.analytics.enabled = env.ENABLE_ANALYTICS === "true";
|
|
151
280
|
}
|
|
152
281
|
// Cache configuration
|
|
153
282
|
if (env.CACHE_ENABLED !== undefined) {
|
|
154
|
-
config.cache.enabled = env.CACHE_ENABLED ===
|
|
283
|
+
config.cache.enabled = env.CACHE_ENABLED === "true";
|
|
155
284
|
}
|
|
156
285
|
if (env.CACHE_TTL) {
|
|
157
286
|
config.cache.ttl = env.CACHE_TTL;
|
|
@@ -160,11 +289,11 @@ class ConfigManager {
|
|
|
160
289
|
config.cache.storage = env.CACHE_STORAGE;
|
|
161
290
|
}
|
|
162
291
|
// Debug mode
|
|
163
|
-
if (env.GUARDIAN_DEBUG ===
|
|
164
|
-
|
|
165
|
-
|
|
292
|
+
if (env.GUARDIAN_DEBUG === "true") {
|
|
293
|
+
logger.setLevel("debug");
|
|
294
|
+
logger.setVerbose(true);
|
|
166
295
|
}
|
|
167
|
-
|
|
296
|
+
logger.debug("Applied environment variable overrides");
|
|
168
297
|
return config;
|
|
169
298
|
}
|
|
170
299
|
/**
|
|
@@ -174,36 +303,36 @@ class ConfigManager {
|
|
|
174
303
|
const errors = [];
|
|
175
304
|
// Validate AI provider credentials
|
|
176
305
|
if (!config.providers.ai.provider) {
|
|
177
|
-
errors.push(
|
|
306
|
+
errors.push("AI provider must be specified");
|
|
178
307
|
}
|
|
179
308
|
// Validate Git provider credentials
|
|
180
309
|
if (!config.providers.git.credentials.username) {
|
|
181
|
-
errors.push(
|
|
310
|
+
errors.push("Git username must be specified");
|
|
182
311
|
}
|
|
183
312
|
if (!config.providers.git.credentials.token) {
|
|
184
|
-
errors.push(
|
|
313
|
+
errors.push("Git token must be specified");
|
|
185
314
|
}
|
|
186
315
|
// Validate enabled features have required configuration
|
|
187
316
|
if (config.features.codeReview.enabled) {
|
|
188
317
|
if (!config.features.codeReview.severityLevels?.length) {
|
|
189
|
-
errors.push(
|
|
318
|
+
errors.push("Code review severity levels must be specified when enabled");
|
|
190
319
|
}
|
|
191
320
|
}
|
|
192
321
|
if (config.features.descriptionEnhancement.enabled) {
|
|
193
322
|
if (!config.features.descriptionEnhancement.requiredSections?.length) {
|
|
194
|
-
errors.push(
|
|
323
|
+
errors.push("Description enhancement required sections must be specified when enabled");
|
|
195
324
|
}
|
|
196
325
|
}
|
|
197
326
|
// Validate cache configuration
|
|
198
327
|
if (config.cache?.enabled) {
|
|
199
328
|
if (!config.cache.storage) {
|
|
200
|
-
errors.push(
|
|
329
|
+
errors.push("Cache storage type must be specified when cache is enabled");
|
|
201
330
|
}
|
|
202
331
|
}
|
|
203
332
|
if (errors.length > 0) {
|
|
204
|
-
throw new
|
|
333
|
+
throw new ConfigurationError(`Configuration validation failed:\n${errors.map((e) => ` - ${e}`).join("\n")}`);
|
|
205
334
|
}
|
|
206
|
-
|
|
335
|
+
logger.debug("Configuration validation passed");
|
|
207
336
|
}
|
|
208
337
|
/**
|
|
209
338
|
* Merge two configuration objects deeply
|
|
@@ -217,7 +346,9 @@ class ConfigManager {
|
|
|
217
346
|
deepMerge(target, source) {
|
|
218
347
|
const result = { ...target };
|
|
219
348
|
for (const key in source) {
|
|
220
|
-
if (source[key] !== null &&
|
|
349
|
+
if (source[key] !== null &&
|
|
350
|
+
typeof source[key] === "object" &&
|
|
351
|
+
!Array.isArray(source[key])) {
|
|
221
352
|
result[key] = this.deepMerge(target[key] || {}, source[key]);
|
|
222
353
|
}
|
|
223
354
|
else {
|
|
@@ -237,7 +368,7 @@ class ConfigManager {
|
|
|
237
368
|
*/
|
|
238
369
|
getConfig() {
|
|
239
370
|
if (!this.config) {
|
|
240
|
-
throw new
|
|
371
|
+
throw new ConfigurationError("Configuration not loaded. Call loadConfig() first.");
|
|
241
372
|
}
|
|
242
373
|
return this.config;
|
|
243
374
|
}
|
|
@@ -245,15 +376,15 @@ class ConfigManager {
|
|
|
245
376
|
* Create default configuration file
|
|
246
377
|
*/
|
|
247
378
|
async createDefaultConfig(outputPath) {
|
|
248
|
-
const defaultPath = outputPath ||
|
|
249
|
-
const configContent =
|
|
379
|
+
const defaultPath = outputPath || path.join(process.cwd(), "yama.config.yaml");
|
|
380
|
+
const configContent = yaml.stringify(ConfigManager.DEFAULT_CONFIG, {
|
|
250
381
|
indent: 2,
|
|
251
|
-
lineWidth: 100
|
|
382
|
+
lineWidth: 100,
|
|
252
383
|
});
|
|
253
384
|
// Add comments to make the config file more user-friendly
|
|
254
385
|
const commentedConfig = this.addConfigComments(configContent);
|
|
255
|
-
|
|
256
|
-
|
|
386
|
+
fs.writeFileSync(defaultPath, commentedConfig, "utf8");
|
|
387
|
+
logger.info(`Default configuration created at: ${defaultPath}`);
|
|
257
388
|
return defaultPath;
|
|
258
389
|
}
|
|
259
390
|
/**
|
|
@@ -266,19 +397,19 @@ class ConfigManager {
|
|
|
266
397
|
|
|
267
398
|
`;
|
|
268
399
|
const sections = [
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
400
|
+
"# AI Provider Configuration",
|
|
401
|
+
"# Git Platform Configuration",
|
|
402
|
+
"# Feature Configuration",
|
|
403
|
+
"# Cache Configuration",
|
|
404
|
+
"# Performance Configuration",
|
|
405
|
+
"# Custom Rules Configuration",
|
|
406
|
+
"# Reporting Configuration",
|
|
276
407
|
];
|
|
277
408
|
let commented = header + content;
|
|
278
409
|
// Add section comments (this is a simplified approach)
|
|
279
|
-
sections.forEach(section => {
|
|
280
|
-
const key = section.split(
|
|
281
|
-
commented = commented.replace(new RegExp(`^(${key}:)`,
|
|
410
|
+
sections.forEach((section) => {
|
|
411
|
+
const key = section.split(" ")[1].toLowerCase();
|
|
412
|
+
commented = commented.replace(new RegExp(`^(${key}:)`, "m"), `${section}\n$1`);
|
|
282
413
|
});
|
|
283
414
|
return commented;
|
|
284
415
|
}
|
|
@@ -288,30 +419,33 @@ class ConfigManager {
|
|
|
288
419
|
validateSection(section, config) {
|
|
289
420
|
try {
|
|
290
421
|
switch (section) {
|
|
291
|
-
case
|
|
422
|
+
case "providers":
|
|
292
423
|
return this.validateProviders(config);
|
|
293
|
-
case
|
|
424
|
+
case "features":
|
|
294
425
|
return this.validateFeatures(config);
|
|
295
|
-
case
|
|
426
|
+
case "cache":
|
|
296
427
|
return this.validateCache(config);
|
|
297
428
|
default:
|
|
298
429
|
return true;
|
|
299
430
|
}
|
|
300
431
|
}
|
|
301
432
|
catch (error) {
|
|
302
|
-
|
|
433
|
+
logger.error(`Validation failed for section ${String(section)}:`, error);
|
|
303
434
|
return false;
|
|
304
435
|
}
|
|
305
436
|
}
|
|
306
437
|
validateProviders(providers) {
|
|
307
|
-
return !!(providers?.ai?.provider &&
|
|
438
|
+
return !!(providers?.ai?.provider &&
|
|
439
|
+
providers?.git?.credentials?.username &&
|
|
440
|
+
providers?.git?.credentials?.token);
|
|
308
441
|
}
|
|
309
442
|
validateFeatures(features) {
|
|
310
|
-
return !!(features && typeof features ===
|
|
443
|
+
return !!(features && typeof features === "object");
|
|
311
444
|
}
|
|
312
445
|
validateCache(cacheConfig) {
|
|
313
|
-
if (!cacheConfig?.enabled)
|
|
446
|
+
if (!cacheConfig?.enabled) {
|
|
314
447
|
return true;
|
|
448
|
+
}
|
|
315
449
|
return !!(cacheConfig.storage && cacheConfig.ttl);
|
|
316
450
|
}
|
|
317
451
|
/**
|
|
@@ -319,71 +453,74 @@ class ConfigManager {
|
|
|
319
453
|
*/
|
|
320
454
|
getSchema() {
|
|
321
455
|
return {
|
|
322
|
-
type:
|
|
323
|
-
required: [
|
|
456
|
+
type: "object",
|
|
457
|
+
required: ["providers", "features"],
|
|
324
458
|
properties: {
|
|
325
459
|
providers: {
|
|
326
|
-
type:
|
|
327
|
-
required: [
|
|
460
|
+
type: "object",
|
|
461
|
+
required: ["ai", "git"],
|
|
328
462
|
properties: {
|
|
329
463
|
ai: {
|
|
330
|
-
type:
|
|
331
|
-
required: [
|
|
464
|
+
type: "object",
|
|
465
|
+
required: ["provider"],
|
|
332
466
|
properties: {
|
|
333
|
-
provider: { type:
|
|
334
|
-
model: { type:
|
|
335
|
-
enableFallback: { type:
|
|
336
|
-
enableAnalytics: { type:
|
|
337
|
-
timeout: { type: [
|
|
338
|
-
temperature: { type:
|
|
339
|
-
maxTokens: { type:
|
|
340
|
-
}
|
|
467
|
+
provider: { type: "string" },
|
|
468
|
+
model: { type: "string" },
|
|
469
|
+
enableFallback: { type: "boolean" },
|
|
470
|
+
enableAnalytics: { type: "boolean" },
|
|
471
|
+
timeout: { type: ["string", "number"] },
|
|
472
|
+
temperature: { type: "number", minimum: 0, maximum: 2 },
|
|
473
|
+
maxTokens: { type: "number", minimum: 1 },
|
|
474
|
+
},
|
|
341
475
|
},
|
|
342
476
|
git: {
|
|
343
|
-
type:
|
|
344
|
-
required: [
|
|
477
|
+
type: "object",
|
|
478
|
+
required: ["platform", "credentials"],
|
|
345
479
|
properties: {
|
|
346
|
-
platform: {
|
|
480
|
+
platform: {
|
|
481
|
+
type: "string",
|
|
482
|
+
enum: ["bitbucket", "github", "gitlab", "azure-devops"],
|
|
483
|
+
},
|
|
347
484
|
credentials: {
|
|
348
|
-
type:
|
|
349
|
-
required: [
|
|
485
|
+
type: "object",
|
|
486
|
+
required: ["username", "token"],
|
|
350
487
|
properties: {
|
|
351
|
-
username: { type:
|
|
352
|
-
token: { type:
|
|
353
|
-
baseUrl: { type:
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
488
|
+
username: { type: "string" },
|
|
489
|
+
token: { type: "string" },
|
|
490
|
+
baseUrl: { type: "string" },
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
},
|
|
359
496
|
},
|
|
360
497
|
features: {
|
|
361
|
-
type:
|
|
362
|
-
required: [
|
|
498
|
+
type: "object",
|
|
499
|
+
required: ["codeReview", "descriptionEnhancement"],
|
|
363
500
|
properties: {
|
|
364
501
|
codeReview: {
|
|
365
|
-
type:
|
|
366
|
-
required: [
|
|
502
|
+
type: "object",
|
|
503
|
+
required: ["enabled"],
|
|
367
504
|
properties: {
|
|
368
|
-
enabled: { type:
|
|
369
|
-
severityLevels: { type:
|
|
370
|
-
categories: { type:
|
|
371
|
-
excludePatterns: { type:
|
|
372
|
-
}
|
|
505
|
+
enabled: { type: "boolean" },
|
|
506
|
+
severityLevels: { type: "array", items: { type: "string" } },
|
|
507
|
+
categories: { type: "array", items: { type: "string" } },
|
|
508
|
+
excludePatterns: { type: "array", items: { type: "string" } },
|
|
509
|
+
},
|
|
373
510
|
},
|
|
374
511
|
descriptionEnhancement: {
|
|
375
|
-
type:
|
|
376
|
-
required: [
|
|
512
|
+
type: "object",
|
|
513
|
+
required: ["enabled"],
|
|
377
514
|
properties: {
|
|
378
|
-
enabled: { type:
|
|
379
|
-
preserveContent: { type:
|
|
380
|
-
requiredSections: { type:
|
|
381
|
-
autoFormat: { type:
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
515
|
+
enabled: { type: "boolean" },
|
|
516
|
+
preserveContent: { type: "boolean" },
|
|
517
|
+
requiredSections: { type: "array" },
|
|
518
|
+
autoFormat: { type: "boolean" },
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
},
|
|
523
|
+
},
|
|
387
524
|
};
|
|
388
525
|
}
|
|
389
526
|
/**
|
|
@@ -391,7 +528,7 @@ class ConfigManager {
|
|
|
391
528
|
*/
|
|
392
529
|
findConfigFile() {
|
|
393
530
|
for (const configPath of this.configPaths) {
|
|
394
|
-
if (
|
|
531
|
+
if (fs.existsSync(configPath)) {
|
|
395
532
|
return configPath;
|
|
396
533
|
}
|
|
397
534
|
}
|
|
@@ -402,32 +539,32 @@ class ConfigManager {
|
|
|
402
539
|
*/
|
|
403
540
|
watchConfig(callback) {
|
|
404
541
|
if (!this.configPaths.length) {
|
|
405
|
-
|
|
542
|
+
logger.warn("No configuration file found to watch");
|
|
406
543
|
return () => { };
|
|
407
544
|
}
|
|
408
545
|
const configPath = this.findConfigFile();
|
|
409
546
|
if (!configPath) {
|
|
410
|
-
|
|
547
|
+
logger.warn("No configuration file found to watch");
|
|
411
548
|
return () => { };
|
|
412
549
|
}
|
|
413
|
-
|
|
414
|
-
|
|
550
|
+
logger.debug(`Watching configuration file: ${configPath}`);
|
|
551
|
+
fs.watchFile(configPath, { interval: 1000 }, async () => {
|
|
415
552
|
try {
|
|
416
|
-
|
|
553
|
+
logger.info("Configuration file changed, reloading...");
|
|
417
554
|
const newConfig = await this.loadConfig();
|
|
418
555
|
if (callback) {
|
|
419
556
|
callback(newConfig);
|
|
420
557
|
}
|
|
421
|
-
|
|
558
|
+
logger.success("Configuration reloaded successfully");
|
|
422
559
|
}
|
|
423
560
|
catch (error) {
|
|
424
|
-
|
|
561
|
+
logger.error(`Failed to reload configuration: ${error.message}`);
|
|
425
562
|
}
|
|
426
563
|
});
|
|
427
564
|
// Return cleanup function
|
|
428
565
|
return () => {
|
|
429
|
-
|
|
430
|
-
|
|
566
|
+
fs.unwatchFile(configPath);
|
|
567
|
+
logger.debug("Stopped watching configuration file");
|
|
431
568
|
};
|
|
432
569
|
}
|
|
433
570
|
/**
|
|
@@ -437,119 +574,10 @@ class ConfigManager {
|
|
|
437
574
|
return this.watchConfig(callback);
|
|
438
575
|
}
|
|
439
576
|
}
|
|
440
|
-
exports.ConfigManager = ConfigManager;
|
|
441
|
-
/**
|
|
442
|
-
* Default configuration
|
|
443
|
-
*/
|
|
444
|
-
ConfigManager.DEFAULT_CONFIG = {
|
|
445
|
-
providers: {
|
|
446
|
-
ai: {
|
|
447
|
-
provider: 'auto',
|
|
448
|
-
enableFallback: true,
|
|
449
|
-
enableAnalytics: false,
|
|
450
|
-
enableEvaluation: false,
|
|
451
|
-
timeout: '10m',
|
|
452
|
-
retryAttempts: 3,
|
|
453
|
-
temperature: 0.3,
|
|
454
|
-
maxTokens: 1000000
|
|
455
|
-
},
|
|
456
|
-
git: {
|
|
457
|
-
platform: 'bitbucket',
|
|
458
|
-
credentials: {
|
|
459
|
-
username: process.env.BITBUCKET_USERNAME || '',
|
|
460
|
-
token: process.env.BITBUCKET_TOKEN || '',
|
|
461
|
-
baseUrl: process.env.BITBUCKET_BASE_URL || 'https://your-bitbucket-server.com'
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
},
|
|
465
|
-
features: {
|
|
466
|
-
codeReview: {
|
|
467
|
-
enabled: true,
|
|
468
|
-
severityLevels: ['CRITICAL', 'MAJOR', 'MINOR', 'SUGGESTION'],
|
|
469
|
-
categories: ['security', 'performance', 'maintainability', 'functionality', 'error_handling', 'testing'],
|
|
470
|
-
excludePatterns: ['*.lock', '*.svg', '*.min.js', '*.map'],
|
|
471
|
-
contextLines: 3,
|
|
472
|
-
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.',
|
|
473
|
-
focusAreas: ['๐ Security Analysis (CRITICAL PRIORITY)', 'โก Performance Review', '๐๏ธ Code Quality', '๐งช Testing & Error Handling']
|
|
474
|
-
},
|
|
475
|
-
descriptionEnhancement: {
|
|
476
|
-
enabled: true,
|
|
477
|
-
preserveContent: true,
|
|
478
|
-
requiredSections: [
|
|
479
|
-
{ key: 'changelog', name: 'Changelog (Modules Modified)', required: true },
|
|
480
|
-
{ key: 'testcases', name: 'Test Cases (What to be tested)', required: true },
|
|
481
|
-
{ key: 'config_changes', name: 'CAC Config Or Service Config Changes', required: true }
|
|
482
|
-
],
|
|
483
|
-
autoFormat: true,
|
|
484
|
-
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.',
|
|
485
|
-
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]',
|
|
486
|
-
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:'
|
|
487
|
-
},
|
|
488
|
-
securityScan: {
|
|
489
|
-
enabled: true,
|
|
490
|
-
level: 'strict',
|
|
491
|
-
scanTypes: ['secrets', 'vulnerabilities', 'dependencies']
|
|
492
|
-
},
|
|
493
|
-
analytics: {
|
|
494
|
-
enabled: true,
|
|
495
|
-
trackMetrics: true,
|
|
496
|
-
exportFormat: 'json'
|
|
497
|
-
}
|
|
498
|
-
},
|
|
499
|
-
cache: {
|
|
500
|
-
enabled: true,
|
|
501
|
-
ttl: '1h',
|
|
502
|
-
maxSize: '100MB',
|
|
503
|
-
storage: 'memory'
|
|
504
|
-
},
|
|
505
|
-
performance: {
|
|
506
|
-
batch: {
|
|
507
|
-
enabled: true,
|
|
508
|
-
maxConcurrent: 5,
|
|
509
|
-
delayBetween: '1s'
|
|
510
|
-
},
|
|
511
|
-
optimization: {
|
|
512
|
-
reuseConnections: true,
|
|
513
|
-
compressRequests: true,
|
|
514
|
-
enableHttp2: true
|
|
515
|
-
}
|
|
516
|
-
},
|
|
517
|
-
rules: {
|
|
518
|
-
security: [
|
|
519
|
-
{
|
|
520
|
-
name: 'No hardcoded secrets',
|
|
521
|
-
pattern: '(password|secret|key|token)\\s*[=:]\\s*[\'"][^\'"]{8,}[\'"]',
|
|
522
|
-
severity: 'CRITICAL',
|
|
523
|
-
message: 'Hardcoded secrets detected',
|
|
524
|
-
suggestion: 'Use environment variables or secure configuration management'
|
|
525
|
-
}
|
|
526
|
-
],
|
|
527
|
-
performance: [
|
|
528
|
-
{
|
|
529
|
-
name: 'Avoid N+1 queries',
|
|
530
|
-
pattern: 'for.*\\.(find|get|query|select)',
|
|
531
|
-
severity: 'MAJOR',
|
|
532
|
-
message: 'Potential N+1 query pattern detected',
|
|
533
|
-
suggestion: 'Consider using batch queries or joins'
|
|
534
|
-
}
|
|
535
|
-
]
|
|
536
|
-
},
|
|
537
|
-
reporting: {
|
|
538
|
-
formats: ['markdown', 'json'],
|
|
539
|
-
includeAnalytics: true,
|
|
540
|
-
includeMetrics: true
|
|
541
|
-
},
|
|
542
|
-
monitoring: {
|
|
543
|
-
enabled: true,
|
|
544
|
-
metrics: ['performance', 'cache', 'api_calls'],
|
|
545
|
-
exportFormat: 'json',
|
|
546
|
-
interval: '5m'
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
577
|
// Export singleton instance
|
|
550
|
-
|
|
578
|
+
export const configManager = new ConfigManager();
|
|
551
579
|
// Export factory function
|
|
552
|
-
function createConfigManager() {
|
|
580
|
+
export function createConfigManager() {
|
|
553
581
|
return new ConfigManager();
|
|
554
582
|
}
|
|
555
583
|
//# sourceMappingURL=ConfigManager.js.map
|