cntx-ui 2.0.3 → 2.0.5
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/README.md +312 -1
- package/bin/cntx-ui.js +17 -4
- package/lib/mcp-server.js +387 -0
- package/lib/mcp-transport.js +97 -0
- package/mcp-config-example.json +9 -0
- package/package.json +4 -2
- package/server.js +398 -13
- package/web/dist/assets/index-DZnz-iQT.js +526 -0
- package/web/dist/assets/index-dtGilZT4.css +1 -0
- package/web/dist/index.html +6 -3
- package/web/dist/assets/index-DfyThajP.js +0 -505
- package/web/dist/assets/index-vqmctNU6.css +0 -1
package/server.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createServer } from 'http';
|
|
|
4
4
|
import { WebSocketServer } from 'ws';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import path from 'path';
|
|
7
|
+
import { startMCPTransport } from './lib/mcp-transport.js';
|
|
7
8
|
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
|
|
@@ -32,6 +33,7 @@ export class CntxServer {
|
|
|
32
33
|
this.HIDDEN_FILES_CONFIG = join(this.CNTX_DIR, 'hidden-files.json');
|
|
33
34
|
this.IGNORE_FILE = join(cwd, '.cntxignore');
|
|
34
35
|
this.CURSOR_RULES_FILE = join(cwd, '.cursorrules');
|
|
36
|
+
this.CLAUDE_MD_FILE = join(cwd, 'CLAUDE.md');
|
|
35
37
|
|
|
36
38
|
this.bundles = new Map();
|
|
37
39
|
this.ignorePatterns = [];
|
|
@@ -215,8 +217,67 @@ export class CntxServer {
|
|
|
215
217
|
|
|
216
218
|
loadIgnorePatterns() {
|
|
217
219
|
const systemPatterns = [
|
|
220
|
+
// Version control
|
|
218
221
|
'**/.git/**',
|
|
222
|
+
'**/.svn/**',
|
|
223
|
+
'**/.hg/**',
|
|
224
|
+
|
|
225
|
+
// Dependencies
|
|
219
226
|
'**/node_modules/**',
|
|
227
|
+
'**/vendor/**',
|
|
228
|
+
'**/.pnp/**',
|
|
229
|
+
|
|
230
|
+
// Build outputs
|
|
231
|
+
'**/dist/**',
|
|
232
|
+
'**/build/**',
|
|
233
|
+
'**/out/**',
|
|
234
|
+
'**/.next/**',
|
|
235
|
+
'**/.nuxt/**',
|
|
236
|
+
'**/target/**',
|
|
237
|
+
|
|
238
|
+
// Package files
|
|
239
|
+
'**/*.tgz',
|
|
240
|
+
'**/*.tar.gz',
|
|
241
|
+
'**/*.zip',
|
|
242
|
+
'**/*.rar',
|
|
243
|
+
'**/*.7z',
|
|
244
|
+
|
|
245
|
+
// Logs
|
|
246
|
+
'**/*.log',
|
|
247
|
+
'**/logs/**',
|
|
248
|
+
|
|
249
|
+
// Cache directories
|
|
250
|
+
'**/.cache/**',
|
|
251
|
+
'**/.parcel-cache/**',
|
|
252
|
+
'**/.nyc_output/**',
|
|
253
|
+
'**/coverage/**',
|
|
254
|
+
'**/.pytest_cache/**',
|
|
255
|
+
'**/__pycache__/**',
|
|
256
|
+
|
|
257
|
+
// IDE/Editor files
|
|
258
|
+
'**/.vscode/**',
|
|
259
|
+
'**/.idea/**',
|
|
260
|
+
'**/*.swp',
|
|
261
|
+
'**/*.swo',
|
|
262
|
+
'**/*~',
|
|
263
|
+
|
|
264
|
+
// OS files
|
|
265
|
+
'**/.DS_Store',
|
|
266
|
+
'**/Thumbs.db',
|
|
267
|
+
'**/desktop.ini',
|
|
268
|
+
|
|
269
|
+
// Environment files
|
|
270
|
+
'**/.env',
|
|
271
|
+
'**/.env.local',
|
|
272
|
+
'**/.env.*.local',
|
|
273
|
+
|
|
274
|
+
// Lock files
|
|
275
|
+
'**/package-lock.json',
|
|
276
|
+
'**/yarn.lock',
|
|
277
|
+
'**/pnpm-lock.yaml',
|
|
278
|
+
'**/Cargo.lock',
|
|
279
|
+
|
|
280
|
+
// cntx-ui specific
|
|
220
281
|
'**/.cntx/**'
|
|
221
282
|
];
|
|
222
283
|
|
|
@@ -496,6 +557,92 @@ Add your specific project rules and preferences below:
|
|
|
496
557
|
writeFileSync(this.CURSOR_RULES_FILE, content, 'utf8');
|
|
497
558
|
}
|
|
498
559
|
|
|
560
|
+
loadClaudeMd() {
|
|
561
|
+
if (existsSync(this.CLAUDE_MD_FILE)) {
|
|
562
|
+
return readFileSync(this.CLAUDE_MD_FILE, 'utf8');
|
|
563
|
+
}
|
|
564
|
+
return this.getDefaultClaudeMd();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
getDefaultClaudeMd() {
|
|
568
|
+
// Get project info for context
|
|
569
|
+
let projectInfo = { name: 'unknown', description: '', type: 'general' };
|
|
570
|
+
const pkgPath = join(this.CWD, 'package.json');
|
|
571
|
+
|
|
572
|
+
if (existsSync(pkgPath)) {
|
|
573
|
+
try {
|
|
574
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
575
|
+
projectInfo = {
|
|
576
|
+
name: pkg.name || 'unknown',
|
|
577
|
+
description: pkg.description || '',
|
|
578
|
+
type: this.detectProjectType(pkg)
|
|
579
|
+
};
|
|
580
|
+
} catch (e) {
|
|
581
|
+
// Use defaults if package.json is invalid
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return this.generateClaudeMdTemplate(projectInfo);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
generateClaudeMdTemplate(projectInfo) {
|
|
589
|
+
const { name, description, type } = projectInfo;
|
|
590
|
+
|
|
591
|
+
let template = `# ${name}
|
|
592
|
+
|
|
593
|
+
${description ? `${description}\n\n` : ''}## Project Structure
|
|
594
|
+
|
|
595
|
+
This project uses cntx-ui for bundle management and AI context organization.
|
|
596
|
+
|
|
597
|
+
### Bundles
|
|
598
|
+
|
|
599
|
+
`;
|
|
600
|
+
|
|
601
|
+
// Add bundle information
|
|
602
|
+
this.bundles.forEach((bundle, bundleName) => {
|
|
603
|
+
template += `- **${bundleName}**: ${bundle.files.length} files\n`;
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
template += `
|
|
607
|
+
### Development Guidelines
|
|
608
|
+
|
|
609
|
+
- Follow the existing code style and patterns
|
|
610
|
+
- Use TypeScript for type safety
|
|
611
|
+
- Write meaningful commit messages
|
|
612
|
+
- Test changes thoroughly
|
|
613
|
+
|
|
614
|
+
### Key Files
|
|
615
|
+
|
|
616
|
+
- \`.cntx/config.json\` - Bundle configuration
|
|
617
|
+
- \`.cursorrules\` - AI assistant rules
|
|
618
|
+
- \`CLAUDE.md\` - Project context for Claude
|
|
619
|
+
`;
|
|
620
|
+
|
|
621
|
+
if (type === 'react') {
|
|
622
|
+
template += `
|
|
623
|
+
### React Specific
|
|
624
|
+
|
|
625
|
+
- Use functional components with hooks
|
|
626
|
+
- Follow React best practices
|
|
627
|
+
- Use TypeScript interfaces for props
|
|
628
|
+
`;
|
|
629
|
+
} else if (type === 'node') {
|
|
630
|
+
template += `
|
|
631
|
+
### Node.js Specific
|
|
632
|
+
|
|
633
|
+
- Use ES modules (import/export)
|
|
634
|
+
- Follow async/await patterns
|
|
635
|
+
- Proper error handling
|
|
636
|
+
`;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
return template;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
saveClaudeMd(content) {
|
|
643
|
+
writeFileSync(this.CLAUDE_MD_FILE, content, 'utf8');
|
|
644
|
+
}
|
|
645
|
+
|
|
499
646
|
shouldIgnoreFile(filePath) {
|
|
500
647
|
const relativePath = relative(this.CWD, filePath).replace(/\\\\/g, '/');
|
|
501
648
|
|
|
@@ -730,6 +877,7 @@ Add your specific project rules and preferences below:
|
|
|
730
877
|
<cntx:bundle xmlns:cntx="https://cntx.dev/schema" name="${bundleName}" generated="${new Date().toISOString()}">
|
|
731
878
|
`;
|
|
732
879
|
|
|
880
|
+
// Project information
|
|
733
881
|
const pkgPath = join(this.CWD, 'package.json');
|
|
734
882
|
if (existsSync(pkgPath)) {
|
|
735
883
|
try {
|
|
@@ -748,27 +896,66 @@ Add your specific project rules and preferences below:
|
|
|
748
896
|
}
|
|
749
897
|
}
|
|
750
898
|
|
|
751
|
-
|
|
899
|
+
// Bundle overview section
|
|
900
|
+
const filesByType = this.categorizeFiles(files);
|
|
901
|
+
const entryPoints = this.identifyEntryPoints(files);
|
|
902
|
+
|
|
903
|
+
xml += ` <cntx:overview>
|
|
904
|
+
<cntx:purpose>${this.escapeXml(this.getBundlePurpose(bundleName))}</cntx:purpose>
|
|
905
|
+
<cntx:file-types>
|
|
752
906
|
`;
|
|
753
907
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
xml += ` <cntx:file path="${file}" ext="${extname(file)}">
|
|
908
|
+
Object.entries(filesByType).forEach(([type, typeFiles]) => {
|
|
909
|
+
xml += ` <cntx:type name="${type}" count="${typeFiles.length}" />
|
|
757
910
|
`;
|
|
911
|
+
});
|
|
758
912
|
|
|
759
|
-
|
|
760
|
-
const stat = statSync(fullPath);
|
|
761
|
-
const content = readFileSync(fullPath, 'utf8');
|
|
762
|
-
xml += ` <cntx:meta size="${stat.size}" modified="${stat.mtime.toISOString()}" />
|
|
763
|
-
<cntx:content><![CDATA[${content}]]></cntx:content>
|
|
913
|
+
xml += ` </cntx:file-types>
|
|
764
914
|
`;
|
|
765
|
-
|
|
766
|
-
|
|
915
|
+
|
|
916
|
+
if (entryPoints.length > 0) {
|
|
917
|
+
xml += ` <cntx:entry-points>
|
|
918
|
+
`;
|
|
919
|
+
entryPoints.forEach(file => {
|
|
920
|
+
xml += ` <cntx:file>${file}</cntx:file>
|
|
921
|
+
`;
|
|
922
|
+
});
|
|
923
|
+
xml += ` </cntx:entry-points>
|
|
924
|
+
`;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
xml += ` </cntx:overview>
|
|
928
|
+
`;
|
|
929
|
+
|
|
930
|
+
// Files organized by type
|
|
931
|
+
xml += ` <cntx:files count="${files.length}">
|
|
767
932
|
`;
|
|
768
|
-
}
|
|
769
933
|
|
|
770
|
-
|
|
934
|
+
// Entry points first
|
|
935
|
+
if (entryPoints.length > 0) {
|
|
936
|
+
xml += ` <cntx:group type="entry-points" description="Main entry files for this bundle">
|
|
937
|
+
`;
|
|
938
|
+
entryPoints.forEach(file => {
|
|
939
|
+
xml += this.generateFileXML(file);
|
|
940
|
+
});
|
|
941
|
+
xml += ` </cntx:group>
|
|
771
942
|
`;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Then organize by file type
|
|
946
|
+
Object.entries(filesByType).forEach(([type, typeFiles]) => {
|
|
947
|
+
if (type === 'entry-points') return; // Already handled above
|
|
948
|
+
|
|
949
|
+
const remainingFiles = typeFiles.filter(file => !entryPoints.includes(file));
|
|
950
|
+
if (remainingFiles.length > 0) {
|
|
951
|
+
xml += ` <cntx:group type="${type}" description="${this.getTypeDescription(type)}">
|
|
952
|
+
`;
|
|
953
|
+
remainingFiles.forEach(file => {
|
|
954
|
+
xml += this.generateFileXML(file);
|
|
955
|
+
});
|
|
956
|
+
xml += ` </cntx:group>
|
|
957
|
+
`;
|
|
958
|
+
}
|
|
772
959
|
});
|
|
773
960
|
|
|
774
961
|
xml += ` </cntx:files>
|
|
@@ -776,6 +963,156 @@ Add your specific project rules and preferences below:
|
|
|
776
963
|
return xml;
|
|
777
964
|
}
|
|
778
965
|
|
|
966
|
+
categorizeFiles(files) {
|
|
967
|
+
const categories = {
|
|
968
|
+
'components': [],
|
|
969
|
+
'hooks': [],
|
|
970
|
+
'utilities': [],
|
|
971
|
+
'configuration': [],
|
|
972
|
+
'styles': [],
|
|
973
|
+
'types': [],
|
|
974
|
+
'tests': [],
|
|
975
|
+
'documentation': [],
|
|
976
|
+
'other': []
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
files.forEach(file => {
|
|
980
|
+
const ext = extname(file).toLowerCase();
|
|
981
|
+
const basename = file.toLowerCase();
|
|
982
|
+
|
|
983
|
+
if (basename.includes('component') || file.includes('/components/') ||
|
|
984
|
+
ext === '.jsx' || ext === '.tsx' && !basename.includes('test')) {
|
|
985
|
+
categories.components.push(file);
|
|
986
|
+
} else if (basename.includes('hook') || file.includes('/hooks/')) {
|
|
987
|
+
categories.hooks.push(file);
|
|
988
|
+
} else if (basename.includes('util') || file.includes('/utils/') ||
|
|
989
|
+
basename.includes('helper') || file.includes('/lib/')) {
|
|
990
|
+
categories.utilities.push(file);
|
|
991
|
+
} else if (ext === '.json' || basename.includes('config') ||
|
|
992
|
+
ext === '.yaml' || ext === '.yml' || ext === '.toml') {
|
|
993
|
+
categories.configuration.push(file);
|
|
994
|
+
} else if (ext === '.css' || ext === '.scss' || ext === '.less') {
|
|
995
|
+
categories.styles.push(file);
|
|
996
|
+
} else if (basename.includes('type') || ext === '.d.ts' ||
|
|
997
|
+
file.includes('/types/')) {
|
|
998
|
+
categories.types.push(file);
|
|
999
|
+
} else if (basename.includes('test') || basename.includes('spec') ||
|
|
1000
|
+
file.includes('/test/') || file.includes('/__tests__/')) {
|
|
1001
|
+
categories.tests.push(file);
|
|
1002
|
+
} else if (ext === '.md' || basename.includes('readme') ||
|
|
1003
|
+
basename.includes('doc')) {
|
|
1004
|
+
categories.documentation.push(file);
|
|
1005
|
+
} else {
|
|
1006
|
+
categories.other.push(file);
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
// Remove empty categories
|
|
1011
|
+
Object.keys(categories).forEach(key => {
|
|
1012
|
+
if (categories[key].length === 0) {
|
|
1013
|
+
delete categories[key];
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
return categories;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
identifyEntryPoints(files) {
|
|
1021
|
+
const entryPoints = [];
|
|
1022
|
+
|
|
1023
|
+
files.forEach(file => {
|
|
1024
|
+
const basename = file.toLowerCase();
|
|
1025
|
+
|
|
1026
|
+
// Common entry point patterns
|
|
1027
|
+
if (basename.includes('main.') || basename.includes('index.') ||
|
|
1028
|
+
basename.includes('app.') || basename === 'server.js' ||
|
|
1029
|
+
file.endsWith('/App.tsx') || file.endsWith('/App.jsx') ||
|
|
1030
|
+
file.endsWith('/main.tsx') || file.endsWith('/main.js') ||
|
|
1031
|
+
file.endsWith('/index.tsx') || file.endsWith('/index.js')) {
|
|
1032
|
+
entryPoints.push(file);
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
return entryPoints;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
getBundlePurpose(bundleName) {
|
|
1040
|
+
const purposes = {
|
|
1041
|
+
'master': 'Complete project overview with all source files',
|
|
1042
|
+
'frontend': 'User interface components, pages, and client-side logic',
|
|
1043
|
+
'backend': 'Server-side logic, APIs, and backend services',
|
|
1044
|
+
'api': 'API endpoints, routes, and server communication logic',
|
|
1045
|
+
'server': 'Main server application and core backend functionality',
|
|
1046
|
+
'components': 'Reusable UI components and interface elements',
|
|
1047
|
+
'ui-components': 'User interface components and design system elements',
|
|
1048
|
+
'config': 'Configuration files, settings, and environment setup',
|
|
1049
|
+
'docs': 'Documentation, README files, and project guides',
|
|
1050
|
+
'utils': 'Utility functions, helpers, and shared libraries',
|
|
1051
|
+
'types': 'TypeScript type definitions and interfaces',
|
|
1052
|
+
'tests': 'Test files, test utilities, and testing configuration'
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
return purposes[bundleName] || `Bundle containing ${bundleName}-related files`;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
getTypeDescription(type) {
|
|
1059
|
+
const descriptions = {
|
|
1060
|
+
'components': 'React/UI components and interface elements',
|
|
1061
|
+
'hooks': 'Custom React hooks and state management',
|
|
1062
|
+
'utilities': 'Helper functions, utilities, and shared libraries',
|
|
1063
|
+
'configuration': 'Configuration files, settings, and build configs',
|
|
1064
|
+
'styles': 'CSS, SCSS, and styling files',
|
|
1065
|
+
'types': 'TypeScript type definitions and interfaces',
|
|
1066
|
+
'tests': 'Test files and testing utilities',
|
|
1067
|
+
'documentation': 'README files, docs, and guides',
|
|
1068
|
+
'other': 'Additional project files'
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
return descriptions[type] || `Files categorized as ${type}`;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
generateFileXML(file) {
|
|
1075
|
+
const fullPath = join(this.CWD, file);
|
|
1076
|
+
let fileXml = ` <cntx:file path="${file}" ext="${extname(file)}">
|
|
1077
|
+
`;
|
|
1078
|
+
|
|
1079
|
+
try {
|
|
1080
|
+
const stat = statSync(fullPath);
|
|
1081
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
1082
|
+
|
|
1083
|
+
// Add role indicator for certain files
|
|
1084
|
+
const role = this.getFileRole(file);
|
|
1085
|
+
const roleAttr = role ? ` role="${role}"` : '';
|
|
1086
|
+
|
|
1087
|
+
fileXml = ` <cntx:file path="${file}" ext="${extname(file)}"${roleAttr}>
|
|
1088
|
+
`;
|
|
1089
|
+
fileXml += ` <cntx:meta size="${stat.size}" modified="${stat.mtime.toISOString()}" lines="${content.split('\n').length}" />
|
|
1090
|
+
<cntx:content><![CDATA[${content}]]></cntx:content>
|
|
1091
|
+
`;
|
|
1092
|
+
} catch (e) {
|
|
1093
|
+
fileXml += ` <cntx:error>Could not read file: ${e.message}</cntx:error>
|
|
1094
|
+
`;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
fileXml += ` </cntx:file>
|
|
1098
|
+
`;
|
|
1099
|
+
return fileXml;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
getFileRole(file) {
|
|
1103
|
+
const basename = file.toLowerCase();
|
|
1104
|
+
|
|
1105
|
+
if (basename.includes('main.') || basename.includes('index.')) return 'entry-point';
|
|
1106
|
+
if (basename.includes('app.')) return 'main-component';
|
|
1107
|
+
if (file === 'server.js') return 'server-entry';
|
|
1108
|
+
if (basename.includes('config')) return 'configuration';
|
|
1109
|
+
if (basename.includes('package.json')) return 'package-config';
|
|
1110
|
+
if (basename.includes('readme')) return 'documentation';
|
|
1111
|
+
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
|
|
779
1116
|
escapeXml(text) {
|
|
780
1117
|
return String(text)
|
|
781
1118
|
.replace(/&/g, '&')
|
|
@@ -1067,6 +1404,27 @@ Add your specific project rules and preferences below:
|
|
|
1067
1404
|
};
|
|
1068
1405
|
res.end(JSON.stringify(templates));
|
|
1069
1406
|
|
|
1407
|
+
} else if (url.pathname === '/api/claude-md') {
|
|
1408
|
+
if (req.method === 'GET') {
|
|
1409
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
1410
|
+
const claudeMd = this.loadClaudeMd();
|
|
1411
|
+
res.end(claudeMd);
|
|
1412
|
+
} else if (req.method === 'POST') {
|
|
1413
|
+
let body = '';
|
|
1414
|
+
req.on('data', chunk => body += chunk);
|
|
1415
|
+
req.on('end', () => {
|
|
1416
|
+
try {
|
|
1417
|
+
const { content } = JSON.parse(body);
|
|
1418
|
+
this.saveClaudeMd(content);
|
|
1419
|
+
res.writeHead(200);
|
|
1420
|
+
res.end('OK');
|
|
1421
|
+
} catch (e) {
|
|
1422
|
+
res.writeHead(400);
|
|
1423
|
+
res.end('Invalid request');
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1070
1428
|
} else if (url.pathname === '/api/test-pattern') {
|
|
1071
1429
|
if (req.method === 'POST') {
|
|
1072
1430
|
let body = '';
|
|
@@ -1222,6 +1580,26 @@ Add your specific project rules and preferences below:
|
|
|
1222
1580
|
|
|
1223
1581
|
res.end(JSON.stringify(stats));
|
|
1224
1582
|
|
|
1583
|
+
} else if (url.pathname.startsWith('/api/bundle-categories/')) {
|
|
1584
|
+
const bundleName = url.pathname.split('/').pop();
|
|
1585
|
+
const bundle = this.bundles.get(bundleName);
|
|
1586
|
+
|
|
1587
|
+
if (bundle) {
|
|
1588
|
+
const filesByType = this.categorizeFiles(bundle.files);
|
|
1589
|
+
const entryPoints = this.identifyEntryPoints(bundle.files);
|
|
1590
|
+
|
|
1591
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
1592
|
+
res.end(JSON.stringify({
|
|
1593
|
+
purpose: this.getBundlePurpose(bundleName),
|
|
1594
|
+
filesByType,
|
|
1595
|
+
entryPoints,
|
|
1596
|
+
totalFiles: bundle.files.length
|
|
1597
|
+
}));
|
|
1598
|
+
} else {
|
|
1599
|
+
res.writeHead(404);
|
|
1600
|
+
res.end('Bundle not found');
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1225
1603
|
} else if (url.pathname === '/api/reset-hidden-files') {
|
|
1226
1604
|
if (req.method === 'POST') {
|
|
1227
1605
|
let body = '';
|
|
@@ -1305,6 +1683,13 @@ export function startServer(options = {}) {
|
|
|
1305
1683
|
return server.startServer(options.port);
|
|
1306
1684
|
}
|
|
1307
1685
|
|
|
1686
|
+
export function startMCPServer(options = {}) {
|
|
1687
|
+
const server = new CntxServer(options.cwd);
|
|
1688
|
+
server.init();
|
|
1689
|
+
startMCPTransport(server);
|
|
1690
|
+
return server;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1308
1693
|
export function generateBundle(name = 'master', cwd = process.cwd()) {
|
|
1309
1694
|
const server = new CntxServer(cwd);
|
|
1310
1695
|
server.init();
|