cntx-ui 3.0.5 → 3.0.7
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.
- package/bin/cntx-ui.js +16 -49
- package/lib/agent-runtime.js +70 -0
- package/lib/api-router.js +1 -136
- package/lib/mcp-server.js +0 -146
- package/package.json +1 -1
- package/server.js +93 -380
- package/templates/TOOLS.md +0 -6
- package/templates/agent-config.yaml +0 -5
- package/templates/agent-instructions.md +2 -15
- package/templates/agent-rules/project-specific/architecture.md +0 -1
- package/templates/hidden-files.json +0 -1
- package/web/dist/assets/index-D2RTcdqV.js +1968 -0
- package/web/dist/assets/index-DJi03HLz.css +1 -0
- package/web/dist/index.html +2 -2
- package/templates/activities/README.md +0 -67
- package/templates/activities/activities/create-project-bundles/README.md +0 -84
- package/templates/activities/activities/create-project-bundles/notes.md +0 -98
- package/templates/activities/activities/create-project-bundles/progress.md +0 -63
- package/templates/activities/activities/create-project-bundles/tasks.md +0 -39
- package/templates/activities/activities.json +0 -219
- package/templates/activities/lib/.markdownlint.jsonc +0 -18
- package/templates/activities/lib/create-activity.mdc +0 -63
- package/templates/activities/lib/generate-tasks.mdc +0 -64
- package/templates/activities/lib/process-task-list.mdc +0 -52
- package/templates/agent-rules/capabilities/activities-system.md +0 -147
- package/web/dist/assets/index-4p3KJ0jf.js +0 -2016
- package/web/dist/assets/index-BPIipM25.css +0 -1
package/server.js
CHANGED
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
import { createServer } from 'http';
|
|
7
7
|
import { join, dirname, relative, extname } from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync
|
|
10
|
-
import * as fs from 'fs';
|
|
11
|
-
import { homedir } from 'os';
|
|
9
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
12
10
|
|
|
13
11
|
// Import our modular components
|
|
14
12
|
import ConfigurationManager from './lib/configuration-manager.js';
|
|
@@ -17,7 +15,6 @@ import FileSystemManager from './lib/file-system-manager.js';
|
|
|
17
15
|
import BundleManager from './lib/bundle-manager.js';
|
|
18
16
|
import APIRouter from './lib/api-router.js';
|
|
19
17
|
import WebSocketManager from './lib/websocket-manager.js';
|
|
20
|
-
import { AgentRuntime } from './lib/agent-runtime.js';
|
|
21
18
|
|
|
22
19
|
// Import existing lib modules
|
|
23
20
|
import { startMCPTransport } from './lib/mcp-transport.js';
|
|
@@ -51,7 +48,6 @@ export class CntxServer {
|
|
|
51
48
|
this.verbose = options.verbose || false;
|
|
52
49
|
this.mcpServerStarted = false;
|
|
53
50
|
this.mcpServer = null;
|
|
54
|
-
this.initMessages = []; // Track initialization messages
|
|
55
51
|
|
|
56
52
|
// Ensure directory exists early
|
|
57
53
|
if (!existsSync(this.CNTX_DIR)) mkdirSync(this.CNTX_DIR, { recursive: true });
|
|
@@ -78,9 +74,6 @@ export class CntxServer {
|
|
|
78
74
|
this.lastSemanticAnalysis = null;
|
|
79
75
|
this.vectorStoreInitialized = false;
|
|
80
76
|
|
|
81
|
-
// Initialize Agent Runtime
|
|
82
|
-
this.agentRuntime = new AgentRuntime(this);
|
|
83
|
-
|
|
84
77
|
// Create semantic analysis manager object for API router
|
|
85
78
|
this.semanticAnalysisManager = {
|
|
86
79
|
getSemanticAnalysis: () => this.getSemanticAnalysis(),
|
|
@@ -89,13 +82,6 @@ export class CntxServer {
|
|
|
89
82
|
lastSemanticAnalysis: this.lastSemanticAnalysis
|
|
90
83
|
};
|
|
91
84
|
|
|
92
|
-
// Create activity manager placeholder
|
|
93
|
-
this.activityManager = {
|
|
94
|
-
loadActivities: () => this.loadActivities(),
|
|
95
|
-
executeActivity: (id) => this.executeActivity(id),
|
|
96
|
-
stopActivity: (id) => this.stopActivity(id)
|
|
97
|
-
};
|
|
98
|
-
|
|
99
85
|
// Initialize API router with all managers
|
|
100
86
|
this.apiRouter = new APIRouter(
|
|
101
87
|
this,
|
|
@@ -103,8 +89,7 @@ export class CntxServer {
|
|
|
103
89
|
this.bundleManager,
|
|
104
90
|
this.fileSystemManager,
|
|
105
91
|
this.semanticAnalysisManager,
|
|
106
|
-
this.vectorStore
|
|
107
|
-
this.activityManager
|
|
92
|
+
this.vectorStore
|
|
108
93
|
);
|
|
109
94
|
|
|
110
95
|
// Add references for cross-module communication
|
|
@@ -112,87 +97,35 @@ export class CntxServer {
|
|
|
112
97
|
this.bundleManager.webSocketManager = this.webSocketManager;
|
|
113
98
|
}
|
|
114
99
|
|
|
115
|
-
//
|
|
116
|
-
async showProgressBar(message, minTime = 500) {
|
|
117
|
-
const startTime = Date.now();
|
|
118
|
-
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
119
|
-
let frameIndex = 0;
|
|
120
|
-
|
|
121
|
-
const interval = setInterval(() => {
|
|
122
|
-
process.stdout.write(`\r${frames[frameIndex]} ${message}`);
|
|
123
|
-
frameIndex = (frameIndex + 1) % frames.length;
|
|
124
|
-
}, 80);
|
|
100
|
+
// === Proxy methods for MCP compatibility ===
|
|
125
101
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const remaining = Math.max(0, minTime - elapsed);
|
|
102
|
+
get bundles() {
|
|
103
|
+
return this.configManager.getBundles();
|
|
104
|
+
}
|
|
130
105
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
return Promise.resolve();
|
|
135
|
-
};
|
|
106
|
+
getAllFiles() {
|
|
107
|
+
return this.fileSystemManager.getAllFiles();
|
|
136
108
|
}
|
|
137
109
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
let currentStep = 0;
|
|
110
|
+
getFileTree() {
|
|
111
|
+
return this.fileSystemManager.getFileTree();
|
|
112
|
+
}
|
|
142
113
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const filledLength = Math.round((progress / 100) * barLength);
|
|
147
|
-
const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength);
|
|
114
|
+
generateBundle(name) {
|
|
115
|
+
return this.bundleManager.regenerateBundle(name);
|
|
116
|
+
}
|
|
148
117
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
118
|
+
generateAllBundles() {
|
|
119
|
+
return this.bundleManager.generateAllBundles();
|
|
120
|
+
}
|
|
152
121
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
next: (stepName, minTime = 800) => {
|
|
158
|
-
return new Promise(async (resolve) => {
|
|
159
|
-
const startTime = Date.now();
|
|
160
|
-
|
|
161
|
-
// Move to next step
|
|
162
|
-
currentStep++;
|
|
163
|
-
|
|
164
|
-
if (currentStep < totalSteps) {
|
|
165
|
-
updateProgress(steps[currentStep]);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Add random delay between 200-800ms on top of minimum time
|
|
169
|
-
const randomDelay = Math.floor(Math.random() * 600) + 200;
|
|
170
|
-
const totalDelay = minTime + randomDelay;
|
|
171
|
-
|
|
172
|
-
// Wait minimum time + random delay
|
|
173
|
-
const elapsed = Date.now() - startTime;
|
|
174
|
-
const remaining = Math.max(0, totalDelay - elapsed);
|
|
175
|
-
if (remaining > 0) {
|
|
176
|
-
await new Promise(resolve => setTimeout(resolve, remaining));
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
resolve();
|
|
180
|
-
});
|
|
181
|
-
},
|
|
182
|
-
complete: () => {
|
|
183
|
-
const progress = 100;
|
|
184
|
-
const barLength = 30;
|
|
185
|
-
const bar = '█'.repeat(barLength);
|
|
186
|
-
process.stdout.write(`\r[${bar}] ${progress}% - Complete${' '.repeat(20)}\n`);
|
|
187
|
-
}
|
|
188
|
-
};
|
|
122
|
+
saveBundleStates() {
|
|
123
|
+
return this.configManager.saveBundleStates();
|
|
189
124
|
}
|
|
190
125
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.initMessages.push(message);
|
|
195
|
-
}
|
|
126
|
+
loadIgnorePatterns() {
|
|
127
|
+
this.configManager.loadIgnorePatterns();
|
|
128
|
+
this.fileSystemManager.setIgnorePatterns(this.configManager.getIgnorePatterns());
|
|
196
129
|
}
|
|
197
130
|
|
|
198
131
|
// === Initialization ===
|
|
@@ -202,55 +135,45 @@ export class CntxServer {
|
|
|
202
135
|
|
|
203
136
|
const { skipFileWatcher = false, skipBundleGeneration = false } = options;
|
|
204
137
|
|
|
205
|
-
|
|
206
|
-
? ['Loading configuration', 'Loading semantic cache']
|
|
207
|
-
: ['Loading configuration', 'Setting up file watcher', 'Loading semantic cache', 'Starting file watcher', 'Generating bundles'];
|
|
208
|
-
|
|
209
|
-
const progress = await this.showInitProgress(steps);
|
|
210
|
-
|
|
211
|
-
// Step 1: Loading configuration
|
|
138
|
+
// Step 1: Load configuration
|
|
212
139
|
this.configManager.loadConfig();
|
|
213
140
|
this.configManager.loadHiddenFilesConfig();
|
|
214
141
|
this.configManager.loadIgnorePatterns();
|
|
215
142
|
this.configManager.loadBundleStates();
|
|
216
|
-
|
|
143
|
+
console.log(' Configuration loaded');
|
|
217
144
|
|
|
218
145
|
if (!skipFileWatcher) {
|
|
219
|
-
// Step 2:
|
|
146
|
+
// Step 2: Set up file watcher
|
|
220
147
|
this.fileSystemManager.setIgnorePatterns(this.configManager.getIgnorePatterns());
|
|
221
|
-
|
|
148
|
+
console.log(' File watcher configured');
|
|
222
149
|
}
|
|
223
150
|
|
|
224
|
-
// Step 3:
|
|
151
|
+
// Step 3: Load semantic cache
|
|
225
152
|
const cacheData = this.configManager.loadSemanticCache();
|
|
226
153
|
if (cacheData) {
|
|
227
154
|
this.semanticCache = cacheData.analysis;
|
|
228
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)');
|
|
229
159
|
}
|
|
230
|
-
await progress.next(skipFileWatcher ? steps[1] : steps[2], 800);
|
|
231
160
|
|
|
232
161
|
if (!skipFileWatcher) {
|
|
233
|
-
// Step 4:
|
|
162
|
+
// Step 4: Start file watcher
|
|
234
163
|
this.startWatching();
|
|
235
|
-
|
|
164
|
+
console.log(' File watcher started');
|
|
236
165
|
|
|
237
166
|
// Trigger initial semantic analysis in background if no cache
|
|
238
167
|
if (!this.semanticCache) {
|
|
239
168
|
this.getSemanticAnalysis().catch(err => console.error('Initial semantic analysis failed:', err.message));
|
|
240
169
|
}
|
|
241
170
|
|
|
242
|
-
// Step 5:
|
|
171
|
+
// Step 5: Generate bundles (awaited to prevent race conditions)
|
|
243
172
|
if (!skipBundleGeneration) {
|
|
244
|
-
this.bundleManager.generateAllBundles();
|
|
245
|
-
|
|
173
|
+
await this.bundleManager.generateAllBundles();
|
|
174
|
+
console.log(' Bundles generated');
|
|
246
175
|
}
|
|
247
176
|
}
|
|
248
|
-
|
|
249
|
-
// Complete progress bar
|
|
250
|
-
progress.complete();
|
|
251
|
-
|
|
252
|
-
// Generate Agent Manifest
|
|
253
|
-
await this.agentRuntime.generateAgentManifest();
|
|
254
177
|
}
|
|
255
178
|
|
|
256
179
|
// Display initialization summary
|
|
@@ -281,7 +204,7 @@ export class CntxServer {
|
|
|
281
204
|
// Display summary
|
|
282
205
|
if (summary.length > 0) {
|
|
283
206
|
console.log('Initialization complete:');
|
|
284
|
-
summary.forEach(msg => console.log(`
|
|
207
|
+
summary.forEach(msg => console.log(` - ${msg}`));
|
|
285
208
|
console.log('');
|
|
286
209
|
}
|
|
287
210
|
}
|
|
@@ -291,13 +214,13 @@ export class CntxServer {
|
|
|
291
214
|
startWatching() {
|
|
292
215
|
this.fileSystemManager.startWatching(async (eventType, filename) => {
|
|
293
216
|
if (this.verbose) {
|
|
294
|
-
console.log(
|
|
217
|
+
console.log(`File ${eventType}: ${filename}`);
|
|
295
218
|
}
|
|
296
219
|
|
|
297
220
|
// Skip processing files in .cntx directory to prevent infinite loops
|
|
298
221
|
if (filename.startsWith('.cntx/')) {
|
|
299
222
|
if (this.verbose) {
|
|
300
|
-
console.log(
|
|
223
|
+
console.log(`Skipping .cntx file: ${filename}`);
|
|
301
224
|
}
|
|
302
225
|
return;
|
|
303
226
|
}
|
|
@@ -337,7 +260,7 @@ export class CntxServer {
|
|
|
337
260
|
// Regenerate each affected bundle
|
|
338
261
|
for (const bundleName of affectedBundles) {
|
|
339
262
|
if (this.verbose) {
|
|
340
|
-
console.log(
|
|
263
|
+
console.log(`Auto-regenerating bundle: ${bundleName}`);
|
|
341
264
|
}
|
|
342
265
|
await this.bundleManager.regenerateBundle(bundleName);
|
|
343
266
|
}
|
|
@@ -417,16 +340,19 @@ export class CntxServer {
|
|
|
417
340
|
// === Semantic Analysis (Legacy methods for compatibility) ===
|
|
418
341
|
|
|
419
342
|
async getSemanticAnalysis() {
|
|
343
|
+
// Return cached result if available
|
|
344
|
+
if (this.semanticCache) {
|
|
345
|
+
return this.semanticCache;
|
|
346
|
+
}
|
|
347
|
+
|
|
420
348
|
// 1. Try to load from SQLite first
|
|
421
349
|
try {
|
|
422
350
|
const dbChunks = this.databaseManager.db.prepare('SELECT * FROM semantic_chunks').all();
|
|
423
351
|
if (dbChunks.length > 0) {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
};
|
|
429
|
-
}
|
|
352
|
+
this.semanticCache = {
|
|
353
|
+
chunks: dbChunks.map(row => this.databaseManager.mapChunkRow(row)),
|
|
354
|
+
summary: { totalChunks: dbChunks.length }
|
|
355
|
+
};
|
|
430
356
|
return this.semanticCache;
|
|
431
357
|
}
|
|
432
358
|
} catch (e) {
|
|
@@ -439,7 +365,7 @@ export class CntxServer {
|
|
|
439
365
|
const files = this.fileSystemManager.getAllFiles()
|
|
440
366
|
.filter(f => supportedExtensions.includes(extname(f).toLowerCase()))
|
|
441
367
|
.map(f => relative(this.CWD, f));
|
|
442
|
-
|
|
368
|
+
|
|
443
369
|
let bundleConfig = null;
|
|
444
370
|
if (existsSync(this.configManager.CONFIG_FILE)) {
|
|
445
371
|
bundleConfig = JSON.parse(readFileSync(this.configManager.CONFIG_FILE, 'utf8'));
|
|
@@ -454,7 +380,9 @@ export class CntxServer {
|
|
|
454
380
|
}
|
|
455
381
|
|
|
456
382
|
// 4. Trigger background embedding enhancement
|
|
457
|
-
this.enhanceSemanticChunksIfNeeded(this.semanticCache)
|
|
383
|
+
this.enhanceSemanticChunksIfNeeded(this.semanticCache).catch(err => {
|
|
384
|
+
console.error('Background embedding enhancement failed:', err.message);
|
|
385
|
+
});
|
|
458
386
|
|
|
459
387
|
return this.semanticCache;
|
|
460
388
|
} catch (error) {
|
|
@@ -464,12 +392,12 @@ export class CntxServer {
|
|
|
464
392
|
}
|
|
465
393
|
|
|
466
394
|
async refreshSemanticAnalysis() {
|
|
467
|
-
console.log('
|
|
468
|
-
|
|
395
|
+
console.log('Refreshing semantic analysis and database...');
|
|
396
|
+
|
|
469
397
|
// Clear the database table but keep other data
|
|
470
398
|
this.databaseManager.db.prepare('DELETE FROM semantic_chunks').run();
|
|
471
399
|
this.databaseManager.db.prepare('DELETE FROM vector_embeddings').run();
|
|
472
|
-
|
|
400
|
+
|
|
473
401
|
this.semanticCache = null;
|
|
474
402
|
this.lastSemanticAnalysis = null;
|
|
475
403
|
|
|
@@ -488,11 +416,11 @@ export class CntxServer {
|
|
|
488
416
|
}
|
|
489
417
|
|
|
490
418
|
if (chunksNeedingEmbeddings.length === 0) {
|
|
491
|
-
console.log('
|
|
419
|
+
console.log('All chunks already have persistent embeddings');
|
|
492
420
|
return;
|
|
493
421
|
}
|
|
494
422
|
|
|
495
|
-
console.log(
|
|
423
|
+
console.log(`Enhancing ${chunksNeedingEmbeddings.length} chunks with persistent embeddings...`);
|
|
496
424
|
|
|
497
425
|
// Initialize vector store if needed
|
|
498
426
|
if (!this.vectorStoreInitialized) {
|
|
@@ -508,7 +436,7 @@ export class CntxServer {
|
|
|
508
436
|
console.error(`Failed to generate/persist embedding for chunk ${chunk.id}:`, error.message);
|
|
509
437
|
}
|
|
510
438
|
}
|
|
511
|
-
console.log('
|
|
439
|
+
console.log('Background embedding enhancement complete');
|
|
512
440
|
}
|
|
513
441
|
|
|
514
442
|
invalidateSemanticCache() {
|
|
@@ -527,121 +455,6 @@ export class CntxServer {
|
|
|
527
455
|
return this.bundleManager.generateFileXML(chunk.filePath);
|
|
528
456
|
}
|
|
529
457
|
|
|
530
|
-
invalidateSemanticCache() {
|
|
531
|
-
this.semanticCache = null;
|
|
532
|
-
this.lastSemanticAnalysis = null;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// === Activity Management (Placeholder) ===
|
|
536
|
-
|
|
537
|
-
async loadActivities() {
|
|
538
|
-
try {
|
|
539
|
-
const activitiesPath = join(this.CWD, '.cntx', 'activities');
|
|
540
|
-
const activitiesJsonPath = join(activitiesPath, 'activities.json');
|
|
541
|
-
|
|
542
|
-
console.log('DEBUG: Looking for activities at:', activitiesJsonPath);
|
|
543
|
-
console.log('DEBUG: File exists:', fs.existsSync(activitiesJsonPath));
|
|
544
|
-
console.log('DEBUG: CWD is:', this.CWD);
|
|
545
|
-
|
|
546
|
-
if (!fs.existsSync(activitiesJsonPath)) {
|
|
547
|
-
console.log('Activities file not found, returning empty array');
|
|
548
|
-
return [];
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
const activitiesData = JSON.parse(fs.readFileSync(activitiesJsonPath, 'utf8'));
|
|
552
|
-
|
|
553
|
-
return activitiesData.map((activity, index) => {
|
|
554
|
-
// Extract the actual directory name from the references field
|
|
555
|
-
let activityId = activity.title.toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
556
|
-
if (activity.references && activity.references.length > 0) {
|
|
557
|
-
// Extract directory name from path like ".cntx/activities/activities/refactor-js-to-ts/README.md"
|
|
558
|
-
const refPath = activity.references[0];
|
|
559
|
-
const pathParts = refPath.split('/');
|
|
560
|
-
if (pathParts.length >= 4) {
|
|
561
|
-
activityId = pathParts[3]; // activities/activities/{this-part}/README.md
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
const activityDir = join(activitiesPath, 'activities', activityId);
|
|
565
|
-
|
|
566
|
-
// Load markdown files
|
|
567
|
-
const files = {
|
|
568
|
-
readme: this.loadMarkdownFile(join(activityDir, 'README.md')),
|
|
569
|
-
progress: this.loadMarkdownFile(join(activityDir, 'progress.md')),
|
|
570
|
-
tasks: this.loadMarkdownFile(join(activityDir, 'tasks.md')),
|
|
571
|
-
notes: this.loadMarkdownFile(join(activityDir, 'notes.md'))
|
|
572
|
-
};
|
|
573
|
-
|
|
574
|
-
// Calculate progress from progress.md file
|
|
575
|
-
const progress = this.parseProgressFromMarkdown(files.progress);
|
|
576
|
-
|
|
577
|
-
return {
|
|
578
|
-
id: activityId,
|
|
579
|
-
name: activity.title,
|
|
580
|
-
description: activity.description,
|
|
581
|
-
status: activity.status === 'todo' ? 'pending' : activity.status,
|
|
582
|
-
priority: activity.tags?.includes('high') ? 'high' : activity.tags?.includes('low') ? 'low' : 'medium',
|
|
583
|
-
progress,
|
|
584
|
-
updatedAt: new Date().toISOString(),
|
|
585
|
-
category: activity.tags?.[0] || 'general',
|
|
586
|
-
files,
|
|
587
|
-
tags: activity.tags
|
|
588
|
-
};
|
|
589
|
-
});
|
|
590
|
-
} catch (error) {
|
|
591
|
-
console.error('Failed to load activities:', error);
|
|
592
|
-
return [];
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
loadMarkdownFile(filePath) {
|
|
597
|
-
try {
|
|
598
|
-
if (fs.existsSync(filePath)) {
|
|
599
|
-
return fs.readFileSync(filePath, 'utf8');
|
|
600
|
-
}
|
|
601
|
-
return 'No content available';
|
|
602
|
-
} catch (error) {
|
|
603
|
-
return `Error loading file: ${error.message}`;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
parseProgressFromMarkdown(progressContent) {
|
|
608
|
-
try {
|
|
609
|
-
if (!progressContent || progressContent === 'No content available') {
|
|
610
|
-
return 0;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Look for "Overall Completion: XX%" pattern
|
|
614
|
-
const overallMatch = progressContent.match(/(?:Overall Completion|Progress):\s*(\d+)%/i);
|
|
615
|
-
if (overallMatch) {
|
|
616
|
-
return parseInt(overallMatch[1], 10);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
// Fallback: count completed tasks vs total tasks in checkbox format
|
|
620
|
-
const taskMatches = progressContent.match(/- \[([x✓✅\s])\]/gi);
|
|
621
|
-
if (taskMatches && taskMatches.length > 0) {
|
|
622
|
-
const completedTasks = taskMatches.filter(match =>
|
|
623
|
-
match.includes('[x]') || match.includes('[✓]') || match.includes('[✅]')
|
|
624
|
-
).length;
|
|
625
|
-
return Math.round((completedTasks / taskMatches.length) * 100);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
return 0;
|
|
629
|
-
} catch (error) {
|
|
630
|
-
console.error('Error parsing progress:', error);
|
|
631
|
-
return 0;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
async executeActivity(activityId) {
|
|
636
|
-
// Placeholder - would execute specific activity
|
|
637
|
-
return { success: false, message: 'Activity execution not implemented' };
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
async stopActivity(activityId) {
|
|
641
|
-
// Placeholder - would stop running activity
|
|
642
|
-
return { success: false, message: 'Activity stopping not implemented' };
|
|
643
|
-
}
|
|
644
|
-
|
|
645
458
|
// === MCP Server Integration ===
|
|
646
459
|
|
|
647
460
|
startMCPServer() {
|
|
@@ -650,7 +463,7 @@ export class CntxServer {
|
|
|
650
463
|
this.mcpServerStarted = true;
|
|
651
464
|
|
|
652
465
|
if (this.verbose) {
|
|
653
|
-
console.log('
|
|
466
|
+
console.log('MCP server started');
|
|
654
467
|
}
|
|
655
468
|
}
|
|
656
469
|
}
|
|
@@ -668,8 +481,8 @@ export class CntxServer {
|
|
|
668
481
|
// Start server and show progress
|
|
669
482
|
server.listen(port, host, () => {
|
|
670
483
|
console.log('');
|
|
671
|
-
console.log(
|
|
672
|
-
console.log(
|
|
484
|
+
console.log(`Server running at http://${host}:${port}`);
|
|
485
|
+
console.log(`Serving ${this.bundleManager.getAllBundleInfo().length} bundles from your project`);
|
|
673
486
|
console.log('');
|
|
674
487
|
|
|
675
488
|
// Display initialization summary
|
|
@@ -678,11 +491,11 @@ export class CntxServer {
|
|
|
678
491
|
|
|
679
492
|
// Handle graceful shutdown
|
|
680
493
|
process.on('SIGINT', () => {
|
|
681
|
-
console.log('\
|
|
494
|
+
console.log('\nShutting down server...');
|
|
682
495
|
this.webSocketManager.close();
|
|
683
496
|
this.fileSystemManager.destroy();
|
|
684
497
|
server.close(() => {
|
|
685
|
-
console.log('
|
|
498
|
+
console.log('Server stopped');
|
|
686
499
|
process.exit(0);
|
|
687
500
|
});
|
|
688
501
|
});
|
|
@@ -691,11 +504,25 @@ export class CntxServer {
|
|
|
691
504
|
}
|
|
692
505
|
}
|
|
693
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
|
+
|
|
694
522
|
// Export function for CLI compatibility
|
|
695
523
|
export async function startServer(options = {}) {
|
|
696
524
|
const server = new CntxServer(options.cwd, { verbose: options.verbose });
|
|
697
525
|
|
|
698
|
-
// Show ASCII art first
|
|
699
526
|
const asciiArt = `
|
|
700
527
|
██████ ███ ██ ████████ ██ ██ ██ ██ ██
|
|
701
528
|
██ ████ ██ ██ ██ ██ ██ ██ ██
|
|
@@ -704,12 +531,13 @@ export async function startServer(options = {}) {
|
|
|
704
531
|
██████ ██ ████ ██ ██ ██ ██████ ██
|
|
705
532
|
`;
|
|
706
533
|
console.log(asciiArt);
|
|
707
|
-
console.log(''); // Add blank line after art
|
|
708
534
|
|
|
709
|
-
// Now start initialization
|
|
535
|
+
// Now start initialization
|
|
710
536
|
await server.init();
|
|
711
537
|
|
|
712
|
-
|
|
538
|
+
// Enable MCP status tracking by default
|
|
539
|
+
const withMcp = options.withMcp !== false;
|
|
540
|
+
if (withMcp) {
|
|
713
541
|
server.startMCPServer();
|
|
714
542
|
}
|
|
715
543
|
|
|
@@ -723,7 +551,7 @@ export async function startMCPServer(options = {}) {
|
|
|
723
551
|
server.startMCPServer();
|
|
724
552
|
|
|
725
553
|
// For MCP mode, we don't start the web server, just keep the process alive
|
|
726
|
-
console.log('
|
|
554
|
+
console.log('MCP server running on stdio...');
|
|
727
555
|
}
|
|
728
556
|
|
|
729
557
|
export async function generateBundle(bundleName = 'master') {
|
|
@@ -743,11 +571,11 @@ export async function generateBundle(bundleName = 'master') {
|
|
|
743
571
|
// Initialize project configuration
|
|
744
572
|
export async function initConfig(cwd = process.cwd()) {
|
|
745
573
|
const server = new CntxServer(cwd);
|
|
746
|
-
|
|
574
|
+
|
|
747
575
|
// 1. Initialize directory structure
|
|
748
576
|
if (!existsSync(server.CNTX_DIR)) {
|
|
749
577
|
mkdirSync(server.CNTX_DIR, { recursive: true });
|
|
750
|
-
console.log('
|
|
578
|
+
console.log('Created .cntx directory');
|
|
751
579
|
}
|
|
752
580
|
|
|
753
581
|
// 2. Create .mcp.json for Claude Code discovery
|
|
@@ -762,11 +590,11 @@ export async function initConfig(cwd = process.cwd()) {
|
|
|
762
590
|
}
|
|
763
591
|
};
|
|
764
592
|
writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2), 'utf8');
|
|
765
|
-
console.log('
|
|
593
|
+
console.log('Created .mcp.json for MCP auto-discovery');
|
|
766
594
|
|
|
767
595
|
// 3. Initialize basic configuration with better defaults and auto-suggestions
|
|
768
596
|
server.configManager.loadConfig();
|
|
769
|
-
|
|
597
|
+
|
|
770
598
|
const suggestedBundles = {
|
|
771
599
|
master: ['**/*']
|
|
772
600
|
};
|
|
@@ -784,7 +612,7 @@ export async function initConfig(cwd = process.cwd()) {
|
|
|
784
612
|
commonDirs.forEach(d => {
|
|
785
613
|
if (existsSync(join(cwd, d.dir))) {
|
|
786
614
|
suggestedBundles[d.name] = [`${d.dir}/**`];
|
|
787
|
-
console.log(
|
|
615
|
+
console.log(` Suggested bundle: ${d.name} (${d.dir}/**)`);
|
|
788
616
|
}
|
|
789
617
|
});
|
|
790
618
|
|
|
@@ -820,89 +648,10 @@ export async function initConfig(cwd = process.cwd()) {
|
|
|
820
648
|
.mcp.json
|
|
821
649
|
`;
|
|
822
650
|
writeFileSync(ignorePath, defaultIgnore, 'utf8');
|
|
823
|
-
console.log('
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
console.log('⚙️ Basic configuration initialized');
|
|
827
|
-
|
|
828
|
-
// ... (rest of the file remains same)
|
|
829
|
-
const templateDir = join(__dirname, 'templates');
|
|
830
|
-
|
|
831
|
-
// Copy agent configuration files
|
|
832
|
-
const agentFiles = [
|
|
833
|
-
'agent-config.yaml',
|
|
834
|
-
'agent-instructions.md'
|
|
835
|
-
];
|
|
836
|
-
|
|
837
|
-
for (const file of agentFiles) {
|
|
838
|
-
const sourcePath = join(templateDir, file);
|
|
839
|
-
const destPath = join(server.CNTX_DIR, file);
|
|
840
|
-
|
|
841
|
-
if (existsSync(sourcePath) && !existsSync(destPath)) {
|
|
842
|
-
copyFileSync(sourcePath, destPath);
|
|
843
|
-
console.log(`📄 Created ${file}`);
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
// Copy agent-rules directory structure
|
|
848
|
-
const agentRulesSource = join(templateDir, 'agent-rules');
|
|
849
|
-
const agentRulesDest = join(server.CNTX_DIR, 'agent-rules');
|
|
850
|
-
|
|
851
|
-
if (existsSync(agentRulesSource) && !existsSync(agentRulesDest)) {
|
|
852
|
-
cpSync(agentRulesSource, agentRulesDest, { recursive: true });
|
|
853
|
-
console.log('📁 Created agent-rules directory with templates');
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
// Copy activities framework
|
|
857
|
-
const activitiesDir = join(server.CNTX_DIR, 'activities');
|
|
858
|
-
if (!existsSync(activitiesDir)) {
|
|
859
|
-
mkdirSync(activitiesDir, { recursive: true });
|
|
651
|
+
console.log('Created .cntxignore with smart defaults');
|
|
860
652
|
}
|
|
861
653
|
|
|
862
|
-
|
|
863
|
-
const activitiesReadmeSource = join(templateDir, 'activities', 'README.md');
|
|
864
|
-
const activitiesReadmeDest = join(activitiesDir, 'README.md');
|
|
865
|
-
|
|
866
|
-
if (existsSync(activitiesReadmeSource) && !existsSync(activitiesReadmeDest)) {
|
|
867
|
-
copyFileSync(activitiesReadmeSource, activitiesReadmeDest);
|
|
868
|
-
console.log('📄 Created activities/README.md');
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
// Copy activities lib directory (MDC templates)
|
|
872
|
-
const activitiesLibSource = join(templateDir, 'activities', 'lib');
|
|
873
|
-
const activitiesLibDest = join(activitiesDir, 'lib');
|
|
874
|
-
|
|
875
|
-
if (existsSync(activitiesLibSource) && !existsSync(activitiesLibDest)) {
|
|
876
|
-
cpSync(activitiesLibSource, activitiesLibDest, { recursive: true });
|
|
877
|
-
console.log('📁 Created activities/lib with MDC templates');
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
// Copy activities.json from templates
|
|
881
|
-
const activitiesJsonPath = join(activitiesDir, 'activities.json');
|
|
882
|
-
const templateActivitiesJsonPath = join(templateDir, 'activities', 'activities.json');
|
|
883
|
-
if (!existsSync(activitiesJsonPath) && existsSync(templateActivitiesJsonPath)) {
|
|
884
|
-
copyFileSync(templateActivitiesJsonPath, activitiesJsonPath);
|
|
885
|
-
console.log('📄 Created activities.json with bundle example activity');
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
// Copy example activity from templates
|
|
889
|
-
const activitiesDestDir = join(activitiesDir, 'activities');
|
|
890
|
-
const templateActivitiesDir = join(templateDir, 'activities', 'activities');
|
|
891
|
-
if (!existsSync(activitiesDestDir) && existsSync(templateActivitiesDir)) {
|
|
892
|
-
cpSync(templateActivitiesDir, activitiesDestDir, { recursive: true });
|
|
893
|
-
console.log('📁 Created example activity with templates');
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
console.log('');
|
|
897
|
-
console.log('🎉 cntx-ui initialized with full scaffolding!');
|
|
898
|
-
console.log('');
|
|
899
|
-
console.log('Next steps:');
|
|
900
|
-
console.log(' 1️⃣ Start the server: cntx-ui watch');
|
|
901
|
-
console.log(' 2️⃣ Open web UI: http://localhost:3333');
|
|
902
|
-
console.log(' 3️⃣ Read .cntx/agent-instructions.md for AI integration');
|
|
903
|
-
console.log(' 4️⃣ Explore .cntx/activities/README.md for project management');
|
|
904
|
-
console.log('');
|
|
905
|
-
console.log('💡 Pro tip: Use "cntx-ui status" to see your project overview');
|
|
654
|
+
console.log('Configuration initialized');
|
|
906
655
|
}
|
|
907
656
|
|
|
908
657
|
export async function getStatus() {
|
|
@@ -912,13 +661,13 @@ export async function getStatus() {
|
|
|
912
661
|
const bundles = server.bundleManager.getAllBundleInfo();
|
|
913
662
|
const totalFiles = server.fileSystemManager.getAllFiles().length;
|
|
914
663
|
|
|
915
|
-
console.log('
|
|
664
|
+
console.log('cntx-ui Status');
|
|
916
665
|
console.log('================');
|
|
917
666
|
console.log(`Total files: ${totalFiles}`);
|
|
918
667
|
console.log(`Bundles: ${bundles.length}`);
|
|
919
668
|
|
|
920
669
|
bundles.forEach(bundle => {
|
|
921
|
-
console.log(`
|
|
670
|
+
console.log(` - ${bundle.name}: ${bundle.fileCount} files (${Math.round(bundle.size / 1024)}KB)`);
|
|
922
671
|
});
|
|
923
672
|
|
|
924
673
|
return {
|
|
@@ -928,46 +677,10 @@ export async function getStatus() {
|
|
|
928
677
|
};
|
|
929
678
|
}
|
|
930
679
|
|
|
931
|
-
export function setupMCP() {
|
|
932
|
-
const configPath = join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
933
|
-
const projectPath = process.cwd();
|
|
934
|
-
|
|
935
|
-
console.log('🔧 Setting up MCP integration...');
|
|
936
|
-
console.log(`Project: ${projectPath}`);
|
|
937
|
-
console.log(`Claude config: ${configPath}`);
|
|
938
|
-
|
|
939
|
-
try {
|
|
940
|
-
let config = {};
|
|
941
|
-
if (existsSync(configPath)) {
|
|
942
|
-
config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
if (!config.mcpServers) {
|
|
946
|
-
config.mcpServers = {};
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
config.mcpServers['cntx-ui'] = {
|
|
950
|
-
command: 'node',
|
|
951
|
-
args: [join(projectPath, 'bin', 'cntx-ui.js'), 'mcp'],
|
|
952
|
-
env: {}
|
|
953
|
-
};
|
|
954
|
-
|
|
955
|
-
// Ensure directory exists
|
|
956
|
-
mkdirSync(dirname(configPath), { recursive: true });
|
|
957
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
958
|
-
|
|
959
|
-
console.log('✅ MCP integration configured');
|
|
960
|
-
console.log('💡 Restart Claude Desktop to apply changes');
|
|
961
|
-
} catch (error) {
|
|
962
|
-
console.error('❌ Failed to setup MCP:', error.message);
|
|
963
|
-
console.log('💡 You may need to manually add the configuration to Claude Desktop');
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
|
|
967
680
|
// Auto-start server when run directly
|
|
968
681
|
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
969
682
|
if (isMainModule) {
|
|
970
|
-
console.log('
|
|
683
|
+
console.log('Starting cntx-ui server...');
|
|
971
684
|
const server = new CntxServer();
|
|
972
685
|
server.init();
|
|
973
686
|
server.listen(3333, 'localhost');
|