cntx-ui 2.0.15 → 3.0.1

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 (32) hide show
  1. package/README.md +40 -344
  2. package/bin/cntx-ui-mcp.sh +3 -0
  3. package/bin/cntx-ui.js +2 -1
  4. package/lib/agent-runtime.js +161 -1340
  5. package/lib/agent-tools.js +9 -7
  6. package/lib/api-router.js +262 -79
  7. package/lib/bundle-manager.js +172 -407
  8. package/lib/configuration-manager.js +94 -59
  9. package/lib/database-manager.js +397 -0
  10. package/lib/file-system-manager.js +17 -0
  11. package/lib/heuristics-manager.js +119 -17
  12. package/lib/mcp-server.js +125 -55
  13. package/lib/semantic-splitter.js +222 -481
  14. package/lib/simple-vector-store.js +69 -300
  15. package/package.json +18 -31
  16. package/server.js +151 -73
  17. package/templates/TOOLS.md +41 -0
  18. package/templates/activities/activities/create-project-bundles/README.md +4 -3
  19. package/templates/activities/activities/create-project-bundles/notes.md +15 -19
  20. package/templates/activities/activities/create-project-bundles/tasks.md +4 -4
  21. package/templates/activities/activities.json +1 -1
  22. package/templates/agent-config.yaml +0 -13
  23. package/templates/agent-instructions.md +22 -6
  24. package/templates/agent-rules/capabilities/bundle-system.md +1 -1
  25. package/templates/agent-rules/project-specific/architecture.md +1 -1
  26. package/web/dist/assets/index-B2OdTzzI.css +1 -0
  27. package/web/dist/assets/index-D0tBsKiR.js +2016 -0
  28. package/web/dist/index.html +2 -2
  29. package/mcp-config-example.json +0 -9
  30. package/web/dist/assets/heuristics-manager-browser-DfonOP5I.js +0 -1
  31. package/web/dist/assets/index-dF3qg-y_.js +0 -2486
  32. package/web/dist/assets/index-h5FGSg_P.css +0 -1
@@ -1,329 +1,98 @@
1
1
  /**
2
- * Simple In-Memory Vector Store for cntx-ui
3
- * Free, local embeddings without external dependencies
2
+ * Simple Vector Store with SQLite Persistence
3
+ * Powered by Transformers.js for local embeddings
4
+ * Persists vectors to SQLite for instant startup
4
5
  */
5
6
 
6
- import { pipeline } from '@xenova/transformers'
7
+ import { pipeline } from '@xenova/transformers';
7
8
 
8
- class SimpleVectorStore {
9
- constructor(options = {}) {
10
- this.modelName = options.modelName || 'Xenova/all-MiniLM-L6-v2'
11
- this.collectionName = options.collectionName || 'code-chunks'
12
- this.embedder = null
13
- this.vectors = new Map() // id -> { embedding, metadata }
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;
14
15
  }
15
16
 
16
- /**
17
- * Initialize the embedding model
18
- */
19
- async initialize() {
20
- console.log('🔧 Initializing simple vector store...')
21
-
22
- // Load the embedding model (downloads on first run)
23
- this.embedder = await pipeline('feature-extraction', this.modelName)
24
- console.log('✅ Embedding model loaded')
25
- }
26
-
27
- /**
28
- * Alias for initialize() to match server expectations
29
- */
30
17
  async init() {
31
- return this.initialize()
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');
32
23
  }
33
24
 
34
- /**
35
- * Generate embedding for a single text
36
- */
37
25
  async generateEmbedding(text) {
38
- if (!this.embedder) {
39
- await this.initialize()
40
- }
41
-
42
- try {
43
- const result = await this.embedder(text, {
44
- pooling: 'mean',
45
- normalize: true
46
- })
47
-
48
- return Array.from(result.data)
49
- } catch (error) {
50
- console.error('❌ Failed to generate embedding:', error.message)
51
- throw error
52
- }
26
+ await this.init();
27
+ const output = await this.pipe(text, { pooling: 'mean', normalize: true });
28
+ return new Float32Array(output.data);
53
29
  }
54
30
 
55
31
  /**
56
- * Create embeddings for semantic chunks
32
+ * Upsert a chunk's embedding to persistence
57
33
  */
58
- async createEmbeddings(chunks) {
59
- if (!this.embedder) {
60
- await this.initialize()
61
- }
62
-
63
- console.log(`🔍 Creating embeddings for ${chunks.length} chunks...`)
64
-
65
- const embeddings = []
66
- const batchSize = 10 // Process in batches to avoid memory issues
67
-
68
- for (let i = 0; i < chunks.length; i += batchSize) {
69
- const batch = chunks.slice(i, i + batchSize)
70
- console.log(`📦 Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(chunks.length / batchSize)}`)
71
-
72
- for (const chunk of batch) {
73
- try {
74
- // Create searchable text from chunk data
75
- const searchableText = this.createSearchableText(chunk)
76
-
77
- // Generate embedding
78
- const result = await this.embedder(searchableText, {
79
- pooling: 'mean',
80
- normalize: true
81
- })
82
-
83
- // Convert to array format
84
- const embedding = Array.from(result.data)
85
-
86
- embeddings.push({
87
- id: chunk.name && chunk.filePath
88
- ? `${chunk.name}:${chunk.filePath}:${chunk.startLine || ''}`
89
- : `chunk-${i}`,
90
- embedding: embedding,
91
- metadata: {
92
- content: chunk.code || '',
93
- semanticType: chunk.semanticType || '',
94
- businessDomain: chunk.businessDomain || [],
95
- technicalPatterns: chunk.technicalPatterns || [],
96
- purpose: chunk.purpose || '',
97
- files: chunk.files || [],
98
- size: chunk.size || 0,
99
- complexity: chunk.complexity || 0
100
- }
101
- })
102
- } catch (error) {
103
- console.warn(`⚠️ Failed to embed chunk ${chunk.name}:`, error.message)
104
- }
105
- }
106
- }
107
-
108
- console.log(`✅ Created ${embeddings.length} embeddings`)
109
- return embeddings
110
- }
111
-
112
- /**
113
- * Create searchable text from semantic chunk
114
- */
115
- createSearchableText(chunk) {
116
- const parts = []
117
-
118
- // Add code content if available
119
- if (chunk.code) {
120
- parts.push(chunk.code)
121
- }
122
-
123
- // Add semantic type
124
- if (chunk.semanticType) {
125
- parts.push(`Type: ${chunk.semanticType}`)
126
- }
127
-
128
- // Add business domains
129
- if (chunk.businessDomain && chunk.businessDomain.length > 0) {
130
- parts.push(`Domain: ${chunk.businessDomain.join(', ')}`)
131
- }
132
-
133
- // Add technical patterns
134
- if (chunk.technicalPatterns && chunk.technicalPatterns.length > 0) {
135
- parts.push(`Patterns: ${chunk.technicalPatterns.join(', ')}`)
136
- }
137
-
138
- // Add purpose
139
- if (chunk.purpose) {
140
- parts.push(`Purpose: ${chunk.purpose}`)
141
- }
142
-
143
- // Add file names for context
144
- if (chunk.files && chunk.files.length > 0) {
145
- const fileNames = chunk.files.map(f => f.split('/').pop()).join(', ')
146
- parts.push(`Files: ${fileNames}`)
147
- }
148
-
149
- return parts.join(' | ')
150
- }
151
-
152
- /**
153
- * Store chunks in vector database
154
- */
155
- async storeChunks(chunks) {
156
- const embeddings = await this.createEmbeddings(chunks)
157
-
158
- if (embeddings.length === 0) {
159
- console.warn('⚠️ No embeddings created, skipping storage')
160
- return
161
- }
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;
162
39
 
163
- // Store in memory
164
- for (const embedding of embeddings) {
165
- this.vectors.set(embedding.id, {
166
- embedding: embedding.embedding,
167
- metadata: embedding.metadata
168
- })
169
- }
170
-
171
- console.log(`✅ Stored ${embeddings.length} chunks in memory vector store`)
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;
172
47
  }
173
48
 
174
49
  /**
175
- * Store chunks that already have embeddings (e.g., from cache)
50
+ * Semantic Search across persistent embeddings
176
51
  */
177
- async storePrecomputedChunks(chunks) {
178
- if (!chunks || chunks.length === 0) {
179
- console.warn('⚠️ No chunks provided for storage')
180
- return
181
- }
182
-
183
- let storedCount = 0
184
- for (const chunk of chunks) {
185
- if (chunk.embedding && chunk.embedding.length > 0) {
186
- this.vectors.set(chunk.id || chunk.name, {
187
- embedding: chunk.embedding,
188
- metadata: chunk.metadata || {
189
- content: chunk.code || chunk.content || '',
190
- semanticType: chunk.semanticType || 'unknown',
191
- businessDomain: chunk.businessDomain || [],
192
- technicalPatterns: chunk.technicalPatterns || [],
193
- purpose: chunk.purpose || '',
194
- files: chunk.files || [chunk.filePath].filter(Boolean),
195
- size: chunk.size || 0,
196
- complexity: chunk.complexity || 0
197
- }
198
- })
199
- storedCount++
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
+ });
200
71
  }
201
72
  }
202
73
 
203
- console.log(`✅ Stored ${storedCount} precomputed chunks in memory vector store`)
204
- return storedCount
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
+ });
205
85
  }
206
86
 
207
- /**
208
- * Calculate cosine similarity between two vectors
209
- */
210
87
  cosineSimilarity(vecA, vecB) {
211
- let dotProduct = 0
212
- let normA = 0
213
- let normB = 0
214
-
88
+ let dotProduct = 0;
89
+ let normA = 0;
90
+ let normB = 0;
215
91
  for (let i = 0; i < vecA.length; i++) {
216
- dotProduct += vecA[i] * vecB[i]
217
- normA += vecA[i] * vecA[i]
218
- normB += vecB[i] * vecB[i]
219
- }
220
-
221
- return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB))
222
- }
223
-
224
- /**
225
- * Find similar code chunks
226
- */
227
- async findSimilar(query, options = {}) {
228
- if (!this.embedder) {
229
- await this.initialize()
230
- }
231
-
232
- const {
233
- limit = 10,
234
- minSimilarity = 0.5
235
- } = options
236
-
237
- try {
238
- // Create embedding for the query
239
- const queryEmbedding = await this.embedder(query, {
240
- pooling: 'mean',
241
- normalize: true
242
- })
243
-
244
- const queryVector = Array.from(queryEmbedding.data)
245
-
246
- // Calculate similarities with all stored vectors
247
- const similarities = []
248
- for (const [id, vector] of this.vectors) {
249
- const similarity = this.cosineSimilarity(queryVector, vector.embedding)
250
- if (similarity >= minSimilarity) {
251
- similarities.push({
252
- id: id,
253
- similarity: similarity,
254
- metadata: vector.metadata
255
- })
256
- }
257
- }
258
-
259
- // Sort by similarity and return top results
260
- return similarities
261
- .sort((a, b) => b.similarity - a.similarity)
262
- .slice(0, limit)
263
- } catch (error) {
264
- console.error('❌ Search failed:', error)
265
- return []
92
+ dotProduct += vecA[i] * vecB[i];
93
+ normA += vecA[i] * vecA[i];
94
+ normB += vecB[i] * vecB[i];
266
95
  }
267
- }
268
-
269
- /**
270
- * Find chunks by semantic type
271
- */
272
- async findByType(semanticType, limit = 10) {
273
- return this.findSimilar(`Type: ${semanticType}`, {
274
- limit,
275
- minSimilarity: 0.7
276
- })
277
- }
278
-
279
- /**
280
- * Find chunks by business domain
281
- */
282
- async findByDomain(domain, limit = 10) {
283
- return this.findSimilar(`Domain: ${domain}`, {
284
- limit,
285
- minSimilarity: 0.6
286
- })
287
- }
288
-
289
- /**
290
- * Find chunks by technical pattern
291
- */
292
- async findByPattern(pattern, limit = 10) {
293
- return this.findSimilar(`Patterns: ${pattern}`, {
294
- limit,
295
- minSimilarity: 0.6
296
- })
297
- }
298
-
299
- /**
300
- * Get collection statistics
301
- */
302
- async getStats() {
303
- return {
304
- totalChunks: this.vectors.size,
305
- collectionName: this.collectionName,
306
- modelName: this.modelName
307
- }
308
- }
309
-
310
- /**
311
- * Clear all stored chunks
312
- */
313
- async clear() {
314
- this.vectors.clear()
315
- console.log('✅ Cleared in-memory vector store')
316
- }
317
-
318
- /**
319
- * Add/update a single chunk (legacy compatibility)
320
- */
321
- async upsert(id, embedding, metadata) {
322
- this.vectors.set(id, {
323
- embedding: embedding,
324
- metadata: metadata
325
- })
96
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
326
97
  }
327
98
  }
328
-
329
- export default SimpleVectorStore
package/package.json CHANGED
@@ -1,24 +1,18 @@
1
1
  {
2
2
  "name": "cntx-ui",
3
3
  "type": "module",
4
- "version": "2.0.15",
5
- "description": "File context management tool with web UI and MCP server for AI development workflows - bundle project files for LLM consumption",
4
+ "version": "3.0.1",
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
- "ai-development",
7
+ "repository-intelligence",
8
+ "semantic-code-search",
9
+ "ai-agent-context",
8
10
  "mcp-server",
9
- "file-bundling",
10
- "context-management",
11
- "llm-tools",
12
- "claude-desktop",
13
- "model-context-protocol",
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,14 +24,13 @@
30
24
  "cntx-ui": "./bin/cntx-ui.js"
31
25
  },
32
26
  "files": [
33
- "bin/cntx-ui.js",
27
+ "bin/",
34
28
  "server.js",
35
29
  "lib/",
36
- "README.md",
30
+ "templates/",
37
31
  "web/dist/",
38
- "web/README.md",
39
- "mcp-config-example.json",
40
- "templates/"
32
+ "README.md",
33
+ "VISION.md"
41
34
  ],
42
35
  "engines": {
43
36
  "node": ">=18.0.0"
@@ -45,19 +38,13 @@
45
38
  "scripts": {
46
39
  "dev": "node server.js",
47
40
  "build": "cd web && npm install && npm run build",
48
- "build:web": "cd web && npm install && npm run build",
49
- "dev:web": "cd web && npm run dev",
50
- "prebuild": "npm run build:web",
51
- "prepublishOnly": "npm run build:web",
52
- "prepack": "npm run build:web",
53
- "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"
54
43
  },
55
44
  "dependencies": {
56
- "@radix-ui/react-tooltip": "^1.2.7",
57
45
  "@xenova/transformers": "^2.17.2",
58
- "chromadb": "^1.10.5",
59
- "glob": "^8.1.0",
60
- "recharts": "^3.0.2",
46
+ "better-sqlite3": "^12.2.0",
47
+ "glob": "^9.0.0",
61
48
  "tree-sitter": "^0.21.1",
62
49
  "tree-sitter-javascript": "^0.23.1",
63
50
  "tree-sitter-typescript": "^0.23.2",