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.
- package/README.md +51 -339
- package/VISION.md +110 -0
- package/bin/cntx-ui-mcp.sh +3 -0
- package/bin/cntx-ui.js +138 -55
- package/lib/agent-runtime.js +301 -0
- package/lib/agent-tools.js +370 -0
- package/lib/api-router.js +1161 -0
- package/lib/bundle-manager.js +236 -0
- package/lib/configuration-manager.js +760 -0
- package/lib/database-manager.js +397 -0
- package/lib/file-system-manager.js +489 -0
- package/lib/heuristics-manager.js +527 -0
- package/lib/mcp-server.js +1125 -2
- package/lib/semantic-splitter.js +225 -491
- package/lib/simple-vector-store.js +98 -0
- package/lib/websocket-manager.js +470 -0
- package/package.json +19 -25
- package/server.js +742 -1935
- package/templates/TOOLS.md +41 -0
- package/templates/activities/README.md +67 -0
- package/templates/activities/activities/create-project-bundles/README.md +84 -0
- package/templates/activities/activities/create-project-bundles/notes.md +98 -0
- package/templates/activities/activities/create-project-bundles/progress.md +63 -0
- package/templates/activities/activities/create-project-bundles/tasks.md +39 -0
- package/templates/activities/activities.json +219 -0
- package/templates/activities/lib/.markdownlint.jsonc +18 -0
- package/templates/activities/lib/create-activity.mdc +63 -0
- package/templates/activities/lib/generate-tasks.mdc +64 -0
- package/templates/activities/lib/process-task-list.mdc +52 -0
- package/templates/agent-config.yaml +65 -0
- package/templates/agent-instructions.md +234 -0
- package/templates/agent-rules/capabilities/activities-system.md +147 -0
- package/templates/agent-rules/capabilities/bundle-system.md +131 -0
- package/templates/agent-rules/capabilities/vector-search.md +135 -0
- package/templates/agent-rules/core/codebase-navigation.md +91 -0
- package/templates/agent-rules/core/performance-hierarchy.md +48 -0
- package/templates/agent-rules/core/response-formatting.md +120 -0
- package/templates/agent-rules/project-specific/architecture.md +145 -0
- package/templates/config.json +76 -0
- package/templates/hidden-files.json +14 -0
- package/web/dist/assets/index-B2OdTzzI.css +1 -0
- package/web/dist/assets/index-D0tBsKiR.js +2016 -0
- package/web/dist/cntx-ui.svg +18 -0
- package/web/dist/index.html +25 -8
- package/lib/semantic-integration.js +0 -441
- package/mcp-config-example.json +0 -9
- package/web/dist/assets/index-Ci1Q-YrQ.js +0 -611
- package/web/dist/assets/index-IUp4q_fr.css +0 -1
- 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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
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
|
+
}
|