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,489 +0,0 @@
1
- /**
2
- * File System Manager for cntx-ui
3
- * Handles file operations, pattern matching, and directory traversal
4
- */
5
-
6
- import { readdirSync, readFileSync, statSync, existsSync, watch } from 'fs';
7
- import { join, relative, extname, basename, dirname } from 'path';
8
-
9
- export default class FileSystemManager {
10
- constructor(cwd = process.cwd(), options = {}) {
11
- this.CWD = cwd;
12
- this.verbose = options.verbose || false;
13
- this.watchers = [];
14
- this.ignorePatterns = [];
15
- this.loadIgnorePatterns();
16
- }
17
-
18
- loadIgnorePatterns() {
19
- const ignorePath = join(this.CWD, '.cntxignore');
20
- if (existsSync(ignorePath)) {
21
- try {
22
- const content = readFileSync(ignorePath, 'utf8');
23
- const patterns = content.split('\n')
24
- .map(line => line.trim())
25
- .filter(line => line && !line.startsWith('#'));
26
- this.ignorePatterns = patterns;
27
- if (this.verbose) console.log(`📋 Loaded ${patterns.length} patterns from .cntxignore`);
28
- } catch (e) {
29
- console.error('Failed to load .cntxignore:', e.message);
30
- }
31
- }
32
- }
33
-
34
- // === File Traversal ===
35
-
36
- getAllFiles(dir = this.CWD, files = []) {
37
- try {
38
- const items = readdirSync(dir);
39
-
40
- for (const item of items) {
41
- const fullPath = join(dir, item);
42
-
43
- // Skip if should be ignored
44
- if (this.shouldIgnoreAnything(item, fullPath)) {
45
- continue;
46
- }
47
-
48
- try {
49
- const stat = statSync(fullPath);
50
-
51
- if (stat.isDirectory()) {
52
- this.getAllFiles(fullPath, files);
53
- } else if (stat.isFile()) {
54
- if (!this.shouldIgnoreFile(fullPath)) {
55
- files.push(fullPath);
56
- }
57
- }
58
- } catch (error) {
59
- // Skip files we can't stat (permission issues, broken symlinks, etc.)
60
- continue;
61
- }
62
- }
63
- } catch (error) {
64
- if (this.verbose) {
65
- console.warn(`Cannot read directory ${dir}: ${error.message}`);
66
- }
67
- }
68
-
69
- return files;
70
- }
71
-
72
- getFileTree() {
73
- const files = this.getAllFiles();
74
-
75
- return files.map(file => {
76
- const stats = this.getFileStats(file);
77
- const relativePath = relative(this.CWD, file);
78
-
79
- return {
80
- path: relativePath,
81
- fullPath: file,
82
- size: stats.size,
83
- modified: stats.mtime.toISOString(),
84
- type: this.getFileType(file)
85
- };
86
- });
87
- }
88
-
89
- // === Pattern Matching ===
90
-
91
- matchesPattern(path, pattern) {
92
- // Convert glob pattern to regex
93
- let regexPattern = pattern
94
- .replace(/\./g, '\\.') // Escape dots
95
- .replace(/\*\*/g, '___GLOBSTAR___') // Temporary replace **
96
- .replace(/\*/g, '[^/]*') // * matches anything except /
97
- .replace(/___GLOBSTAR___/g, '.*') // ** matches anything including /
98
- .replace(/\?/g, '[^/]'); // ? matches single char except /
99
-
100
- // Ensure pattern matches from start or after a directory separator
101
- if (!regexPattern.startsWith('.*') && !regexPattern.startsWith('[^/]*')) {
102
- regexPattern = '(^|/)' + regexPattern;
103
- }
104
-
105
- // Ensure pattern matches to end or before a directory separator
106
- if (!regexPattern.endsWith('.*') && !regexPattern.endsWith('[^/]*')) {
107
- regexPattern = regexPattern + '($|/)';
108
- }
109
-
110
- const regex = new RegExp(regexPattern);
111
- const relativePath = relative(this.CWD, path);
112
-
113
- return regex.test(relativePath) || regex.test(path);
114
- }
115
-
116
- shouldIgnoreFile(filePath) {
117
- const relativePath = relative(this.CWD, filePath);
118
-
119
- return this.ignorePatterns.some(pattern =>
120
- this.matchesPattern(filePath, pattern) ||
121
- this.matchesPattern(relativePath, pattern)
122
- );
123
- }
124
-
125
- shouldIgnoreAnything(itemName, fullPath) {
126
- // Hardcoded bad directories and files to always ignore
127
- const badDirs = [
128
- 'node_modules', '.git', '.svn', '.hg', '.bzr', '_darcs',
129
- 'CVS', '.cvs', 'RCS', 'SCCS', '{arch}', '.arch-ids',
130
- '.monotone', '_MTN', '.fslckout', '_FOSSIL_',
131
- '.fos', 'BitKeeper', 'ChangeSet', '.teamcity',
132
- '.idea', '.vscode', '.vs', '.gradle', '.settings',
133
- 'target', 'build', 'dist', 'out', 'bin', 'obj',
134
- '.next', '.nuxt', '.vite', '.tmp', '.temp',
135
- '__pycache__', '.pytest_cache', '.coverage',
136
- '.nyc_output', 'coverage', 'lcov-report'
137
- ];
138
-
139
- const badExtensions = [
140
- '.log', '.tmp', '.temp', '.cache', '.pid', '.lock',
141
- '.swp', '.swo', '.DS_Store', 'Thumbs.db', '.env',
142
- '.min.js', '.min.css', '.map', '.pyc', '.pyo',
143
- '.class', '.jar', '.exe', '.dll', '.so', '.dylib',
144
- '.o', '.a', '.obj', '.lib', '.pdb'
145
- ];
146
-
147
- const badFiles = [
148
- '.gitignore', '.gitkeep', '.gitattributes',
149
- '.eslintcache', '.prettierignore',
150
- 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml',
151
- 'npm-debug.log', 'yarn-debug.log', 'yarn-error.log'
152
- ];
153
-
154
- // Check bad directories
155
- if (badDirs.includes(itemName)) {
156
- return true;
157
- }
158
-
159
- // Check bad extensions
160
- const ext = extname(itemName);
161
- if (badExtensions.includes(ext)) {
162
- return true;
163
- }
164
-
165
- // Check bad files
166
- if (badFiles.includes(itemName)) {
167
- return true;
168
- }
169
-
170
- // Check if it's a hidden file/directory (starts with .)
171
- if (itemName.startsWith('.') && itemName !== '.cntx') {
172
- return true;
173
- }
174
-
175
- // Check ignore patterns if loaded
176
- if (this.ignorePatterns.length > 0) {
177
- const relativePath = relative(this.CWD, fullPath);
178
- if (this.ignorePatterns.some(pattern =>
179
- this.matchesPattern(fullPath, pattern) ||
180
- this.matchesPattern(relativePath, pattern)
181
- )) {
182
- return true;
183
- }
184
- }
185
-
186
- return false;
187
- }
188
-
189
- // === File Metadata ===
190
-
191
- getFileStats(filePath) {
192
- try {
193
- const stats = statSync(filePath);
194
- return {
195
- size: stats.size,
196
- mtime: stats.mtime,
197
- ctime: stats.ctime,
198
- isDirectory: stats.isDirectory(),
199
- isFile: stats.isFile()
200
- };
201
- } catch (error) {
202
- return {
203
- size: 0,
204
- mtime: new Date(0),
205
- ctime: new Date(0),
206
- isDirectory: false,
207
- isFile: false
208
- };
209
- }
210
- }
211
-
212
- getFileType(filePath) {
213
- const ext = extname(filePath).toLowerCase();
214
- const fileName = basename(filePath).toLowerCase();
215
-
216
- // Programming languages
217
- if (ext.match(/\.(js|jsx|mjs|cjs)$/)) return 'javascript';
218
- if (ext.match(/\.(ts|tsx)$/)) return 'typescript';
219
- if (ext.match(/\.(py|pyw)$/)) return 'python';
220
- if (ext.match(/\.(java|class)$/)) return 'java';
221
- if (ext.match(/\.(c|h)$/)) return 'c';
222
- if (ext.match(/\.(cpp|cxx|cc|hpp|hxx)$/)) return 'cpp';
223
- if (ext.match(/\.(cs)$/)) return 'csharp';
224
- if (ext.match(/\.(go)$/)) return 'go';
225
- if (ext.match(/\.(rs)$/)) return 'rust';
226
- if (ext.match(/\.(php)$/)) return 'php';
227
- if (ext.match(/\.(rb)$/)) return 'ruby';
228
-
229
- // Web technologies
230
- if (ext.match(/\.(html|htm)$/)) return 'html';
231
- if (ext.match(/\.(css|scss|sass|less|styl)$/)) return 'stylesheet';
232
- if (ext.match(/\.(vue)$/)) return 'vue';
233
-
234
- // Data formats
235
- if (ext.match(/\.(json)$/)) return 'json';
236
- if (ext.match(/\.(xml)$/)) return 'xml';
237
- if (ext.match(/\.(yaml|yml)$/)) return 'yaml';
238
- if (ext.match(/\.(toml)$/)) return 'toml';
239
- if (ext.match(/\.(ini)$/)) return 'ini';
240
- if (ext.match(/\.(csv)$/)) return 'csv';
241
-
242
- // Documentation
243
- if (ext.match(/\.(md|markdown)$/)) return 'markdown';
244
- if (ext.match(/\.(txt)$/)) return 'text';
245
- if (ext.match(/\.(rst)$/)) return 'restructuredtext';
246
-
247
- // Media
248
- if (ext.match(/\.(png|jpg|jpeg|gif|svg|webp|bmp|ico)$/)) return 'image';
249
- if (ext.match(/\.(mp4|avi|mov|wmv|flv|webm)$/)) return 'video';
250
- if (ext.match(/\.(mp3|wav|flac|aac|ogg)$/)) return 'audio';
251
-
252
- // Archives
253
- if (ext.match(/\.(zip|tar|gz|bz2|xz|7z|rar)$/)) return 'archive';
254
-
255
- // Configuration
256
- if (fileName.includes('config') || fileName.includes('setup')) return 'configuration';
257
-
258
- return 'unknown';
259
- }
260
-
261
- getFileRole(file) {
262
- const fileName = basename(file).toLowerCase();
263
- const filePath = file.toLowerCase();
264
- const ext = extname(file).toLowerCase();
265
-
266
- // Entry points
267
- if (fileName.match(/^(main|index|app)\.(js|jsx|ts|tsx)$/)) {
268
- return 'entry_point';
269
- }
270
-
271
- // Configuration
272
- if (fileName.includes('config') || fileName.includes('setup') ||
273
- ext.match(/\.(json|yaml|yml|toml|ini)$/)) {
274
- return 'configuration';
275
- }
276
-
277
- // Documentation
278
- if (fileName.includes('readme') || ext === '.md' || ext === '.txt') {
279
- return 'documentation';
280
- }
281
-
282
- // Tests
283
- if (fileName.includes('.test.') || fileName.includes('.spec.') ||
284
- filePath.includes('/test/') || filePath.includes('/__tests__/')) {
285
- return 'test';
286
- }
287
-
288
- // Components
289
- if (ext.match(/\.(jsx|tsx|vue)$/) || filePath.includes('/components/')) {
290
- return 'component';
291
- }
292
-
293
- // Utilities
294
- if (filePath.includes('/utils/') || filePath.includes('/helpers/') ||
295
- filePath.includes('/lib/')) {
296
- return 'utility';
297
- }
298
-
299
- // Styles
300
- if (ext.match(/\.(css|scss|sass|less|styl)$/)) {
301
- return 'style';
302
- }
303
-
304
- return 'implementation';
305
- }
306
-
307
- // === File Categorization ===
308
-
309
- categorizeFiles(files) {
310
- const categories = {
311
- 'entry_points': [],
312
- 'components': [],
313
- 'hooks': [],
314
- 'utilities': [],
315
- 'types': [],
316
- 'styles': [],
317
- 'tests': [],
318
- 'configuration': [],
319
- 'documentation': [],
320
- 'other': []
321
- };
322
-
323
- files.forEach(file => {
324
- const ext = extname(file).toLowerCase();
325
- const fileName = basename(file).toLowerCase();
326
- const filePath = file.toLowerCase();
327
-
328
- // Entry points
329
- if (fileName.match(/^(main|index|app)\.(js|jsx|ts|tsx)$/)) {
330
- categories.entry_points.push(file);
331
- }
332
- // Components
333
- else if (ext.match(/\.(jsx|tsx|vue)$/) || filePath.includes('/components/')) {
334
- categories.components.push(file);
335
- }
336
- // Hooks
337
- else if (filePath.includes('/hooks/') || fileName.startsWith('use') && ext.match(/\.(js|ts)$/)) {
338
- categories.hooks.push(file);
339
- }
340
- // Utilities
341
- else if (filePath.includes('/utils/') || filePath.includes('/helpers/') || filePath.includes('/lib/')) {
342
- categories.utilities.push(file);
343
- }
344
- // Types
345
- else if (fileName.includes('.d.ts') || filePath.includes('/types/') || fileName.includes('types')) {
346
- categories.types.push(file);
347
- }
348
- // Styles
349
- else if (ext.match(/\.(css|scss|sass|less|styl)$/)) {
350
- categories.styles.push(file);
351
- }
352
- // Tests
353
- else if (fileName.includes('.test.') || fileName.includes('.spec.') || filePath.includes('/test/') || filePath.includes('/__tests__/')) {
354
- categories.tests.push(file);
355
- }
356
- // Configuration
357
- else if (ext.match(/\.(json|yaml|yml|toml|ini)$/) || fileName.includes('config')) {
358
- categories.configuration.push(file);
359
- }
360
- // Documentation
361
- else if (ext.match(/\.(md|txt|rst)$/)) {
362
- categories.documentation.push(file);
363
- }
364
- // Other
365
- else {
366
- categories.other.push(file);
367
- }
368
- });
369
-
370
- return categories;
371
- }
372
-
373
- identifyEntryPoints(files) {
374
- const entryPoints = [];
375
- const entryPatterns = [
376
- /^(main|index|app)\.(js|jsx|ts|tsx)$/i,
377
- /^server\.(js|ts)$/i,
378
- /^app\.(js|jsx|ts|tsx)$/i
379
- ];
380
-
381
- files.forEach(file => {
382
- const fileName = basename(file);
383
- if (entryPatterns.some(pattern => pattern.test(fileName))) {
384
- entryPoints.push(file);
385
- }
386
- });
387
-
388
- return entryPoints;
389
- }
390
-
391
- // === File Watching ===
392
-
393
- startWatching(onFileChange) {
394
- try {
395
- // Watch the current working directory recursively
396
- const watcher = watch(this.CWD, { recursive: true }, (eventType, filename) => {
397
- if (filename && !this.shouldIgnoreAnything(basename(filename), join(this.CWD, filename))) {
398
- onFileChange?.(eventType, filename);
399
- }
400
- });
401
-
402
- this.watchers.push(watcher);
403
- if (this.verbose) {
404
- console.log('📁 File watcher started');
405
- }
406
- } catch (error) {
407
- if (this.verbose) {
408
- console.error('Failed to start file watcher:', error.message);
409
- }
410
- }
411
- }
412
-
413
- stopWatching() {
414
- this.watchers.forEach(watcher => {
415
- try {
416
- watcher.close();
417
- } catch (error) {
418
- if (this.verbose) {
419
- console.error('Failed to close watcher:', error.message);
420
- }
421
- }
422
- });
423
- this.watchers = [];
424
- if (this.verbose) {
425
- console.log('📁 File watchers stopped');
426
- }
427
- }
428
-
429
- // === Content Type Detection ===
430
-
431
- getContentType(filePath) {
432
- const ext = extname(filePath).toLowerCase();
433
- const contentTypes = {
434
- '.html': 'text/html',
435
- '.js': 'application/javascript',
436
- '.css': 'text/css',
437
- '.json': 'application/json',
438
- '.png': 'image/png',
439
- '.jpg': 'image/jpeg',
440
- '.jpeg': 'image/jpeg',
441
- '.gif': 'image/gif',
442
- '.svg': 'image/svg+xml',
443
- '.ico': 'image/x-icon',
444
- '.txt': 'text/plain',
445
- '.md': 'text/markdown',
446
- '.xml': 'application/xml',
447
- '.pdf': 'application/pdf',
448
- '.zip': 'application/zip'
449
- };
450
- return contentTypes[ext] || 'text/plain';
451
- }
452
-
453
- // === Utilities ===
454
-
455
- setIgnorePatterns(patterns) {
456
- this.ignorePatterns = patterns;
457
- }
458
-
459
- relativePath(filePath) {
460
- return relative(this.CWD, filePath);
461
- }
462
-
463
- absolutePath(relativePath) {
464
- return join(this.CWD, relativePath);
465
- }
466
-
467
- isValidPath(filePath) {
468
- try {
469
- return existsSync(filePath);
470
- } catch (error) {
471
- return false;
472
- }
473
- }
474
-
475
- getDirectoryContents(dirPath) {
476
- try {
477
- const items = readdirSync(dirPath);
478
- return items.filter(item => !this.shouldIgnoreAnything(item, join(dirPath, item)));
479
- } catch (error) {
480
- return [];
481
- }
482
- }
483
-
484
- // === Cleanup ===
485
-
486
- destroy() {
487
- this.stopWatching();
488
- }
489
- }