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
@@ -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
+ }