cntx-ui 3.0.7 → 3.0.9

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 (36) hide show
  1. package/dist/bin/cntx-ui.js +70 -0
  2. package/dist/lib/agent-runtime.js +269 -0
  3. package/dist/lib/agent-tools.js +162 -0
  4. package/dist/lib/api-router.js +387 -0
  5. package/dist/lib/bundle-manager.js +236 -0
  6. package/dist/lib/configuration-manager.js +230 -0
  7. package/dist/lib/database-manager.js +277 -0
  8. package/dist/lib/file-system-manager.js +305 -0
  9. package/dist/lib/function-level-chunker.js +144 -0
  10. package/dist/lib/heuristics-manager.js +491 -0
  11. package/dist/lib/mcp-server.js +159 -0
  12. package/dist/lib/mcp-transport.js +10 -0
  13. package/dist/lib/semantic-splitter.js +335 -0
  14. package/dist/lib/simple-vector-store.js +98 -0
  15. package/dist/lib/treesitter-semantic-chunker.js +277 -0
  16. package/dist/lib/websocket-manager.js +268 -0
  17. package/dist/server.js +225 -0
  18. package/package.json +18 -8
  19. package/bin/cntx-ui-mcp.sh +0 -3
  20. package/bin/cntx-ui.js +0 -123
  21. package/lib/agent-runtime.js +0 -371
  22. package/lib/agent-tools.js +0 -370
  23. package/lib/api-router.js +0 -1026
  24. package/lib/bundle-manager.js +0 -326
  25. package/lib/configuration-manager.js +0 -760
  26. package/lib/database-manager.js +0 -397
  27. package/lib/file-system-manager.js +0 -489
  28. package/lib/function-level-chunker.js +0 -406
  29. package/lib/heuristics-manager.js +0 -529
  30. package/lib/mcp-server.js +0 -1380
  31. package/lib/mcp-transport.js +0 -97
  32. package/lib/semantic-splitter.js +0 -304
  33. package/lib/simple-vector-store.js +0 -108
  34. package/lib/treesitter-semantic-chunker.js +0 -1485
  35. package/lib/websocket-manager.js +0 -470
  36. package/server.js +0 -687
@@ -1,370 +0,0 @@
1
- /**
2
- * Agent Tools for Codebase Exploration
3
- * Built on top of existing cntx-ui infrastructure
4
- */
5
-
6
- import { readFileSync, existsSync, statSync } from 'fs';
7
- import { join, relative, extname } from 'path';
8
- import fs from 'fs';
9
- import path from 'path';
10
- import { exec } from 'child_process';
11
- import { promisify } from 'util';
12
-
13
- const execAsync = promisify(exec);
14
-
15
- export class AgentTools {
16
- constructor(cntxServer) {
17
- this.cntxServer = cntxServer;
18
- }
19
-
20
- /**
21
- * Read file contents with bundle context
22
- */
23
- async readFile(filePath, options = {}) {
24
- try {
25
- const fullPath = join(this.cntxServer.CWD, filePath);
26
-
27
- if (!existsSync(fullPath)) {
28
- throw new Error(`File not found: ${filePath}`);
29
- }
30
-
31
- const content = readFileSync(fullPath, 'utf8');
32
- const bundles = this.getFileBundles(filePath);
33
-
34
- return {
35
- path: filePath,
36
- content: options.truncate ? this.truncateContent(content, options.maxLength) : content,
37
- size: content.length,
38
- lines: content.split('\n').length,
39
- bundles,
40
- mimeType: this.getMimeType(filePath)
41
- };
42
- } catch (error) {
43
- throw new Error(`Failed to read file ${filePath}: ${error.message}`);
44
- }
45
- }
46
-
47
- /**
48
- * List files with bundle awareness and filtering
49
- */
50
- async listFiles(options = {}) {
51
- const { bundle, pattern, type, limit = 100 } = options;
52
-
53
- try {
54
- let files = [];
55
-
56
- if (bundle) {
57
- const bundleObj = this.cntxServer.bundles.get(bundle);
58
- if (!bundleObj) {
59
- throw new Error(`Bundle '${bundle}' not found`);
60
- }
61
- files = bundleObj.files.map(f => ({
62
- path: f,
63
- bundle,
64
- size: this.getFileSize(f),
65
- type: this.getFileType(f)
66
- }));
67
- } else {
68
- // Get all files across bundles
69
- const allFiles = this.cntxServer.getAllFiles();
70
- files = allFiles.map(f => ({
71
- path: f,
72
- bundles: this.getFileBundles(f),
73
- size: this.getFileSize(f),
74
- type: this.getFileType(f)
75
- }));
76
- }
77
-
78
- // Apply filters
79
- if (pattern) {
80
- const regex = new RegExp(pattern, 'i');
81
- files = files.filter(f => regex.test(f.path));
82
- }
83
-
84
- if (type) {
85
- files = files.filter(f => f.type === type);
86
- }
87
-
88
- // Limit results
89
- return files.slice(0, limit);
90
- } catch (error) {
91
- throw new Error(`Failed to list files: ${error.message}`);
92
- }
93
- }
94
-
95
- /**
96
- * Search semantic chunks using existing vector search
97
- */
98
- async searchSemanticChunks(query, options = {}) {
99
- try {
100
- const analysis = await this.cntxServer.getSemanticAnalysis();
101
- if (!analysis || !analysis.chunks) {
102
- return { chunks: [], message: 'No semantic analysis available' };
103
- }
104
-
105
- let chunks = analysis.chunks;
106
- const { bundle, type, complexity, maxResults = 10 } = options;
107
-
108
- // Apply semantic search if vector store is available
109
- if (this.cntxServer.vectorStoreInitialized) {
110
- try {
111
- const searchResults = await this.cntxServer.vectorStore.search(query, { limit: maxResults * 2 });
112
- const chunkIds = searchResults.map(r => r.id || r.chunkId).filter(Boolean);
113
- chunks = chunks.filter(c => chunkIds.includes(c.id));
114
- } catch (error) {
115
- // Fall back to text-based search
116
- chunks = chunks.filter(c =>
117
- c.purpose?.toLowerCase().includes(query.toLowerCase()) ||
118
- c.name?.toLowerCase().includes(query.toLowerCase()) ||
119
- c.code?.toLowerCase().includes(query.toLowerCase())
120
- );
121
- }
122
- } else {
123
- // Text-based semantic search
124
- chunks = chunks.filter(c =>
125
- c.purpose?.toLowerCase().includes(query.toLowerCase()) ||
126
- c.name?.toLowerCase().includes(query.toLowerCase()) ||
127
- c.code?.toLowerCase().includes(query.toLowerCase())
128
- );
129
- }
130
-
131
- // Apply filters
132
- if (bundle) {
133
- chunks = chunks.filter(c => c.bundles && c.bundles.includes(bundle));
134
- }
135
-
136
- if (type) {
137
- chunks = chunks.filter(c => c.subtype === type);
138
- }
139
-
140
- if (complexity) {
141
- chunks = chunks.filter(c => c.complexity?.level === complexity);
142
- }
143
-
144
- // Clean and limit results
145
- const cleanChunks = chunks.slice(0, maxResults).map(chunk => ({
146
- ...chunk,
147
- code: this.truncateContent(chunk.code, 500),
148
- bundles: chunk.bundles || [],
149
- relevanceScore: chunk.score || 0
150
- }));
151
-
152
- return {
153
- query,
154
- chunks: cleanChunks,
155
- totalResults: chunks.length,
156
- hasMore: chunks.length > maxResults
157
- };
158
- } catch (error) {
159
- throw new Error(`Semantic search failed: ${error.message}`);
160
- }
161
- }
162
-
163
- /**
164
- * Get bundle information
165
- */
166
- async getBundle(bundleName) {
167
- try {
168
- const bundle = this.cntxServer.bundles.get(bundleName);
169
- if (!bundle) {
170
- throw new Error(`Bundle '${bundleName}' not found`);
171
- }
172
-
173
- return {
174
- name: bundleName,
175
- patterns: bundle.patterns,
176
- files: bundle.files,
177
- fileCount: bundle.files.length,
178
- size: bundle.size,
179
- lastGenerated: bundle.lastGenerated,
180
- changed: bundle.changed,
181
- content: bundle.content ? this.truncateContent(bundle.content, 2000) : null
182
- };
183
- } catch (error) {
184
- throw new Error(`Failed to get bundle: ${error.message}`);
185
- }
186
- }
187
-
188
- /**
189
- * Parse AST using existing tree-sitter infrastructure
190
- */
191
- async parseAST(filePath, options = {}) {
192
- try {
193
- const fullPath = join(this.cntxServer.CWD, filePath);
194
-
195
- if (!existsSync(fullPath)) {
196
- throw new Error(`File not found: ${filePath}`);
197
- }
198
-
199
- // Use existing semantic analysis if available
200
- const analysis = await this.cntxServer.getSemanticAnalysis();
201
- const fileChunks = analysis?.chunks?.filter(c => c.filePath === filePath) || [];
202
-
203
- if (fileChunks.length > 0) {
204
- return {
205
- file: filePath,
206
- chunks: fileChunks.map(chunk => ({
207
- name: chunk.name,
208
- type: chunk.subtype,
209
- purpose: chunk.purpose,
210
- startLine: chunk.startLine,
211
- endLine: chunk.endLine,
212
- complexity: chunk.complexity,
213
- isExported: chunk.isExported,
214
- isAsync: chunk.isAsync,
215
- imports: chunk.includes?.imports || [],
216
- dependencies: chunk.dependencies || []
217
- }))
218
- };
219
- }
220
-
221
- // Fallback: basic file info
222
- const content = readFileSync(fullPath, 'utf8');
223
- return {
224
- file: filePath,
225
- lines: content.split('\n').length,
226
- size: content.length,
227
- type: this.getFileType(filePath),
228
- message: 'AST parsing requires semantic analysis to be run first'
229
- };
230
- } catch (error) {
231
- throw new Error(`AST parsing failed: ${error.message}`);
232
- }
233
- }
234
-
235
- /**
236
- * Execute safe CLI commands (restricted set)
237
- */
238
- async runCommand(command, options = {}) {
239
- const { timeout = 10000, cwd } = options;
240
-
241
- // Whitelist of safe commands
242
- const safeCommands = [
243
- 'ls', 'find', 'grep', 'wc', 'head', 'tail',
244
- 'git status', 'git log', 'git diff', 'git branch',
245
- 'npm list', 'npm outdated', 'npm audit',
246
- 'node --version', 'npm --version'
247
- ];
248
-
249
- const isCommandSafe = safeCommands.some(safe => command.startsWith(safe));
250
-
251
- if (!isCommandSafe) {
252
- throw new Error(`Command not allowed: ${command}. Only safe read-only commands are permitted.`);
253
- }
254
-
255
- try {
256
- const { stdout, stderr } = await execAsync(command, {
257
- cwd: cwd || this.cntxServer.CWD,
258
- timeout,
259
- maxBuffer: 1024 * 1024 // 1MB limit
260
- });
261
-
262
- return {
263
- command,
264
- stdout: stdout.trim(),
265
- stderr: stderr.trim(),
266
- success: true
267
- };
268
- } catch (error) {
269
- return {
270
- command,
271
- stdout: error.stdout || '',
272
- stderr: error.stderr || error.message,
273
- success: false,
274
- error: error.message
275
- };
276
- }
277
- }
278
-
279
- /**
280
- * Get full semantic analysis
281
- */
282
- async getSemanticAnalysis(options = {}) {
283
- try {
284
- const analysis = await this.cntxServer.getSemanticAnalysis();
285
-
286
- if (!analysis) {
287
- return { message: 'No semantic analysis available. Run semantic analysis first.' };
288
- }
289
-
290
- const { includeCode = false, maxChunks = 50 } = options;
291
-
292
- return {
293
- timestamp: analysis.timestamp,
294
- summary: analysis.summary,
295
- chunks: analysis.chunks.slice(0, maxChunks).map(chunk => ({
296
- ...chunk,
297
- code: includeCode ? chunk.code : this.truncateContent(chunk.code, 200),
298
- bundles: chunk.bundles || []
299
- })),
300
- totalChunks: analysis.chunks?.length || 0,
301
- truncated: analysis.chunks?.length > maxChunks
302
- };
303
- } catch (error) {
304
- throw new Error(`Failed to get semantic analysis: ${error.message}`);
305
- }
306
- }
307
-
308
- // Helper methods
309
-
310
- getFileBundles(filePath) {
311
- const bundles = [];
312
- for (const [bundleName, bundle] of this.cntxServer.bundles) {
313
- if (bundle.files.includes(filePath)) {
314
- bundles.push(bundleName);
315
- }
316
- }
317
- return bundles;
318
- }
319
-
320
- getFileSize(filePath) {
321
- try {
322
- const fullPath = join(this.cntxServer.CWD, filePath);
323
- const stats = statSync(fullPath);
324
- return stats.size;
325
- } catch {
326
- return 0;
327
- }
328
- }
329
-
330
- getFileType(filePath) {
331
- const ext = extname(filePath).toLowerCase();
332
- const typeMap = {
333
- '.js': 'javascript',
334
- '.jsx': 'javascript',
335
- '.ts': 'typescript',
336
- '.tsx': 'typescript',
337
- '.json': 'json',
338
- '.md': 'markdown',
339
- '.css': 'css',
340
- '.html': 'html',
341
- '.py': 'python',
342
- '.java': 'java',
343
- '.go': 'go',
344
- '.rs': 'rust'
345
- };
346
- return typeMap[ext] || 'text';
347
- }
348
-
349
- getMimeType(filePath) {
350
- const ext = path.extname(filePath).toLowerCase();
351
- const mimeTypes = {
352
- '.js': 'application/javascript',
353
- '.jsx': 'application/javascript',
354
- '.ts': 'application/typescript',
355
- '.tsx': 'application/typescript',
356
- '.json': 'application/json',
357
- '.md': 'text/markdown',
358
- '.css': 'text/css',
359
- '.html': 'text/html'
360
- };
361
- return mimeTypes[ext] || 'text/plain';
362
- }
363
-
364
- truncateContent(content, maxLength = 1000) {
365
- if (!content || content.length <= maxLength) return content;
366
- return content.substring(0, maxLength) + '...';
367
- }
368
- }
369
-
370
- export default AgentTools;