cntx-ui 3.0.7 → 3.0.9

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