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