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
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heuristics Manager - Centralized service for loading and applying heuristics
|
|
3
|
+
* Manages configuration-based code categorization logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, existsSync, watchFile } from 'fs'
|
|
7
|
+
import { join } from 'path'
|
|
8
|
+
|
|
9
|
+
export default class HeuristicsManager {
|
|
10
|
+
constructor(configPath = './heuristics-config.json') {
|
|
11
|
+
this.configPath = configPath
|
|
12
|
+
this.config = null
|
|
13
|
+
this.cache = new Map()
|
|
14
|
+
this.isWatching = false
|
|
15
|
+
this.lastLoaded = null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Load heuristics configuration from file
|
|
20
|
+
*/
|
|
21
|
+
loadConfig() {
|
|
22
|
+
try {
|
|
23
|
+
if (!existsSync(this.configPath)) {
|
|
24
|
+
console.warn(`Heuristics config not found at ${this.configPath}, using fallback`)
|
|
25
|
+
this.config = this.getFallbackConfig()
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const configContent = readFileSync(this.configPath, 'utf8')
|
|
30
|
+
const newConfig = JSON.parse(configContent)
|
|
31
|
+
|
|
32
|
+
// Validate config structure
|
|
33
|
+
this.validateConfig(newConfig)
|
|
34
|
+
|
|
35
|
+
this.config = newConfig
|
|
36
|
+
this.lastLoaded = Date.now()
|
|
37
|
+
this.cache.clear() // Clear cache when config changes
|
|
38
|
+
|
|
39
|
+
// Set up file watching if not already watching
|
|
40
|
+
if (!this.isWatching) {
|
|
41
|
+
this.setupFileWatcher()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('✅ Heuristics configuration loaded successfully')
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('❌ Failed to load heuristics config:', error.message)
|
|
47
|
+
console.log('📦 Falling back to hardcoded heuristics')
|
|
48
|
+
this.config = this.getFallbackConfig()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Validate heuristics configuration structure
|
|
54
|
+
*/
|
|
55
|
+
validateConfig(config) {
|
|
56
|
+
const required = ['purposeHeuristics', 'bundleHeuristics', 'semanticTypeMapping']
|
|
57
|
+
|
|
58
|
+
for (const field of required) {
|
|
59
|
+
if (!config[field]) {
|
|
60
|
+
throw new Error(`Missing required field: ${field}`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Validate purpose heuristics structure
|
|
65
|
+
if (!config.purposeHeuristics.patterns || !config.purposeHeuristics.fallback) {
|
|
66
|
+
throw new Error('Invalid purposeHeuristics structure')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate bundle heuristics structure
|
|
70
|
+
if (!config.bundleHeuristics.patterns || !config.bundleHeuristics.fallback) {
|
|
71
|
+
throw new Error('Invalid bundleHeuristics structure')
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Set up file watcher for config changes
|
|
77
|
+
*/
|
|
78
|
+
setupFileWatcher() {
|
|
79
|
+
if (!existsSync(this.configPath)) return
|
|
80
|
+
|
|
81
|
+
watchFile(this.configPath, (curr, prev) => {
|
|
82
|
+
if (curr.mtime !== prev.mtime) {
|
|
83
|
+
console.log('📝 Heuristics config file changed, reloading...')
|
|
84
|
+
this.loadConfig()
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
this.isWatching = true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get configuration, loading if necessary
|
|
93
|
+
*/
|
|
94
|
+
getConfig() {
|
|
95
|
+
if (!this.config) {
|
|
96
|
+
this.loadConfig()
|
|
97
|
+
}
|
|
98
|
+
return this.config
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Determine function purpose using configured heuristics
|
|
103
|
+
*/
|
|
104
|
+
determinePurpose(func) {
|
|
105
|
+
const config = this.getConfig()
|
|
106
|
+
const name = func.name.toLowerCase()
|
|
107
|
+
|
|
108
|
+
// Check each purpose pattern
|
|
109
|
+
for (const [patternName, pattern] of Object.entries(config.purposeHeuristics.patterns)) {
|
|
110
|
+
if (this.evaluateConditions(pattern.conditions, { func, name })) {
|
|
111
|
+
return pattern.purpose
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Return fallback
|
|
116
|
+
return config.purposeHeuristics.fallback.purpose
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Suggest bundles for file using configured heuristics
|
|
121
|
+
*/
|
|
122
|
+
suggestBundlesForFile(filePath) {
|
|
123
|
+
const config = this.getConfig()
|
|
124
|
+
const fileName = filePath.toLowerCase()
|
|
125
|
+
const pathParts = fileName.split('/')
|
|
126
|
+
const suggestions = []
|
|
127
|
+
|
|
128
|
+
// Check each bundle pattern
|
|
129
|
+
for (const [patternName, pattern] of Object.entries(config.bundleHeuristics.patterns)) {
|
|
130
|
+
if (this.evaluateConditions(pattern.conditions, { fileName, filePath, pathParts })) {
|
|
131
|
+
suggestions.push(pattern.bundle)
|
|
132
|
+
|
|
133
|
+
// Check sub-patterns
|
|
134
|
+
if (pattern.subPatterns) {
|
|
135
|
+
for (const [subName, subPattern] of Object.entries(pattern.subPatterns)) {
|
|
136
|
+
if (this.evaluateConditions(subPattern.conditions, { fileName, filePath, pathParts })) {
|
|
137
|
+
suggestions.push(subPattern.bundle)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Apply fallback logic if no suggestions
|
|
145
|
+
if (suggestions.length === 0) {
|
|
146
|
+
const fallback = config.bundleHeuristics.fallback
|
|
147
|
+
|
|
148
|
+
if (fallback.webFallback && this.evaluateConditions(fallback.webFallback.conditions, { fileName, filePath, pathParts })) {
|
|
149
|
+
suggestions.push(fallback.webFallback.bundle)
|
|
150
|
+
} else if (fallback.defaultFallback) {
|
|
151
|
+
suggestions.push(...fallback.defaultFallback.bundles)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [...new Set(suggestions)] // Remove duplicates
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get semantic type cluster mapping
|
|
160
|
+
*/
|
|
161
|
+
getSemanticTypeMapping() {
|
|
162
|
+
const config = this.getConfig()
|
|
163
|
+
const mapping = {}
|
|
164
|
+
|
|
165
|
+
for (const [clusterName, cluster] of Object.entries(config.semanticTypeMapping.clusters)) {
|
|
166
|
+
for (const type of cluster.types) {
|
|
167
|
+
mapping[type] = cluster.clusterId
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return mapping
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Evaluate condition strings against context
|
|
176
|
+
*/
|
|
177
|
+
evaluateConditions(conditions, context) {
|
|
178
|
+
if (!Array.isArray(conditions)) {
|
|
179
|
+
conditions = [conditions]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// For React hook pattern, we need AND logic (all conditions must be true)
|
|
183
|
+
// For other patterns, we use OR logic (any condition can be true)
|
|
184
|
+
const needsAndLogic = this.requiresAndLogic(conditions)
|
|
185
|
+
|
|
186
|
+
if (needsAndLogic) {
|
|
187
|
+
return conditions.every(condition => {
|
|
188
|
+
try {
|
|
189
|
+
return this.evaluateCondition(condition, context)
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.warn(`Failed to evaluate condition: ${condition}`, error)
|
|
192
|
+
return false
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
} else {
|
|
196
|
+
return conditions.some(condition => {
|
|
197
|
+
try {
|
|
198
|
+
return this.evaluateCondition(condition, context)
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.warn(`Failed to evaluate condition: ${condition}`, error)
|
|
201
|
+
return false
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Determine if conditions require AND logic vs OR logic
|
|
209
|
+
*/
|
|
210
|
+
requiresAndLogic(conditions) {
|
|
211
|
+
// React hook pattern specifically needs AND logic
|
|
212
|
+
if (conditions.length === 2 &&
|
|
213
|
+
conditions.some(c => c.includes('name.startsWith')) &&
|
|
214
|
+
conditions.some(c => c.includes('func.type'))) {
|
|
215
|
+
return true
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Frontend pattern needs AND logic for web + src
|
|
219
|
+
if (conditions.length === 2 &&
|
|
220
|
+
conditions.some(c => c.includes("pathParts.includes('web')")) &&
|
|
221
|
+
conditions.some(c => c.includes("pathParts.includes('src')"))) {
|
|
222
|
+
return true
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Default to OR logic for other patterns
|
|
226
|
+
return false
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Evaluate a single condition
|
|
231
|
+
*/
|
|
232
|
+
evaluateCondition(condition, context) {
|
|
233
|
+
const { func, name, fileName, filePath, pathParts } = context
|
|
234
|
+
|
|
235
|
+
// Handle function type conditions
|
|
236
|
+
if (condition.includes('func.type ===')) {
|
|
237
|
+
const typeMatch = condition.match(/func\.type === ['"]([^'"]+)['"]/)
|
|
238
|
+
if (typeMatch && func) {
|
|
239
|
+
return func.type === typeMatch[1]
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Handle name-based conditions
|
|
244
|
+
if (condition.includes('name.startsWith(')) {
|
|
245
|
+
const prefixMatch = condition.match(/name\.startsWith\(['"]([^'"]+)['"]\)/)
|
|
246
|
+
if (prefixMatch && name) {
|
|
247
|
+
return name.startsWith(prefixMatch[1])
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (condition.includes('name.includes(')) {
|
|
252
|
+
const includesMatch = condition.match(/name\.includes\(['"]([^'"]+)['"]\)/)
|
|
253
|
+
if (includesMatch && name) {
|
|
254
|
+
return name.includes(includesMatch[1])
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Handle fileName conditions
|
|
259
|
+
if (condition.includes('fileName.includes(')) {
|
|
260
|
+
const includesMatch = condition.match(/fileName\.includes\(['"]([^'"]+)['"]\)/)
|
|
261
|
+
if (includesMatch && fileName) {
|
|
262
|
+
return fileName.includes(includesMatch[1])
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (condition.includes('fileName.endsWith(')) {
|
|
267
|
+
const endsWithMatch = condition.match(/fileName\.endsWith\(['"]([^'"]+)['"]\)/)
|
|
268
|
+
if (endsWithMatch && fileName) {
|
|
269
|
+
return fileName.endsWith(endsWithMatch[1])
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Handle pathParts conditions
|
|
274
|
+
if (condition.includes('pathParts.includes(')) {
|
|
275
|
+
const includesMatch = condition.match(/pathParts\.includes\(['"]([^'"]+)['"]\)/)
|
|
276
|
+
if (includesMatch && pathParts) {
|
|
277
|
+
return pathParts.includes(includesMatch[1])
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return false
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Fallback configuration for when config file is unavailable
|
|
286
|
+
*/
|
|
287
|
+
getFallbackConfig() {
|
|
288
|
+
return {
|
|
289
|
+
version: "1.0.0",
|
|
290
|
+
purposeHeuristics: {
|
|
291
|
+
patterns: {
|
|
292
|
+
reactComponent: {
|
|
293
|
+
conditions: ["func.type === 'react_component'"],
|
|
294
|
+
purpose: "React component",
|
|
295
|
+
confidence: 0.95
|
|
296
|
+
},
|
|
297
|
+
reactHook: {
|
|
298
|
+
conditions: ["name.startsWith('use')", "func.type === 'function'"],
|
|
299
|
+
purpose: "React hook",
|
|
300
|
+
confidence: 0.9
|
|
301
|
+
},
|
|
302
|
+
apiHandler: {
|
|
303
|
+
conditions: ["name.includes('api')", "name.includes('endpoint')"],
|
|
304
|
+
purpose: "API handler",
|
|
305
|
+
confidence: 0.85
|
|
306
|
+
},
|
|
307
|
+
dataRetrieval: {
|
|
308
|
+
conditions: ["name.includes('get')", "name.includes('fetch')"],
|
|
309
|
+
purpose: "Data retrieval",
|
|
310
|
+
confidence: 0.8
|
|
311
|
+
},
|
|
312
|
+
dataCreation: {
|
|
313
|
+
conditions: ["name.includes('create')", "name.includes('add')"],
|
|
314
|
+
purpose: "Data creation",
|
|
315
|
+
confidence: 0.8
|
|
316
|
+
},
|
|
317
|
+
dataModification: {
|
|
318
|
+
conditions: ["name.includes('update')", "name.includes('edit')"],
|
|
319
|
+
purpose: "Data modification",
|
|
320
|
+
confidence: 0.8
|
|
321
|
+
},
|
|
322
|
+
dataDeletion: {
|
|
323
|
+
conditions: ["name.includes('delete')", "name.includes('remove')"],
|
|
324
|
+
purpose: "Data deletion",
|
|
325
|
+
confidence: 0.8
|
|
326
|
+
},
|
|
327
|
+
validation: {
|
|
328
|
+
conditions: ["name.includes('validate')", "name.includes('check')"],
|
|
329
|
+
purpose: "Validation",
|
|
330
|
+
confidence: 0.75
|
|
331
|
+
},
|
|
332
|
+
dataProcessing: {
|
|
333
|
+
conditions: ["name.includes('parse')", "name.includes('format')"],
|
|
334
|
+
purpose: "Data processing",
|
|
335
|
+
confidence: 0.75
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
fallback: {
|
|
339
|
+
purpose: "Utility function",
|
|
340
|
+
confidence: 0.5
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
bundleHeuristics: {
|
|
344
|
+
patterns: {
|
|
345
|
+
frontend: {
|
|
346
|
+
conditions: ["pathParts.includes('web')", "pathParts.includes('src')"],
|
|
347
|
+
bundle: "frontend",
|
|
348
|
+
confidence: 0.8,
|
|
349
|
+
subPatterns: {
|
|
350
|
+
uiComponents: {
|
|
351
|
+
conditions: ["pathParts.includes('components')"],
|
|
352
|
+
bundle: "ui-components",
|
|
353
|
+
confidence: 0.9
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
server: {
|
|
358
|
+
conditions: ["fileName.includes('server')", "fileName.includes('api')", "pathParts.includes('bin')"],
|
|
359
|
+
bundle: "server",
|
|
360
|
+
confidence: 0.85
|
|
361
|
+
},
|
|
362
|
+
configuration: {
|
|
363
|
+
conditions: ["fileName.includes('config')", "fileName.includes('setup')", "fileName.endsWith('.json')", "fileName.endsWith('.sh')", "fileName.includes('package')"],
|
|
364
|
+
bundle: "config",
|
|
365
|
+
confidence: 0.9
|
|
366
|
+
},
|
|
367
|
+
documentation: {
|
|
368
|
+
conditions: ["fileName.endsWith('.md')", "fileName.includes('doc')", "fileName.includes('readme')"],
|
|
369
|
+
bundle: "docs",
|
|
370
|
+
confidence: 0.95
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
fallback: {
|
|
374
|
+
webFallback: {
|
|
375
|
+
conditions: ["pathParts.includes('web')"],
|
|
376
|
+
bundle: "frontend",
|
|
377
|
+
confidence: 0.6
|
|
378
|
+
},
|
|
379
|
+
defaultFallback: {
|
|
380
|
+
bundles: ["server", "config"],
|
|
381
|
+
confidence: 0.4
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
semanticTypeMapping: {
|
|
386
|
+
clusters: {
|
|
387
|
+
businessLogic: { types: ["business_logic", "algorithm"], clusterId: 0 },
|
|
388
|
+
dataLayer: { types: ["data_processing", "database"], clusterId: 1 },
|
|
389
|
+
apiLayer: { types: ["api_integration", "middleware", "routing"], clusterId: 2 },
|
|
390
|
+
uiLayer: { types: ["ui_component", "page_component", "layout_component", "hook"], clusterId: 3 },
|
|
391
|
+
utilities: { types: ["utility", "configuration", "function", "type_definition"], clusterId: 4 },
|
|
392
|
+
testing: { types: ["testing", "documentation", "monitoring"], clusterId: 5 },
|
|
393
|
+
infrastructure: { types: ["error_handling", "performance", "security"], clusterId: 6 },
|
|
394
|
+
unknown: { types: ["unknown"], clusterId: 7 }
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Update configuration (for API endpoints)
|
|
402
|
+
*/
|
|
403
|
+
async updateConfig(newConfig) {
|
|
404
|
+
this.validateConfig(newConfig)
|
|
405
|
+
|
|
406
|
+
// TODO: Implement file writing when API endpoints are created
|
|
407
|
+
// For now, just update in-memory config
|
|
408
|
+
this.config = newConfig
|
|
409
|
+
this.cache.clear()
|
|
410
|
+
|
|
411
|
+
return true
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Get performance metrics
|
|
416
|
+
*/
|
|
417
|
+
getPerformanceMetrics() {
|
|
418
|
+
// TODO: Implement performance tracking
|
|
419
|
+
return {
|
|
420
|
+
totalEvaluations: 0,
|
|
421
|
+
accuracyScore: 0.0,
|
|
422
|
+
lastUpdated: this.lastLoaded
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|