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.
- package/dist/bin/cntx-ui.js +70 -0
- package/dist/lib/agent-runtime.js +269 -0
- package/dist/lib/agent-tools.js +162 -0
- package/dist/lib/api-router.js +387 -0
- package/dist/lib/bundle-manager.js +236 -0
- package/dist/lib/configuration-manager.js +230 -0
- package/dist/lib/database-manager.js +277 -0
- package/dist/lib/file-system-manager.js +305 -0
- package/dist/lib/function-level-chunker.js +144 -0
- package/dist/lib/heuristics-manager.js +491 -0
- package/dist/lib/mcp-server.js +159 -0
- package/dist/lib/mcp-transport.js +10 -0
- package/dist/lib/semantic-splitter.js +335 -0
- package/dist/lib/simple-vector-store.js +98 -0
- package/dist/lib/treesitter-semantic-chunker.js +277 -0
- package/dist/lib/websocket-manager.js +268 -0
- package/dist/server.js +225 -0
- package/package.json +18 -8
- package/bin/cntx-ui-mcp.sh +0 -3
- package/bin/cntx-ui.js +0 -123
- package/lib/agent-runtime.js +0 -371
- package/lib/agent-tools.js +0 -370
- package/lib/api-router.js +0 -1026
- package/lib/bundle-manager.js +0 -326
- package/lib/configuration-manager.js +0 -760
- package/lib/database-manager.js +0 -397
- package/lib/file-system-manager.js +0 -489
- package/lib/function-level-chunker.js +0 -406
- package/lib/heuristics-manager.js +0 -529
- package/lib/mcp-server.js +0 -1380
- package/lib/mcp-transport.js +0 -97
- package/lib/semantic-splitter.js +0 -304
- package/lib/simple-vector-store.js +0 -108
- package/lib/treesitter-semantic-chunker.js +0 -1485
- package/lib/websocket-manager.js +0 -470
- package/server.js +0 -687
package/lib/mcp-server.js
DELETED
|
@@ -1,1380 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, statSync, mkdirSync, copyFileSync } from 'fs';
|
|
2
|
-
import { join, relative, dirname } from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
import fs from 'fs';
|
|
5
|
-
import AgentRuntime from './agent-runtime.js';
|
|
6
|
-
|
|
7
|
-
export class MCPServer {
|
|
8
|
-
constructor(cntxServer) {
|
|
9
|
-
this.cntxServer = cntxServer;
|
|
10
|
-
this.clientCapabilities = null;
|
|
11
|
-
this.serverInfo = {
|
|
12
|
-
name: 'cntx-ui',
|
|
13
|
-
version: this.getServerVersion()
|
|
14
|
-
};
|
|
15
|
-
this.agentRuntime = new AgentRuntime(cntxServer);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
getServerVersion() {
|
|
19
|
-
try {
|
|
20
|
-
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
const packagePath = join(currentDir, '..', 'package.json');
|
|
22
|
-
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
23
|
-
return packageJson.version || '0.0.0';
|
|
24
|
-
} catch (error) {
|
|
25
|
-
return '0.0.0';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// JSON-RPC 2.0 message handler
|
|
30
|
-
async handleMessage(message) {
|
|
31
|
-
try {
|
|
32
|
-
const request = typeof message === 'string' ? JSON.parse(message) : message;
|
|
33
|
-
|
|
34
|
-
// Handle JSON-RPC 2.0 format
|
|
35
|
-
if (!request.jsonrpc || request.jsonrpc !== '2.0') {
|
|
36
|
-
return this.createErrorResponse(null, -32600, 'Invalid Request');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const response = await this.routeRequest(request);
|
|
40
|
-
return response;
|
|
41
|
-
} catch (error) {
|
|
42
|
-
return this.createErrorResponse(null, -32700, 'Parse error');
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async routeRequest(request) {
|
|
47
|
-
const { method, params, id } = request;
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
switch (method) {
|
|
51
|
-
case 'initialize':
|
|
52
|
-
return this.handleInitialize(params, id);
|
|
53
|
-
|
|
54
|
-
case 'initialized':
|
|
55
|
-
case 'notifications/initialized':
|
|
56
|
-
return null; // No response needed for notification
|
|
57
|
-
|
|
58
|
-
case 'resources/list':
|
|
59
|
-
return this.handleListResources(id);
|
|
60
|
-
|
|
61
|
-
case 'resources/read':
|
|
62
|
-
return this.handleReadResource(params, id);
|
|
63
|
-
|
|
64
|
-
case 'tools/list':
|
|
65
|
-
return this.handleListTools(id);
|
|
66
|
-
|
|
67
|
-
case 'tools/call':
|
|
68
|
-
return this.handleCallTool(params, id);
|
|
69
|
-
|
|
70
|
-
case 'prompts/list':
|
|
71
|
-
return this.handleListPrompts(id);
|
|
72
|
-
|
|
73
|
-
case 'prompts/get':
|
|
74
|
-
return this.handleGetPrompt(params, id);
|
|
75
|
-
|
|
76
|
-
case 'prompts/list':
|
|
77
|
-
return this.createErrorResponse(id, -32601, 'Method not found');
|
|
78
|
-
|
|
79
|
-
default:
|
|
80
|
-
return this.createErrorResponse(id, -32601, 'Method not found');
|
|
81
|
-
}
|
|
82
|
-
} catch (error) {
|
|
83
|
-
return this.createErrorResponse(id, -32603, 'Internal error', error.message);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Initialize MCP session
|
|
88
|
-
handleInitialize(params, id) {
|
|
89
|
-
this.clientCapabilities = params?.capabilities || {};
|
|
90
|
-
|
|
91
|
-
return this.createSuccessResponse(id, {
|
|
92
|
-
protocolVersion: '2024-11-05',
|
|
93
|
-
capabilities: {
|
|
94
|
-
resources: {
|
|
95
|
-
subscribe: true,
|
|
96
|
-
listChanged: true
|
|
97
|
-
},
|
|
98
|
-
tools: {}
|
|
99
|
-
},
|
|
100
|
-
serverInfo: this.serverInfo
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// List available resources (bundles)
|
|
105
|
-
handleListResources(id) {
|
|
106
|
-
const resources = [];
|
|
107
|
-
|
|
108
|
-
this.cntxServer.bundles.forEach((bundle, name) => {
|
|
109
|
-
resources.push({
|
|
110
|
-
uri: `cntx://bundle/${name}`,
|
|
111
|
-
name: `Bundle: ${name}`,
|
|
112
|
-
description: `File bundle containing ${bundle.files.length} files`,
|
|
113
|
-
mimeType: 'application/xml'
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Add individual file resources
|
|
118
|
-
const allFiles = this.cntxServer.getAllFiles();
|
|
119
|
-
allFiles.slice(0, 100).forEach((filePath) => { // Limit to first 100 files
|
|
120
|
-
resources.push({
|
|
121
|
-
uri: `cntx://file/${filePath}`,
|
|
122
|
-
name: `File: ${filePath}`,
|
|
123
|
-
description: `Individual file: ${filePath}`,
|
|
124
|
-
mimeType: this.getMimeType(filePath)
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
return this.createSuccessResponse(id, {
|
|
129
|
-
resources
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Read a specific resource
|
|
134
|
-
handleReadResource(params, id) {
|
|
135
|
-
const { uri } = params;
|
|
136
|
-
|
|
137
|
-
if (!uri || !uri.startsWith('cntx://')) {
|
|
138
|
-
return this.createErrorResponse(id, -32602, 'Invalid URI');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
if (uri.startsWith('cntx://bundle/')) {
|
|
143
|
-
const bundleName = uri.replace('cntx://bundle/', '');
|
|
144
|
-
const bundle = this.cntxServer.bundles.get(bundleName);
|
|
145
|
-
|
|
146
|
-
if (!bundle) {
|
|
147
|
-
return this.createErrorResponse(id, -32602, 'Bundle not found');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return this.createSuccessResponse(id, {
|
|
151
|
-
contents: [{
|
|
152
|
-
uri,
|
|
153
|
-
mimeType: 'application/xml',
|
|
154
|
-
text: bundle.content
|
|
155
|
-
}]
|
|
156
|
-
});
|
|
157
|
-
} else if (uri.startsWith('cntx://file/')) {
|
|
158
|
-
const filePath = uri.replace('cntx://file/', '');
|
|
159
|
-
const fullPath = join(this.cntxServer.CWD, filePath);
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const content = readFileSync(fullPath, 'utf8');
|
|
163
|
-
return this.createSuccessResponse(id, {
|
|
164
|
-
contents: [{
|
|
165
|
-
uri,
|
|
166
|
-
mimeType: this.getMimeType(filePath),
|
|
167
|
-
text: content
|
|
168
|
-
}]
|
|
169
|
-
});
|
|
170
|
-
} catch (error) {
|
|
171
|
-
return this.createErrorResponse(id, -32602, 'File not found');
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return this.createErrorResponse(id, -32602, 'Invalid resource URI');
|
|
176
|
-
} catch (error) {
|
|
177
|
-
return this.createErrorResponse(id, -32603, 'Internal error reading resource');
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// List available prompts
|
|
182
|
-
handleListPrompts(id) {
|
|
183
|
-
const prompts = [
|
|
184
|
-
{
|
|
185
|
-
name: 'onboard-me',
|
|
186
|
-
description: 'Guided walkthrough of the current codebase architecture.',
|
|
187
|
-
arguments: []
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
name: 'refactor-planner',
|
|
191
|
-
description: 'Plan a refactor by analyzing semantic dependencies.',
|
|
192
|
-
arguments: [
|
|
193
|
-
{ name: 'target', description: 'The function or file to refactor', required: true }
|
|
194
|
-
]
|
|
195
|
-
}
|
|
196
|
-
];
|
|
197
|
-
return this.createSuccessResponse(id, { prompts });
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Get a specific prompt
|
|
201
|
-
handleGetPrompt(params, id) {
|
|
202
|
-
const { name, arguments: args } = params;
|
|
203
|
-
|
|
204
|
-
if (name === 'onboard-me') {
|
|
205
|
-
return this.createSuccessResponse(id, {
|
|
206
|
-
description: 'Codebase Onboarding',
|
|
207
|
-
messages: [
|
|
208
|
-
{
|
|
209
|
-
role: 'user',
|
|
210
|
-
content: {
|
|
211
|
-
type: 'text',
|
|
212
|
-
text: 'Please perform a comprehensive discovery of this codebase using the `agent/discover` tool. Focus on identifying the primary entry points and the core architectural patterns. Use the results to explain how the project is organized.'
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
]
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (name === 'refactor-planner') {
|
|
220
|
-
return this.createSuccessResponse(id, {
|
|
221
|
-
description: 'Refactor Planning',
|
|
222
|
-
messages: [
|
|
223
|
-
{
|
|
224
|
-
role: 'user',
|
|
225
|
-
content: {
|
|
226
|
-
type: 'text',
|
|
227
|
-
text: `I want to refactor ${args.target}. Use the \`agent/query\` tool to find all semantic chunks related to this target and the \`agent/investigate\` tool to identify potential impact on other modules. Present a step-by-step refactoring plan.`
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
]
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return this.createErrorResponse(id, -32602, 'Prompt not found');
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// List available tools
|
|
238
|
-
handleListTools(id) {
|
|
239
|
-
const tools = [
|
|
240
|
-
{
|
|
241
|
-
name: 'list_bundles',
|
|
242
|
-
description: 'List all available file bundles',
|
|
243
|
-
inputSchema: {
|
|
244
|
-
type: 'object',
|
|
245
|
-
properties: {},
|
|
246
|
-
required: []
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
name: 'get_bundle',
|
|
251
|
-
description: 'Get the content of a specific bundle',
|
|
252
|
-
inputSchema: {
|
|
253
|
-
type: 'object',
|
|
254
|
-
properties: {
|
|
255
|
-
name: {
|
|
256
|
-
type: 'string',
|
|
257
|
-
description: 'Name of the bundle to retrieve'
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
required: ['name']
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
name: 'generate_bundle',
|
|
265
|
-
description: 'Regenerate a specific bundle',
|
|
266
|
-
inputSchema: {
|
|
267
|
-
type: 'object',
|
|
268
|
-
properties: {
|
|
269
|
-
name: {
|
|
270
|
-
type: 'string',
|
|
271
|
-
description: 'Name of the bundle to regenerate'
|
|
272
|
-
}
|
|
273
|
-
},
|
|
274
|
-
required: ['name']
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
{
|
|
278
|
-
name: 'get_file_tree',
|
|
279
|
-
description: 'Get the project file tree',
|
|
280
|
-
inputSchema: {
|
|
281
|
-
type: 'object',
|
|
282
|
-
properties: {},
|
|
283
|
-
required: []
|
|
284
|
-
}
|
|
285
|
-
},
|
|
286
|
-
{
|
|
287
|
-
name: 'get_project_status',
|
|
288
|
-
description: 'Get current project status and bundle information',
|
|
289
|
-
inputSchema: {
|
|
290
|
-
type: 'object',
|
|
291
|
-
properties: {},
|
|
292
|
-
required: []
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
name: 'get_semantic_chunks',
|
|
297
|
-
description: 'Get function-level semantic chunks from the codebase',
|
|
298
|
-
inputSchema: {
|
|
299
|
-
type: 'object',
|
|
300
|
-
properties: {},
|
|
301
|
-
required: []
|
|
302
|
-
}
|
|
303
|
-
},
|
|
304
|
-
{
|
|
305
|
-
name: 'get_semantic_chunks_filtered',
|
|
306
|
-
description: 'Get semantic chunks filtered by purpose, type, complexity, or bundle',
|
|
307
|
-
inputSchema: {
|
|
308
|
-
type: 'object',
|
|
309
|
-
properties: {
|
|
310
|
-
purpose: {
|
|
311
|
-
type: 'string',
|
|
312
|
-
description: 'Filter by function purpose (e.g., "API handler", "React component", "Data retrieval")'
|
|
313
|
-
},
|
|
314
|
-
type: {
|
|
315
|
-
type: 'string',
|
|
316
|
-
description: 'Filter by function type (e.g., "arrow_function", "react_component", "method")'
|
|
317
|
-
},
|
|
318
|
-
complexity: {
|
|
319
|
-
type: 'string',
|
|
320
|
-
description: 'Filter by complexity level ("low", "medium", "high")'
|
|
321
|
-
},
|
|
322
|
-
bundle: {
|
|
323
|
-
type: 'string',
|
|
324
|
-
description: 'Filter by bundle membership'
|
|
325
|
-
},
|
|
326
|
-
exported: {
|
|
327
|
-
type: 'boolean',
|
|
328
|
-
description: 'Filter by export status'
|
|
329
|
-
},
|
|
330
|
-
async: {
|
|
331
|
-
type: 'boolean',
|
|
332
|
-
description: 'Filter by async functions'
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
required: []
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
{
|
|
339
|
-
name: 'analyze_bundle_suggestions',
|
|
340
|
-
description: 'Analyze codebase and suggest optimal bundle organization based on semantic chunks',
|
|
341
|
-
inputSchema: {
|
|
342
|
-
type: 'object',
|
|
343
|
-
properties: {
|
|
344
|
-
max_suggestions: {
|
|
345
|
-
type: 'number',
|
|
346
|
-
description: 'Maximum number of bundle suggestions to return (default: 5)'
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
required: []
|
|
350
|
-
}
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
name: 'create_bundle',
|
|
354
|
-
description: 'Create a new bundle with specified patterns',
|
|
355
|
-
inputSchema: {
|
|
356
|
-
type: 'object',
|
|
357
|
-
properties: {
|
|
358
|
-
name: {
|
|
359
|
-
type: 'string',
|
|
360
|
-
description: 'Name of the new bundle'
|
|
361
|
-
},
|
|
362
|
-
patterns: {
|
|
363
|
-
type: 'array',
|
|
364
|
-
items: { type: 'string' },
|
|
365
|
-
description: 'Array of glob patterns for the bundle (e.g., ["src/api/**", "src/services/**"])'
|
|
366
|
-
},
|
|
367
|
-
description: {
|
|
368
|
-
type: 'string',
|
|
369
|
-
description: 'Optional description of the bundle purpose'
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
required: ['name', 'patterns']
|
|
373
|
-
}
|
|
374
|
-
},
|
|
375
|
-
{
|
|
376
|
-
name: 'update_bundle',
|
|
377
|
-
description: 'Update an existing bundle\'s patterns',
|
|
378
|
-
inputSchema: {
|
|
379
|
-
type: 'object',
|
|
380
|
-
properties: {
|
|
381
|
-
name: {
|
|
382
|
-
type: 'string',
|
|
383
|
-
description: 'Name of the bundle to update'
|
|
384
|
-
},
|
|
385
|
-
patterns: {
|
|
386
|
-
type: 'array',
|
|
387
|
-
items: { type: 'string' },
|
|
388
|
-
description: 'New array of glob patterns for the bundle'
|
|
389
|
-
},
|
|
390
|
-
description: {
|
|
391
|
-
type: 'string',
|
|
392
|
-
description: 'Optional updated description'
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
required: ['name', 'patterns']
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
{
|
|
399
|
-
name: 'delete_bundle',
|
|
400
|
-
description: 'Delete an existing bundle',
|
|
401
|
-
inputSchema: {
|
|
402
|
-
type: 'object',
|
|
403
|
-
properties: {
|
|
404
|
-
name: {
|
|
405
|
-
type: 'string',
|
|
406
|
-
description: 'Name of the bundle to delete'
|
|
407
|
-
}
|
|
408
|
-
},
|
|
409
|
-
required: ['name']
|
|
410
|
-
}
|
|
411
|
-
},
|
|
412
|
-
{
|
|
413
|
-
name: 'update_cntxignore',
|
|
414
|
-
description: 'Update the .cntxignore file with new ignore patterns',
|
|
415
|
-
inputSchema: {
|
|
416
|
-
type: 'object',
|
|
417
|
-
properties: {
|
|
418
|
-
content: {
|
|
419
|
-
type: 'string',
|
|
420
|
-
description: 'Full content for the .cntxignore file (newline-separated patterns)'
|
|
421
|
-
}
|
|
422
|
-
},
|
|
423
|
-
required: ['content']
|
|
424
|
-
}
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
name: 'agent_discover',
|
|
428
|
-
description: 'Agent Discovery Mode: Get comprehensive codebase overview including bundles, architecture, and patterns',
|
|
429
|
-
inputSchema: {
|
|
430
|
-
type: 'object',
|
|
431
|
-
properties: {
|
|
432
|
-
scope: {
|
|
433
|
-
type: 'string',
|
|
434
|
-
description: 'Scope of discovery: "all" for full codebase or specific bundle name (default: "all")'
|
|
435
|
-
},
|
|
436
|
-
includeDetails: {
|
|
437
|
-
type: 'boolean',
|
|
438
|
-
description: 'Include detailed semantic analysis and complexity metrics (default: true)'
|
|
439
|
-
}
|
|
440
|
-
},
|
|
441
|
-
required: []
|
|
442
|
-
}
|
|
443
|
-
},
|
|
444
|
-
{
|
|
445
|
-
name: 'agent_query',
|
|
446
|
-
description: 'Agent Query Mode: Answer specific questions about the codebase using semantic search and analysis',
|
|
447
|
-
inputSchema: {
|
|
448
|
-
type: 'object',
|
|
449
|
-
properties: {
|
|
450
|
-
question: {
|
|
451
|
-
type: 'string',
|
|
452
|
-
description: 'The question to answer about the codebase (e.g., "Where is user authentication handled?")'
|
|
453
|
-
},
|
|
454
|
-
scope: {
|
|
455
|
-
type: 'string',
|
|
456
|
-
description: 'Optional bundle to limit search scope'
|
|
457
|
-
},
|
|
458
|
-
maxResults: {
|
|
459
|
-
type: 'number',
|
|
460
|
-
description: 'Maximum number of results to return (default: 10)'
|
|
461
|
-
},
|
|
462
|
-
includeCode: {
|
|
463
|
-
type: 'boolean',
|
|
464
|
-
description: 'Include code snippets in the response (default: false)'
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
required: ['question']
|
|
468
|
-
}
|
|
469
|
-
},
|
|
470
|
-
{
|
|
471
|
-
name: 'agent_investigate',
|
|
472
|
-
description: 'Agent Investigation Mode: Investigate existing implementations for a feature and find integration points',
|
|
473
|
-
inputSchema: {
|
|
474
|
-
type: 'object',
|
|
475
|
-
properties: {
|
|
476
|
-
featureDescription: {
|
|
477
|
-
type: 'string',
|
|
478
|
-
description: 'Description of the feature to investigate (e.g., "dark mode", "user authentication", "form validation")'
|
|
479
|
-
},
|
|
480
|
-
includeRecommendations: {
|
|
481
|
-
type: 'boolean',
|
|
482
|
-
description: 'Include implementation recommendations and approach suggestions (default: true)'
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
required: ['featureDescription']
|
|
486
|
-
}
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
name: 'agent_discuss',
|
|
490
|
-
description: 'Agent Passive Mode: Engage in discussion about codebase architecture, design decisions, and planning',
|
|
491
|
-
inputSchema: {
|
|
492
|
-
type: 'object',
|
|
493
|
-
properties: {
|
|
494
|
-
userInput: {
|
|
495
|
-
type: 'string',
|
|
496
|
-
description: 'The topic or question for discussion (e.g., "Let\'s discuss the architecture before I make changes")'
|
|
497
|
-
},
|
|
498
|
-
context: {
|
|
499
|
-
type: 'object',
|
|
500
|
-
description: 'Additional context for the discussion',
|
|
501
|
-
properties: {
|
|
502
|
-
scope: {
|
|
503
|
-
type: 'string',
|
|
504
|
-
description: 'Specific area of focus (e.g., "frontend", "api", "database")'
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
},
|
|
509
|
-
required: ['userInput']
|
|
510
|
-
}
|
|
511
|
-
},
|
|
512
|
-
{
|
|
513
|
-
name: 'agent_organize',
|
|
514
|
-
description: 'Agent Project Organizer Mode: Setup and maintenance of project organization - adapts to project maturity',
|
|
515
|
-
inputSchema: {
|
|
516
|
-
type: 'object',
|
|
517
|
-
properties: {
|
|
518
|
-
activity: {
|
|
519
|
-
type: 'string',
|
|
520
|
-
enum: ['detect', 'analyze', 'bundle', 'create', 'optimize', 'audit', 'cleanup', 'validate'],
|
|
521
|
-
description: 'Activity to perform: detect project state, analyze semantics, suggest bundles, create bundles, optimize organization, audit health, cleanup issues, or validate structure'
|
|
522
|
-
},
|
|
523
|
-
autoDetect: {
|
|
524
|
-
type: 'boolean',
|
|
525
|
-
description: 'Automatically detect appropriate activity based on project state (default: true)',
|
|
526
|
-
default: true
|
|
527
|
-
},
|
|
528
|
-
force: {
|
|
529
|
-
type: 'boolean',
|
|
530
|
-
description: 'Force execution even if preconditions are not met (default: false)',
|
|
531
|
-
default: false
|
|
532
|
-
}
|
|
533
|
-
},
|
|
534
|
-
required: []
|
|
535
|
-
}
|
|
536
|
-
},
|
|
537
|
-
{
|
|
538
|
-
name: 'read_file',
|
|
539
|
-
description: 'Read contents of a specific file with bundle context and metadata',
|
|
540
|
-
inputSchema: {
|
|
541
|
-
type: 'object',
|
|
542
|
-
properties: {
|
|
543
|
-
path: {
|
|
544
|
-
type: 'string',
|
|
545
|
-
description: 'File path relative to project root'
|
|
546
|
-
},
|
|
547
|
-
includeMetadata: {
|
|
548
|
-
type: 'boolean',
|
|
549
|
-
description: 'Include file metadata (size, bundles, etc.) - default: true'
|
|
550
|
-
}
|
|
551
|
-
},
|
|
552
|
-
required: ['path']
|
|
553
|
-
}
|
|
554
|
-
},
|
|
555
|
-
{
|
|
556
|
-
name: 'write_file',
|
|
557
|
-
description: 'Write content to a file with validation and safety checks',
|
|
558
|
-
inputSchema: {
|
|
559
|
-
type: 'object',
|
|
560
|
-
properties: {
|
|
561
|
-
path: {
|
|
562
|
-
type: 'string',
|
|
563
|
-
description: 'File path relative to project root'
|
|
564
|
-
},
|
|
565
|
-
content: {
|
|
566
|
-
type: 'string',
|
|
567
|
-
description: 'Content to write to the file'
|
|
568
|
-
},
|
|
569
|
-
backup: {
|
|
570
|
-
type: 'boolean',
|
|
571
|
-
description: 'Create backup before writing - default: true'
|
|
572
|
-
},
|
|
573
|
-
createDirs: {
|
|
574
|
-
type: 'boolean',
|
|
575
|
-
description: 'Create parent directories if they don\'t exist - default: true'
|
|
576
|
-
}
|
|
577
|
-
},
|
|
578
|
-
required: ['path', 'content']
|
|
579
|
-
}
|
|
580
|
-
},
|
|
581
|
-
];
|
|
582
|
-
|
|
583
|
-
return this.createSuccessResponse(id, { tools });
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// Handle tool execution
|
|
587
|
-
async handleCallTool(params, id) {
|
|
588
|
-
const { name, arguments: args = {} } = params;
|
|
589
|
-
|
|
590
|
-
try {
|
|
591
|
-
switch (name) {
|
|
592
|
-
case 'list_bundles':
|
|
593
|
-
return this.toolListBundles(id);
|
|
594
|
-
|
|
595
|
-
case 'get_bundle':
|
|
596
|
-
return this.toolGetBundle(args, id);
|
|
597
|
-
|
|
598
|
-
case 'generate_bundle':
|
|
599
|
-
return this.toolGenerateBundle(args, id);
|
|
600
|
-
|
|
601
|
-
case 'get_file_tree':
|
|
602
|
-
return this.toolGetFileTree(id);
|
|
603
|
-
|
|
604
|
-
case 'get_project_status':
|
|
605
|
-
return this.toolGetProjectStatus(id);
|
|
606
|
-
|
|
607
|
-
case 'get_semantic_chunks':
|
|
608
|
-
return this.toolGetSemanticChunks(id);
|
|
609
|
-
|
|
610
|
-
case 'get_semantic_chunks_filtered':
|
|
611
|
-
return this.toolGetSemanticChunksFiltered(args, id);
|
|
612
|
-
|
|
613
|
-
case 'analyze_bundle_suggestions':
|
|
614
|
-
return this.toolAnalyzeBundleSuggestions(args, id);
|
|
615
|
-
|
|
616
|
-
case 'create_bundle':
|
|
617
|
-
return this.toolCreateBundle(args, id);
|
|
618
|
-
|
|
619
|
-
case 'update_bundle':
|
|
620
|
-
return this.toolUpdateBundle(args, id);
|
|
621
|
-
|
|
622
|
-
case 'delete_bundle':
|
|
623
|
-
return this.toolDeleteBundle(args, id);
|
|
624
|
-
|
|
625
|
-
case 'update_cntxignore':
|
|
626
|
-
return this.toolUpdateCntxignore(args, id);
|
|
627
|
-
|
|
628
|
-
case 'agent_discover':
|
|
629
|
-
return this.toolAgentDiscover(args, id);
|
|
630
|
-
|
|
631
|
-
case 'agent_query':
|
|
632
|
-
return this.toolAgentQuery(args, id);
|
|
633
|
-
|
|
634
|
-
case 'agent_investigate':
|
|
635
|
-
return this.toolAgentInvestigate(args, id);
|
|
636
|
-
|
|
637
|
-
case 'agent_discuss':
|
|
638
|
-
return this.toolAgentDiscuss(args, id);
|
|
639
|
-
|
|
640
|
-
case 'agent_organize':
|
|
641
|
-
return this.toolAgentOrganize(args, id);
|
|
642
|
-
|
|
643
|
-
case 'read_file':
|
|
644
|
-
return this.toolReadFile(args, id);
|
|
645
|
-
|
|
646
|
-
case 'write_file':
|
|
647
|
-
return this.toolWriteFile(args, id);
|
|
648
|
-
|
|
649
|
-
default:
|
|
650
|
-
return this.createErrorResponse(id, -32602, 'Unknown tool');
|
|
651
|
-
}
|
|
652
|
-
} catch (error) {
|
|
653
|
-
return this.createErrorResponse(id, -32603, 'Tool execution failed', error.message);
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// Tool implementations
|
|
658
|
-
toolListBundles(id) {
|
|
659
|
-
const bundles = [];
|
|
660
|
-
this.cntxServer.bundles.forEach((bundle, name) => {
|
|
661
|
-
bundles.push({
|
|
662
|
-
name,
|
|
663
|
-
fileCount: bundle.files.length,
|
|
664
|
-
size: bundle.size,
|
|
665
|
-
lastGenerated: bundle.lastGenerated,
|
|
666
|
-
changed: bundle.changed,
|
|
667
|
-
patterns: bundle.patterns
|
|
668
|
-
});
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
return this.createSuccessResponse(id, {
|
|
672
|
-
content: [{
|
|
673
|
-
type: 'text',
|
|
674
|
-
text: `Available bundles:\n${bundles.map(b =>
|
|
675
|
-
`• ${b.name}: ${b.fileCount} files (${(b.size / 1024).toFixed(1)}KB) ${b.changed ? '[CHANGED]' : '[SYNCED]'}`
|
|
676
|
-
).join('\n')}`
|
|
677
|
-
}]
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
toolGetBundle(args, id) {
|
|
682
|
-
const { name } = args;
|
|
683
|
-
const bundle = this.cntxServer.bundles.get(name);
|
|
684
|
-
|
|
685
|
-
if (!bundle) {
|
|
686
|
-
return this.createErrorResponse(id, -32602, `Bundle '${name}' not found`);
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
return this.createSuccessResponse(id, {
|
|
690
|
-
content: [{
|
|
691
|
-
type: 'text',
|
|
692
|
-
text: bundle.content
|
|
693
|
-
}]
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
toolGenerateBundle(args, id) {
|
|
698
|
-
const { name } = args;
|
|
699
|
-
|
|
700
|
-
if (!this.cntxServer.bundles.has(name)) {
|
|
701
|
-
return this.createErrorResponse(id, -32602, `Bundle '${name}' not found`);
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
this.cntxServer.generateBundle(name);
|
|
705
|
-
this.cntxServer.saveBundleStates();
|
|
706
|
-
|
|
707
|
-
const bundle = this.cntxServer.bundles.get(name);
|
|
708
|
-
|
|
709
|
-
return this.createSuccessResponse(id, {
|
|
710
|
-
content: [{
|
|
711
|
-
type: 'text',
|
|
712
|
-
text: `Bundle '${name}' regenerated successfully. Contains ${bundle.files.length} files (${(bundle.size / 1024).toFixed(1)}KB).`
|
|
713
|
-
}]
|
|
714
|
-
});
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
toolGetFileTree(id) {
|
|
718
|
-
const fileTree = this.cntxServer.getFileTree();
|
|
719
|
-
const treeText = fileTree.map(file =>
|
|
720
|
-
`${file.path} (${(file.size / 1024).toFixed(1)}KB)`
|
|
721
|
-
).join('\n');
|
|
722
|
-
|
|
723
|
-
return this.createSuccessResponse(id, {
|
|
724
|
-
content: [{
|
|
725
|
-
type: 'text',
|
|
726
|
-
text: `Project file tree:\n${treeText}`
|
|
727
|
-
}]
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
toolGetProjectStatus(id) {
|
|
732
|
-
const bundleCount = this.cntxServer.bundles.size;
|
|
733
|
-
const changedBundles = Array.from(this.cntxServer.bundles.entries())
|
|
734
|
-
.filter(([_, bundle]) => bundle.changed)
|
|
735
|
-
.map(([name, _]) => name);
|
|
736
|
-
|
|
737
|
-
const statusText = `Project Status:
|
|
738
|
-
Working Directory: ${relative(process.cwd(), this.cntxServer.CWD)}
|
|
739
|
-
Total Bundles: ${bundleCount}
|
|
740
|
-
Changed Bundles: ${changedBundles.length > 0 ? changedBundles.join(', ') : 'None'}
|
|
741
|
-
|
|
742
|
-
Bundle Details:
|
|
743
|
-
${Array.from(this.cntxServer.bundles.entries()).map(([name, bundle]) =>
|
|
744
|
-
`• ${name}: ${bundle.files.length} files, ${(bundle.size / 1024).toFixed(1)}KB ${bundle.changed ? '[CHANGED]' : '[SYNCED]'}`
|
|
745
|
-
).join('\n')}`;
|
|
746
|
-
|
|
747
|
-
return this.createSuccessResponse(id, {
|
|
748
|
-
content: [{
|
|
749
|
-
type: 'text',
|
|
750
|
-
text: statusText
|
|
751
|
-
}]
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
// New semantic chunks tools
|
|
756
|
-
async toolGetSemanticChunks(id) {
|
|
757
|
-
try {
|
|
758
|
-
const analysis = await this.cntxServer.getSemanticAnalysis();
|
|
759
|
-
|
|
760
|
-
// Clean the analysis data to prevent JSON issues
|
|
761
|
-
const cleanAnalysis = {
|
|
762
|
-
...analysis,
|
|
763
|
-
chunks: analysis.chunks?.map(chunk => ({
|
|
764
|
-
...chunk,
|
|
765
|
-
code: chunk.code ? chunk.code.substring(0, 500) + (chunk.code.length > 500 ? '...' : '') : '',
|
|
766
|
-
bundles: chunk.bundles || [],
|
|
767
|
-
includes: {
|
|
768
|
-
imports: chunk.includes?.imports || [],
|
|
769
|
-
types: chunk.includes?.types || []
|
|
770
|
-
}
|
|
771
|
-
})) || []
|
|
772
|
-
};
|
|
773
|
-
|
|
774
|
-
return this.createSuccessResponse(id, {
|
|
775
|
-
content: [{
|
|
776
|
-
type: 'text',
|
|
777
|
-
text: JSON.stringify(cleanAnalysis, null, 2)
|
|
778
|
-
}]
|
|
779
|
-
});
|
|
780
|
-
} catch (error) {
|
|
781
|
-
return this.createErrorResponse(id, -32603, 'Failed to get semantic chunks', error.message);
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
async toolGetSemanticChunksFiltered(args, id) {
|
|
786
|
-
try {
|
|
787
|
-
const analysis = await this.cntxServer.getSemanticAnalysis();
|
|
788
|
-
let chunks = analysis.chunks || [];
|
|
789
|
-
|
|
790
|
-
// Apply filters
|
|
791
|
-
if (args.purpose) {
|
|
792
|
-
chunks = chunks.filter(chunk =>
|
|
793
|
-
chunk.purpose && chunk.purpose.toLowerCase().includes(args.purpose.toLowerCase())
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
if (args.type) {
|
|
798
|
-
chunks = chunks.filter(chunk => chunk.subtype === args.type);
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
if (args.complexity) {
|
|
802
|
-
chunks = chunks.filter(chunk => chunk.complexity?.level === args.complexity);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
if (args.bundle) {
|
|
806
|
-
chunks = chunks.filter(chunk =>
|
|
807
|
-
chunk.bundles && chunk.bundles.includes(args.bundle)
|
|
808
|
-
);
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (args.exported !== undefined) {
|
|
812
|
-
chunks = chunks.filter(chunk => chunk.isExported === args.exported);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
if (args.async !== undefined) {
|
|
816
|
-
chunks = chunks.filter(chunk => chunk.isAsync === args.async);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Clean chunks for JSON safety
|
|
820
|
-
const cleanChunks = chunks.map(chunk => ({
|
|
821
|
-
...chunk,
|
|
822
|
-
code: chunk.code ? chunk.code.substring(0, 300) + (chunk.code.length > 300 ? '...' : '') : '',
|
|
823
|
-
bundles: chunk.bundles || [],
|
|
824
|
-
includes: {
|
|
825
|
-
imports: chunk.includes?.imports || [],
|
|
826
|
-
types: chunk.includes?.types || []
|
|
827
|
-
}
|
|
828
|
-
}));
|
|
829
|
-
|
|
830
|
-
const filteredAnalysis = {
|
|
831
|
-
...analysis,
|
|
832
|
-
chunks: cleanChunks,
|
|
833
|
-
summary: {
|
|
834
|
-
...analysis.summary,
|
|
835
|
-
totalChunks: cleanChunks.length,
|
|
836
|
-
filteredCount: cleanChunks.length,
|
|
837
|
-
originalCount: analysis.chunks?.length || 0
|
|
838
|
-
}
|
|
839
|
-
};
|
|
840
|
-
|
|
841
|
-
return this.createSuccessResponse(id, {
|
|
842
|
-
content: [{
|
|
843
|
-
type: 'text',
|
|
844
|
-
text: JSON.stringify(filteredAnalysis, null, 2)
|
|
845
|
-
}]
|
|
846
|
-
});
|
|
847
|
-
} catch (error) {
|
|
848
|
-
return this.createErrorResponse(id, -32603, 'Failed to filter semantic chunks', error.message);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
async toolAnalyzeBundleSuggestions(args, id) {
|
|
853
|
-
try {
|
|
854
|
-
const analysis = await this.cntxServer.getSemanticAnalysis();
|
|
855
|
-
const chunks = analysis.chunks || [];
|
|
856
|
-
const maxSuggestions = args.max_suggestions || 5;
|
|
857
|
-
|
|
858
|
-
// Group chunks by purpose and file location
|
|
859
|
-
const purposeGroups = {};
|
|
860
|
-
const locationGroups = {};
|
|
861
|
-
|
|
862
|
-
chunks.forEach(chunk => {
|
|
863
|
-
// Group by purpose
|
|
864
|
-
if (!purposeGroups[chunk.purpose]) {
|
|
865
|
-
purposeGroups[chunk.purpose] = [];
|
|
866
|
-
}
|
|
867
|
-
purposeGroups[chunk.purpose].push(chunk);
|
|
868
|
-
|
|
869
|
-
// Group by file location patterns
|
|
870
|
-
const pathParts = chunk.filePath.split('/');
|
|
871
|
-
if (pathParts.length > 1) {
|
|
872
|
-
const dirPattern = pathParts.slice(0, -1).join('/') + '/**';
|
|
873
|
-
if (!locationGroups[dirPattern]) {
|
|
874
|
-
locationGroups[dirPattern] = [];
|
|
875
|
-
}
|
|
876
|
-
locationGroups[dirPattern].push(chunk);
|
|
877
|
-
}
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
const suggestions = [];
|
|
881
|
-
|
|
882
|
-
// Suggest bundles by purpose
|
|
883
|
-
Object.entries(purposeGroups).forEach(([purpose, chunks]) => {
|
|
884
|
-
if (chunks.length >= 3) { // Only suggest if enough functions
|
|
885
|
-
const bundleName = purpose.toLowerCase().replace(/\s+/g, '-');
|
|
886
|
-
const patterns = [...new Set(chunks.map(c => {
|
|
887
|
-
const dir = c.filePath.split('/').slice(0, -1).join('/');
|
|
888
|
-
return dir ? `${dir}/**` : c.filePath;
|
|
889
|
-
}))];
|
|
890
|
-
|
|
891
|
-
suggestions.push({
|
|
892
|
-
name: bundleName,
|
|
893
|
-
reason: `Groups ${chunks.length} functions with purpose: ${purpose}`,
|
|
894
|
-
patterns,
|
|
895
|
-
chunkCount: chunks.length,
|
|
896
|
-
files: [...new Set(chunks.map(c => c.filePath))]
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
|
|
901
|
-
// Suggest bundles by common directory patterns
|
|
902
|
-
Object.entries(locationGroups).forEach(([pattern, chunks]) => {
|
|
903
|
-
if (chunks.length >= 5) { // Only suggest if enough functions in same location
|
|
904
|
-
const dirName = pattern.split('/').pop().replace('/**', '');
|
|
905
|
-
const bundleName = dirName === '*' ? 'utils' : dirName;
|
|
906
|
-
|
|
907
|
-
suggestions.push({
|
|
908
|
-
name: bundleName,
|
|
909
|
-
reason: `Groups ${chunks.length} functions from ${pattern}`,
|
|
910
|
-
patterns: [pattern],
|
|
911
|
-
chunkCount: chunks.length,
|
|
912
|
-
files: [...new Set(chunks.map(c => c.filePath))]
|
|
913
|
-
});
|
|
914
|
-
}
|
|
915
|
-
});
|
|
916
|
-
|
|
917
|
-
// Sort by chunk count and take top suggestions
|
|
918
|
-
const topSuggestions = suggestions
|
|
919
|
-
.sort((a, b) => b.chunkCount - a.chunkCount)
|
|
920
|
-
.slice(0, maxSuggestions);
|
|
921
|
-
|
|
922
|
-
const result = {
|
|
923
|
-
totalSuggestions: suggestions.length,
|
|
924
|
-
suggestions: topSuggestions,
|
|
925
|
-
analysis: {
|
|
926
|
-
totalChunks: chunks.length,
|
|
927
|
-
purposeGroups: Object.keys(purposeGroups).length,
|
|
928
|
-
locationGroups: Object.keys(locationGroups).length
|
|
929
|
-
}
|
|
930
|
-
};
|
|
931
|
-
|
|
932
|
-
return this.createSuccessResponse(id, {
|
|
933
|
-
content: [{
|
|
934
|
-
type: 'text',
|
|
935
|
-
text: JSON.stringify(result, null, 2)
|
|
936
|
-
}]
|
|
937
|
-
});
|
|
938
|
-
} catch (error) {
|
|
939
|
-
return this.createErrorResponse(id, -32603, 'Failed to analyze bundle suggestions', error.message);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// Bundle management tools
|
|
944
|
-
async toolCreateBundle(args, id) {
|
|
945
|
-
try {
|
|
946
|
-
const { name, patterns, description } = args;
|
|
947
|
-
|
|
948
|
-
if (!name || !patterns || !Array.isArray(patterns)) {
|
|
949
|
-
return this.createErrorResponse(id, -32602, 'Invalid arguments: name and patterns array required');
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
// Prevent overwriting existing bundles
|
|
953
|
-
if (this.cntxServer.bundles.has(name)) {
|
|
954
|
-
return this.createErrorResponse(id, -32602, `Bundle '${name}' already exists`);
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
// Create bundle in bundle-states.json (single source of truth)
|
|
958
|
-
this.cntxServer.configManager.bundleStates.set(name, {
|
|
959
|
-
patterns: patterns,
|
|
960
|
-
files: [],
|
|
961
|
-
content: '',
|
|
962
|
-
changed: false,
|
|
963
|
-
size: 0,
|
|
964
|
-
generated: null
|
|
965
|
-
});
|
|
966
|
-
|
|
967
|
-
// Save bundle states
|
|
968
|
-
this.cntxServer.configManager.saveBundleStates();
|
|
969
|
-
|
|
970
|
-
// Regenerate bundles
|
|
971
|
-
this.cntxServer.generateAllBundles();
|
|
972
|
-
|
|
973
|
-
const result = {
|
|
974
|
-
success: true,
|
|
975
|
-
bundle: {
|
|
976
|
-
name,
|
|
977
|
-
patterns,
|
|
978
|
-
description,
|
|
979
|
-
created: new Date().toISOString()
|
|
980
|
-
}
|
|
981
|
-
};
|
|
982
|
-
|
|
983
|
-
return this.createSuccessResponse(id, {
|
|
984
|
-
content: [{
|
|
985
|
-
type: 'text',
|
|
986
|
-
text: JSON.stringify(result, null, 2)
|
|
987
|
-
}]
|
|
988
|
-
});
|
|
989
|
-
} catch (error) {
|
|
990
|
-
return this.createErrorResponse(id, -32603, 'Failed to create bundle', error.message);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
async toolUpdateBundle(args, id) {
|
|
995
|
-
try {
|
|
996
|
-
const { name, patterns, description } = args;
|
|
997
|
-
|
|
998
|
-
if (!name || !patterns || !Array.isArray(patterns)) {
|
|
999
|
-
return this.createErrorResponse(id, -32602, 'Invalid arguments: name and patterns array required');
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
// Check if bundle exists
|
|
1003
|
-
if (!this.cntxServer.bundles.has(name)) {
|
|
1004
|
-
return this.createErrorResponse(id, -32602, `Bundle '${name}' not found`);
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
// Prevent updating master bundle
|
|
1008
|
-
if (name === 'master') {
|
|
1009
|
-
return this.createErrorResponse(id, -32602, 'Cannot update master bundle');
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
// Update bundle in bundle-states.json (single source of truth)
|
|
1013
|
-
const bundle = this.cntxServer.configManager.bundleStates.get(name);
|
|
1014
|
-
bundle.patterns = patterns;
|
|
1015
|
-
bundle.changed = true;
|
|
1016
|
-
|
|
1017
|
-
// Save bundle states
|
|
1018
|
-
this.cntxServer.configManager.saveBundleStates();
|
|
1019
|
-
|
|
1020
|
-
// Regenerate bundles
|
|
1021
|
-
this.cntxServer.generateAllBundles();
|
|
1022
|
-
|
|
1023
|
-
const result = {
|
|
1024
|
-
success: true,
|
|
1025
|
-
bundle: {
|
|
1026
|
-
name,
|
|
1027
|
-
patterns,
|
|
1028
|
-
description,
|
|
1029
|
-
updated: new Date().toISOString()
|
|
1030
|
-
}
|
|
1031
|
-
};
|
|
1032
|
-
|
|
1033
|
-
return this.createSuccessResponse(id, {
|
|
1034
|
-
content: [{
|
|
1035
|
-
type: 'text',
|
|
1036
|
-
text: JSON.stringify(result, null, 2)
|
|
1037
|
-
}]
|
|
1038
|
-
});
|
|
1039
|
-
} catch (error) {
|
|
1040
|
-
return this.createErrorResponse(id, -32603, 'Failed to update bundle', error.message);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
async toolDeleteBundle(args, id) {
|
|
1045
|
-
try {
|
|
1046
|
-
const { name } = args;
|
|
1047
|
-
|
|
1048
|
-
if (!name) {
|
|
1049
|
-
return this.createErrorResponse(id, -32602, 'Bundle name required');
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
// Check if bundle exists
|
|
1053
|
-
if (!this.cntxServer.bundles.has(name)) {
|
|
1054
|
-
return this.createErrorResponse(id, -32602, `Bundle '${name}' not found`);
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
// Prevent deleting master bundle
|
|
1058
|
-
if (name === 'master') {
|
|
1059
|
-
return this.createErrorResponse(id, -32602, 'Cannot delete master bundle');
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
// Remove bundle from bundle-states.json (single source of truth)
|
|
1063
|
-
this.cntxServer.configManager.bundleStates.delete(name);
|
|
1064
|
-
|
|
1065
|
-
// Save bundle states
|
|
1066
|
-
this.cntxServer.configManager.saveBundleStates();
|
|
1067
|
-
|
|
1068
|
-
// Regenerate bundles
|
|
1069
|
-
this.cntxServer.generateAllBundles();
|
|
1070
|
-
|
|
1071
|
-
const result = {
|
|
1072
|
-
success: true,
|
|
1073
|
-
deleted: name,
|
|
1074
|
-
timestamp: new Date().toISOString()
|
|
1075
|
-
};
|
|
1076
|
-
|
|
1077
|
-
return this.createSuccessResponse(id, {
|
|
1078
|
-
content: [{
|
|
1079
|
-
type: 'text',
|
|
1080
|
-
text: JSON.stringify(result, null, 2)
|
|
1081
|
-
}]
|
|
1082
|
-
});
|
|
1083
|
-
} catch (error) {
|
|
1084
|
-
return this.createErrorResponse(id, -32603, 'Failed to delete bundle', error.message);
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
async toolUpdateCntxignore(args, id) {
|
|
1089
|
-
try {
|
|
1090
|
-
const { content } = args;
|
|
1091
|
-
|
|
1092
|
-
if (content === undefined) {
|
|
1093
|
-
return this.createErrorResponse(id, -32602, 'Content required');
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
const ignorePath = join(this.cntxServer.CWD, '.cntxignore');
|
|
1097
|
-
|
|
1098
|
-
// Write the .cntxignore file
|
|
1099
|
-
writeFileSync(ignorePath, content);
|
|
1100
|
-
|
|
1101
|
-
// Reload ignore patterns
|
|
1102
|
-
this.cntxServer.loadIgnorePatterns();
|
|
1103
|
-
this.cntxServer.generateAllBundles();
|
|
1104
|
-
|
|
1105
|
-
const result = {
|
|
1106
|
-
success: true,
|
|
1107
|
-
file: '.cntxignore',
|
|
1108
|
-
lines: content.split('\n').length,
|
|
1109
|
-
patterns: content.split('\n').filter(line => line.trim() && !line.trim().startsWith('#')).length,
|
|
1110
|
-
updated: new Date().toISOString()
|
|
1111
|
-
};
|
|
1112
|
-
|
|
1113
|
-
return this.createSuccessResponse(id, {
|
|
1114
|
-
content: [{
|
|
1115
|
-
type: 'text',
|
|
1116
|
-
text: JSON.stringify(result, null, 2)
|
|
1117
|
-
}]
|
|
1118
|
-
});
|
|
1119
|
-
} catch (error) {
|
|
1120
|
-
return this.createErrorResponse(id, -32603, 'Failed to update .cntxignore', error.message);
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// Agent Tools Implementation
|
|
1125
|
-
async toolAgentDiscover(args, id) {
|
|
1126
|
-
try {
|
|
1127
|
-
const { scope = 'all', includeDetails = true } = args;
|
|
1128
|
-
const result = await this.agentRuntime.discoverCodebase({ scope, includeDetails });
|
|
1129
|
-
|
|
1130
|
-
return this.createSuccessResponse(id, {
|
|
1131
|
-
content: [{
|
|
1132
|
-
type: 'text',
|
|
1133
|
-
text: JSON.stringify(result, null, 2)
|
|
1134
|
-
}]
|
|
1135
|
-
});
|
|
1136
|
-
} catch (error) {
|
|
1137
|
-
return this.createErrorResponse(id, -32603, 'Agent discovery failed', error.message);
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
async toolAgentQuery(args, id) {
|
|
1142
|
-
try {
|
|
1143
|
-
const { question, scope, maxResults = 10, includeCode = false } = args;
|
|
1144
|
-
|
|
1145
|
-
if (!question) {
|
|
1146
|
-
return this.createErrorResponse(id, -32602, 'Question is required');
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
const result = await this.agentRuntime.answerQuery(question, { scope, maxResults, includeCode });
|
|
1150
|
-
|
|
1151
|
-
return this.createSuccessResponse(id, {
|
|
1152
|
-
content: [{
|
|
1153
|
-
type: 'text',
|
|
1154
|
-
text: JSON.stringify(result, null, 2)
|
|
1155
|
-
}]
|
|
1156
|
-
});
|
|
1157
|
-
} catch (error) {
|
|
1158
|
-
return this.createErrorResponse(id, -32603, 'Agent query failed', error.message);
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
async toolAgentInvestigate(args, id) {
|
|
1163
|
-
try {
|
|
1164
|
-
const { featureDescription, includeRecommendations = true } = args;
|
|
1165
|
-
|
|
1166
|
-
if (!featureDescription) {
|
|
1167
|
-
return this.createErrorResponse(id, -32602, 'Feature description is required');
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
const result = await this.agentRuntime.investigateFeature(featureDescription, { includeRecommendations });
|
|
1171
|
-
|
|
1172
|
-
return this.createSuccessResponse(id, {
|
|
1173
|
-
content: [{
|
|
1174
|
-
type: 'text',
|
|
1175
|
-
text: JSON.stringify(result, null, 2)
|
|
1176
|
-
}]
|
|
1177
|
-
});
|
|
1178
|
-
} catch (error) {
|
|
1179
|
-
return this.createErrorResponse(id, -32603, 'Agent investigation failed', error.message);
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
async toolAgentDiscuss(args, id) {
|
|
1184
|
-
try {
|
|
1185
|
-
const { userInput, context = {} } = args;
|
|
1186
|
-
|
|
1187
|
-
if (!userInput) {
|
|
1188
|
-
return this.createErrorResponse(id, -32602, 'User input is required');
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
const result = await this.agentRuntime.discussAndPlan(userInput, context);
|
|
1192
|
-
|
|
1193
|
-
return this.createSuccessResponse(id, {
|
|
1194
|
-
content: [{
|
|
1195
|
-
type: 'text',
|
|
1196
|
-
text: JSON.stringify(result, null, 2)
|
|
1197
|
-
}]
|
|
1198
|
-
});
|
|
1199
|
-
} catch (error) {
|
|
1200
|
-
return this.createErrorResponse(id, -32603, 'Agent discussion failed', error.message);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
async toolAgentOrganize(args, id) {
|
|
1205
|
-
try {
|
|
1206
|
-
const { activity = 'detect', autoDetect = true, force = false } = args;
|
|
1207
|
-
|
|
1208
|
-
const result = await this.agentRuntime.organizeProject({ activity, autoDetect, force });
|
|
1209
|
-
|
|
1210
|
-
return this.createSuccessResponse(id, {
|
|
1211
|
-
content: [{
|
|
1212
|
-
type: 'text',
|
|
1213
|
-
text: JSON.stringify(result, null, 2)
|
|
1214
|
-
}]
|
|
1215
|
-
});
|
|
1216
|
-
} catch (error) {
|
|
1217
|
-
return this.createErrorResponse(id, -32603, 'Agent organization failed', error.message);
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
// New tool implementations
|
|
1222
|
-
async toolReadFile(args, id) {
|
|
1223
|
-
const { path: filePath, includeMetadata = true } = args;
|
|
1224
|
-
|
|
1225
|
-
if (!filePath) {
|
|
1226
|
-
return this.createErrorResponse(id, -32602, 'Path is required');
|
|
1227
|
-
}
|
|
1228
|
-
|
|
1229
|
-
try {
|
|
1230
|
-
const fullPath = join(this.cntxServer.CWD, filePath);
|
|
1231
|
-
|
|
1232
|
-
if (!existsSync(fullPath)) {
|
|
1233
|
-
return this.createErrorResponse(id, -32602, `File not found: ${filePath}`);
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1236
|
-
const content = readFileSync(fullPath, 'utf8');
|
|
1237
|
-
|
|
1238
|
-
// Fetch semantic context for this file
|
|
1239
|
-
const chunks = this.cntxServer.databaseManager.getChunksByFile(filePath);
|
|
1240
|
-
const totalComplexity = chunks.reduce((sum, c) => sum + (c.complexity?.score || 0), 0);
|
|
1241
|
-
const avgComplexity = chunks.length > 0 ? Math.round(totalComplexity / chunks.length) : 0;
|
|
1242
|
-
|
|
1243
|
-
let semanticHeader = `--- SEMANTIC CONTEXT ---\n`;
|
|
1244
|
-
semanticHeader += `File importance: ${chunks.length} semantic chunks found.\n`;
|
|
1245
|
-
semanticHeader += `Aggregate Complexity: ${totalComplexity} (Avg: ${avgComplexity})\n`;
|
|
1246
|
-
if (chunks.length > 0) {
|
|
1247
|
-
semanticHeader += `Primary purposes: ${[...new Set(chunks.map(c => c.purpose))].join(', ')}\n`;
|
|
1248
|
-
}
|
|
1249
|
-
semanticHeader += `------------------------\n\n`;
|
|
1250
|
-
|
|
1251
|
-
const result = {
|
|
1252
|
-
path: filePath,
|
|
1253
|
-
content: semanticHeader + content
|
|
1254
|
-
};
|
|
1255
|
-
|
|
1256
|
-
if (includeMetadata) {
|
|
1257
|
-
const stats = fs.statSync(fullPath);
|
|
1258
|
-
const bundles = [];
|
|
1259
|
-
|
|
1260
|
-
// Find which bundles include this file
|
|
1261
|
-
this.cntxServer.bundles.forEach((bundle, name) => {
|
|
1262
|
-
if (bundle.files && bundle.files.includes(filePath)) {
|
|
1263
|
-
bundles.push(name);
|
|
1264
|
-
}
|
|
1265
|
-
});
|
|
1266
|
-
|
|
1267
|
-
result.metadata = {
|
|
1268
|
-
size: stats.size,
|
|
1269
|
-
mimeType: this.getMimeType(filePath),
|
|
1270
|
-
modified: stats.mtime.toISOString(),
|
|
1271
|
-
lines: content.split('\n').length,
|
|
1272
|
-
bundles: bundles
|
|
1273
|
-
};
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
return this.createSuccessResponse(id, {
|
|
1277
|
-
content: [{
|
|
1278
|
-
type: 'text',
|
|
1279
|
-
text: JSON.stringify(result, null, 2)
|
|
1280
|
-
}]
|
|
1281
|
-
});
|
|
1282
|
-
} catch (error) {
|
|
1283
|
-
return this.createErrorResponse(id, -32603, 'Failed to read file', error.message);
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
async toolWriteFile(args, id) {
|
|
1288
|
-
const { path: filePath, content, backup = true, createDirs = true } = args;
|
|
1289
|
-
|
|
1290
|
-
if (!filePath || content === undefined) {
|
|
1291
|
-
return this.createErrorResponse(id, -32602, 'Path and content are required');
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
|
-
try {
|
|
1295
|
-
const fullPath = join(this.cntxServer.CWD, filePath);
|
|
1296
|
-
const parentDir = dirname(fullPath);
|
|
1297
|
-
|
|
1298
|
-
// Create parent directories if needed
|
|
1299
|
-
if (createDirs && !existsSync(parentDir)) {
|
|
1300
|
-
fs.mkdirSync(parentDir, { recursive: true });
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
// Create backup if file exists
|
|
1304
|
-
if (backup && existsSync(fullPath)) {
|
|
1305
|
-
const backupPath = `${fullPath}.backup.${Date.now()}`;
|
|
1306
|
-
fs.copyFileSync(fullPath, backupPath);
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
// Write the file
|
|
1310
|
-
writeFileSync(fullPath, content, 'utf8');
|
|
1311
|
-
|
|
1312
|
-
// Mark relevant bundles as changed
|
|
1313
|
-
this.cntxServer.bundles.forEach((bundle, name) => {
|
|
1314
|
-
if (bundle.patterns && bundle.patterns.some(pattern =>
|
|
1315
|
-
this.cntxServer.fileSystemManager.matchesPattern(fullPath, pattern)
|
|
1316
|
-
)) {
|
|
1317
|
-
bundle.changed = true;
|
|
1318
|
-
}
|
|
1319
|
-
});
|
|
1320
|
-
|
|
1321
|
-
const stats = fs.statSync(fullPath);
|
|
1322
|
-
|
|
1323
|
-
return this.createSuccessResponse(id, {
|
|
1324
|
-
content: [{
|
|
1325
|
-
type: 'text',
|
|
1326
|
-
text: JSON.stringify({
|
|
1327
|
-
path: filePath,
|
|
1328
|
-
written: true,
|
|
1329
|
-
size: stats.size,
|
|
1330
|
-
modified: stats.mtime.toISOString()
|
|
1331
|
-
}, null, 2)
|
|
1332
|
-
}]
|
|
1333
|
-
});
|
|
1334
|
-
} catch (error) {
|
|
1335
|
-
return this.createErrorResponse(id, -32603, 'Failed to write file', error.message);
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
// Helper methods
|
|
1340
|
-
getMimeType(filePath) {
|
|
1341
|
-
const ext = filePath.split('.').pop()?.toLowerCase();
|
|
1342
|
-
const mimeTypes = {
|
|
1343
|
-
'js': 'application/javascript',
|
|
1344
|
-
'jsx': 'application/javascript',
|
|
1345
|
-
'ts': 'application/typescript',
|
|
1346
|
-
'tsx': 'application/typescript',
|
|
1347
|
-
'json': 'application/json',
|
|
1348
|
-
'xml': 'application/xml',
|
|
1349
|
-
'html': 'text/html',
|
|
1350
|
-
'css': 'text/css',
|
|
1351
|
-
'md': 'text/markdown',
|
|
1352
|
-
'txt': 'text/plain',
|
|
1353
|
-
'py': 'text/x-python',
|
|
1354
|
-
'java': 'text/x-java',
|
|
1355
|
-
'c': 'text/x-c',
|
|
1356
|
-
'cpp': 'text/x-c++',
|
|
1357
|
-
'php': 'text/x-php'
|
|
1358
|
-
};
|
|
1359
|
-
return mimeTypes[ext] || 'text/plain';
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
createSuccessResponse(id, result) {
|
|
1363
|
-
return {
|
|
1364
|
-
jsonrpc: '2.0',
|
|
1365
|
-
id,
|
|
1366
|
-
result
|
|
1367
|
-
};
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
createErrorResponse(id, code, message, data = null) {
|
|
1371
|
-
const error = { code, message };
|
|
1372
|
-
if (data) error.data = data;
|
|
1373
|
-
|
|
1374
|
-
return {
|
|
1375
|
-
jsonrpc: '2.0',
|
|
1376
|
-
id,
|
|
1377
|
-
error
|
|
1378
|
-
};
|
|
1379
|
-
}
|
|
1380
|
-
}
|