cntx-ui 3.0.2 → 3.0.4

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.
@@ -3,7 +3,7 @@
3
3
  * Handles file operations, pattern matching, and directory traversal
4
4
  */
5
5
 
6
- import { readdirSync, statSync, existsSync, watch } from 'fs';
6
+ import { readdirSync, readFileSync, statSync, existsSync, watch } from 'fs';
7
7
  import { join, relative, extname, basename, dirname } from 'path';
8
8
 
9
9
  export default class FileSystemManager {
@@ -127,7 +127,7 @@ export default class HeuristicsManager {
127
127
  const domains = new Set();
128
128
  const name = func.name.toLowerCase();
129
129
  const path = (func.pathParts || []).join('/').toLowerCase();
130
- const imports = func.includes?.imports || [];
130
+ const imports = (func.includes?.imports || []).filter(i => typeof i === 'string');
131
131
 
132
132
  // Path-based domains
133
133
  if (path.includes('auth')) domains.add('authentication');
@@ -330,7 +330,9 @@ export default class HeuristicsManager {
330
330
  if (condition.includes('chunk.imports.includes(')) {
331
331
  const importMatch = condition.match(/chunk\.imports\.includes\(['"]([^'"]+)['"]\)/)
332
332
  if (importMatch && func.includes?.imports) {
333
- return func.includes.imports.some(imp => imp.includes(importMatch[1]))
333
+ return func.includes.imports
334
+ .filter(i => typeof i === 'string')
335
+ .some(imp => imp.includes(importMatch[1]))
334
336
  }
335
337
  }
336
338
 
@@ -6,7 +6,6 @@
6
6
 
7
7
  import { readFileSync, existsSync } from 'fs'
8
8
  import { join, extname } from 'path'
9
- import glob from 'glob'
10
9
  import Parser from 'tree-sitter'
11
10
  import JavaScript from 'tree-sitter-javascript'
12
11
  import TypeScript from 'tree-sitter-typescript'
@@ -45,18 +44,14 @@ export default class SemanticSplitter {
45
44
 
46
45
  /**
47
46
  * Main entry point - extract semantic chunks from project
47
+ * Now accepts a pre-filtered list of files from FileSystemManager
48
48
  */
49
- async extractSemanticChunks(projectPath, patterns = ['**/*.{js,jsx,ts,tsx,mjs}'], bundleConfig = null) {
50
- console.log('🔪 Starting surgical semantic splitting via tree-sitter...');
51
- console.log(`📂 Project path: ${projectPath}`);
52
- console.log(`📋 Patterns: ${patterns}`);
49
+ async extractSemanticChunks(projectPath, files = [], bundleConfig = null) {
50
+ console.log('🔪 Starting surgical semantic splitting via tree-sitter...')
51
+ console.log(`📂 Project path: ${projectPath}`)
53
52
 
54
- this.bundleConfig = bundleConfig;
55
- const files = this.findFiles(projectPath, patterns);
56
- console.log(`📁 Found ${files.length} files to split`);
57
- if (files.length > 0) {
58
- console.log('📄 Sample files:', files.slice(0, 5));
59
- }
53
+ this.bundleConfig = bundleConfig
54
+ console.log(`📁 Processing ${files.length} filtered files`)
60
55
 
61
56
  const allChunks = []
62
57
 
@@ -74,48 +69,32 @@ export default class SemanticSplitter {
74
69
  summary: {
75
70
  totalFiles: files.length,
76
71
  totalChunks: allChunks.length,
77
- averageSize: allChunks.reduce((sum, c) => sum + c.code.length, 0) / allChunks.length
72
+ averageSize: allChunks.length > 0 ? allChunks.reduce((sum, c) => sum + c.code.length, 0) / allChunks.length : 0
78
73
  },
79
74
  chunks: allChunks
80
75
  }
81
76
  }
82
77
 
83
- findFiles(projectPath, patterns) {
84
- const files = []
85
- for (const pattern of patterns) {
86
- const matches = glob.sync(pattern, {
87
- cwd: projectPath,
88
- ignore: ['node_modules/**', 'dist/**', '.git/**', '*.test.*', '*.spec.*']
89
- })
90
- files.push(...matches)
91
- }
92
- return [...new Set(files)]
93
- }
94
-
95
78
  processFile(relativePath, projectPath) {
96
- const fullPath = join(projectPath, relativePath);
97
- if (!existsSync(fullPath)) return [];
79
+ const fullPath = join(projectPath, relativePath)
80
+ if (!existsSync(fullPath)) return []
98
81
 
99
- // console.log(` 📄 Processing: ${relativePath}`);
100
- const content = readFileSync(fullPath, 'utf8');
101
- const parser = this.getParser(relativePath);
102
- const tree = parser.parse(content);
103
- const root = tree.rootNode;
82
+ const content = readFileSync(fullPath, 'utf8')
83
+ const parser = this.getParser(relativePath)
84
+ const tree = parser.parse(content)
85
+ const root = tree.rootNode
104
86
 
105
87
  const elements = {
106
88
  functions: [],
107
89
  types: [],
108
90
  imports: this.extractImports(root, content, relativePath)
109
- };
91
+ }
110
92
 
111
93
  // Traverse AST for functions and types
112
- this.traverse(root, content, relativePath, elements);
94
+ this.traverse(root, content, relativePath, elements)
113
95
 
114
- const chunks = this.createChunks(elements, content, relativePath);
115
- // if (chunks.length > 0) {
116
- // console.log(` ✅ Found ${chunks.length} chunks in ${relativePath}`);
117
- // }
118
- return chunks;
96
+ // Create chunks from elements
97
+ return this.createChunks(elements, content, relativePath)
119
98
  }
120
99
 
121
100
  traverse(node, content, filePath, elements) {
@@ -235,10 +214,6 @@ export default class SemanticSplitter {
235
214
  const technicalPatterns = this.heuristicsManager.inferTechnicalPatterns(heuristicContext);
236
215
  const tags = this.generateTags(func);
237
216
 
238
- // if (businessDomain.length > 0) {
239
- // console.log(` 💎 ${func.name}: Domains: [${businessDomain}], Patterns: [${technicalPatterns}]`);
240
- // }
241
-
242
217
  let chunkCode = '';
243
218
  if (this.options.includeContext) {
244
219
  const relevantImports = elements.imports
@@ -54,20 +54,30 @@ export default class SimpleVectorStore {
54
54
  const queryEmbedding = await this.generateEmbedding(query);
55
55
 
56
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
57
  const rows = this.db.db.prepare('SELECT chunk_id, embedding FROM vector_embeddings WHERE model_name = ?').all(this.modelName);
60
58
 
61
59
  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);
60
+ const batchSize = 100;
61
+
62
+ // Process in batches to prevent blocking the event loop
63
+ for (let i = 0; i < rows.length; i += batchSize) {
64
+ const batch = rows.slice(i, i + batchSize);
65
+
66
+ for (const row of batch) {
67
+ const embedding = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4);
68
+ const similarity = this.cosineSimilarity(queryEmbedding, embedding);
69
+
70
+ if (similarity >= threshold) {
71
+ results.push({
72
+ chunkId: row.chunk_id,
73
+ similarity
74
+ });
75
+ }
76
+ }
65
77
 
66
- if (similarity >= threshold) {
67
- results.push({
68
- chunkId: row.chunk_id,
69
- similarity
70
- });
78
+ // Give other tasks a chance to run
79
+ if (i + batchSize < rows.length) {
80
+ await new Promise(resolve => setImmediate(resolve));
71
81
  }
72
82
  }
73
83
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "cntx-ui",
3
3
  "type": "module",
4
- "version": "3.0.2",
4
+ "version": "3.0.4",
5
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
7
  "repository-intelligence",
package/server.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { createServer } from 'http';
7
- import { join, dirname } from 'path';
7
+ import { join, dirname, relative, extname } from 'path';
8
8
  import { fileURLToPath } from 'url';
9
9
  import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, cpSync } from 'fs';
10
10
  import * as fs from 'fs';
@@ -435,13 +435,17 @@ export class CntxServer {
435
435
 
436
436
  // 2. Perform fresh analysis if DB is empty
437
437
  try {
438
- const patterns = ['**/*.{js,jsx,ts,tsx,mjs}'];
438
+ const supportedExtensions = ['.js', '.jsx', '.ts', '.tsx', '.mjs'];
439
+ const files = this.fileSystemManager.getAllFiles()
440
+ .filter(f => supportedExtensions.includes(extname(f).toLowerCase()))
441
+ .map(f => relative(this.CWD, f));
442
+
439
443
  let bundleConfig = null;
440
444
  if (existsSync(this.configManager.CONFIG_FILE)) {
441
445
  bundleConfig = JSON.parse(readFileSync(this.configManager.CONFIG_FILE, 'utf8'));
442
446
  }
443
447
 
444
- this.semanticCache = await this.semanticSplitter.extractSemanticChunks(this.CWD, patterns, bundleConfig);
448
+ this.semanticCache = await this.semanticSplitter.extractSemanticChunks(this.CWD, files, bundleConfig);
445
449
  this.lastSemanticAnalysis = Date.now();
446
450
 
447
451
  // 3. Persist chunks to SQLite immediately