cntx-ui 2.0.13 → 3.0.0
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/README.md +51 -339
- package/VISION.md +110 -0
- package/bin/cntx-ui-mcp.sh +3 -0
- package/bin/cntx-ui.js +138 -55
- package/lib/agent-runtime.js +301 -0
- package/lib/agent-tools.js +370 -0
- package/lib/api-router.js +1161 -0
- package/lib/bundle-manager.js +236 -0
- package/lib/configuration-manager.js +760 -0
- package/lib/database-manager.js +397 -0
- package/lib/file-system-manager.js +489 -0
- package/lib/heuristics-manager.js +527 -0
- package/lib/mcp-server.js +1125 -2
- package/lib/semantic-splitter.js +225 -491
- package/lib/simple-vector-store.js +98 -0
- package/lib/websocket-manager.js +470 -0
- package/package.json +19 -25
- package/server.js +742 -1935
- package/templates/TOOLS.md +41 -0
- package/templates/activities/README.md +67 -0
- package/templates/activities/activities/create-project-bundles/README.md +84 -0
- package/templates/activities/activities/create-project-bundles/notes.md +98 -0
- package/templates/activities/activities/create-project-bundles/progress.md +63 -0
- package/templates/activities/activities/create-project-bundles/tasks.md +39 -0
- package/templates/activities/activities.json +219 -0
- package/templates/activities/lib/.markdownlint.jsonc +18 -0
- package/templates/activities/lib/create-activity.mdc +63 -0
- package/templates/activities/lib/generate-tasks.mdc +64 -0
- package/templates/activities/lib/process-task-list.mdc +52 -0
- package/templates/agent-config.yaml +65 -0
- package/templates/agent-instructions.md +234 -0
- package/templates/agent-rules/capabilities/activities-system.md +147 -0
- package/templates/agent-rules/capabilities/bundle-system.md +131 -0
- package/templates/agent-rules/capabilities/vector-search.md +135 -0
- package/templates/agent-rules/core/codebase-navigation.md +91 -0
- package/templates/agent-rules/core/performance-hierarchy.md +48 -0
- package/templates/agent-rules/core/response-formatting.md +120 -0
- package/templates/agent-rules/project-specific/architecture.md +145 -0
- package/templates/config.json +76 -0
- package/templates/hidden-files.json +14 -0
- package/web/dist/assets/index-B2OdTzzI.css +1 -0
- package/web/dist/assets/index-D0tBsKiR.js +2016 -0
- package/web/dist/cntx-ui.svg +18 -0
- package/web/dist/index.html +25 -8
- package/lib/semantic-integration.js +0 -441
- package/mcp-config-example.json +0 -9
- package/web/dist/assets/index-Ci1Q-YrQ.js +0 -611
- package/web/dist/assets/index-IUp4q_fr.css +0 -1
- package/web/dist/vite.svg +0 -21
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Vector Store with SQLite Persistence
|
|
3
|
+
* Powered by Transformers.js for local embeddings
|
|
4
|
+
* Persists vectors to SQLite for instant startup
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { pipeline } from '@xenova/transformers';
|
|
8
|
+
|
|
9
|
+
export default class SimpleVectorStore {
|
|
10
|
+
constructor(databaseManager, options = {}) {
|
|
11
|
+
this.db = databaseManager;
|
|
12
|
+
this.modelName = options.modelName || 'Xenova/all-MiniLM-L6-v2';
|
|
13
|
+
this.pipe = null;
|
|
14
|
+
this.initialized = false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async init() {
|
|
18
|
+
if (this.initialized) return;
|
|
19
|
+
console.log(`🤖 Initializing local RAG engine (${this.modelName})...`);
|
|
20
|
+
this.pipe = await pipeline('feature-extraction', this.modelName);
|
|
21
|
+
this.initialized = true;
|
|
22
|
+
console.log('✅ Local RAG engine ready');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async generateEmbedding(text) {
|
|
26
|
+
await this.init();
|
|
27
|
+
const output = await this.pipe(text, { pooling: 'mean', normalize: true });
|
|
28
|
+
return new Float32Array(output.data);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Upsert a chunk's embedding to persistence
|
|
33
|
+
*/
|
|
34
|
+
async upsertChunk(chunk) {
|
|
35
|
+
const chunkId = chunk.id;
|
|
36
|
+
// Check if we already have it in DB
|
|
37
|
+
const existing = this.db.getEmbedding(chunkId);
|
|
38
|
+
if (existing) return existing;
|
|
39
|
+
|
|
40
|
+
// Generate new embedding
|
|
41
|
+
const textToEmbed = `${chunk.name} ${chunk.purpose} ${chunk.code}`;
|
|
42
|
+
const embedding = await this.generateEmbedding(textToEmbed);
|
|
43
|
+
|
|
44
|
+
// Save to SQLite
|
|
45
|
+
this.db.saveEmbedding(chunkId, embedding, this.modelName);
|
|
46
|
+
return embedding;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Semantic Search across persistent embeddings
|
|
51
|
+
*/
|
|
52
|
+
async search(query, options = {}) {
|
|
53
|
+
const { limit = 10, threshold = 0.5 } = options;
|
|
54
|
+
const queryEmbedding = await this.generateEmbedding(query);
|
|
55
|
+
|
|
56
|
+
// Load all embeddings from DB
|
|
57
|
+
// Optimization: In a huge codebase, we'd use a real vector DB or FAISS
|
|
58
|
+
// For now, SQLite + Manual Cosine Similarity is fine for local repos
|
|
59
|
+
const rows = this.db.db.prepare('SELECT chunk_id, embedding FROM vector_embeddings WHERE model_name = ?').all(this.modelName);
|
|
60
|
+
|
|
61
|
+
const results = [];
|
|
62
|
+
for (const row of rows) {
|
|
63
|
+
const embedding = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4);
|
|
64
|
+
const similarity = this.cosineSimilarity(queryEmbedding, embedding);
|
|
65
|
+
|
|
66
|
+
if (similarity >= threshold) {
|
|
67
|
+
results.push({
|
|
68
|
+
chunkId: row.chunk_id,
|
|
69
|
+
similarity
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Sort by similarity and get chunk details
|
|
75
|
+
return results
|
|
76
|
+
.sort((a, b) => b.similarity - a.similarity)
|
|
77
|
+
.slice(0, limit)
|
|
78
|
+
.map(res => {
|
|
79
|
+
const chunk = this.db.db.prepare('SELECT * FROM semantic_chunks WHERE id = ?').get(res.chunkId);
|
|
80
|
+
return {
|
|
81
|
+
...this.db.mapChunkRow(chunk),
|
|
82
|
+
similarity: res.similarity
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
cosineSimilarity(vecA, vecB) {
|
|
88
|
+
let dotProduct = 0;
|
|
89
|
+
let normA = 0;
|
|
90
|
+
let normB = 0;
|
|
91
|
+
for (let i = 0; i < vecA.length; i++) {
|
|
92
|
+
dotProduct += vecA[i] * vecB[i];
|
|
93
|
+
normA += vecA[i] * vecA[i];
|
|
94
|
+
normB += vecB[i] * vecB[i];
|
|
95
|
+
}
|
|
96
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Manager for cntx-ui
|
|
3
|
+
* Handles real-time client communication and updates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { WebSocketServer } from 'ws';
|
|
7
|
+
|
|
8
|
+
export default class WebSocketManager {
|
|
9
|
+
constructor(bundleManager, configManager, options = {}) {
|
|
10
|
+
this.bundleManager = bundleManager;
|
|
11
|
+
this.configManager = configManager;
|
|
12
|
+
this.verbose = options.verbose || false;
|
|
13
|
+
this.clients = new Set();
|
|
14
|
+
this.wss = null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// === WebSocket Server Setup ===
|
|
18
|
+
|
|
19
|
+
initialize(httpServer) {
|
|
20
|
+
this.wss = new WebSocketServer({ server: httpServer });
|
|
21
|
+
|
|
22
|
+
this.wss.on('connection', (ws) => {
|
|
23
|
+
this.handleConnection(ws);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
if (this.verbose) {
|
|
27
|
+
console.log('🔌 WebSocket server initialized');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
handleConnection(ws) {
|
|
32
|
+
// Add client to our set
|
|
33
|
+
this.clients.add(ws);
|
|
34
|
+
if (this.verbose) {
|
|
35
|
+
console.log(`📱 WebSocket client connected (${this.clients.size} total clients)`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Send initial update to the new client
|
|
39
|
+
this.sendUpdate(ws);
|
|
40
|
+
|
|
41
|
+
// Handle client disconnect
|
|
42
|
+
ws.on('close', () => {
|
|
43
|
+
this.clients.delete(ws);
|
|
44
|
+
if (this.verbose) {
|
|
45
|
+
console.log(`📱 WebSocket client disconnected (${this.clients.size} total clients)`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Handle client errors
|
|
50
|
+
ws.on('error', (error) => {
|
|
51
|
+
if (this.verbose) {
|
|
52
|
+
console.error('WebSocket client error:', error.message);
|
|
53
|
+
}
|
|
54
|
+
this.clients.delete(ws);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Optional: Handle incoming messages from clients
|
|
58
|
+
ws.on('message', (message) => {
|
|
59
|
+
try {
|
|
60
|
+
const data = JSON.parse(message.toString());
|
|
61
|
+
this.handleClientMessage(ws, data);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (this.verbose) {
|
|
64
|
+
console.error('Invalid WebSocket message:', error.message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
handleClientMessage(ws, data) {
|
|
71
|
+
// Handle different types of client messages
|
|
72
|
+
switch (data.type) {
|
|
73
|
+
case 'ping':
|
|
74
|
+
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case 'request-update':
|
|
78
|
+
this.sendUpdate(ws);
|
|
79
|
+
break;
|
|
80
|
+
|
|
81
|
+
case 'subscribe':
|
|
82
|
+
// Future: Handle subscription to specific bundle updates
|
|
83
|
+
ws.subscriptions = data.bundles || [];
|
|
84
|
+
break;
|
|
85
|
+
|
|
86
|
+
default:
|
|
87
|
+
if (this.verbose) {
|
|
88
|
+
console.warn('Unknown WebSocket message type:', data.type);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// === Client Management ===
|
|
94
|
+
|
|
95
|
+
getClientCount() {
|
|
96
|
+
return this.clients.size;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getActiveClients() {
|
|
100
|
+
// Filter out clients that might be in a closed state
|
|
101
|
+
const activeClients = new Set();
|
|
102
|
+
|
|
103
|
+
this.clients.forEach(client => {
|
|
104
|
+
if (client.readyState === 1) { // WebSocket.OPEN
|
|
105
|
+
activeClients.add(client);
|
|
106
|
+
} else {
|
|
107
|
+
// Remove dead connections
|
|
108
|
+
this.clients.delete(client);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return activeClients;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// === Broadcasting Updates ===
|
|
116
|
+
|
|
117
|
+
broadcastUpdate() {
|
|
118
|
+
const activeClients = this.getActiveClients();
|
|
119
|
+
|
|
120
|
+
if (activeClients.size === 0) {
|
|
121
|
+
return; // No clients to update
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (this.verbose) {
|
|
125
|
+
console.log(`📡 Broadcasting update to ${activeClients.size} client(s)`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
activeClients.forEach(client => {
|
|
129
|
+
this.sendUpdate(client);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
sendUpdate(client) {
|
|
134
|
+
if (client.readyState !== 1) { // Not WebSocket.OPEN
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const updateData = this.prepareUpdateData();
|
|
140
|
+
client.send(JSON.stringify(updateData));
|
|
141
|
+
} catch (error) {
|
|
142
|
+
if (this.verbose) {
|
|
143
|
+
console.error('Failed to send WebSocket update:', error.message);
|
|
144
|
+
}
|
|
145
|
+
this.clients.delete(client);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
prepareUpdateData() {
|
|
150
|
+
const bundles = this.configManager.getBundles();
|
|
151
|
+
|
|
152
|
+
const bundleData = Array.from(bundles.entries()).map(([name, bundle]) => ({
|
|
153
|
+
name,
|
|
154
|
+
changed: bundle.changed,
|
|
155
|
+
fileCount: bundle.files.length,
|
|
156
|
+
content: bundle.content.substring(0, 2000) + (bundle.content.length > 2000 ? '...' : ''),
|
|
157
|
+
files: bundle.files,
|
|
158
|
+
lastGenerated: bundle.generated,
|
|
159
|
+
size: bundle.size,
|
|
160
|
+
patterns: bundle.patterns
|
|
161
|
+
}));
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
type: 'bundle-update',
|
|
165
|
+
timestamp: new Date().toISOString(),
|
|
166
|
+
bundles: bundleData,
|
|
167
|
+
serverStatus: {
|
|
168
|
+
uptime: process.uptime(),
|
|
169
|
+
scanning: this.bundleManager._isScanning || false,
|
|
170
|
+
totalFiles: this.bundleManager.fileSystemManager?.getAllFiles()?.length || 0
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// === Targeted Updates ===
|
|
176
|
+
|
|
177
|
+
broadcastBundleUpdate(bundleName) {
|
|
178
|
+
const activeClients = this.getActiveClients();
|
|
179
|
+
|
|
180
|
+
if (activeClients.size === 0) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (this.verbose) {
|
|
185
|
+
console.log(`📡 Broadcasting ${bundleName} bundle update to ${activeClients.size} client(s)`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const bundle = this.configManager.getBundles().get(bundleName);
|
|
189
|
+
if (!bundle) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const updateData = {
|
|
194
|
+
type: 'bundle-specific-update',
|
|
195
|
+
timestamp: new Date().toISOString(),
|
|
196
|
+
bundleName,
|
|
197
|
+
bundle: {
|
|
198
|
+
name: bundleName,
|
|
199
|
+
changed: bundle.changed,
|
|
200
|
+
fileCount: bundle.files.length,
|
|
201
|
+
content: bundle.content.substring(0, 2000) + (bundle.content.length > 2000 ? '...' : ''),
|
|
202
|
+
files: bundle.files,
|
|
203
|
+
lastGenerated: bundle.generated,
|
|
204
|
+
size: bundle.size,
|
|
205
|
+
patterns: bundle.patterns
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
activeClients.forEach(client => {
|
|
210
|
+
try {
|
|
211
|
+
if (client.readyState === 1) {
|
|
212
|
+
client.send(JSON.stringify(updateData));
|
|
213
|
+
}
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (this.verbose) {
|
|
216
|
+
console.error('Failed to send bundle update:', error.message);
|
|
217
|
+
}
|
|
218
|
+
this.clients.delete(client);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
broadcastFileChange(filename, eventType) {
|
|
224
|
+
const activeClients = this.getActiveClients();
|
|
225
|
+
|
|
226
|
+
if (activeClients.size === 0) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const updateData = {
|
|
231
|
+
type: 'file-change',
|
|
232
|
+
timestamp: new Date().toISOString(),
|
|
233
|
+
filename,
|
|
234
|
+
eventType,
|
|
235
|
+
message: `File ${eventType}: ${filename}`
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
activeClients.forEach(client => {
|
|
239
|
+
try {
|
|
240
|
+
if (client.readyState === 1) {
|
|
241
|
+
client.send(JSON.stringify(updateData));
|
|
242
|
+
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (this.verbose) {
|
|
245
|
+
console.error('Failed to send file change update:', error.message);
|
|
246
|
+
}
|
|
247
|
+
this.clients.delete(client);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
broadcastStatusUpdate(status) {
|
|
253
|
+
const activeClients = this.getActiveClients();
|
|
254
|
+
|
|
255
|
+
if (activeClients.size === 0) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const updateData = {
|
|
260
|
+
type: 'status-update',
|
|
261
|
+
timestamp: new Date().toISOString(),
|
|
262
|
+
status
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
activeClients.forEach(client => {
|
|
266
|
+
try {
|
|
267
|
+
if (client.readyState === 1) {
|
|
268
|
+
client.send(JSON.stringify(updateData));
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (this.verbose) {
|
|
272
|
+
console.error('Failed to send status update:', error.message);
|
|
273
|
+
}
|
|
274
|
+
this.clients.delete(client);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// === Utility Methods ===
|
|
280
|
+
|
|
281
|
+
ping() {
|
|
282
|
+
const activeClients = this.getActiveClients();
|
|
283
|
+
|
|
284
|
+
const pingData = {
|
|
285
|
+
type: 'ping',
|
|
286
|
+
timestamp: new Date().toISOString(),
|
|
287
|
+
serverTime: Date.now()
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
activeClients.forEach(client => {
|
|
291
|
+
try {
|
|
292
|
+
if (client.readyState === 1) {
|
|
293
|
+
client.send(JSON.stringify(pingData));
|
|
294
|
+
}
|
|
295
|
+
} catch (error) {
|
|
296
|
+
this.clients.delete(client);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// === Cleanup ===
|
|
302
|
+
|
|
303
|
+
close() {
|
|
304
|
+
if (this.wss) {
|
|
305
|
+
if (this.verbose) {
|
|
306
|
+
console.log('🔌 Closing WebSocket server...');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Close all client connections
|
|
310
|
+
this.clients.forEach(client => {
|
|
311
|
+
try {
|
|
312
|
+
if (client.readyState === 1) {
|
|
313
|
+
client.close(1000, 'Server shutting down');
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (this.verbose) {
|
|
317
|
+
console.error('Error closing WebSocket client:', error.message);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Close the WebSocket server
|
|
323
|
+
this.wss.close(() => {
|
|
324
|
+
if (this.verbose) {
|
|
325
|
+
console.log('🔌 WebSocket server closed');
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
this.clients.clear();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// === Health Check ===
|
|
334
|
+
|
|
335
|
+
getHealthStatus() {
|
|
336
|
+
return {
|
|
337
|
+
connected: this.clients.size,
|
|
338
|
+
active: this.getActiveClients().size,
|
|
339
|
+
server: this.wss ? 'running' : 'stopped'
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// === Event Handlers for Integration ===
|
|
344
|
+
|
|
345
|
+
onBundleGenerated(bundleName) {
|
|
346
|
+
this.broadcastBundleUpdate(bundleName);
|
|
347
|
+
this.broadcastBundleSyncCompleted(bundleName);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
onBundlesGenerated() {
|
|
351
|
+
this.broadcastUpdate();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
onConfigChanged() {
|
|
355
|
+
this.broadcastUpdate();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
onFileChanged(filename, eventType) {
|
|
359
|
+
// Notify which bundles are affected by this file change
|
|
360
|
+
const affectedBundles = this.getAffectedBundles(filename);
|
|
361
|
+
|
|
362
|
+
affectedBundles.forEach(bundleName => {
|
|
363
|
+
this.broadcastBundleFileChanged(bundleName, filename);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
this.broadcastFileChange(filename, eventType);
|
|
367
|
+
|
|
368
|
+
// Also broadcast bundle updates after a short delay to allow bundle regeneration
|
|
369
|
+
setTimeout(() => {
|
|
370
|
+
this.broadcastUpdate();
|
|
371
|
+
}, 500);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// === New Bundle Sync Event Handlers ===
|
|
375
|
+
|
|
376
|
+
onBundleSyncStarted(bundleName) {
|
|
377
|
+
const updateData = {
|
|
378
|
+
type: 'bundle-sync-started',
|
|
379
|
+
bundleName,
|
|
380
|
+
timestamp: new Date().toISOString()
|
|
381
|
+
};
|
|
382
|
+
this.broadcastToActiveClients(updateData);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
onBundleSyncCompleted(bundleName) {
|
|
386
|
+
const updateData = {
|
|
387
|
+
type: 'bundle-sync-completed',
|
|
388
|
+
bundleName,
|
|
389
|
+
timestamp: new Date().toISOString()
|
|
390
|
+
};
|
|
391
|
+
this.broadcastToActiveClients(updateData);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
onBundleSyncFailed(bundleName, error) {
|
|
395
|
+
const updateData = {
|
|
396
|
+
type: 'bundle-sync-failed',
|
|
397
|
+
bundleName,
|
|
398
|
+
error: error.message || error,
|
|
399
|
+
timestamp: new Date().toISOString()
|
|
400
|
+
};
|
|
401
|
+
this.broadcastToActiveClients(updateData);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
broadcastBundleFileChanged(bundleName, filename) {
|
|
405
|
+
const updateData = {
|
|
406
|
+
type: 'bundle-file-changed',
|
|
407
|
+
bundleName,
|
|
408
|
+
filename,
|
|
409
|
+
timestamp: new Date().toISOString()
|
|
410
|
+
};
|
|
411
|
+
this.broadcastToActiveClients(updateData);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
broadcastBundleSyncCompleted(bundleName) {
|
|
415
|
+
const updateData = {
|
|
416
|
+
type: 'bundle-sync-completed',
|
|
417
|
+
bundleName,
|
|
418
|
+
timestamp: new Date().toISOString()
|
|
419
|
+
};
|
|
420
|
+
this.broadcastToActiveClients(updateData);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Helper method to broadcast to all active clients
|
|
424
|
+
broadcastToActiveClients(data) {
|
|
425
|
+
const activeClients = this.getActiveClients();
|
|
426
|
+
|
|
427
|
+
if (activeClients.size === 0) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
activeClients.forEach(client => {
|
|
432
|
+
try {
|
|
433
|
+
if (client.readyState === 1) {
|
|
434
|
+
client.send(JSON.stringify(data));
|
|
435
|
+
}
|
|
436
|
+
} catch (error) {
|
|
437
|
+
if (this.verbose) {
|
|
438
|
+
console.error('Failed to send WebSocket update:', error.message);
|
|
439
|
+
}
|
|
440
|
+
this.clients.delete(client);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Helper to find which bundles are affected by a file change
|
|
446
|
+
getAffectedBundles(filename) {
|
|
447
|
+
const bundles = this.configManager.getBundles();
|
|
448
|
+
const affectedBundles = [];
|
|
449
|
+
|
|
450
|
+
bundles.forEach((bundle, name) => {
|
|
451
|
+
const matchesBundle = bundle.patterns.some(pattern =>
|
|
452
|
+
this.bundleManager.fileSystemManager.matchesPattern(filename, pattern)
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
if (matchesBundle) {
|
|
456
|
+
affectedBundles.push(name);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
return affectedBundles;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
onHiddenFilesChanged() {
|
|
464
|
+
this.broadcastUpdate();
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
onIgnorePatternsChanged() {
|
|
468
|
+
this.broadcastUpdate();
|
|
469
|
+
}
|
|
470
|
+
}
|
package/package.json
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cntx-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "3.0.0",
|
|
5
|
+
"description": "Autonomous Repository Intelligence engine with web UI and MCP server. Unified semantic code understanding, local RAG, and agent working memory.",
|
|
6
6
|
"keywords": [
|
|
7
|
-
"
|
|
7
|
+
"repository-intelligence",
|
|
8
|
+
"semantic-code-search",
|
|
9
|
+
"ai-agent-context",
|
|
8
10
|
"mcp-server",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"file-aggregation",
|
|
15
|
-
"project-context",
|
|
16
|
-
"ai-workflow",
|
|
17
|
-
"codebase-bundling",
|
|
18
|
-
"development-tools",
|
|
19
|
-
"websocket",
|
|
20
|
-
"react",
|
|
21
|
-
"cli-tool"
|
|
11
|
+
"local-rag",
|
|
12
|
+
"tree-sitter",
|
|
13
|
+
"codebase-indexing",
|
|
14
|
+
"ai-development",
|
|
15
|
+
"context-management"
|
|
22
16
|
],
|
|
23
17
|
"repository": {
|
|
24
18
|
"type": "git",
|
|
@@ -30,12 +24,13 @@
|
|
|
30
24
|
"cntx-ui": "./bin/cntx-ui.js"
|
|
31
25
|
},
|
|
32
26
|
"files": [
|
|
33
|
-
"bin/
|
|
27
|
+
"bin/",
|
|
34
28
|
"server.js",
|
|
35
29
|
"lib/",
|
|
30
|
+
"templates/",
|
|
31
|
+
"web/dist/",
|
|
36
32
|
"README.md",
|
|
37
|
-
"
|
|
38
|
-
"mcp-config-example.json"
|
|
33
|
+
"VISION.md"
|
|
39
34
|
],
|
|
40
35
|
"engines": {
|
|
41
36
|
"node": ">=18.0.0"
|
|
@@ -43,14 +38,13 @@
|
|
|
43
38
|
"scripts": {
|
|
44
39
|
"dev": "node server.js",
|
|
45
40
|
"build": "cd web && npm install && npm run build",
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"prebuild": "npm run build:web",
|
|
49
|
-
"prepublishOnly": "npm run build:web",
|
|
50
|
-
"test:local": "npm pack && npm install -g ./cntx-ui-2.0.0.tgz"
|
|
41
|
+
"prepublishOnly": "npm run build",
|
|
42
|
+
"test:local": "npm pack && npm install -g ./cntx-ui-3.0.0.tgz"
|
|
51
43
|
},
|
|
52
44
|
"dependencies": {
|
|
53
|
-
"
|
|
45
|
+
"@xenova/transformers": "^2.17.2",
|
|
46
|
+
"better-sqlite3": "^12.2.0",
|
|
47
|
+
"glob": "^9.0.0",
|
|
54
48
|
"tree-sitter": "^0.21.1",
|
|
55
49
|
"tree-sitter-javascript": "^0.23.1",
|
|
56
50
|
"tree-sitter-typescript": "^0.23.2",
|