cntx-ui 2.0.13 → 3.0.0

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 (49) hide show
  1. package/README.md +51 -339
  2. package/VISION.md +110 -0
  3. package/bin/cntx-ui-mcp.sh +3 -0
  4. package/bin/cntx-ui.js +138 -55
  5. package/lib/agent-runtime.js +301 -0
  6. package/lib/agent-tools.js +370 -0
  7. package/lib/api-router.js +1161 -0
  8. package/lib/bundle-manager.js +236 -0
  9. package/lib/configuration-manager.js +760 -0
  10. package/lib/database-manager.js +397 -0
  11. package/lib/file-system-manager.js +489 -0
  12. package/lib/heuristics-manager.js +527 -0
  13. package/lib/mcp-server.js +1125 -2
  14. package/lib/semantic-splitter.js +225 -491
  15. package/lib/simple-vector-store.js +98 -0
  16. package/lib/websocket-manager.js +470 -0
  17. package/package.json +19 -25
  18. package/server.js +742 -1935
  19. package/templates/TOOLS.md +41 -0
  20. package/templates/activities/README.md +67 -0
  21. package/templates/activities/activities/create-project-bundles/README.md +84 -0
  22. package/templates/activities/activities/create-project-bundles/notes.md +98 -0
  23. package/templates/activities/activities/create-project-bundles/progress.md +63 -0
  24. package/templates/activities/activities/create-project-bundles/tasks.md +39 -0
  25. package/templates/activities/activities.json +219 -0
  26. package/templates/activities/lib/.markdownlint.jsonc +18 -0
  27. package/templates/activities/lib/create-activity.mdc +63 -0
  28. package/templates/activities/lib/generate-tasks.mdc +64 -0
  29. package/templates/activities/lib/process-task-list.mdc +52 -0
  30. package/templates/agent-config.yaml +65 -0
  31. package/templates/agent-instructions.md +234 -0
  32. package/templates/agent-rules/capabilities/activities-system.md +147 -0
  33. package/templates/agent-rules/capabilities/bundle-system.md +131 -0
  34. package/templates/agent-rules/capabilities/vector-search.md +135 -0
  35. package/templates/agent-rules/core/codebase-navigation.md +91 -0
  36. package/templates/agent-rules/core/performance-hierarchy.md +48 -0
  37. package/templates/agent-rules/core/response-formatting.md +120 -0
  38. package/templates/agent-rules/project-specific/architecture.md +145 -0
  39. package/templates/config.json +76 -0
  40. package/templates/hidden-files.json +14 -0
  41. package/web/dist/assets/index-B2OdTzzI.css +1 -0
  42. package/web/dist/assets/index-D0tBsKiR.js +2016 -0
  43. package/web/dist/cntx-ui.svg +18 -0
  44. package/web/dist/index.html +25 -8
  45. package/lib/semantic-integration.js +0 -441
  46. package/mcp-config-example.json +0 -9
  47. package/web/dist/assets/index-Ci1Q-YrQ.js +0 -611
  48. package/web/dist/assets/index-IUp4q_fr.css +0 -1
  49. package/web/dist/vite.svg +0 -21
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Bundle Manager for cntx-ui
3
+ * Handles Smart Dynamic Bundles and traditional XML generation
4
+ */
5
+
6
+ import { readFileSync, statSync } from 'fs';
7
+ import { relative, extname, basename, dirname } from 'path';
8
+
9
+ export default class BundleManager {
10
+ constructor(configManager, fileSystemManager, verbose = false) {
11
+ this.configManager = configManager;
12
+ this.fileSystemManager = fileSystemManager;
13
+ this.db = configManager.dbManager;
14
+ this.verbose = verbose;
15
+ this._isScanning = false;
16
+ }
17
+
18
+ /**
19
+ * Get all bundle information, including Smart Dynamic Bundles
20
+ */
21
+ getAllBundleInfo() {
22
+ if (this.verbose) console.log('📦 Getting all bundle info...');
23
+ const manualBundles = Array.from(this.configManager.getBundles().entries()).map(([name, bundle]) => ({
24
+ name,
25
+ fileCount: bundle.files?.length || 0,
26
+ size: bundle.size || 0,
27
+ generated: bundle.generated,
28
+ changed: bundle.changed,
29
+ patterns: bundle.patterns,
30
+ type: 'manual'
31
+ }));
32
+
33
+ if (this.verbose) console.log(`📦 Found ${manualBundles.length} manual bundles`);
34
+
35
+ const smartBundles = this.generateSmartBundleDefinitions();
36
+ if (this.verbose) console.log(`📦 Found ${smartBundles.length} smart bundle definitions`);
37
+
38
+ // Filter out smart bundles that have no files
39
+ const activeSmartBundles = smartBundles.map(b => {
40
+ const files = this.resolveSmartBundle(b.name);
41
+ return {
42
+ ...b,
43
+ fileCount: files.length,
44
+ files
45
+ };
46
+ }).filter(b => b.fileCount > 0);
47
+
48
+ if (this.verbose) console.log(`📦 Active smart bundles: ${activeSmartBundles.length}`);
49
+
50
+ return [...manualBundles, ...activeSmartBundles];
51
+ }
52
+
53
+ /**
54
+ * Generate Smart Bundle definitions from indexed semantic data
55
+ */
56
+ generateSmartBundleDefinitions() {
57
+ const smartBundles = [];
58
+ try {
59
+ // 1. Group by Purpose (Heuristics)
60
+ const purposeRows = this.db.db.prepare('SELECT DISTINCT purpose, COUNT(*) as count FROM semantic_chunks GROUP BY purpose').all();
61
+ purposeRows.forEach(row => {
62
+ if (!row.purpose) return;
63
+ const name = `smart:${row.purpose.toLowerCase().replace(/\s+/g, '-')}`;
64
+ smartBundles.push({
65
+ name,
66
+ purpose: row.purpose,
67
+ fileCount: row.count,
68
+ type: 'smart',
69
+ description: `Automatically grouped by purpose: ${row.purpose}`
70
+ });
71
+ });
72
+
73
+ // 2. Group by Component Types (Subtypes)
74
+ const subtypeRows = this.db.db.prepare('SELECT DISTINCT subtype, COUNT(*) as count FROM semantic_chunks GROUP BY subtype').all();
75
+ subtypeRows.forEach(row => {
76
+ if (!row.subtype) return;
77
+ const name = `smart:type-${row.subtype.toLowerCase().replace(/_/g, '-')}`;
78
+ smartBundles.push({
79
+ name,
80
+ purpose: row.subtype,
81
+ fileCount: row.count,
82
+ type: 'smart',
83
+ description: `All ${row.subtype} elements across the codebase`
84
+ });
85
+ });
86
+ } catch (e) {
87
+ if (this.verbose) console.warn('Smart bundle discovery failed:', e.message);
88
+ }
89
+ return smartBundles;
90
+ }
91
+
92
+ /**
93
+ * Resolve files for a bundle (Manual or Smart)
94
+ */
95
+ async resolveBundleFiles(bundleName) {
96
+ if (bundleName.startsWith('smart:')) {
97
+ return this.resolveSmartBundle(bundleName);
98
+ }
99
+
100
+ const bundle = this.configManager.getBundles().get(bundleName);
101
+ if (!bundle) return [];
102
+
103
+ const allFiles = this.fileSystemManager.getAllFiles();
104
+ return allFiles.filter(file =>
105
+ bundle.patterns.some(pattern => this.fileSystemManager.matchesPattern(file, pattern))
106
+ ).map(f => this.fileSystemManager.relativePath(f));
107
+ }
108
+
109
+ /**
110
+ * Resolve a Smart Bundle query against SQLite
111
+ */
112
+ resolveSmartBundle(bundleName) {
113
+ const query = bundleName.replace('smart:', '');
114
+ let rows = [];
115
+
116
+ if (query.startsWith('type-')) {
117
+ const type = query.replace('type-', '').replace(/-/g, '_');
118
+ rows = this.db.db.prepare('SELECT DISTINCT file_path FROM semantic_chunks WHERE LOWER(subtype) = ?').all(type);
119
+ } else {
120
+ const purposeRows = this.db.db.prepare('SELECT DISTINCT purpose FROM semantic_chunks').all();
121
+ const matched = purposeRows.find(r => r.purpose?.toLowerCase().replace(/\s+/g, '-') === query);
122
+ if (matched) {
123
+ rows = this.db.db.prepare('SELECT DISTINCT file_path FROM semantic_chunks WHERE purpose = ?').all(matched.purpose);
124
+ }
125
+ }
126
+ return rows.map(r => r.file_path);
127
+ }
128
+
129
+ // === Bundle Generation ===
130
+
131
+ async generateAllBundles() {
132
+ this._isScanning = true;
133
+ try {
134
+ const bundles = this.configManager.getBundles();
135
+ for (const [name] of bundles) {
136
+ await this.regenerateBundle(name);
137
+ }
138
+ } finally {
139
+ this._isScanning = false;
140
+ }
141
+ }
142
+
143
+ async regenerateBundle(bundleName) {
144
+ if (this.verbose) console.log(`🔄 Regenerating bundle: ${bundleName}`);
145
+
146
+ const files = await this.resolveBundleFiles(bundleName);
147
+ const content = await this.generateBundleXML(bundleName, files);
148
+
149
+ const bundleData = {
150
+ files,
151
+ content,
152
+ size: Buffer.byteLength(content, 'utf8'),
153
+ generated: new Date().toISOString(),
154
+ changed: false
155
+ };
156
+
157
+ if (!bundleName.startsWith('smart:')) {
158
+ const existing = this.configManager.getBundles().get(bundleName);
159
+ this.configManager.getBundles().set(bundleName, { ...existing, ...bundleData });
160
+ this.configManager.saveBundleStates();
161
+ }
162
+
163
+ return bundleData;
164
+ }
165
+
166
+ async generateBundleXML(bundleName, relativeFiles) {
167
+ const projectInfo = this.getProjectInfo();
168
+ let xml = `<?xml version="1.0" encoding="UTF-8"?>\n<codebase>\n`;
169
+ xml += ` <project_info>\n <name>${this.escapeXml(projectInfo.name)}</name>\n <bundle>${this.escapeXml(bundleName)}</bundle>\n </project_info>\n`;
170
+
171
+ for (const relPath of relativeFiles) {
172
+ xml += await this.generateFileXML(relPath);
173
+ }
174
+
175
+ xml += `</codebase>`;
176
+ return xml;
177
+ }
178
+
179
+ async generateFileXML(relativePath) {
180
+ try {
181
+ const fullPath = this.fileSystemManager.fullPath(relativePath);
182
+ const content = readFileSync(fullPath, 'utf8');
183
+ const chunks = this.db.getChunksByFile(relativePath);
184
+
185
+ let xml = ` <file path="${this.escapeXml(relativePath)}">\n`;
186
+ if (chunks.length > 0) {
187
+ xml += ` <semantic_context>\n`;
188
+ chunks.forEach(c => {
189
+ xml += ` <chunk name="${this.escapeXml(c.name)}" purpose="${this.escapeXml(c.purpose)}" complexity="${c.complexity?.score || 0}" />\n`;
190
+ });
191
+ xml += ` </semantic_context>\n`;
192
+ }
193
+ xml += ` <content><![CDATA[${content}]]></content>\n </file>\n`;
194
+ return xml;
195
+ } catch (e) {
196
+ return ` <file path="${this.escapeXml(relativePath)}" error="${this.escapeXml(e.message)}" />\n`;
197
+ }
198
+ }
199
+
200
+ getProjectInfo() {
201
+ try {
202
+ const pkg = JSON.parse(readFileSync(this.fileSystemManager.fullPath('package.json'), 'utf8'));
203
+ return { name: pkg.name || 'Unknown', version: pkg.version || '1.0.0' };
204
+ } catch {
205
+ return { name: 'Unknown', version: '1.0.0' };
206
+ }
207
+ }
208
+
209
+ escapeXml(text) {
210
+ return String(text).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
211
+ }
212
+
213
+ getBundleContent(bundleName) {
214
+ if (bundleName.startsWith('smart:')) {
215
+ // For smart bundles, we generate on the fly if not cached
216
+ return this.regenerateBundle(bundleName).then(data => data.content);
217
+ }
218
+ const bundle = this.configManager.getBundles().get(bundleName);
219
+ return bundle ? bundle.content : null;
220
+ }
221
+
222
+ markBundlesChanged(filename) {
223
+ this.configManager.getBundles().forEach((bundle, name) => {
224
+ if (bundle.patterns?.some(p => this.fileSystemManager.matchesPattern(filename, p))) {
225
+ bundle.changed = true;
226
+ }
227
+ });
228
+ }
229
+
230
+ getBundleInfo(bundleName) {
231
+ if (bundleName.startsWith('smart:')) {
232
+ return this.generateSmartBundleDefinitions().find(b => b.name === bundleName);
233
+ }
234
+ return this.configManager.getBundles().get(bundleName);
235
+ }
236
+ }