cntx-ui 2.0.13 → 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/bin/cntx-ui.js +137 -55
  2. package/lib/agent-runtime.js +1480 -0
  3. package/lib/agent-tools.js +368 -0
  4. package/lib/api-router.js +978 -0
  5. package/lib/bundle-manager.js +471 -0
  6. package/lib/configuration-manager.js +725 -0
  7. package/lib/file-system-manager.js +472 -0
  8. package/lib/heuristics-manager.js +425 -0
  9. package/lib/mcp-server.js +1054 -1
  10. package/lib/semantic-splitter.js +7 -14
  11. package/lib/simple-vector-store.js +329 -0
  12. package/lib/websocket-manager.js +470 -0
  13. package/package.json +10 -3
  14. package/server.js +662 -1933
  15. package/templates/activities/README.md +67 -0
  16. package/templates/activities/activities/create-project-bundles/README.md +83 -0
  17. package/templates/activities/activities/create-project-bundles/notes.md +102 -0
  18. package/templates/activities/activities/create-project-bundles/progress.md +63 -0
  19. package/templates/activities/activities/create-project-bundles/tasks.md +39 -0
  20. package/templates/activities/activities.json +219 -0
  21. package/templates/activities/lib/.markdownlint.jsonc +18 -0
  22. package/templates/activities/lib/create-activity.mdc +63 -0
  23. package/templates/activities/lib/generate-tasks.mdc +64 -0
  24. package/templates/activities/lib/process-task-list.mdc +52 -0
  25. package/templates/agent-config.yaml +78 -0
  26. package/templates/agent-instructions.md +218 -0
  27. package/templates/agent-rules/capabilities/activities-system.md +147 -0
  28. package/templates/agent-rules/capabilities/bundle-system.md +131 -0
  29. package/templates/agent-rules/capabilities/vector-search.md +135 -0
  30. package/templates/agent-rules/core/codebase-navigation.md +91 -0
  31. package/templates/agent-rules/core/performance-hierarchy.md +48 -0
  32. package/templates/agent-rules/core/response-formatting.md +120 -0
  33. package/templates/agent-rules/project-specific/architecture.md +145 -0
  34. package/templates/config.json +76 -0
  35. package/templates/hidden-files.json +14 -0
  36. package/web/dist/assets/heuristics-manager-browser-DfonOP5I.js +1 -0
  37. package/web/dist/assets/index-dF3qg-y_.js +2486 -0
  38. package/web/dist/assets/index-h5FGSg_P.css +1 -0
  39. package/web/dist/cntx-ui.svg +18 -0
  40. package/web/dist/index.html +25 -8
  41. package/lib/semantic-integration.js +0 -441
  42. package/web/dist/assets/index-Ci1Q-YrQ.js +0 -611
  43. package/web/dist/assets/index-IUp4q_fr.css +0 -1
  44. package/web/dist/vite.svg +0 -21
@@ -7,6 +7,7 @@
7
7
  import { readFileSync, existsSync } from 'fs'
8
8
  import { extname, basename, dirname, join } from 'path'
9
9
  import glob from 'glob'
10
+ import HeuristicsManager from './heuristics-manager.js'
10
11
 
11
12
  export default class SemanticSplitter {
12
13
  constructor(options = {}) {
@@ -17,6 +18,9 @@ export default class SemanticSplitter {
17
18
  minFunctionSize: 50, // Skip tiny functions
18
19
  ...options
19
20
  }
21
+
22
+ // Initialize heuristics manager
23
+ this.heuristicsManager = new HeuristicsManager()
20
24
  }
21
25
 
22
26
  /**
@@ -414,6 +418,7 @@ export default class SemanticSplitter {
414
418
  */
415
419
  createTypeChunk(type, allImports) {
416
420
  let chunkCode = ''
421
+ const includedImports = new Set()
417
422
 
418
423
  // Add relevant imports if any
419
424
  const fileImports = allImports.filter(imp => imp.filePath === type.filePath)
@@ -523,22 +528,10 @@ export default class SemanticSplitter {
523
528
  }
524
529
 
525
530
  /**
526
- * Determine function purpose
531
+ * Determine function purpose using heuristics configuration
527
532
  */
528
533
  determinePurpose(func) {
529
- const name = func.name.toLowerCase()
530
-
531
- if (func.type === 'react_component') return 'React component'
532
- if (name.startsWith('use') && func.type === 'function') return 'React hook'
533
- if (name.includes('api') || name.includes('endpoint')) return 'API handler'
534
- if (name.includes('get') || name.includes('fetch')) return 'Data retrieval'
535
- if (name.includes('create') || name.includes('add')) return 'Data creation'
536
- if (name.includes('update') || name.includes('edit')) return 'Data modification'
537
- if (name.includes('delete') || name.includes('remove')) return 'Data deletion'
538
- if (name.includes('validate') || name.includes('check')) return 'Validation'
539
- if (name.includes('parse') || name.includes('format')) return 'Data processing'
540
-
541
- return 'Utility function'
534
+ return this.heuristicsManager.determinePurpose(func)
542
535
  }
543
536
 
544
537
  /**
@@ -0,0 +1,329 @@
1
+ /**
2
+ * Simple In-Memory Vector Store for cntx-ui
3
+ * Free, local embeddings without external dependencies
4
+ */
5
+
6
+ import { pipeline } from '@xenova/transformers'
7
+
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 }
14
+ }
15
+
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
+ async init() {
31
+ return this.initialize()
32
+ }
33
+
34
+ /**
35
+ * Generate embedding for a single text
36
+ */
37
+ 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
+ }
53
+ }
54
+
55
+ /**
56
+ * Create embeddings for semantic chunks
57
+ */
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
+ }
162
+
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`)
172
+ }
173
+
174
+ /**
175
+ * Store chunks that already have embeddings (e.g., from cache)
176
+ */
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++
200
+ }
201
+ }
202
+
203
+ console.log(`✅ Stored ${storedCount} precomputed chunks in memory vector store`)
204
+ return storedCount
205
+ }
206
+
207
+ /**
208
+ * Calculate cosine similarity between two vectors
209
+ */
210
+ cosineSimilarity(vecA, vecB) {
211
+ let dotProduct = 0
212
+ let normA = 0
213
+ let normB = 0
214
+
215
+ 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 []
266
+ }
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
+ })
326
+ }
327
+ }
328
+
329
+ export default SimpleVectorStore