cntx-ui 2.0.13 → 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/bin/cntx-ui.js +137 -55
  2. package/lib/agent-runtime.js +1480 -0
  3. package/lib/agent-tools.js +368 -0
  4. package/lib/api-router.js +978 -0
  5. package/lib/bundle-manager.js +471 -0
  6. package/lib/configuration-manager.js +725 -0
  7. package/lib/file-system-manager.js +472 -0
  8. package/lib/heuristics-manager.js +425 -0
  9. package/lib/mcp-server.js +1054 -1
  10. package/lib/semantic-splitter.js +7 -14
  11. package/lib/simple-vector-store.js +329 -0
  12. package/lib/websocket-manager.js +470 -0
  13. package/package.json +10 -3
  14. package/server.js +662 -1933
  15. package/templates/activities/README.md +67 -0
  16. package/templates/activities/activities/create-project-bundles/README.md +83 -0
  17. package/templates/activities/activities/create-project-bundles/notes.md +102 -0
  18. package/templates/activities/activities/create-project-bundles/progress.md +63 -0
  19. package/templates/activities/activities/create-project-bundles/tasks.md +39 -0
  20. package/templates/activities/activities.json +219 -0
  21. package/templates/activities/lib/.markdownlint.jsonc +18 -0
  22. package/templates/activities/lib/create-activity.mdc +63 -0
  23. package/templates/activities/lib/generate-tasks.mdc +64 -0
  24. package/templates/activities/lib/process-task-list.mdc +52 -0
  25. package/templates/agent-config.yaml +78 -0
  26. package/templates/agent-instructions.md +218 -0
  27. package/templates/agent-rules/capabilities/activities-system.md +147 -0
  28. package/templates/agent-rules/capabilities/bundle-system.md +131 -0
  29. package/templates/agent-rules/capabilities/vector-search.md +135 -0
  30. package/templates/agent-rules/core/codebase-navigation.md +91 -0
  31. package/templates/agent-rules/core/performance-hierarchy.md +48 -0
  32. package/templates/agent-rules/core/response-formatting.md +120 -0
  33. package/templates/agent-rules/project-specific/architecture.md +145 -0
  34. package/templates/config.json +76 -0
  35. package/templates/hidden-files.json +14 -0
  36. package/web/dist/assets/heuristics-manager-browser-DfonOP5I.js +1 -0
  37. package/web/dist/assets/index-dF3qg-y_.js +2486 -0
  38. package/web/dist/assets/index-h5FGSg_P.css +1 -0
  39. package/web/dist/cntx-ui.svg +18 -0
  40. package/web/dist/index.html +25 -8
  41. package/lib/semantic-integration.js +0 -441
  42. package/web/dist/assets/index-Ci1Q-YrQ.js +0 -611
  43. package/web/dist/assets/index-IUp4q_fr.css +0 -1
  44. package/web/dist/vite.svg +0 -21
@@ -0,0 +1,725 @@
1
+ /**
2
+ * Configuration Manager for cntx-ui
3
+ * Handles all configuration files, settings, and persistence
4
+ */
5
+
6
+ import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs';
7
+ import { join, relative } from 'path';
8
+
9
+ export default class ConfigurationManager {
10
+ constructor(cwd = process.cwd(), options = {}) {
11
+ this.CWD = cwd;
12
+ this.CNTX_DIR = join(cwd, '.cntx');
13
+ this.verbose = options.verbose || false;
14
+
15
+ // Configuration files
16
+ this.CONFIG_FILE = join(this.CNTX_DIR, 'config.json');
17
+ this.BUNDLE_STATES_FILE = join(this.CNTX_DIR, 'bundle-states.json');
18
+ this.HIDDEN_FILES_CONFIG_FILE = join(this.CNTX_DIR, 'hidden-files.json');
19
+ this.IGNORE_FILE = join(this.CWD, '.cntxignore');
20
+ this.GITIGNORE_FILE = join(this.CWD, '.gitignore');
21
+ this.CURSOR_RULES_FILE = join(this.CWD, '.cursorrules');
22
+ this.CLAUDE_MD_FILE = join(this.CWD, 'CLAUDE.md');
23
+ this.HEURISTICS_CONFIG_FILE = join(this.CNTX_DIR, 'heuristics-config.json');
24
+ this.SEMANTIC_CACHE_FILE = join(this.CNTX_DIR, 'semantic-cache.json');
25
+
26
+ // Configuration data
27
+ this.config = {};
28
+ this.bundleStates = new Map();
29
+ this.hiddenFilesConfig = {
30
+ hiddenFiles: new Map(),
31
+ userIgnorePatterns: []
32
+ };
33
+ this.ignorePatterns = [];
34
+
35
+ // Load initial configuration
36
+ this.loadConfig();
37
+ }
38
+
39
+ // === Bundle Configuration ===
40
+
41
+ loadConfig() {
42
+ // Clear existing bundles to ensure deleted ones are removed
43
+ this.bundleStates.clear();
44
+
45
+ if (existsSync(this.CONFIG_FILE)) {
46
+ const config = JSON.parse(readFileSync(this.CONFIG_FILE, 'utf8'));
47
+
48
+ // Load editor setting
49
+ this.editor = config.editor || 'code';
50
+
51
+ Object.entries(config.bundles || {}).forEach(([name, patterns]) => {
52
+ this.bundleStates.set(name, {
53
+ patterns: Array.isArray(patterns) ? patterns : [patterns],
54
+ files: [],
55
+ content: '',
56
+ changed: false,
57
+ size: 0,
58
+ generated: null
59
+ });
60
+ });
61
+ }
62
+
63
+ // Ensure 'master' bundle exists
64
+ if (!this.bundleStates.has('master')) {
65
+ this.bundleStates.set('master', {
66
+ patterns: ['**/*'],
67
+ files: [],
68
+ content: '',
69
+ changed: false,
70
+ size: 0,
71
+ generated: null
72
+ });
73
+ }
74
+ }
75
+
76
+ saveConfig(config) {
77
+ writeFileSync(this.CONFIG_FILE, JSON.stringify(config, null, 2));
78
+ }
79
+
80
+ // === Bundle States Persistence ===
81
+
82
+ loadBundleStates() {
83
+ if (existsSync(this.BUNDLE_STATES_FILE)) {
84
+ try {
85
+ const bundleStates = JSON.parse(readFileSync(this.BUNDLE_STATES_FILE, 'utf8'));
86
+
87
+ bundleStates.forEach(state => {
88
+ if (this.bundleStates.has(state.name)) {
89
+ const bundle = this.bundleStates.get(state.name);
90
+ bundle.content = state.content || '';
91
+ bundle.size = state.size || 0;
92
+ bundle.generated = state.generated || null;
93
+ bundle.changed = false;
94
+ if (state.files) {
95
+ // Migrate absolute paths to relative paths for portability
96
+ bundle.files = state.files.map(file => {
97
+ if (file.startsWith('/')) {
98
+ // Convert absolute to relative
99
+ const relativePath = require('path').relative(this.CWD, file);
100
+ return relativePath;
101
+ } else {
102
+ // Already relative
103
+ return file;
104
+ }
105
+ });
106
+ }
107
+ }
108
+ });
109
+ } catch (error) {
110
+ console.error('Failed to load bundle states:', error.message);
111
+ }
112
+ }
113
+ }
114
+
115
+ saveBundleStates() {
116
+ try {
117
+ const bundleStates = Array.from(this.bundleStates.entries()).map(([name, bundle]) => ({
118
+ name,
119
+ // Don't save the full content - it's too large and can cause RangeError
120
+ contentPreview: bundle.content ? bundle.content.substring(0, 200) + '...' : '',
121
+ size: bundle.size,
122
+ fileCount: bundle.files ? bundle.files.length : 0,
123
+ generated: bundle.generated,
124
+ changed: bundle.changed,
125
+ patterns: bundle.patterns,
126
+ // Always save relative paths for portability
127
+ files: (bundle.files || []).map(file => {
128
+ if (file.startsWith('/')) {
129
+ // Convert absolute to relative
130
+ return relative(this.CWD, file);
131
+ } else {
132
+ // Already relative
133
+ return file;
134
+ }
135
+ })
136
+ }));
137
+
138
+ writeFileSync(this.BUNDLE_STATES_FILE, JSON.stringify(bundleStates, null, 2));
139
+ if (this.verbose) {
140
+ console.log(`💾 Saved bundle states for ${bundleStates.length} bundles`);
141
+ }
142
+ } catch (error) {
143
+ if (this.verbose) {
144
+ console.warn('⚠️ Failed to save bundle states:', error.message);
145
+ }
146
+ // Try to save just the essential info
147
+ try {
148
+ const minimalStates = Array.from(this.bundleStates.entries()).map(([name, bundle]) => ({
149
+ name,
150
+ size: bundle.size || 0,
151
+ fileCount: bundle.files ? bundle.files.length : 0,
152
+ generated: bundle.generated || new Date().toISOString()
153
+ }));
154
+ writeFileSync(this.BUNDLE_STATES_FILE, JSON.stringify(minimalStates, null, 2));
155
+ if (this.verbose) {
156
+ console.log(`💾 Saved minimal bundle states for ${minimalStates.length} bundles`);
157
+ }
158
+ } catch (fallbackError) {
159
+ if (this.verbose) {
160
+ console.error('❌ Failed to save even minimal bundle states:', fallbackError.message);
161
+ }
162
+ }
163
+ }
164
+ }
165
+
166
+ // === Hidden Files Configuration ===
167
+
168
+ loadHiddenFilesConfig() {
169
+ if (existsSync(this.HIDDEN_FILES_CONFIG_FILE)) {
170
+ try {
171
+ this.hiddenFilesConfig = JSON.parse(readFileSync(this.HIDDEN_FILES_CONFIG_FILE, 'utf8'));
172
+ } catch (error) {
173
+ console.error('Failed to load hidden files config:', error.message);
174
+ }
175
+ }
176
+ }
177
+
178
+ saveHiddenFilesConfig() {
179
+ writeFileSync(this.HIDDEN_FILES_CONFIG_FILE, JSON.stringify(this.hiddenFilesConfig, null, 2));
180
+ }
181
+
182
+ isFileHidden(filePath, bundleName = null) {
183
+ // Check global hidden files
184
+ if (this.hiddenFilesConfig.globalHidden.includes(filePath)) {
185
+ return true;
186
+ }
187
+
188
+ // Check bundle-specific hidden files
189
+ if (bundleName && this.hiddenFilesConfig.bundleSpecific[bundleName]?.includes(filePath)) {
190
+ return true;
191
+ }
192
+
193
+ return false;
194
+ }
195
+
196
+ toggleFileVisibility(filePath, bundleName = null, forceHide = null) {
197
+ if (bundleName) {
198
+ // Bundle-specific hiding
199
+ if (!this.hiddenFilesConfig.bundleSpecific[bundleName]) {
200
+ this.hiddenFilesConfig.bundleSpecific[bundleName] = [];
201
+ }
202
+
203
+ const bundleHidden = this.hiddenFilesConfig.bundleSpecific[bundleName];
204
+ const isCurrentlyHidden = bundleHidden.includes(filePath);
205
+
206
+ if (forceHide === true || (forceHide === null && !isCurrentlyHidden)) {
207
+ if (!isCurrentlyHidden) {
208
+ bundleHidden.push(filePath);
209
+ }
210
+ } else {
211
+ const index = bundleHidden.indexOf(filePath);
212
+ if (index > -1) {
213
+ bundleHidden.splice(index, 1);
214
+ }
215
+ }
216
+ } else {
217
+ // Global hiding
218
+ const isCurrentlyHidden = this.hiddenFilesConfig.globalHidden.includes(filePath);
219
+
220
+ if (forceHide === true || (forceHide === null && !isCurrentlyHidden)) {
221
+ if (!isCurrentlyHidden) {
222
+ this.hiddenFilesConfig.globalHidden.push(filePath);
223
+ }
224
+ } else {
225
+ const index = this.hiddenFilesConfig.globalHidden.indexOf(filePath);
226
+ if (index > -1) {
227
+ this.hiddenFilesConfig.globalHidden.splice(index, 1);
228
+ }
229
+ }
230
+ }
231
+
232
+ this.saveHiddenFilesConfig();
233
+ }
234
+
235
+ bulkToggleFileVisibility(filePaths, bundleName = null, forceHide = null) {
236
+ filePaths.forEach(filePath => {
237
+ this.toggleFileVisibility(filePath, bundleName, forceHide);
238
+ });
239
+ }
240
+
241
+ // === Ignore Patterns Management ===
242
+
243
+ addUserIgnorePattern(pattern) {
244
+ if (!this.hiddenFilesConfig.userIgnorePatterns.some(p => p.pattern === pattern)) {
245
+ this.hiddenFilesConfig.userIgnorePatterns.push({
246
+ pattern,
247
+ enabled: true,
248
+ addedAt: new Date().toISOString()
249
+ });
250
+ this.saveHiddenFilesConfig();
251
+ }
252
+ }
253
+
254
+ removeUserIgnorePattern(pattern) {
255
+ const index = this.hiddenFilesConfig.userIgnorePatterns.findIndex(p => p.pattern === pattern);
256
+ if (index > -1) {
257
+ this.hiddenFilesConfig.userIgnorePatterns.splice(index, 1);
258
+ this.saveHiddenFilesConfig();
259
+ }
260
+ }
261
+
262
+ toggleSystemIgnorePattern(pattern) {
263
+ const isDisabled = this.hiddenFilesConfig.disabledSystemPatterns.includes(pattern);
264
+
265
+ if (isDisabled) {
266
+ const index = this.hiddenFilesConfig.disabledSystemPatterns.indexOf(pattern);
267
+ this.hiddenFilesConfig.disabledSystemPatterns.splice(index, 1);
268
+ } else {
269
+ this.hiddenFilesConfig.disabledSystemPatterns.push(pattern);
270
+ }
271
+
272
+ this.saveHiddenFilesConfig();
273
+ }
274
+
275
+ loadIgnorePatterns() {
276
+ this.ignorePatterns = [];
277
+
278
+ // System patterns - common files to ignore
279
+ const systemPatterns = [
280
+ 'node_modules/**/*', '.git/**/*', '.svn/**/*', '.hg/**/*',
281
+ '*.log', '*.tmp', '*.temp', '*.cache', '*.pid',
282
+ '.DS_Store', 'Thumbs.db', '.env', '.env.*',
283
+ 'dist/**/*', 'build/**/*', 'coverage/**/*',
284
+ '*.min.js', '*.min.css', '*.map',
285
+ '**/*.lock', 'yarn.lock', 'package-lock.json',
286
+ '.vscode/**/*', '.idea/**/*', '*.swp', '*.swo',
287
+ '__pycache__/**/*', '*.pyc', '*.pyo',
288
+ '.pytest_cache/**/*', '.coverage',
289
+ 'target/**/*', '*.class', '*.jar',
290
+ 'bin/**/*', 'obj/**/*', '*.exe', '*.dll',
291
+ '.next/**/*', '.nuxt/**/*', '.vite/**/*',
292
+ 'public/build/**/*', 'static/build/**/*'
293
+ ];
294
+
295
+ // Add all system patterns (always enabled now)
296
+ this.ignorePatterns.push(...systemPatterns);
297
+
298
+ // File-based patterns (.cntxignore)
299
+ if (existsSync(this.IGNORE_FILE)) {
300
+ try {
301
+ const content = readFileSync(this.IGNORE_FILE, 'utf8');
302
+ content.split('\n')
303
+ .map(line => line.trim())
304
+ .filter(line => line && !line.startsWith('#'))
305
+ .forEach(pattern => this.ignorePatterns.push(pattern));
306
+ } catch (error) {
307
+ console.error('Failed to load .cntxignore:', error.message);
308
+ }
309
+ }
310
+
311
+ if (this.verbose) {
312
+ console.log(`🚫 Loaded ${this.ignorePatterns.length} ignore patterns`);
313
+ }
314
+ }
315
+
316
+ updateIgnoreFile() {
317
+ const header = '# cntx-ui ignore patterns\n# This file is auto-generated. User patterns are managed via the UI.\n\n';
318
+ const userPatterns = this.hiddenFilesConfig.userIgnorePatterns
319
+ .filter(p => p.enabled)
320
+ .map(p => p.pattern)
321
+ .join('\n');
322
+
323
+ writeFileSync(this.IGNORE_FILE, header + userPatterns);
324
+ }
325
+
326
+ loadCntxignore() {
327
+ if (existsSync(this.IGNORE_FILE)) {
328
+ return readFileSync(this.IGNORE_FILE, 'utf8');
329
+ }
330
+ return '';
331
+ }
332
+
333
+ loadGitignore() {
334
+ if (existsSync(this.GITIGNORE_FILE)) {
335
+ return readFileSync(this.GITIGNORE_FILE, 'utf8');
336
+ }
337
+ return '';
338
+ }
339
+
340
+ saveGitignore(content) {
341
+ writeFileSync(this.GITIGNORE_FILE, content);
342
+ }
343
+
344
+ // === Cursor Rules Management ===
345
+
346
+ loadCursorRules() {
347
+ if (existsSync(this.CURSOR_RULES_FILE)) {
348
+ return readFileSync(this.CURSOR_RULES_FILE, 'utf8');
349
+ }
350
+ return this.getDefaultCursorRules();
351
+ }
352
+
353
+ saveCursorRules(content) {
354
+ writeFileSync(this.CURSOR_RULES_FILE, content);
355
+ }
356
+
357
+ getDefaultCursorRules() {
358
+ let pkg = {};
359
+ try {
360
+ const packagePath = join(this.CWD, 'package.json');
361
+ if (existsSync(packagePath)) {
362
+ pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
363
+ }
364
+ } catch (error) {
365
+ console.error('Failed to read package.json:', error.message);
366
+ }
367
+
368
+ const projectType = this.detectProjectType(pkg);
369
+ return this.generateCursorRulesTemplate({ projectType, name: pkg.name });
370
+ }
371
+
372
+ detectProjectType(pkg) {
373
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
374
+
375
+ if (deps.react || deps['@types/react']) return 'react';
376
+ if (deps.vue || deps['@vue/cli']) return 'vue';
377
+ if (deps.angular || deps['@angular/core']) return 'angular';
378
+ if (deps.next || deps['next']) return 'next';
379
+ if (deps.express || deps.fastify) return 'node-backend';
380
+
381
+ return 'javascript';
382
+ }
383
+
384
+ generateCursorRulesTemplate(projectInfo) {
385
+ const { projectType, name } = projectInfo;
386
+
387
+ const baseRules = `# Cursor Rules for ${name || 'Project'}
388
+
389
+ ## Project Overview
390
+ This is a ${projectType} project. Please follow these guidelines when making changes:
391
+
392
+ ## Code Style
393
+ - Use consistent indentation (2 spaces for JS/TS, 4 for Python)
394
+ - Follow existing naming conventions
395
+ - Add comments for complex logic
396
+ - Keep functions small and focused
397
+
398
+ ## Architecture
399
+ - Maintain separation of concerns
400
+ - Follow established patterns in the codebase
401
+ - Consider performance implications
402
+ - Ensure code is testable
403
+
404
+ ## Testing
405
+ - Write tests for new functionality
406
+ - Update existing tests when modifying code
407
+ - Aim for good test coverage
408
+ - Use descriptive test names`;
409
+
410
+ const typeSpecificRules = {
411
+ react: `
412
+ ## React Specific
413
+ - Use functional components with hooks
414
+ - Follow component composition patterns
415
+ - Keep components focused on single responsibility
416
+ - Use TypeScript for type safety
417
+ - Handle loading and error states properly`,
418
+
419
+ vue: `
420
+ ## Vue Specific
421
+ - Use Composition API for new components
422
+ - Follow single-file component structure
423
+ - Use reactive refs appropriately
424
+ - Implement proper event handling
425
+ - Follow Vue style guide conventions`,
426
+
427
+ angular: `
428
+ ## Angular Specific
429
+ - Follow Angular style guide
430
+ - Use dependency injection properly
431
+ - Implement proper lifecycle hooks
432
+ - Use reactive forms for complex forms
433
+ - Follow module organization patterns`,
434
+
435
+ 'node-backend': `
436
+ ## Backend Specific
437
+ - Implement proper error handling
438
+ - Use middleware for common functionality
439
+ - Validate input data
440
+ - Follow REST API conventions
441
+ - Implement proper logging`,
442
+
443
+ javascript: `
444
+ ## JavaScript Specific
445
+ - Use modern ES6+ features appropriately
446
+ - Handle promises and async operations properly
447
+ - Implement error handling
448
+ - Follow functional programming principles where applicable
449
+ - Use appropriate data structures`
450
+ };
451
+
452
+ return baseRules + (typeSpecificRules[projectType] || typeSpecificRules.javascript) + `
453
+
454
+ ## File Organization
455
+ - Keep related files together
456
+ - Use descriptive file names
457
+ - Maintain consistent directory structure
458
+ - Avoid deeply nested directories
459
+
460
+ ## Documentation
461
+ - Update README when adding features
462
+ - Document complex algorithms
463
+ - Keep inline comments current
464
+ - Use JSDoc for function documentation`;
465
+ }
466
+
467
+ // === Claude.md Management ===
468
+
469
+ loadClaudeMd() {
470
+ if (existsSync(this.CLAUDE_MD_FILE)) {
471
+ return readFileSync(this.CLAUDE_MD_FILE, 'utf8');
472
+ }
473
+ return this.getDefaultClaudeMd();
474
+ }
475
+
476
+ saveClaudeMd(content) {
477
+ writeFileSync(this.CLAUDE_MD_FILE, content);
478
+ }
479
+
480
+ getDefaultClaudeMd() {
481
+ let projectInfo = {};
482
+ try {
483
+ const packagePath = join(this.CWD, 'package.json');
484
+ if (existsSync(packagePath)) {
485
+ const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
486
+ projectInfo = {
487
+ name: pkg.name,
488
+ description: pkg.description,
489
+ projectType: this.detectProjectType(pkg)
490
+ };
491
+ }
492
+ } catch (error) {
493
+ projectInfo = { name: 'Project', description: '', projectType: 'javascript' };
494
+ }
495
+
496
+ return this.generateClaudeMdTemplate(projectInfo);
497
+ }
498
+
499
+ generateClaudeMdTemplate(projectInfo) {
500
+ const { name, description, projectType } = projectInfo;
501
+
502
+ return `# ${name || 'Project'} - Claude Context
503
+
504
+ ## Project Overview
505
+ ${description || 'A ' + projectType + ' project'}
506
+
507
+ ## Architecture
508
+ Please describe your project architecture here:
509
+ - Main components/modules
510
+ - Data flow patterns
511
+ - Key dependencies
512
+ - Design decisions
513
+
514
+ ## Development Guidelines
515
+ - Code style preferences
516
+ - Testing approach
517
+ - Deployment process
518
+ - Known limitations or considerations
519
+
520
+ ## Context for AI
521
+ When working on this project, please:
522
+ - Follow existing patterns and conventions
523
+ - Consider the overall architecture
524
+ - Maintain code quality and consistency
525
+ - Ask for clarification on complex requirements
526
+
527
+ ## Current Focus
528
+ <!-- Update this section with current development priorities -->
529
+ - Feature development
530
+ - Bug fixes
531
+ - Performance improvements
532
+ - Documentation updates`;
533
+ }
534
+
535
+ // === Heuristics Configuration ===
536
+
537
+ loadHeuristicsConfig() {
538
+ if (existsSync(this.HEURISTICS_CONFIG_FILE)) {
539
+ try {
540
+ return JSON.parse(readFileSync(this.HEURISTICS_CONFIG_FILE, 'utf8'));
541
+ } catch (error) {
542
+ console.error('Failed to load heuristics config:', error.message);
543
+ }
544
+ }
545
+ return this.getDefaultHeuristicsConfig();
546
+ }
547
+
548
+ saveHeuristicsConfig(config) {
549
+ writeFileSync(this.HEURISTICS_CONFIG_FILE, JSON.stringify(config, null, 2));
550
+ }
551
+
552
+ getDefaultHeuristicsConfig() {
553
+ return {
554
+ bundlePurposePatterns: {
555
+ 'frontend': {
556
+ patterns: ['src/components/**', 'src/pages/**', 'src/views/**', 'public/**', 'assets/**', 'styles/**'],
557
+ description: 'User interface components and styling'
558
+ },
559
+ 'backend': {
560
+ patterns: ['src/api/**', 'src/server/**', 'src/routes/**', 'src/controllers/**', 'src/middleware/**'],
561
+ description: 'Server-side logic and API endpoints'
562
+ },
563
+ 'database': {
564
+ patterns: ['src/models/**', 'src/schemas/**', 'src/migrations/**', 'prisma/**', 'db/**'],
565
+ description: 'Database models and migrations'
566
+ },
567
+ 'utilities': {
568
+ patterns: ['src/utils/**', 'src/helpers/**', 'src/lib/**', 'lib/**'],
569
+ description: 'Utility functions and shared libraries'
570
+ },
571
+ 'configuration': {
572
+ patterns: ['config/**', '*.config.js', '*.config.ts', '.env*', 'docker*', 'webpack*'],
573
+ description: 'Configuration files and environment setup'
574
+ },
575
+ 'testing': {
576
+ patterns: ['test/**', 'tests/**', 'spec/**', '**/*.test.*', '**/*.spec.*', '__tests__/**'],
577
+ description: 'Test files and testing utilities'
578
+ },
579
+ 'documentation': {
580
+ patterns: ['docs/**', '*.md', 'README*', 'CHANGELOG*', 'LICENSE*'],
581
+ description: 'Documentation and readme files'
582
+ },
583
+ 'build': {
584
+ patterns: ['build/**', 'dist/**', 'out/**', 'target/**', 'bin/**'],
585
+ description: 'Built/compiled output files'
586
+ }
587
+ },
588
+ fileTypeHeuristics: {
589
+ 'javascript': {
590
+ extensions: ['.js', '.jsx', '.mjs', '.cjs'],
591
+ role: 'Implementation files containing business logic'
592
+ },
593
+ 'typescript': {
594
+ extensions: ['.ts', '.tsx'],
595
+ role: 'Type-safe implementation files'
596
+ },
597
+ 'configuration': {
598
+ extensions: ['.json', '.yaml', '.yml', '.toml', '.ini'],
599
+ role: 'Configuration and data files'
600
+ },
601
+ 'styling': {
602
+ extensions: ['.css', '.scss', '.sass', '.less', '.styl'],
603
+ role: 'Styling and presentation files'
604
+ },
605
+ 'markup': {
606
+ extensions: ['.html', '.htm', '.xml', '.svg'],
607
+ role: 'Markup and template files'
608
+ },
609
+ 'documentation': {
610
+ extensions: ['.md', '.txt', '.rst'],
611
+ role: 'Documentation and text files'
612
+ }
613
+ }
614
+ };
615
+ }
616
+
617
+ // === Semantic Cache Management ===
618
+
619
+ loadSemanticCache() {
620
+ try {
621
+ if (!existsSync(this.SEMANTIC_CACHE_FILE)) {
622
+ console.log('🔍 No semantic cache file found');
623
+ return null;
624
+ }
625
+
626
+ const cacheData = JSON.parse(readFileSync(this.SEMANTIC_CACHE_FILE, 'utf8'));
627
+
628
+ // Simple cache validation - could be enhanced with file modification time checks
629
+ const cacheAge = Date.now() - cacheData.timestamp;
630
+ const maxAge = 24 * 60 * 60 * 1000; // 24 hours
631
+
632
+ if (cacheAge > maxAge) {
633
+ console.log('🔍 Semantic cache is too old, will regenerate');
634
+ return null;
635
+ }
636
+
637
+ // Validate that chunks have embeddings
638
+ const hasEmbeddings = cacheData.analysis?.chunks?.some(chunk => chunk.embedding);
639
+ if (!hasEmbeddings) {
640
+ console.log('🔍 Cached chunks missing embeddings, will regenerate');
641
+ return null;
642
+ }
643
+
644
+ if (this.verbose) {
645
+ console.log(`✅ Loaded semantic cache from disk (${cacheData.analysis.chunks.length} chunks with embeddings)`);
646
+ }
647
+ return { analysis: cacheData.analysis, timestamp: cacheData.timestamp };
648
+ } catch (error) {
649
+ console.error('❌ Failed to load semantic cache:', error.message);
650
+ return null;
651
+ }
652
+ }
653
+
654
+ saveSemanticCache(analysis) {
655
+ try {
656
+ const cacheData = {
657
+ timestamp: Date.now(),
658
+ analysis: analysis,
659
+ version: '1.0'
660
+ };
661
+
662
+ writeFileSync(this.SEMANTIC_CACHE_FILE, JSON.stringify(cacheData, null, 2));
663
+ console.log('💾 Saved semantic cache with embeddings to disk');
664
+ } catch (error) {
665
+ console.error('❌ Failed to save semantic cache:', error.message);
666
+ }
667
+ }
668
+
669
+ invalidateSemanticCache() {
670
+ try {
671
+ if (existsSync(this.SEMANTIC_CACHE_FILE)) {
672
+ unlinkSync(this.SEMANTIC_CACHE_FILE);
673
+ console.log('🗑️ Cleared semantic cache file');
674
+ }
675
+ } catch (error) {
676
+ console.error('❌ Failed to clear cache file:', error.message);
677
+ }
678
+ }
679
+
680
+ // === Bundle Creation from Chunks ===
681
+
682
+ createBundleFromChunk(chunkName, files) {
683
+ const bundleName = `chunk-${chunkName.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
684
+
685
+ // Create bundle configuration
686
+ const patterns = files.map(file => file.replace(/\\/g, '/'));
687
+
688
+ this.bundleStates.set(bundleName, {
689
+ patterns: patterns,
690
+ files: [],
691
+ content: '',
692
+ changed: false,
693
+ size: 0,
694
+ generated: null
695
+ });
696
+
697
+ // Save to config file
698
+ const config = { bundles: {} };
699
+ this.bundleStates.forEach((bundle, name) => {
700
+ config.bundles[name] = bundle.patterns;
701
+ });
702
+
703
+ this.saveConfig(config);
704
+
705
+ return bundleName;
706
+ }
707
+
708
+ // === Getters ===
709
+
710
+ getBundles() {
711
+ return this.bundleStates;
712
+ }
713
+
714
+ getIgnorePatterns() {
715
+ return this.ignorePatterns;
716
+ }
717
+
718
+ getHiddenFilesConfig() {
719
+ return this.hiddenFilesConfig;
720
+ }
721
+
722
+ getEditor() {
723
+ return this.editor;
724
+ }
725
+ }