codesummary 1.0.2 → 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.
@@ -0,0 +1,373 @@
1
+ import fs from "fs-extra";
2
+ import yaml from "js-yaml";
3
+ import path from "path";
4
+
5
+ /**
6
+ * RAG Configuration Manager
7
+ * Loads and validates configuration from raggen.config.yaml
8
+ */
9
+ export class RagConfigManager {
10
+ constructor() {
11
+ this.defaultConfig = this.getDefaultConfig();
12
+ this.configPath = null;
13
+ this.loadedConfig = null;
14
+ }
15
+
16
+ /**
17
+ * Load configuration from YAML file
18
+ * @param {string} configPath - Path to config file (optional)
19
+ * @returns {object} Merged configuration
20
+ */
21
+ async loadConfig(configPath = null) {
22
+ // Try to find config file
23
+ this.configPath = configPath || (await this.findConfigFile());
24
+
25
+ if (this.configPath && (await fs.pathExists(this.configPath))) {
26
+ try {
27
+ const yamlContent = await fs.readFile(this.configPath, "utf8");
28
+ const userConfig = yaml.load(yamlContent);
29
+
30
+ // Merge with defaults
31
+ this.loadedConfig = this.mergeConfigs(this.defaultConfig, userConfig);
32
+
33
+ console.log(`šŸ“‹ RAG config loaded from: ${this.configPath}`);
34
+ return this.loadedConfig;
35
+ } catch (error) {
36
+ console.warn(`āš ļø Error loading RAG config: ${error.message}`);
37
+ console.log(`šŸ“‹ Using default RAG configuration`);
38
+ return this.defaultConfig;
39
+ }
40
+ } else {
41
+ console.log(`šŸ“‹ No RAG config found, using defaults`);
42
+ return this.defaultConfig;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Find configuration file in common locations
48
+ * @returns {string|null} Path to config file or null
49
+ */
50
+ async findConfigFile() {
51
+ const searchPaths = [
52
+ "raggen.config.yaml",
53
+ "raggen.config.yml",
54
+ ".raggen.config.yaml",
55
+ ".raggen.config.yml",
56
+ "config/raggen.yaml",
57
+ "config/raggen.yml",
58
+ ];
59
+
60
+ for (const searchPath of searchPaths) {
61
+ if (await fs.pathExists(searchPath)) {
62
+ return path.resolve(searchPath);
63
+ }
64
+ }
65
+
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * Get default configuration
71
+ * @returns {object} Default config
72
+ */
73
+ getDefaultConfig() {
74
+ return {
75
+ extensions: {
76
+ include: [
77
+ ".json",
78
+ ".ts",
79
+ ".js",
80
+ ".jsx",
81
+ ".tsx",
82
+ ".xml",
83
+ ".html",
84
+ ".css",
85
+ ".scss",
86
+ ".md",
87
+ ".txt",
88
+ ".py",
89
+ ".java",
90
+ ".cs",
91
+ ".cpp",
92
+ ".c",
93
+ ".h",
94
+ ".yaml",
95
+ ".yml",
96
+ ".sh",
97
+ ".bat",
98
+ ],
99
+ },
100
+ chunking: {
101
+ maxTokens: 1000,
102
+ overlap: 200,
103
+ tokenEstimation: "ceil(length/4)",
104
+ },
105
+ handlers: {
106
+ code: {
107
+ splitByFunction: true,
108
+ detectImports: true,
109
+ detectCalls: true,
110
+ complexityAnalysis: true,
111
+ },
112
+ markup: {
113
+ splitByElement: true,
114
+ preserveStructure: true,
115
+ },
116
+ styling: {
117
+ splitByRule: true,
118
+ detectImports: true,
119
+ },
120
+ config: {
121
+ splitBySection: true,
122
+ validateSyntax: false,
123
+ },
124
+ },
125
+ paths: {
126
+ exclude: [
127
+ "node_modules",
128
+ ".git",
129
+ "dist",
130
+ "build",
131
+ "coverage",
132
+ "out",
133
+ "__pycache__",
134
+ ".next",
135
+ ".nuxt",
136
+ ".cache",
137
+ "tmp",
138
+ "temp",
139
+ "logs",
140
+ "bower_components",
141
+ "vendor",
142
+ ],
143
+ },
144
+ files: {
145
+ exclude: [
146
+ "*-lock.json",
147
+ "*.lock",
148
+ "composer.lock",
149
+ "Pipfile.lock",
150
+ "*.min.js",
151
+ "*.min.css",
152
+ "*.map",
153
+ ".DS_Store",
154
+ "Thumbs.db",
155
+ "*-lock.yaml",
156
+ ],
157
+ },
158
+ performance: {
159
+ maxWorkers: 1,
160
+ batchSize: 50,
161
+ maxFileSize: "100MB",
162
+ streamingThreshold: "10MB",
163
+ },
164
+ output: {
165
+ format: "json",
166
+ compression: false,
167
+ validation: true,
168
+ indexing: true,
169
+ },
170
+ metadata: {
171
+ calculateHashes: true,
172
+ extractTags: true,
173
+ trackRelationships: true,
174
+ includeStats: true,
175
+ },
176
+ logging: {
177
+ level: "info",
178
+ progressReporting: true,
179
+ statisticsReporting: true,
180
+ },
181
+ quality: {
182
+ maxChunkSize: "50KB",
183
+ maxOutputSize: "250MB",
184
+ duplicateDetection: true,
185
+ emptyChunkHandling: "skip",
186
+ },
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Deep merge configuration objects
192
+ * @param {object} defaultConfig - Default configuration
193
+ * @param {object} userConfig - User configuration
194
+ * @returns {object} Merged configuration
195
+ */
196
+ mergeConfigs(defaultConfig, userConfig) {
197
+ const merged = JSON.parse(JSON.stringify(defaultConfig)); // Deep clone
198
+
199
+ return this.deepMerge(merged, userConfig);
200
+ }
201
+
202
+ /**
203
+ * Recursively merge objects
204
+ * @param {object} target - Target object
205
+ * @param {object} source - Source object
206
+ * @returns {object} Merged object
207
+ */
208
+ deepMerge(target, source) {
209
+ for (const key in source) {
210
+ if (source.hasOwnProperty(key)) {
211
+ if (
212
+ source[key] &&
213
+ typeof source[key] === "object" &&
214
+ !Array.isArray(source[key])
215
+ ) {
216
+ // Recursive merge for objects
217
+ if (!target[key] || typeof target[key] !== "object") {
218
+ target[key] = {};
219
+ }
220
+ this.deepMerge(target[key], source[key]);
221
+ } else {
222
+ // Direct assignment for primitives and arrays
223
+ target[key] = source[key];
224
+ }
225
+ }
226
+ }
227
+ return target;
228
+ }
229
+
230
+ /**
231
+ * Validate configuration
232
+ * @param {object} config - Configuration to validate
233
+ * @returns {boolean} True if valid
234
+ */
235
+ validateConfig(config) {
236
+ const errors = [];
237
+
238
+ // Validate required sections
239
+ const requiredSections = ["extensions", "chunking", "handlers"];
240
+ for (const section of requiredSections) {
241
+ if (!config[section]) {
242
+ errors.push(`Missing required section: ${section}`);
243
+ }
244
+ }
245
+
246
+ // Validate chunking settings
247
+ if (config.chunking) {
248
+ if (
249
+ typeof config.chunking.maxTokens !== "number" ||
250
+ config.chunking.maxTokens <= 0
251
+ ) {
252
+ errors.push("chunking.maxTokens must be a positive number");
253
+ }
254
+ if (
255
+ typeof config.chunking.overlap !== "number" ||
256
+ config.chunking.overlap < 0
257
+ ) {
258
+ errors.push("chunking.overlap must be a non-negative number");
259
+ }
260
+ }
261
+
262
+ // Validate extensions
263
+ if (config.extensions && config.extensions.include) {
264
+ if (!Array.isArray(config.extensions.include)) {
265
+ errors.push("extensions.include must be an array");
266
+ } else {
267
+ for (const ext of config.extensions.include) {
268
+ if (typeof ext !== "string" || !ext.startsWith(".")) {
269
+ errors.push(`Invalid extension: ${ext} (must start with dot)`);
270
+ }
271
+ }
272
+ }
273
+ }
274
+
275
+ if (errors.length > 0) {
276
+ console.error("āŒ RAG Configuration validation errors:");
277
+ errors.forEach((error) => console.error(` • ${error}`));
278
+ return false;
279
+ }
280
+
281
+ return true;
282
+ }
283
+
284
+ /**
285
+ * Get configuration value with dot notation
286
+ * @param {string} path - Configuration path (e.g., 'chunking.maxTokens')
287
+ * @param {any} defaultValue - Default value if not found
288
+ * @returns {any} Configuration value
289
+ */
290
+ get(path, defaultValue = null) {
291
+ const config = this.loadedConfig || this.defaultConfig;
292
+
293
+ return path.split(".").reduce((obj, key) => {
294
+ return obj && obj[key] !== undefined ? obj[key] : defaultValue;
295
+ }, config);
296
+ }
297
+
298
+ /**
299
+ * Display current configuration
300
+ */
301
+ displayConfig() {
302
+ const config = this.loadedConfig || this.defaultConfig;
303
+
304
+ console.log("\nšŸ“‹ RAG Generator Configuration:");
305
+ console.log(` Source: ${this.configPath ? this.configPath : "defaults"}`);
306
+ console.log(` Extensions: ${config.extensions.include.length} types`);
307
+ console.log(` Max tokens per chunk: ${config.chunking.maxTokens}`);
308
+ console.log(` Token overlap: ${config.chunking.overlap}`);
309
+ console.log(` Max workers: ${config.performance.maxWorkers}`);
310
+ console.log(` Batch size: ${config.performance.batchSize}`);
311
+ console.log();
312
+ }
313
+
314
+ /**
315
+ * Parse file size string to bytes
316
+ * @param {string} sizeStr - Size string (e.g., '100MB', '1GB')
317
+ * @returns {number} Size in bytes
318
+ */
319
+ parseFileSize(sizeStr) {
320
+ if (typeof sizeStr === "number") return sizeStr;
321
+
322
+ const units = {
323
+ B: 1,
324
+ KB: 1024,
325
+ MB: 1024 * 1024,
326
+ GB: 1024 * 1024 * 1024,
327
+ };
328
+
329
+ const match = sizeStr.match(/^(\d+(?:\.\d+)?)\s*([KMGT]?B)$/i);
330
+ if (!match) return 0;
331
+
332
+ const value = parseFloat(match[1]);
333
+ const unit = match[2].toUpperCase();
334
+
335
+ return Math.floor(value * (units[unit] || 1));
336
+ }
337
+
338
+ /**
339
+ * Check if file should be excluded by path
340
+ * @param {string} filePath - File path to check
341
+ * @returns {boolean} True if should be excluded
342
+ */
343
+ shouldExcludePath(filePath) {
344
+ const config = this.loadedConfig || this.defaultConfig;
345
+ const excludePaths = config.paths?.exclude || [];
346
+
347
+ return excludePaths.some((pattern) => {
348
+ return (
349
+ filePath.includes(pattern) ||
350
+ filePath.includes(path.sep + pattern + path.sep)
351
+ );
352
+ });
353
+ }
354
+
355
+ /**
356
+ * Check if file should be excluded by filename pattern
357
+ * @param {string} fileName - File name to check
358
+ * @returns {boolean} True if should be excluded
359
+ */
360
+ shouldExcludeFile(fileName) {
361
+ const config = this.loadedConfig || this.defaultConfig;
362
+ const excludeFiles = config.files?.exclude || [];
363
+
364
+ return excludeFiles.some((pattern) => {
365
+ // Simple glob pattern matching
366
+ const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
367
+ const regex = new RegExp(`^${regexPattern}$`, "i");
368
+ return regex.test(fileName);
369
+ });
370
+ }
371
+ }
372
+
373
+ export default new RagConfigManager();