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.
- package/bin/cntx-ui.js +137 -55
- package/lib/agent-runtime.js +1480 -0
- package/lib/agent-tools.js +368 -0
- package/lib/api-router.js +978 -0
- package/lib/bundle-manager.js +471 -0
- package/lib/configuration-manager.js +725 -0
- package/lib/file-system-manager.js +472 -0
- package/lib/heuristics-manager.js +425 -0
- package/lib/mcp-server.js +1054 -1
- package/lib/semantic-splitter.js +7 -14
- package/lib/simple-vector-store.js +329 -0
- package/lib/websocket-manager.js +470 -0
- package/package.json +10 -3
- package/server.js +662 -1933
- package/templates/activities/README.md +67 -0
- package/templates/activities/activities/create-project-bundles/README.md +83 -0
- package/templates/activities/activities/create-project-bundles/notes.md +102 -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 +78 -0
- package/templates/agent-instructions.md +218 -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/heuristics-manager-browser-DfonOP5I.js +1 -0
- package/web/dist/assets/index-dF3qg-y_.js +2486 -0
- package/web/dist/assets/index-h5FGSg_P.css +1 -0
- package/web/dist/cntx-ui.svg +18 -0
- package/web/dist/index.html +25 -8
- package/lib/semantic-integration.js +0 -441
- 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
package/lib/semantic-splitter.js
CHANGED
|
@@ -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
|
-
|
|
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
|