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