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
package/server.js DELETED
@@ -1,687 +0,0 @@
1
- /**
2
- * Refactored cntx-ui Server
3
- * Lean orchestration layer using modular architecture
4
- */
5
-
6
- import { createServer } from 'http';
7
- import { join, dirname, relative, extname } from 'path';
8
- import { fileURLToPath } from 'url';
9
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
10
-
11
- // Import our modular components
12
- import ConfigurationManager from './lib/configuration-manager.js';
13
- import DatabaseManager from './lib/database-manager.js';
14
- import FileSystemManager from './lib/file-system-manager.js';
15
- import BundleManager from './lib/bundle-manager.js';
16
- import APIRouter from './lib/api-router.js';
17
- import WebSocketManager from './lib/websocket-manager.js';
18
-
19
- // Import existing lib modules
20
- import { startMCPTransport } from './lib/mcp-transport.js';
21
- import SemanticSplitter from './lib/semantic-splitter.js';
22
- import SimpleVectorStore from './lib/simple-vector-store.js';
23
- import { MCPServer } from './lib/mcp-server.js';
24
-
25
- const __dirname = dirname(fileURLToPath(import.meta.url));
26
-
27
- // Utility function for content types
28
- function getContentType(filePath) {
29
- const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase();
30
- const contentTypes = {
31
- '.html': 'text/html',
32
- '.js': 'application/javascript',
33
- '.css': 'text/css',
34
- '.json': 'application/json',
35
- '.png': 'image/png',
36
- '.jpg': 'image/jpeg',
37
- '.gif': 'image/gif',
38
- '.svg': 'image/svg+xml',
39
- '.ico': 'image/x-icon'
40
- };
41
- return contentTypes[ext] || 'text/plain';
42
- }
43
-
44
- export class CntxServer {
45
- constructor(cwd = process.cwd(), options = {}) {
46
- this.CWD = cwd;
47
- this.CNTX_DIR = join(cwd, '.cntx');
48
- this.verbose = options.verbose || false;
49
- this.mcpServerStarted = false;
50
- this.mcpServer = null;
51
-
52
- // Ensure directory exists early
53
- if (!existsSync(this.CNTX_DIR)) mkdirSync(this.CNTX_DIR, { recursive: true });
54
-
55
- // Initialize modular components
56
- this.configManager = new ConfigurationManager(cwd, { verbose: this.verbose });
57
- this.databaseManager = new DatabaseManager(this.CNTX_DIR, { verbose: this.verbose });
58
- this.fileSystemManager = new FileSystemManager(cwd, { verbose: this.verbose });
59
- this.bundleManager = new BundleManager(this.configManager, this.fileSystemManager, this.verbose);
60
- this.webSocketManager = new WebSocketManager(this.bundleManager, this.configManager, { verbose: this.verbose });
61
-
62
- // Initialize semantic analysis components
63
- this.semanticSplitter = new SemanticSplitter({
64
- maxChunkSize: 2000,
65
- includeContext: true,
66
- minFunctionSize: 50
67
- });
68
-
69
- this.vectorStore = new SimpleVectorStore(this.databaseManager, {
70
- modelName: 'Xenova/all-MiniLM-L6-v2'
71
- });
72
-
73
- this.semanticCache = null;
74
- this.lastSemanticAnalysis = null;
75
- this.vectorStoreInitialized = false;
76
-
77
- // Create semantic analysis manager object for API router
78
- this.semanticAnalysisManager = {
79
- getSemanticAnalysis: () => this.getSemanticAnalysis(),
80
- refreshSemanticAnalysis: () => this.refreshSemanticAnalysis(),
81
- exportSemanticChunk: (chunkName) => this.exportSemanticChunk(chunkName),
82
- lastSemanticAnalysis: this.lastSemanticAnalysis
83
- };
84
-
85
- // Initialize API router with all managers
86
- this.apiRouter = new APIRouter(
87
- this,
88
- this.configManager,
89
- this.bundleManager,
90
- this.fileSystemManager,
91
- this.semanticAnalysisManager,
92
- this.vectorStore
93
- );
94
-
95
- // Add references for cross-module communication
96
- this.bundleManager.fileSystemManager = this.fileSystemManager;
97
- this.bundleManager.webSocketManager = this.webSocketManager;
98
- }
99
-
100
- // === Proxy methods for MCP compatibility ===
101
-
102
- get bundles() {
103
- return this.configManager.getBundles();
104
- }
105
-
106
- getAllFiles() {
107
- return this.fileSystemManager.getAllFiles();
108
- }
109
-
110
- getFileTree() {
111
- return this.fileSystemManager.getFileTree();
112
- }
113
-
114
- generateBundle(name) {
115
- return this.bundleManager.regenerateBundle(name);
116
- }
117
-
118
- generateAllBundles() {
119
- return this.bundleManager.generateAllBundles();
120
- }
121
-
122
- saveBundleStates() {
123
- return this.configManager.saveBundleStates();
124
- }
125
-
126
- loadIgnorePatterns() {
127
- this.configManager.loadIgnorePatterns();
128
- this.fileSystemManager.setIgnorePatterns(this.configManager.getIgnorePatterns());
129
- }
130
-
131
- // === Initialization ===
132
-
133
- async init(options = {}) {
134
- if (!existsSync(this.CNTX_DIR)) mkdirSync(this.CNTX_DIR, { recursive: true });
135
-
136
- const { skipFileWatcher = false, skipBundleGeneration = false } = options;
137
-
138
- // Step 1: Load configuration
139
- this.configManager.loadConfig();
140
- this.configManager.loadHiddenFilesConfig();
141
- this.configManager.loadIgnorePatterns();
142
- this.configManager.loadBundleStates();
143
- console.log(' Configuration loaded');
144
-
145
- if (!skipFileWatcher) {
146
- // Step 2: Set up file watcher
147
- this.fileSystemManager.setIgnorePatterns(this.configManager.getIgnorePatterns());
148
- console.log(' File watcher configured');
149
- }
150
-
151
- // Step 3: Load semantic cache
152
- const cacheData = this.configManager.loadSemanticCache();
153
- if (cacheData) {
154
- this.semanticCache = cacheData.analysis;
155
- this.lastSemanticAnalysis = cacheData.timestamp;
156
- console.log(` Semantic cache loaded (${this.semanticCache.chunks.length} chunks)`);
157
- } else {
158
- console.log(' No semantic cache found (will analyze on first request)');
159
- }
160
-
161
- if (!skipFileWatcher) {
162
- // Step 4: Start file watcher
163
- this.startWatching();
164
- console.log(' File watcher started');
165
-
166
- // Trigger initial semantic analysis in background if no cache
167
- if (!this.semanticCache) {
168
- this.getSemanticAnalysis().catch(err => console.error('Initial semantic analysis failed:', err.message));
169
- }
170
-
171
- // Step 5: Generate bundles (awaited to prevent race conditions)
172
- if (!skipBundleGeneration) {
173
- await this.bundleManager.generateAllBundles();
174
- console.log(' Bundles generated');
175
- }
176
- }
177
- }
178
-
179
- // Display initialization summary
180
- displayInitSummary() {
181
- const summary = [];
182
-
183
- // Add semantic cache info
184
- if (this.semanticCache) {
185
- summary.push(`Loaded semantic cache (${this.semanticCache.chunks.length} chunks with embeddings)`);
186
- }
187
-
188
- // Add ignore patterns info
189
- const ignorePatterns = this.configManager.getIgnorePatterns();
190
- if (ignorePatterns.length > 0) {
191
- summary.push(`Loaded ${ignorePatterns.length} ignore patterns`);
192
- }
193
-
194
- // Add bundle info
195
- const bundles = this.bundleManager.getAllBundleInfo();
196
- if (bundles.length > 0) {
197
- summary.push(`Generated ${bundles.length} bundles`);
198
- }
199
-
200
- // Add file watcher info
201
- summary.push('File watcher started');
202
- summary.push('WebSocket server initialized');
203
-
204
- // Display summary
205
- if (summary.length > 0) {
206
- console.log('Initialization complete:');
207
- summary.forEach(msg => console.log(` - ${msg}`));
208
- console.log('');
209
- }
210
- }
211
-
212
- // === File Watching ===
213
-
214
- startWatching() {
215
- this.fileSystemManager.startWatching(async (eventType, filename) => {
216
- if (this.verbose) {
217
- console.log(`File ${eventType}: ${filename}`);
218
- }
219
-
220
- // Skip processing files in .cntx directory to prevent infinite loops
221
- if (filename.startsWith('.cntx/')) {
222
- if (this.verbose) {
223
- console.log(`Skipping .cntx file: ${filename}`);
224
- }
225
- return;
226
- }
227
-
228
- // Mark affected bundles as changed
229
- this.bundleManager.markBundlesChanged(filename);
230
-
231
- // Invalidate semantic cache if needed
232
- this.invalidateSemanticCache();
233
-
234
- // Notify WebSocket clients
235
- this.webSocketManager.onFileChanged(filename, eventType);
236
-
237
- // Automatically regenerate affected bundles after a short delay
238
- setTimeout(async () => {
239
- await this.regenerateChangedBundles(filename);
240
- }, 1000); // 1 second delay to batch multiple rapid changes
241
- });
242
- }
243
-
244
- async regenerateChangedBundles(filename) {
245
- try {
246
- const bundles = this.configManager.getBundles();
247
- const affectedBundles = [];
248
-
249
- // Find which bundles are affected by this file
250
- bundles.forEach((bundle, name) => {
251
- const matchesBundle = bundle.patterns.some(pattern =>
252
- this.fileSystemManager.matchesPattern(filename, pattern)
253
- );
254
-
255
- if (matchesBundle && bundle.changed) {
256
- affectedBundles.push(name);
257
- }
258
- });
259
-
260
- // Regenerate each affected bundle
261
- for (const bundleName of affectedBundles) {
262
- if (this.verbose) {
263
- console.log(`Auto-regenerating bundle: ${bundleName}`);
264
- }
265
- await this.bundleManager.regenerateBundle(bundleName);
266
- }
267
-
268
- } catch (error) {
269
- console.error('Failed to auto-regenerate bundles:', error.message);
270
- }
271
- }
272
-
273
- // === HTTP Server ===
274
-
275
- async handleRequest(req, res) {
276
- const url = new URL(req.url, `http://${req.headers.host}`);
277
-
278
- // Add CORS headers for all requests
279
- res.setHeader('Access-Control-Allow-Origin', '*');
280
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
281
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
282
-
283
- // Handle preflight requests
284
- if (req.method === 'OPTIONS') {
285
- res.writeHead(200);
286
- res.end();
287
- return;
288
- }
289
-
290
- try {
291
- // Handle API routes
292
- if (url.pathname.startsWith('/api/')) {
293
- return await this.apiRouter.handleRequest(req, res, url);
294
- }
295
-
296
- // Handle static files
297
- return this.handleStaticFile(req, res, url);
298
-
299
- } catch (error) {
300
- console.error('Request handling error:', error);
301
- res.writeHead(500, { 'Content-Type': 'application/json' });
302
- res.end(JSON.stringify({ error: 'Internal server error' }));
303
- }
304
- }
305
-
306
- handleStaticFile(req, res, url) {
307
- const webDir = join(__dirname, 'web', 'dist');
308
- let filePath = join(webDir, url.pathname === '/' ? 'index.html' : url.pathname);
309
-
310
- // Security check - ensure path is within web directory
311
- if (!filePath.startsWith(webDir)) {
312
- res.writeHead(403, { 'Content-Type': 'text/plain' });
313
- res.end('Forbidden');
314
- return;
315
- }
316
-
317
- if (!existsSync(filePath)) {
318
- // For SPA routing, serve index.html for non-API routes
319
- if (!url.pathname.startsWith('/api/')) {
320
- filePath = join(webDir, 'index.html');
321
- } else {
322
- res.writeHead(404, { 'Content-Type': 'text/plain' });
323
- res.end('Not Found');
324
- return;
325
- }
326
- }
327
-
328
- try {
329
- const content = readFileSync(filePath);
330
- const contentType = getContentType(filePath);
331
-
332
- res.writeHead(200, { 'Content-Type': contentType });
333
- res.end(content);
334
- } catch (error) {
335
- res.writeHead(500, { 'Content-Type': 'text/plain' });
336
- res.end('Error reading file');
337
- }
338
- }
339
-
340
- // === Semantic Analysis (Legacy methods for compatibility) ===
341
-
342
- async getSemanticAnalysis() {
343
- // Return cached result if available
344
- if (this.semanticCache) {
345
- return this.semanticCache;
346
- }
347
-
348
- // 1. Try to load from SQLite first
349
- try {
350
- const dbChunks = this.databaseManager.db.prepare('SELECT * FROM semantic_chunks').all();
351
- if (dbChunks.length > 0) {
352
- this.semanticCache = {
353
- chunks: dbChunks.map(row => this.databaseManager.mapChunkRow(row)),
354
- summary: { totalChunks: dbChunks.length }
355
- };
356
- return this.semanticCache;
357
- }
358
- } catch (e) {
359
- console.warn('Failed to load chunks from SQLite, performing fresh analysis...');
360
- }
361
-
362
- // 2. Perform fresh analysis if DB is empty
363
- try {
364
- const supportedExtensions = ['.js', '.jsx', '.ts', '.tsx', '.mjs'];
365
- const files = this.fileSystemManager.getAllFiles()
366
- .filter(f => supportedExtensions.includes(extname(f).toLowerCase()))
367
- .map(f => relative(this.CWD, f));
368
-
369
- let bundleConfig = null;
370
- if (existsSync(this.configManager.CONFIG_FILE)) {
371
- bundleConfig = JSON.parse(readFileSync(this.configManager.CONFIG_FILE, 'utf8'));
372
- }
373
-
374
- this.semanticCache = await this.semanticSplitter.extractSemanticChunks(this.CWD, files, bundleConfig);
375
- this.lastSemanticAnalysis = Date.now();
376
-
377
- // 3. Persist chunks to SQLite immediately
378
- if (this.semanticCache.chunks.length > 0) {
379
- this.databaseManager.saveChunks(this.semanticCache.chunks);
380
- }
381
-
382
- // 4. Trigger background embedding enhancement
383
- this.enhanceSemanticChunksIfNeeded(this.semanticCache).catch(err => {
384
- console.error('Background embedding enhancement failed:', err.message);
385
- });
386
-
387
- return this.semanticCache;
388
- } catch (error) {
389
- console.error('Semantic analysis failed:', error.message);
390
- throw new Error(`Semantic analysis failed: ${error.message}`);
391
- }
392
- }
393
-
394
- async refreshSemanticAnalysis() {
395
- console.log('Refreshing semantic analysis and database...');
396
-
397
- // Clear the database table but keep other data
398
- this.databaseManager.db.prepare('DELETE FROM semantic_chunks').run();
399
- this.databaseManager.db.prepare('DELETE FROM vector_embeddings').run();
400
-
401
- this.semanticCache = null;
402
- this.lastSemanticAnalysis = null;
403
-
404
- return this.getSemanticAnalysis();
405
- }
406
-
407
- async enhanceSemanticChunksIfNeeded(analysis) {
408
- if (!analysis || !analysis.chunks) return;
409
-
410
- // Check DB for existing embeddings to find only what's missing
411
- const chunksNeedingEmbeddings = [];
412
- for (const chunk of analysis.chunks) {
413
- if (!this.databaseManager.getEmbedding(chunk.id)) {
414
- chunksNeedingEmbeddings.push(chunk);
415
- }
416
- }
417
-
418
- if (chunksNeedingEmbeddings.length === 0) {
419
- console.log('All chunks already have persistent embeddings');
420
- return;
421
- }
422
-
423
- console.log(`Enhancing ${chunksNeedingEmbeddings.length} chunks with persistent embeddings...`);
424
-
425
- // Initialize vector store if needed
426
- if (!this.vectorStoreInitialized) {
427
- await this.vectorStore.init();
428
- this.vectorStoreInitialized = true;
429
- }
430
-
431
- // Add embeddings to chunks that need them and persist
432
- for (const chunk of chunksNeedingEmbeddings) {
433
- try {
434
- await this.vectorStore.upsertChunk(chunk);
435
- } catch (error) {
436
- console.error(`Failed to generate/persist embedding for chunk ${chunk.id}:`, error.message);
437
- }
438
- }
439
- console.log('Background embedding enhancement complete');
440
- }
441
-
442
- invalidateSemanticCache() {
443
- this.semanticCache = null;
444
- this.lastSemanticAnalysis = null;
445
- }
446
-
447
- async exportSemanticChunk(chunkName) {
448
- const analysis = await this.getSemanticAnalysis();
449
- const chunk = analysis.chunks.find(c => c.name === chunkName || c.id === chunkName);
450
-
451
- if (!chunk) {
452
- throw new Error(`Chunk "${chunkName}" not found`);
453
- }
454
-
455
- return this.bundleManager.generateFileXML(chunk.filePath);
456
- }
457
-
458
- // === MCP Server Integration ===
459
-
460
- startMCPServer() {
461
- if (!this.mcpServer) {
462
- this.mcpServer = new MCPServer(this);
463
- this.mcpServerStarted = true;
464
-
465
- if (this.verbose) {
466
- console.log('MCP server started');
467
- }
468
- }
469
- }
470
-
471
- // === Server Lifecycle ===
472
-
473
- async listen(port = 3333, host = 'localhost') {
474
- const server = createServer((req, res) => {
475
- this.handleRequest(req, res);
476
- });
477
-
478
- // Initialize WebSocket server
479
- this.webSocketManager.initialize(server);
480
-
481
- // Start server and show progress
482
- server.listen(port, host, () => {
483
- console.log('');
484
- console.log(`Server running at http://${host}:${port}`);
485
- console.log(`Serving ${this.bundleManager.getAllBundleInfo().length} bundles from your project`);
486
- console.log('');
487
-
488
- // Display initialization summary
489
- this.displayInitSummary();
490
- });
491
-
492
- // Handle graceful shutdown
493
- process.on('SIGINT', () => {
494
- console.log('\nShutting down server...');
495
- this.webSocketManager.close();
496
- this.fileSystemManager.destroy();
497
- server.close(() => {
498
- console.log('Server stopped');
499
- process.exit(0);
500
- });
501
- });
502
-
503
- return server;
504
- }
505
- }
506
-
507
- // Auto-init and start: checks for .cntx/, runs initConfig() if missing, then starts server
508
- export async function autoInitAndStart(options = {}) {
509
- const cwd = options.cwd || process.cwd();
510
- const cntxDir = join(cwd, '.cntx');
511
-
512
- if (!existsSync(cntxDir)) {
513
- console.log('No .cntx directory found, initializing...');
514
- console.log('');
515
- await initConfig(cwd);
516
- console.log('');
517
- }
518
-
519
- return startServer(options);
520
- }
521
-
522
- // Export function for CLI compatibility
523
- export async function startServer(options = {}) {
524
- const server = new CntxServer(options.cwd, { verbose: options.verbose });
525
-
526
- const asciiArt = `
527
- ██████ ███ ██ ████████ ██ ██ ██ ██ ██
528
- ██ ████ ██ ██ ██ ██ ██ ██ ██
529
- ██ ██ ██ ██ ██ ███ █████ ██ ██ ██
530
- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
531
- ██████ ██ ████ ██ ██ ██ ██████ ██
532
- `;
533
- console.log(asciiArt);
534
-
535
- // Now start initialization
536
- await server.init();
537
-
538
- // Enable MCP status tracking by default
539
- const withMcp = options.withMcp !== false;
540
- if (withMcp) {
541
- server.startMCPServer();
542
- }
543
-
544
- return await server.listen(options.port, options.host);
545
- }
546
-
547
- // CLI Functions for backward compatibility
548
- export async function startMCPServer(options = {}) {
549
- const server = new CntxServer(options.cwd, { verbose: true });
550
- await server.init();
551
- server.startMCPServer();
552
-
553
- // For MCP mode, we don't start the web server, just keep the process alive
554
- console.log('MCP server running on stdio...');
555
- }
556
-
557
- export async function generateBundle(bundleName = 'master') {
558
- const server = new CntxServer(process.cwd(), { verbose: true });
559
- await server.init({ skipFileWatcher: true });
560
-
561
- await server.bundleManager.regenerateBundle(bundleName);
562
- const bundleInfo = server.bundleManager.getBundleInfo(bundleName);
563
-
564
- if (!bundleInfo) {
565
- throw new Error(`Bundle '${bundleName}' not found`);
566
- }
567
-
568
- return bundleInfo;
569
- }
570
-
571
- // Initialize project configuration
572
- export async function initConfig(cwd = process.cwd()) {
573
- const server = new CntxServer(cwd);
574
-
575
- // 1. Initialize directory structure
576
- if (!existsSync(server.CNTX_DIR)) {
577
- mkdirSync(server.CNTX_DIR, { recursive: true });
578
- console.log('Created .cntx directory');
579
- }
580
-
581
- // 2. Create .mcp.json for Claude Code discovery
582
- const mcpConfigPath = join(cwd, '.mcp.json');
583
- const mcpConfig = {
584
- mcpServers: {
585
- "cntx-ui": {
586
- command: "cntx-ui",
587
- args: ["mcp"],
588
- cwd: "."
589
- }
590
- }
591
- };
592
- writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), 'utf8');
593
- console.log('Created .mcp.json for MCP auto-discovery');
594
-
595
- // 3. Initialize basic configuration with better defaults and auto-suggestions
596
- server.configManager.loadConfig();
597
-
598
- const suggestedBundles = {
599
- master: ['**/*']
600
- };
601
-
602
- // Directory-based auto-suggestions
603
- const commonDirs = [
604
- { dir: 'src/components', name: 'ui-components' },
605
- { dir: 'src/services', name: 'services' },
606
- { dir: 'src/lib', name: 'libraries' },
607
- { dir: 'src/hooks', name: 'react-hooks' },
608
- { dir: 'server', name: 'backend-api' },
609
- { dir: 'tests', name: 'test-suite' }
610
- ];
611
-
612
- commonDirs.forEach(d => {
613
- if (existsSync(join(cwd, d.dir))) {
614
- suggestedBundles[d.name] = [`${d.dir}/**`];
615
- console.log(` Suggested bundle: ${d.name} (${d.dir}/**)`);
616
- }
617
- });
618
-
619
- server.configManager.saveConfig({
620
- bundles: suggestedBundles
621
- });
622
-
623
- // 4. Create robust default .cntxignore
624
- const ignorePath = join(cwd, '.cntxignore');
625
- if (!existsSync(ignorePath)) {
626
- const defaultIgnore = `# Binary files
627
- *.db
628
- *.db-journal
629
- *.png
630
- *.jpg
631
- *.jpeg
632
- *.ico
633
- *.icns
634
- *.gif
635
- *.zip
636
- *.tar.gz
637
-
638
- # Generated files
639
- **/gen/**
640
- **/dist/**
641
- **/build/**
642
- **/node_modules/**
643
- **/.next/**
644
- **/.cache/**
645
-
646
- # cntx-ui internals
647
- .cntx/**
648
- .mcp.json
649
- `;
650
- writeFileSync(ignorePath, defaultIgnore, 'utf8');
651
- console.log('Created .cntxignore with smart defaults');
652
- }
653
-
654
- console.log('Configuration initialized');
655
- }
656
-
657
- export async function getStatus() {
658
- const server = new CntxServer(process.cwd(), { verbose: true });
659
- await server.init({ skipFileWatcher: true });
660
-
661
- const bundles = server.bundleManager.getAllBundleInfo();
662
- const totalFiles = server.fileSystemManager.getAllFiles().length;
663
-
664
- console.log('cntx-ui Status');
665
- console.log('================');
666
- console.log(`Total files: ${totalFiles}`);
667
- console.log(`Bundles: ${bundles.length}`);
668
-
669
- bundles.forEach(bundle => {
670
- console.log(` - ${bundle.name}: ${bundle.fileCount} files (${Math.round(bundle.size / 1024)}KB)`);
671
- });
672
-
673
- return {
674
- totalFiles,
675
- bundles: bundles.length,
676
- bundleDetails: bundles
677
- };
678
- }
679
-
680
- // Auto-start server when run directly
681
- const isMainModule = import.meta.url === `file://${process.argv[1]}`;
682
- if (isMainModule) {
683
- console.log('Starting cntx-ui server...');
684
- const server = new CntxServer();
685
- server.init();
686
- server.listen(3333, 'localhost');
687
- }