cntx-ui 2.0.11 → 2.0.13
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/lib/function-level-chunker.js +406 -0
- package/lib/semantic-integration.js +441 -0
- package/lib/semantic-splitter.js +595 -0
- package/lib/treesitter-semantic-chunker.js +1485 -0
- package/package.json +5 -1
- package/server.js +285 -45
- package/web/dist/assets/index-Ci1Q-YrQ.js +611 -0
- package/web/dist/assets/index-IUp4q_fr.css +1 -0
- package/web/dist/index.html +2 -2
- package/web/dist/vite.svg +21 -1
- package/web/dist/assets/index-AIbnyG7t.js +0 -531
- package/web/dist/assets/index-DJxMcuOy.css +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cntx-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.13",
|
|
5
5
|
"description": "File context management tool with web UI and MCP server for AI development workflows - bundle project files for LLM consumption",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai-development",
|
|
@@ -50,6 +50,10 @@
|
|
|
50
50
|
"test:local": "npm pack && npm install -g ./cntx-ui-2.0.0.tgz"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
+
"glob": "^8.1.0",
|
|
54
|
+
"tree-sitter": "^0.21.1",
|
|
55
|
+
"tree-sitter-javascript": "^0.23.1",
|
|
56
|
+
"tree-sitter-typescript": "^0.23.2",
|
|
53
57
|
"ws": "^8.13.0"
|
|
54
58
|
}
|
|
55
59
|
}
|
package/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import { WebSocketServer } from 'ws';
|
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { startMCPTransport } from './lib/mcp-transport.js';
|
|
8
|
+
import SemanticSplitter from './lib/semantic-splitter.js';
|
|
8
9
|
import { homedir } from 'os';
|
|
9
10
|
|
|
10
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -51,6 +52,16 @@ export class CntxServer {
|
|
|
51
52
|
userIgnorePatterns: [], // User-added ignore patterns
|
|
52
53
|
disabledSystemPatterns: [] // System patterns the user disabled
|
|
53
54
|
};
|
|
55
|
+
|
|
56
|
+
// Semantic splitting (parallel to bundle system)
|
|
57
|
+
this.semanticSplitter = new SemanticSplitter({
|
|
58
|
+
maxChunkSize: 2000,
|
|
59
|
+
includeContext: true,
|
|
60
|
+
groupRelated: true,
|
|
61
|
+
minFunctionSize: 50
|
|
62
|
+
});
|
|
63
|
+
this.semanticCache = null;
|
|
64
|
+
this.lastSemanticAnalysis = null;
|
|
54
65
|
}
|
|
55
66
|
|
|
56
67
|
init() {
|
|
@@ -225,12 +236,12 @@ export class CntxServer {
|
|
|
225
236
|
'**/.git/**',
|
|
226
237
|
'**/.svn/**',
|
|
227
238
|
'**/.hg/**',
|
|
228
|
-
|
|
239
|
+
|
|
229
240
|
// Dependencies
|
|
230
241
|
'**/node_modules/**',
|
|
231
242
|
'**/vendor/**',
|
|
232
243
|
'**/.pnp/**',
|
|
233
|
-
|
|
244
|
+
|
|
234
245
|
// Build outputs
|
|
235
246
|
'**/dist/**',
|
|
236
247
|
'**/build/**',
|
|
@@ -238,18 +249,18 @@ export class CntxServer {
|
|
|
238
249
|
'**/.next/**',
|
|
239
250
|
'**/.nuxt/**',
|
|
240
251
|
'**/target/**',
|
|
241
|
-
|
|
252
|
+
|
|
242
253
|
// Package files
|
|
243
254
|
'**/*.tgz',
|
|
244
255
|
'**/*.tar.gz',
|
|
245
256
|
'**/*.zip',
|
|
246
257
|
'**/*.rar',
|
|
247
258
|
'**/*.7z',
|
|
248
|
-
|
|
259
|
+
|
|
249
260
|
// Logs
|
|
250
261
|
'**/*.log',
|
|
251
262
|
'**/logs/**',
|
|
252
|
-
|
|
263
|
+
|
|
253
264
|
// Cache directories
|
|
254
265
|
'**/.cache/**',
|
|
255
266
|
'**/.parcel-cache/**',
|
|
@@ -257,30 +268,30 @@ export class CntxServer {
|
|
|
257
268
|
'**/coverage/**',
|
|
258
269
|
'**/.pytest_cache/**',
|
|
259
270
|
'**/__pycache__/**',
|
|
260
|
-
|
|
271
|
+
|
|
261
272
|
// IDE/Editor files
|
|
262
273
|
'**/.vscode/**',
|
|
263
274
|
'**/.idea/**',
|
|
264
275
|
'**/*.swp',
|
|
265
276
|
'**/*.swo',
|
|
266
277
|
'**/*~',
|
|
267
|
-
|
|
278
|
+
|
|
268
279
|
// OS files
|
|
269
280
|
'**/.DS_Store',
|
|
270
281
|
'**/Thumbs.db',
|
|
271
282
|
'**/desktop.ini',
|
|
272
|
-
|
|
283
|
+
|
|
273
284
|
// Environment files
|
|
274
285
|
'**/.env',
|
|
275
286
|
'**/.env.local',
|
|
276
287
|
'**/.env.*.local',
|
|
277
|
-
|
|
288
|
+
|
|
278
289
|
// Lock files
|
|
279
290
|
'**/package-lock.json',
|
|
280
291
|
'**/yarn.lock',
|
|
281
292
|
'**/pnpm-lock.yaml',
|
|
282
293
|
'**/Cargo.lock',
|
|
283
|
-
|
|
294
|
+
|
|
284
295
|
// cntx-ui specific
|
|
285
296
|
'**/.cntx/**'
|
|
286
297
|
];
|
|
@@ -591,7 +602,7 @@ Add your specific project rules and preferences below:
|
|
|
591
602
|
|
|
592
603
|
generateClaudeMdTemplate(projectInfo) {
|
|
593
604
|
const { name, description, type } = projectInfo;
|
|
594
|
-
|
|
605
|
+
|
|
595
606
|
let template = `# ${name}
|
|
596
607
|
|
|
597
608
|
${description ? `${description}\n\n` : ''}## Project Structure
|
|
@@ -802,6 +813,7 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
802
813
|
if (!this.shouldIgnoreFile(fullPath)) {
|
|
803
814
|
if (!this.isQuietMode) console.log(`File ${eventType}: ${filename}`);
|
|
804
815
|
this.markBundlesChanged(filename.replace(/\\\\/g, '/'));
|
|
816
|
+
this.invalidateSemanticCache(); // Invalidate semantic cache on file changes
|
|
805
817
|
this.broadcastUpdate();
|
|
806
818
|
}
|
|
807
819
|
}
|
|
@@ -903,7 +915,7 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
903
915
|
// Bundle overview section
|
|
904
916
|
const filesByType = this.categorizeFiles(files);
|
|
905
917
|
const entryPoints = this.identifyEntryPoints(files);
|
|
906
|
-
|
|
918
|
+
|
|
907
919
|
xml += ` <cntx:overview>
|
|
908
920
|
<cntx:purpose>${this.escapeXml(this.getBundlePurpose(bundleName))}</cntx:purpose>
|
|
909
921
|
<cntx:file-types>
|
|
@@ -949,7 +961,7 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
949
961
|
// Then organize by file type
|
|
950
962
|
Object.entries(filesByType).forEach(([type, typeFiles]) => {
|
|
951
963
|
if (type === 'entry-points') return; // Already handled above
|
|
952
|
-
|
|
964
|
+
|
|
953
965
|
const remainingFiles = typeFiles.filter(file => !entryPoints.includes(file));
|
|
954
966
|
if (remainingFiles.length > 0) {
|
|
955
967
|
xml += ` <cntx:group type="${type}" description="${this.getTypeDescription(type)}">
|
|
@@ -983,28 +995,28 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
983
995
|
files.forEach(file => {
|
|
984
996
|
const ext = extname(file).toLowerCase();
|
|
985
997
|
const basename = file.toLowerCase();
|
|
986
|
-
|
|
987
|
-
if (basename.includes('component') || file.includes('/components/') ||
|
|
988
|
-
|
|
998
|
+
|
|
999
|
+
if (basename.includes('component') || file.includes('/components/') ||
|
|
1000
|
+
ext === '.jsx' || ext === '.tsx' && !basename.includes('test')) {
|
|
989
1001
|
categories.components.push(file);
|
|
990
1002
|
} else if (basename.includes('hook') || file.includes('/hooks/')) {
|
|
991
1003
|
categories.hooks.push(file);
|
|
992
|
-
} else if (basename.includes('util') || file.includes('/utils/') ||
|
|
993
|
-
|
|
1004
|
+
} else if (basename.includes('util') || file.includes('/utils/') ||
|
|
1005
|
+
basename.includes('helper') || file.includes('/lib/')) {
|
|
994
1006
|
categories.utilities.push(file);
|
|
995
|
-
} else if (ext === '.json' || basename.includes('config') ||
|
|
996
|
-
|
|
1007
|
+
} else if (ext === '.json' || basename.includes('config') ||
|
|
1008
|
+
ext === '.yaml' || ext === '.yml' || ext === '.toml') {
|
|
997
1009
|
categories.configuration.push(file);
|
|
998
1010
|
} else if (ext === '.css' || ext === '.scss' || ext === '.less') {
|
|
999
1011
|
categories.styles.push(file);
|
|
1000
|
-
} else if (basename.includes('type') || ext === '.d.ts' ||
|
|
1001
|
-
|
|
1012
|
+
} else if (basename.includes('type') || ext === '.d.ts' ||
|
|
1013
|
+
file.includes('/types/')) {
|
|
1002
1014
|
categories.types.push(file);
|
|
1003
|
-
} else if (basename.includes('test') || basename.includes('spec') ||
|
|
1004
|
-
|
|
1015
|
+
} else if (basename.includes('test') || basename.includes('spec') ||
|
|
1016
|
+
file.includes('/test/') || file.includes('/__tests__/')) {
|
|
1005
1017
|
categories.tests.push(file);
|
|
1006
|
-
} else if (ext === '.md' || basename.includes('readme') ||
|
|
1007
|
-
|
|
1018
|
+
} else if (ext === '.md' || basename.includes('readme') ||
|
|
1019
|
+
basename.includes('doc')) {
|
|
1008
1020
|
categories.documentation.push(file);
|
|
1009
1021
|
} else {
|
|
1010
1022
|
categories.other.push(file);
|
|
@@ -1023,16 +1035,16 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1023
1035
|
|
|
1024
1036
|
identifyEntryPoints(files) {
|
|
1025
1037
|
const entryPoints = [];
|
|
1026
|
-
|
|
1038
|
+
|
|
1027
1039
|
files.forEach(file => {
|
|
1028
1040
|
const basename = file.toLowerCase();
|
|
1029
|
-
|
|
1041
|
+
|
|
1030
1042
|
// Common entry point patterns
|
|
1031
|
-
if (basename.includes('main.') || basename.includes('index.') ||
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1043
|
+
if (basename.includes('main.') || basename.includes('index.') ||
|
|
1044
|
+
basename.includes('app.') || basename === 'server.js' ||
|
|
1045
|
+
file.endsWith('/App.tsx') || file.endsWith('/App.jsx') ||
|
|
1046
|
+
file.endsWith('/main.tsx') || file.endsWith('/main.js') ||
|
|
1047
|
+
file.endsWith('/index.tsx') || file.endsWith('/index.js')) {
|
|
1036
1048
|
entryPoints.push(file);
|
|
1037
1049
|
}
|
|
1038
1050
|
});
|
|
@@ -1083,11 +1095,11 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1083
1095
|
try {
|
|
1084
1096
|
const stat = statSync(fullPath);
|
|
1085
1097
|
const content = readFileSync(fullPath, 'utf8');
|
|
1086
|
-
|
|
1098
|
+
|
|
1087
1099
|
// Add role indicator for certain files
|
|
1088
1100
|
const role = this.getFileRole(file);
|
|
1089
1101
|
const roleAttr = role ? ` role="${role}"` : '';
|
|
1090
|
-
|
|
1102
|
+
|
|
1091
1103
|
fileXml = ` <cntx:file path="${file}" ext="${extname(file)}"${roleAttr}>
|
|
1092
1104
|
`;
|
|
1093
1105
|
fileXml += ` <cntx:meta size="${stat.size}" modified="${stat.mtime.toISOString()}" lines="${content.split('\n').length}" />
|
|
@@ -1105,18 +1117,17 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1105
1117
|
|
|
1106
1118
|
getFileRole(file) {
|
|
1107
1119
|
const basename = file.toLowerCase();
|
|
1108
|
-
|
|
1120
|
+
|
|
1109
1121
|
if (basename.includes('main.') || basename.includes('index.')) return 'entry-point';
|
|
1110
1122
|
if (basename.includes('app.')) return 'main-component';
|
|
1111
1123
|
if (file === 'server.js') return 'server-entry';
|
|
1112
1124
|
if (basename.includes('config')) return 'configuration';
|
|
1113
1125
|
if (basename.includes('package.json')) return 'package-config';
|
|
1114
1126
|
if (basename.includes('readme')) return 'documentation';
|
|
1115
|
-
|
|
1127
|
+
|
|
1116
1128
|
return null;
|
|
1117
1129
|
}
|
|
1118
1130
|
|
|
1119
|
-
|
|
1120
1131
|
escapeXml(text) {
|
|
1121
1132
|
return String(text)
|
|
1122
1133
|
.replace(/&/g, '&')
|
|
@@ -1276,6 +1287,8 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1276
1287
|
}
|
|
1277
1288
|
|
|
1278
1289
|
// API Routes
|
|
1290
|
+
console.log('🔍 Processing API request:', url.pathname);
|
|
1291
|
+
|
|
1279
1292
|
if (url.pathname === '/api/bundles') {
|
|
1280
1293
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1281
1294
|
const bundleData = Array.from(this.bundles.entries()).map(([name, bundle]) => ({
|
|
@@ -1289,6 +1302,72 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1289
1302
|
}));
|
|
1290
1303
|
res.end(JSON.stringify(bundleData));
|
|
1291
1304
|
|
|
1305
|
+
} else if (url.pathname === '/api/semantic-chunks') {
|
|
1306
|
+
console.log('🔍 Semantic chunks route matched! URL:', url.pathname);
|
|
1307
|
+
this.getSemanticAnalysis()
|
|
1308
|
+
.then(analysis => {
|
|
1309
|
+
console.log('✅ Semantic analysis successful');
|
|
1310
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1311
|
+
res.end(JSON.stringify(analysis));
|
|
1312
|
+
})
|
|
1313
|
+
.catch(error => {
|
|
1314
|
+
console.error('❌ Semantic analysis failed:', error.message);
|
|
1315
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1316
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
} else if (url.pathname === '/api/semantic-chunks/export') {
|
|
1320
|
+
if (req.method === 'POST') {
|
|
1321
|
+
let body = '';
|
|
1322
|
+
req.on('data', chunk => body += chunk);
|
|
1323
|
+
req.on('end', () => {
|
|
1324
|
+
try {
|
|
1325
|
+
const { chunkName } = JSON.parse(body);
|
|
1326
|
+
this.exportSemanticChunk(chunkName)
|
|
1327
|
+
.then(xmlContent => {
|
|
1328
|
+
res.writeHead(200, { 'Content-Type': 'application/xml' });
|
|
1329
|
+
res.end(xmlContent);
|
|
1330
|
+
})
|
|
1331
|
+
.catch(error => {
|
|
1332
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1333
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1334
|
+
});
|
|
1335
|
+
} catch (error) {
|
|
1336
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1337
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
} else {
|
|
1341
|
+
res.writeHead(405);
|
|
1342
|
+
res.end('Method not allowed');
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
} else if (url.pathname === '/api/bundles-from-chunk') {
|
|
1346
|
+
if (req.method === 'POST') {
|
|
1347
|
+
let body = '';
|
|
1348
|
+
req.on('data', chunk => body += chunk);
|
|
1349
|
+
req.on('end', () => {
|
|
1350
|
+
try {
|
|
1351
|
+
const { chunkName, files } = JSON.parse(body);
|
|
1352
|
+
this.createBundleFromChunk(chunkName, files)
|
|
1353
|
+
.then(() => {
|
|
1354
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1355
|
+
res.end(JSON.stringify({ success: true }));
|
|
1356
|
+
})
|
|
1357
|
+
.catch(error => {
|
|
1358
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1359
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1360
|
+
});
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
1363
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
1364
|
+
}
|
|
1365
|
+
});
|
|
1366
|
+
} else {
|
|
1367
|
+
res.writeHead(405);
|
|
1368
|
+
res.end('Method not allowed');
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1292
1371
|
} else if (url.pathname.startsWith('/api/bundles/')) {
|
|
1293
1372
|
const bundleName = url.pathname.split('/').pop();
|
|
1294
1373
|
const bundle = this.bundles.get(bundleName);
|
|
@@ -1590,11 +1669,11 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1590
1669
|
} else if (url.pathname.startsWith('/api/bundle-categories/')) {
|
|
1591
1670
|
const bundleName = url.pathname.split('/').pop();
|
|
1592
1671
|
const bundle = this.bundles.get(bundleName);
|
|
1593
|
-
|
|
1672
|
+
|
|
1594
1673
|
if (bundle) {
|
|
1595
1674
|
const filesByType = this.categorizeFiles(bundle.files);
|
|
1596
1675
|
const entryPoints = this.identifyEntryPoints(bundle.files);
|
|
1597
|
-
|
|
1676
|
+
|
|
1598
1677
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1599
1678
|
res.end(JSON.stringify({
|
|
1600
1679
|
purpose: this.getBundlePurpose(bundleName),
|
|
@@ -1637,6 +1716,36 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1637
1716
|
});
|
|
1638
1717
|
}
|
|
1639
1718
|
|
|
1719
|
+
} else if (url.pathname === '/api/mcp-status') {
|
|
1720
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1721
|
+
|
|
1722
|
+
// Simple check - MCP is available if we can find package.json
|
|
1723
|
+
let isAccessible = true;
|
|
1724
|
+
let testResult = 'available';
|
|
1725
|
+
|
|
1726
|
+
// Check if package.json exists using existing imports
|
|
1727
|
+
try {
|
|
1728
|
+
const packagePath = join(this.CWD, 'package.json');
|
|
1729
|
+
if (existsSync(packagePath)) {
|
|
1730
|
+
testResult = 'local_package_found';
|
|
1731
|
+
} else {
|
|
1732
|
+
testResult = 'using_global_npx';
|
|
1733
|
+
}
|
|
1734
|
+
} catch (error) {
|
|
1735
|
+
testResult = 'check_failed';
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
const mcpStatus = {
|
|
1739
|
+
running: isAccessible,
|
|
1740
|
+
accessible: isAccessible,
|
|
1741
|
+
testResult: testResult,
|
|
1742
|
+
command: 'npx cntx-ui mcp',
|
|
1743
|
+
workingDirectory: this.CWD,
|
|
1744
|
+
lastChecked: new Date().toISOString(),
|
|
1745
|
+
trackingEnabled: this.mcpServerStarted || false
|
|
1746
|
+
};
|
|
1747
|
+
res.end(JSON.stringify(mcpStatus, null, 2));
|
|
1748
|
+
|
|
1640
1749
|
} else if (url.pathname === '/api/status') {
|
|
1641
1750
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1642
1751
|
const statusInfo = {
|
|
@@ -1712,19 +1821,150 @@ This project uses cntx-ui for bundle management and AI context organization.
|
|
|
1712
1821
|
this.watchers.forEach(watcher => watcher.close());
|
|
1713
1822
|
this.saveBundleStates();
|
|
1714
1823
|
}
|
|
1824
|
+
|
|
1825
|
+
// Semantic Chunking Methods
|
|
1826
|
+
async getSemanticAnalysis() {
|
|
1827
|
+
// Force refresh always for now (TODO: implement proper cache invalidation)
|
|
1828
|
+
this.semanticCache = null // Clear cache
|
|
1829
|
+
const shouldRefresh = true
|
|
1830
|
+
|
|
1831
|
+
console.log('🔍 Cache check - shouldRefresh:', shouldRefresh, 'lastAnalysis:', this.lastSemanticAnalysis, 'now:', Date.now());
|
|
1832
|
+
|
|
1833
|
+
if (shouldRefresh) {
|
|
1834
|
+
try {
|
|
1835
|
+
// Auto-discover JavaScript/TypeScript files in the entire project
|
|
1836
|
+
const patterns = ['**/*.{js,jsx,ts,tsx,mjs}'];
|
|
1837
|
+
|
|
1838
|
+
// Load bundle configuration for chunk grouping
|
|
1839
|
+
let bundleConfig = null;
|
|
1840
|
+
if (existsSync(this.CONFIG_FILE)) {
|
|
1841
|
+
bundleConfig = JSON.parse(readFileSync(this.CONFIG_FILE, 'utf8'));
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
this.semanticCache = await this.semanticSplitter.extractSemanticChunks(this.CWD, patterns, bundleConfig);
|
|
1845
|
+
this.lastSemanticAnalysis = Date.now();
|
|
1846
|
+
|
|
1847
|
+
// Debug logging
|
|
1848
|
+
console.log('🔍 Semantic analysis complete. Sample chunk keys:',
|
|
1849
|
+
this.semanticCache.chunks.length > 0 ? Object.keys(this.semanticCache.chunks[0]) : 'No chunks');
|
|
1850
|
+
if (this.semanticCache.chunks.length > 0) {
|
|
1851
|
+
console.log('🔍 Sample chunk businessDomains:', this.semanticCache.chunks[0].businessDomains);
|
|
1852
|
+
}
|
|
1853
|
+
} catch (error) {
|
|
1854
|
+
console.error('Semantic analysis failed:', error.message);
|
|
1855
|
+
throw new Error(`Semantic analysis failed: ${error.message}`);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
return this.semanticCache;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
async exportSemanticChunk(chunkName) {
|
|
1863
|
+
const analysis = await this.getSemanticAnalysis();
|
|
1864
|
+
const chunk = analysis.chunks.find(c => c.name === chunkName);
|
|
1865
|
+
|
|
1866
|
+
if (!chunk) {
|
|
1867
|
+
throw new Error(`Chunk "${chunkName}" not found`);
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// Generate XML content for the chunk files
|
|
1871
|
+
let xmlContent = `<?xml version="1.0" encoding="UTF-8"?>\n`;
|
|
1872
|
+
xmlContent += `<codebase_context semantic_chunk="${chunkName}">\n`;
|
|
1873
|
+
xmlContent += ` <chunk_info>\n`;
|
|
1874
|
+
xmlContent += ` <name>${chunkName}</name>\n`;
|
|
1875
|
+
xmlContent += ` <purpose>${chunk.purpose || 'No description'}</purpose>\n`;
|
|
1876
|
+
xmlContent += ` <file_count>${chunk.files.length}</file_count>\n`;
|
|
1877
|
+
xmlContent += ` <size>${chunk.size} bytes</size>\n`;
|
|
1878
|
+
xmlContent += ` <complexity>${chunk.complexity?.level || 'unknown'}</complexity>\n`;
|
|
1879
|
+
xmlContent += ` <tags>${(chunk.tags || []).join(', ')}</tags>\n`;
|
|
1880
|
+
xmlContent += ` </chunk_info>\n\n`;
|
|
1881
|
+
|
|
1882
|
+
// Add each function in the chunk
|
|
1883
|
+
if (chunk.functions) {
|
|
1884
|
+
for (const func of chunk.functions) {
|
|
1885
|
+
xmlContent += ` <function name="${func.name}" file="${func.filePath}">\n`;
|
|
1886
|
+
xmlContent += ` <signature>${func.signature}</signature>\n`;
|
|
1887
|
+
xmlContent += ` <type>${func.type}</type>\n`;
|
|
1888
|
+
xmlContent += ` <lines>${func.startLine}-${func.endLine}</lines>\n`;
|
|
1889
|
+
|
|
1890
|
+
if (func.context.imports.length > 0) {
|
|
1891
|
+
xmlContent += ` <imports>${func.context.imports.join(', ')}</imports>\n`;
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
xmlContent += ` <code>\n`;
|
|
1895
|
+
xmlContent += func.code
|
|
1896
|
+
.split('\n')
|
|
1897
|
+
.map((line, i) => `${(func.startLine + i).toString().padStart(3)} ${line}`)
|
|
1898
|
+
.join('\n');
|
|
1899
|
+
xmlContent += `\n </code>\n`;
|
|
1900
|
+
xmlContent += ` </function>\n\n`;
|
|
1901
|
+
}
|
|
1902
|
+
} else {
|
|
1903
|
+
// Fallback for file-based chunks
|
|
1904
|
+
for (const filePath of chunk.files || []) {
|
|
1905
|
+
const fullPath = join(this.CWD, filePath);
|
|
1906
|
+
if (existsSync(fullPath)) {
|
|
1907
|
+
try {
|
|
1908
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
1909
|
+
xmlContent += ` <file path="${filePath}">\n`;
|
|
1910
|
+
xmlContent += content
|
|
1911
|
+
.split('\n')
|
|
1912
|
+
.map((line, i) => `${(i + 1).toString().padStart(3)} ${line}`)
|
|
1913
|
+
.join('\n');
|
|
1914
|
+
xmlContent += `\n </file>\n\n`;
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
console.warn(`Could not read file ${filePath}:`, error.message);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
xmlContent += `</codebase_context>`;
|
|
1923
|
+
return xmlContent;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
async createBundleFromChunk(chunkName, files) {
|
|
1927
|
+
// Load current config
|
|
1928
|
+
let config = {};
|
|
1929
|
+
if (existsSync(this.CONFIG_FILE)) {
|
|
1930
|
+
config = JSON.parse(readFileSync(this.CONFIG_FILE, 'utf8'));
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
if (!config.bundles) {
|
|
1934
|
+
config.bundles = {};
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
// Create bundle with the chunk name and files
|
|
1938
|
+
const bundleName = chunkName.toLowerCase().replace(/[-\s]+/g, '-');
|
|
1939
|
+
config.bundles[bundleName] = files;
|
|
1940
|
+
|
|
1941
|
+
// Save config
|
|
1942
|
+
writeFileSync(this.CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
1943
|
+
|
|
1944
|
+
// Reload bundles
|
|
1945
|
+
this.loadConfig();
|
|
1946
|
+
this.generateAllBundles();
|
|
1947
|
+
this.saveBundleStates();
|
|
1948
|
+
this.broadcastUpdate();
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
invalidateSemanticCache() {
|
|
1952
|
+
this.semanticCache = null;
|
|
1953
|
+
this.lastSemanticAnalysis = null;
|
|
1954
|
+
}
|
|
1715
1955
|
}
|
|
1716
1956
|
|
|
1717
1957
|
export function startServer(options = {}) {
|
|
1718
1958
|
const server = new CntxServer(options.cwd, { quiet: options.quiet });
|
|
1719
1959
|
server.init();
|
|
1720
|
-
|
|
1960
|
+
|
|
1721
1961
|
if (options.withMcp) {
|
|
1722
1962
|
server.mcpServerStarted = true;
|
|
1723
1963
|
if (!server.isQuietMode) {
|
|
1724
1964
|
console.log('🔗 MCP server tracking enabled - use /api/status to check MCP configuration');
|
|
1725
1965
|
}
|
|
1726
1966
|
}
|
|
1727
|
-
|
|
1967
|
+
|
|
1728
1968
|
return server.startServer(options.port);
|
|
1729
1969
|
}
|
|
1730
1970
|
|
|
@@ -1900,17 +2140,17 @@ export function setupMCP(cwd = process.cwd(), options = {}) {
|
|
|
1900
2140
|
// Write updated config
|
|
1901
2141
|
try {
|
|
1902
2142
|
writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
1903
|
-
|
|
2143
|
+
|
|
1904
2144
|
if (!isQuiet) {
|
|
1905
2145
|
console.log(`✅ Added MCP server: ${serverName}`);
|
|
1906
2146
|
console.log('📋 Your Claude Desktop config now includes:');
|
|
1907
|
-
|
|
2147
|
+
|
|
1908
2148
|
Object.keys(config.mcpServers).forEach(name => {
|
|
1909
2149
|
if (name.startsWith('cntx-ui-')) {
|
|
1910
2150
|
console.log(` • ${name}: ${config.mcpServers[name].cwd}`);
|
|
1911
2151
|
}
|
|
1912
2152
|
});
|
|
1913
|
-
|
|
2153
|
+
|
|
1914
2154
|
console.log('🔄 Please restart Claude Desktop to use the updated configuration');
|
|
1915
2155
|
}
|
|
1916
2156
|
} catch (error) {
|