cntx-ui 2.0.15 → 3.0.1

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 (32) hide show
  1. package/README.md +40 -344
  2. package/bin/cntx-ui-mcp.sh +3 -0
  3. package/bin/cntx-ui.js +2 -1
  4. package/lib/agent-runtime.js +161 -1340
  5. package/lib/agent-tools.js +9 -7
  6. package/lib/api-router.js +262 -79
  7. package/lib/bundle-manager.js +172 -407
  8. package/lib/configuration-manager.js +94 -59
  9. package/lib/database-manager.js +397 -0
  10. package/lib/file-system-manager.js +17 -0
  11. package/lib/heuristics-manager.js +119 -17
  12. package/lib/mcp-server.js +125 -55
  13. package/lib/semantic-splitter.js +222 -481
  14. package/lib/simple-vector-store.js +69 -300
  15. package/package.json +18 -31
  16. package/server.js +151 -73
  17. package/templates/TOOLS.md +41 -0
  18. package/templates/activities/activities/create-project-bundles/README.md +4 -3
  19. package/templates/activities/activities/create-project-bundles/notes.md +15 -19
  20. package/templates/activities/activities/create-project-bundles/tasks.md +4 -4
  21. package/templates/activities/activities.json +1 -1
  22. package/templates/agent-config.yaml +0 -13
  23. package/templates/agent-instructions.md +22 -6
  24. package/templates/agent-rules/capabilities/bundle-system.md +1 -1
  25. package/templates/agent-rules/project-specific/architecture.md +1 -1
  26. package/web/dist/assets/index-B2OdTzzI.css +1 -0
  27. package/web/dist/assets/index-D0tBsKiR.js +2016 -0
  28. package/web/dist/index.html +2 -2
  29. package/mcp-config-example.json +0 -9
  30. package/web/dist/assets/heuristics-manager-browser-DfonOP5I.js +0 -1
  31. package/web/dist/assets/index-dF3qg-y_.js +0 -2486
  32. package/web/dist/assets/index-h5FGSg_P.css +0 -1
@@ -1,24 +1,109 @@
1
1
  /**
2
2
  * Agent Runtime for Codebase Exploration and Development
3
- * Implements the four behavior modes: Discovery, Query, Feature Investigation, Passive
3
+ * Now stateful with SQLite-based working memory
4
4
  */
5
5
 
6
6
  import AgentTools from './agent-tools.js';
7
+ import crypto from 'crypto';
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
7
11
 
8
12
  export class AgentRuntime {
9
13
  constructor(cntxServer) {
10
14
  this.cntxServer = cntxServer;
15
+ this.db = cntxServer.databaseManager;
11
16
  this.tools = new AgentTools(cntxServer);
17
+ this.currentSessionId = null;
18
+ }
19
+
20
+ /**
21
+ * Initialize or resume a session
22
+ */
23
+ async startSession(id = null, title = 'New Exploration') {
24
+ this.currentSessionId = id || crypto.randomUUID();
25
+ this.db.createSession(this.currentSessionId, title);
26
+ // Refresh manifest when a new session starts
27
+ await this.generateAgentManifest();
28
+ return this.currentSessionId;
29
+ }
30
+
31
+ /**
32
+ * Generates a .cntx/AGENT.md manifest for machine consumption
33
+ */
34
+ async generateAgentManifest() {
35
+ const overview = await this.getCodebaseOverview();
36
+ const summary = await this.getSemanticSummary();
37
+ const bundles = await this.analyzeBundles('all');
38
+
39
+ // Auto-generate tool reference from MCP server
40
+ let toolsReference = '';
41
+ if (this.cntxServer.mcpServer) {
42
+ const tools = this.cntxServer.mcpServer.getToolDefinitions();
43
+ toolsReference = tools.map(t => {
44
+ let params = [];
45
+ if (t.inputSchema?.properties) {
46
+ params = Object.entries(t.inputSchema.properties).map(([name, prop]) => {
47
+ const isReq = t.inputSchema.required?.includes(name) ? 'required' : 'optional';
48
+ return `\`${name}\` (${prop.type}, ${isReq}): ${prop.description}`;
49
+ });
50
+ }
51
+ return `### \`${t.name}\`\n${t.description}\n${params.length > 0 ? '**Parameters:**\n- ' + params.join('\n- ') : '*No parameters required*'}\n`;
52
+ }).join('\n');
53
+ }
54
+
55
+ const manifest = `# 🤖 Agent Handshake: ${overview.projectPath.split('/').pop()}
56
+
57
+ ## Project Overview
58
+ - **Path:** \`${overview.projectPath}\`
59
+ - **Total Files:** ${overview.totalFiles}
60
+ - **Semantic Intelligence:** ${summary.totalChunks} persistent chunks indexed.
61
+
62
+ ## Codebase Organization (Bundles)
63
+ ${bundles.map(b => `- **${b.name}**: ${b.purpose} (${b.fileCount} files)`).join('\n')}
64
+
65
+ ## Intelligence Interface (MCP Tools)
66
+ You have access to a specialized "Repository Intelligence" engine. Use these tools for high-signal exploration:
67
+
68
+ ${toolsReference || '*(MCP Server not yet initialized, tools will appear here)*'}
69
+
70
+ ---
71
+
72
+ ## 🛠 Complete Tool & API Reference
73
+ Refer to the dynamic reference below for full parameter schemas and HTTP fallback endpoints.
74
+
75
+ ${fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '../templates/TOOLS.md'), 'utf8')}
76
+
77
+ ## Working Memory
78
+ This agent is **stateful**. All interactions in this directory are logged to a persistent SQLite database (\`.cntx/bundles.db\`), allowing for context retention across sessions.
79
+
80
+ ---
81
+ *Generated automatically by cntx-ui. Optimized for LLM consumption.*
82
+ `;
83
+
84
+ const manifestPath = path.join(this.cntxServer.CNTX_DIR, 'AGENT.md');
85
+ fs.writeFileSync(manifestPath, manifest, 'utf8');
86
+ if (this.cntxServer.verbose) console.log('📄 Agent manifest updated: .cntx/AGENT.md');
87
+ }
88
+
89
+ /**
90
+ * Log an interaction to the agent's memory
91
+ */
92
+ async logInteraction(role, content, metadata = {}) {
93
+ if (!this.currentSessionId) await this.startSession();
94
+ this.db.addMessage(this.currentSessionId, role, content, metadata);
12
95
  }
13
96
 
14
97
  /**
15
98
  * Discovery Mode: "Tell me about this codebase"
16
- * Summarize bundles, architectural patterns, and code organization
99
+ * Now logs the discovery process to memory
17
100
  */
18
101
  async discoverCodebase(options = {}) {
19
102
  const { scope = 'all', includeDetails = true } = options;
20
103
 
21
104
  try {
105
+ await this.logInteraction('agent', `Starting codebase discovery for scope: ${scope}`);
106
+
22
107
  const discovery = {
23
108
  overview: await this.getCodebaseOverview(),
24
109
  bundles: await this.analyzeBundles(scope),
@@ -33,1448 +118,184 @@ export class AgentRuntime {
33
118
  discovery.complexity = await this.analyzeComplexity();
34
119
  }
35
120
 
36
- // Generate recommendations
37
121
  discovery.recommendations = await this.generateDiscoveryRecommendations(discovery);
122
+
123
+ await this.logInteraction('agent', `Discovery complete. Found ${discovery.overview.totalFiles} files.`, { discovery });
38
124
 
39
125
  return discovery;
40
126
  } catch (error) {
127
+ await this.logInteraction('agent', `Discovery failed: ${error.message}`);
41
128
  throw new Error(`Discovery failed: ${error.message}`);
42
129
  }
43
130
  }
44
131
 
45
132
  /**
46
133
  * Query Mode: "Where is the user authentication handled?"
47
- * Use semantic search and AST analysis for precise answers
134
+ * Now recalls previous context from SQLite
48
135
  */
49
136
  async answerQuery(question, options = {}) {
50
137
  const { scope = null, maxResults = 10, includeCode = false } = options;
51
138
 
52
139
  try {
53
- // Extract key terms from question
54
- const searchTerms = this.extractSearchTerms(question);
140
+ await this.logInteraction('user', question);
55
141
 
56
- // Perform semantic search
57
- const semanticResults = await this.tools.searchSemanticChunks(searchTerms.primary, {
58
- bundle: scope,
59
- maxResults: maxResults * 2
60
- });
61
-
62
- // Perform bundle-aware file search if needed
63
- const fileResults = await this.searchInFiles(searchTerms, scope);
64
-
65
- // Combine and rank results
66
- const combinedResults = this.combineSearchResults(semanticResults, fileResults, question);
142
+ // Perform semantic search via Vector Store
143
+ const combinedResults = await this.cntxServer.vectorStore.search(question, { limit: maxResults });
67
144
 
68
145
  // Generate contextual answer
69
- const answer = await this.generateContextualAnswer(question, combinedResults, includeCode);
146
+ const answer = await this.generateContextualAnswer(question, { chunks: combinedResults, files: [] }, includeCode);
70
147
 
71
- return {
148
+ const response = {
72
149
  question,
73
150
  answer: answer.response,
74
151
  evidence: answer.evidence,
75
152
  confidence: answer.confidence,
76
- suggestions: answer.suggestions,
77
- relatedFiles: combinedResults.files.slice(0, 5),
78
- totalMatches: combinedResults.totalMatches
153
+ relatedFiles: [...new Set(combinedResults.map(c => c.filePath))].slice(0, 5)
79
154
  };
155
+
156
+ await this.logInteraction('agent', response.answer, { response });
157
+
158
+ return response;
80
159
  } catch (error) {
81
160
  throw new Error(`Query failed: ${error.message}`);
82
161
  }
83
162
  }
84
163
 
85
164
  /**
86
- * Feature Investigation Mode: "I want to add dark mode—what already exists?"
87
- * Search for existing implementations and identify integration points
165
+ * Feature Investigation Mode: Now persists the investigation approach
88
166
  */
89
167
  async investigateFeature(featureDescription, options = {}) {
90
168
  const { includeRecommendations = true } = options;
91
169
 
92
170
  try {
171
+ await this.logInteraction('user', `Investigating feature: ${featureDescription}`);
172
+
93
173
  const investigation = {
94
174
  feature: featureDescription,
95
175
  existing: await this.findExistingImplementations(featureDescription),
96
176
  related: await this.findRelatedCode(featureDescription),
97
- dependencies: await this.analyzeDependencies(featureDescription),
98
- integration: await this.findIntegrationPoints(featureDescription),
99
- patterns: await this.identifyImplementationPatterns(featureDescription)
177
+ integration: await this.findIntegrationPoints(featureDescription)
100
178
  };
101
179
 
102
180
  if (includeRecommendations) {
103
- investigation.recommendations = await this.generateImplementationRecommendations(investigation);
104
181
  investigation.approach = await this.suggestImplementationApproach(investigation);
105
182
  }
106
183
 
184
+ await this.logInteraction('agent', `Investigation complete for ${featureDescription}`, { investigation });
185
+
107
186
  return investigation;
108
187
  } catch (error) {
109
188
  throw new Error(`Feature investigation failed: ${error.message}`);
110
189
  }
111
190
  }
112
191
 
113
- /**
114
- * Passive Mode: "Let's discuss the architecture before I make changes"
115
- * Engage in conversation about design decisions and patterns
116
- */
117
- async discussAndPlan(userInput, context = {}) {
118
- try {
119
- const discussion = {
120
- userInput,
121
- context: await this.analyzeDiscussionContext(userInput, context),
122
- insights: await this.generateInsights(userInput),
123
- considerations: await this.identifyConsiderations(userInput),
124
- alternatives: await this.suggestAlternatives(userInput),
125
- questions: await this.generateClarifyingQuestions(userInput)
126
- };
127
-
128
- return discussion;
129
- } catch (error) {
130
- throw new Error(`Discussion planning failed: ${error.message}`);
131
- }
132
- }
133
-
134
- /**
135
- * Project Organizer Mode: Setup and maintenance of project organization
136
- * Adapts to project maturity - setup for fresh projects, optimization for established ones
137
- */
138
- async organizeProject(options = {}) {
139
- const { activity = 'detect', autoDetect = true, force = false } = options;
140
-
141
- try {
142
- const organization = {
143
- projectState: await this.detectProjectState(),
144
- currentActivity: activity,
145
- timestamp: new Date().toISOString()
146
- };
147
-
148
- // Auto-detect appropriate activity if requested
149
- if (autoDetect && activity === 'detect') {
150
- organization.suggestedActivity = this.suggestActivity(organization.projectState);
151
- organization.workflow = this.generateWorkflow(organization.projectState);
152
- }
153
-
154
- // Execute the requested activity
155
- switch (activity) {
156
- case 'detect':
157
- organization.analysis = await this.analyzeProjectMaturity();
158
- organization.recommendations = await this.generateSetupRecommendations(organization.projectState);
159
- break;
160
-
161
- case 'analyze':
162
- organization.semanticAnalysis = await this.performSemanticAnalysis();
163
- organization.readiness = this.assessBundlingReadiness(organization.semanticAnalysis);
164
- break;
165
-
166
- case 'bundle':
167
- organization.bundleSuggestions = await this.generateIntelligentBundles();
168
- organization.preview = await this.previewBundleChanges(organization.bundleSuggestions);
169
- break;
170
-
171
- case 'create':
172
- organization.bundleSuggestions = await this.generateIntelligentBundles();
173
- organization.creation = await this.createSuggestedBundles(organization.bundleSuggestions);
174
- break;
175
-
176
- case 'optimize':
177
- organization.optimizations = await this.analyzeOptimizationOpportunities();
178
- organization.recommendations = await this.generateOptimizationPlan();
179
- break;
180
-
181
- case 'audit':
182
- organization.audit = await this.auditCurrentOrganization();
183
- organization.issues = await this.identifyOrganizationalIssues();
184
- break;
185
-
186
- case 'cleanup':
187
- organization.cleanup = await this.suggestCleanupActions();
188
- organization.impact = await this.assessCleanupImpact();
189
- break;
190
-
191
- case 'validate':
192
- organization.validation = await this.validateCurrentOrganization();
193
- organization.health = await this.calculateOrganizationHealth();
194
- break;
195
-
196
- default:
197
- throw new Error(`Unknown activity: ${activity}`);
198
- }
199
-
200
- // Add next steps
201
- organization.nextSteps = this.generateNextSteps(organization, activity);
202
-
203
- return organization;
204
- } catch (error) {
205
- throw new Error(`Project organization failed: ${error.message}`);
206
- }
207
- }
208
-
209
- // Helper methods for Discovery Mode
192
+ // --- Helper Methods (reusing your existing logic but streamlined) ---
210
193
 
211
194
  async getCodebaseOverview() {
212
- const bundles = Array.from(this.cntxServer.bundles.entries());
213
- const totalFiles = bundles.reduce((sum, [_, bundle]) => sum + bundle.files.length, 0);
214
- const totalSize = bundles.reduce((sum, [_, bundle]) => sum + bundle.size, 0);
195
+ const bundles = Array.from(this.cntxServer.bundleManager.getAllBundleInfo());
196
+ const totalFiles = bundles.reduce((sum, b) => sum + b.fileCount, 0);
197
+ const totalSize = bundles.reduce((sum, b) => sum + b.size, 0);
215
198
 
216
199
  return {
217
200
  projectPath: this.cntxServer.CWD,
218
201
  totalBundles: bundles.length,
219
202
  totalFiles,
220
203
  totalSize,
221
- formattedSize: this.formatBytes(totalSize),
222
- bundleNames: bundles.map(([name]) => name)
204
+ bundleNames: bundles.map(b => b.name)
223
205
  };
224
206
  }
225
207
 
226
208
  async analyzeBundles(scope) {
227
- const bundlesToAnalyze = scope === 'all'
228
- ? Array.from(this.cntxServer.bundles.entries())
229
- : [[scope, this.cntxServer.bundles.get(scope)]].filter(([_, b]) => b);
230
-
231
- return Promise.all(bundlesToAnalyze.map(async ([name, bundle]) => {
232
- const fileTypes = this.categorizeFiles(bundle.files);
233
- return {
234
- name,
235
- fileCount: bundle.files.length,
236
- size: bundle.size,
237
- formattedSize: this.formatBytes(bundle.size),
238
- patterns: bundle.patterns,
239
- fileTypes,
240
- lastGenerated: bundle.lastGenerated,
241
- changed: bundle.changed,
242
- purpose: this.inferBundlePurpose(name, bundle.files, fileTypes)
243
- };
244
- }));
245
- }
246
-
247
- async analyzeArchitecture() {
248
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 100 });
249
-
250
- if (!analysis.chunks) {
251
- return { message: 'No semantic analysis available for architecture detection' };
252
- }
253
-
254
- const patterns = {
255
- frontend: this.detectFrontendPatterns(analysis.chunks),
256
- backend: this.detectBackendPatterns(analysis.chunks),
257
- testing: this.detectTestingPatterns(analysis.chunks),
258
- configuration: this.detectConfigPatterns(analysis.chunks)
259
- };
260
-
261
- return {
262
- type: this.determineArchitectureType(patterns),
263
- patterns,
264
- frameworks: this.identifyFrameworks(analysis.chunks),
265
- languages: this.identifyLanguages(analysis.chunks)
266
- };
267
- }
268
-
269
- async identifyPatterns() {
270
- const files = await this.tools.listFiles({ limit: 200 });
271
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 50 });
272
-
273
- return {
274
- organizational: this.identifyOrganizationalPatterns(files),
275
- coding: this.identifyCodingPatterns(analysis.chunks || []),
276
- naming: this.identifyNamingPatterns(files),
277
- structural: this.identifyStructuralPatterns(files)
278
- };
279
- }
280
-
281
- // Helper methods for Query Mode
282
-
283
- extractSearchTerms(question) {
284
- // Simple keyword extraction - could be enhanced with NLP
285
- const stopWords = ['the', 'is', 'at', 'which', 'on', 'how', 'where', 'what', 'when', 'why'];
286
- const words = question.toLowerCase()
287
- .replace(/[^\w\s]/g, ' ')
288
- .split(/\s+/)
289
- .filter(word => word.length > 2 && !stopWords.includes(word));
290
-
291
- return {
292
- primary: words.join(' '),
293
- keywords: words,
294
- original: question
295
- };
296
- }
297
-
298
- async searchInFiles(searchTerms, scope) {
299
- const files = await this.tools.listFiles({
300
- bundle: scope,
301
- pattern: searchTerms.keywords.join('|'),
302
- limit: 50
303
- });
304
-
305
- return {
306
- files: files.map(f => ({
307
- path: f.path,
308
- bundles: f.bundles,
309
- relevance: this.calculateFileRelevance(f.path, searchTerms.keywords)
310
- })),
311
- totalFiles: files.length
312
- };
313
- }
314
-
315
- combineSearchResults(semanticResults, fileResults, question) {
316
- const allFiles = new Set();
317
- const chunks = semanticResults.chunks || [];
318
-
319
- // Add files from semantic chunks
320
- chunks.forEach(chunk => {
321
- if (chunk.filePath) allFiles.add(chunk.filePath);
322
- });
323
-
324
- // Add files from direct search
325
- fileResults.files.forEach(f => allFiles.add(f.path));
326
-
327
- return {
328
- chunks,
329
- files: Array.from(allFiles),
330
- totalMatches: chunks.length + fileResults.totalFiles,
331
- semanticMatches: semanticResults.totalResults || 0,
332
- fileMatches: fileResults.totalFiles
333
- };
334
- }
335
-
336
- async generateContextualAnswer(question, results, includeCode) {
337
- const evidence = [];
338
- const suggestions = [];
339
- let confidence = 0;
340
-
341
- if (results.chunks.length > 0) {
342
- confidence += 0.6;
343
- evidence.push({
344
- type: 'semantic',
345
- count: results.chunks.length,
346
- message: `Found ${results.chunks.length} relevant code chunks`
347
- });
348
-
349
- if (includeCode) {
350
- evidence.push({
351
- type: 'code',
352
- samples: results.chunks.slice(0, 3).map(chunk => ({
353
- file: chunk.filePath,
354
- name: chunk.name,
355
- purpose: chunk.purpose,
356
- code: chunk.code
357
- }))
358
- });
359
- }
360
- }
361
-
362
- if (results.files.length > 0) {
363
- confidence += 0.3;
364
- evidence.push({
365
- type: 'files',
366
- count: results.files.length,
367
- files: results.files.slice(0, 5)
368
- });
369
- }
370
-
371
- // Generate response based on evidence
372
- let response = `Based on the analysis of your codebase:\n\n`;
373
-
374
- if (results.chunks.length > 0) {
375
- const topChunk = results.chunks[0];
376
- response += `The most relevant code is in \`${topChunk.filePath}\` where `;
377
- response += `${topChunk.purpose || topChunk.name} is implemented`;
378
-
379
- if (topChunk.startLine) {
380
- response += ` (lines ${topChunk.startLine}-${topChunk.endLine})`;
381
- }
382
- response += '.\n\n';
383
-
384
- if (results.chunks.length > 1) {
385
- response += `Additionally, found ${results.chunks.length - 1} other related implementations.\n\n`;
386
- }
387
- }
388
-
389
- if (results.files.length > 0) {
390
- response += `Key files to examine: ${results.files.slice(0, 3).join(', ')}\n\n`;
391
- }
392
-
393
- if (confidence < 0.5) {
394
- suggestions.push('Consider running semantic analysis if not already done');
395
- suggestions.push('Try rephrasing the question with more specific terms');
396
- }
397
-
398
- return {
399
- response: response.trim(),
400
- evidence,
401
- confidence: Math.min(confidence, 1.0),
402
- suggestions
403
- };
404
- }
405
-
406
- // Helper methods for Feature Investigation Mode
407
-
408
- async findExistingImplementations(featureDescription) {
409
- const searchTerms = this.extractSearchTerms(featureDescription);
410
- const results = await this.tools.searchSemanticChunks(searchTerms.primary, { maxResults: 20 });
209
+ const bundles = this.cntxServer.bundleManager.getAllBundleInfo();
210
+ const filtered = scope === 'all' ? bundles : bundles.filter(b => b.name === scope);
411
211
 
412
- return {
413
- found: results.chunks.length > 0,
414
- implementations: results.chunks.map(chunk => ({
415
- file: chunk.filePath,
416
- name: chunk.name,
417
- purpose: chunk.purpose,
418
- type: chunk.subtype,
419
- bundles: chunk.bundles,
420
- confidence: chunk.relevanceScore || 0
421
- })),
422
- summary: `Found ${results.chunks.length} potentially related implementations`
423
- };
424
- }
425
-
426
- async findRelatedCode(featureDescription) {
427
- // Look for related patterns in bundle organization
428
- const bundles = await this.analyzeBundles('all');
429
- const relatedBundles = bundles.filter(bundle =>
430
- this.isFeatureRelated(featureDescription, bundle.name, bundle.purpose)
431
- );
432
-
433
- return {
434
- bundles: relatedBundles,
435
- patterns: this.identifyRelatedPatterns(featureDescription, relatedBundles)
436
- };
437
- }
438
-
439
- async generateImplementationRecommendations(investigation) {
440
- const recommendations = [];
441
-
442
- if (investigation.existing.found) {
443
- recommendations.push({
444
- type: 'extend',
445
- message: 'Extend existing implementation rather than creating new one',
446
- files: investigation.existing.implementations.slice(0, 3).map(impl => impl.file)
447
- });
448
- } else {
449
- recommendations.push({
450
- type: 'create',
451
- message: 'No existing implementation found - create new feature',
452
- suggestedLocation: this.suggestImplementationLocation(investigation.feature)
453
- });
454
- }
455
-
456
- if (investigation.related.bundles.length > 0) {
457
- recommendations.push({
458
- type: 'organize',
459
- message: 'Consider organizing in existing bundle structure',
460
- bundles: investigation.related.bundles.map(b => b.name)
461
- });
462
- }
463
-
464
- return recommendations;
465
- }
466
-
467
- // Utility methods
468
-
469
- formatBytes(bytes) {
470
- if (bytes === 0) return '0 Bytes';
471
- const k = 1024;
472
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
473
- const i = Math.floor(Math.log(bytes) / Math.log(k));
474
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
475
- }
476
-
477
- categorizeFiles(files) {
478
- const categories = {};
479
- files.forEach(file => {
480
- const ext = require('path').extname(file).toLowerCase();
481
- categories[ext] = (categories[ext] || 0) + 1;
482
- });
483
- return categories;
212
+ return filtered.map(b => ({
213
+ ...b,
214
+ purpose: this.inferBundlePurpose(b.name, b.files)
215
+ }));
484
216
  }
485
217
 
486
- inferBundlePurpose(name, files, fileTypes) {
218
+ inferBundlePurpose(name, files) {
487
219
  if (name.includes('component') || name.includes('ui')) return 'UI Components';
488
220
  if (name.includes('api') || name.includes('server')) return 'Backend API';
489
- if (name.includes('test')) return 'Testing';
490
- if (name.includes('config')) return 'Configuration';
491
- if (files.some(f => f.includes('hook'))) return 'React Hooks';
492
- if (Object.keys(fileTypes).includes('.ts') || Object.keys(fileTypes).includes('.tsx')) return 'TypeScript Application';
493
- return 'General Purpose';
494
- }
495
-
496
- detectFrontendPatterns(chunks) {
497
- return {
498
- react: chunks.filter(c => c.subtype === 'react_component').length,
499
- hooks: chunks.filter(c => c.name && c.name.startsWith('use')).length,
500
- components: chunks.filter(c => c.purpose && c.purpose.includes('component')).length
501
- };
502
- }
503
-
504
- detectBackendPatterns(chunks) {
505
- return {
506
- apis: chunks.filter(c => c.purpose && c.purpose.includes('API')).length,
507
- middleware: chunks.filter(c => c.purpose && c.purpose.includes('middleware')).length,
508
- routes: chunks.filter(c => c.purpose && c.purpose.includes('route')).length
509
- };
510
- }
511
-
512
- detectTestingPatterns(chunks) {
513
- return {
514
- tests: chunks.filter(c => c.filePath && c.filePath.includes('test')).length,
515
- specs: chunks.filter(c => c.filePath && c.filePath.includes('spec')).length
516
- };
221
+ return 'General Module';
517
222
  }
518
223
 
519
- detectConfigPatterns(chunks) {
520
- return {
521
- configs: chunks.filter(c => c.purpose && c.purpose.includes('config')).length
522
- };
523
- }
524
-
525
- determineArchitectureType(patterns) {
526
- if (patterns.frontend.react > 0) return 'React Application';
527
- if (patterns.backend.apis > 0) return 'Backend API';
528
- return 'Mixed/Utility';
529
- }
530
-
531
- identifyFrameworks(chunks) {
532
- const frameworks = new Set();
533
- chunks.forEach(chunk => {
534
- if (chunk.includes?.imports) {
535
- chunk.includes.imports.forEach(imp => {
536
- if (imp.includes('react')) frameworks.add('React');
537
- if (imp.includes('express')) frameworks.add('Express');
538
- if (imp.includes('next')) frameworks.add('Next.js');
539
- if (imp.includes('vue')) frameworks.add('Vue');
540
- });
541
- }
542
- });
543
- return Array.from(frameworks);
544
- }
545
-
546
- identifyLanguages(chunks) {
547
- const languages = new Set();
548
- chunks.forEach(chunk => {
549
- if (chunk.filePath?.endsWith('.ts') || chunk.filePath?.endsWith('.tsx')) {
550
- languages.add('TypeScript');
551
- } else if (chunk.filePath?.endsWith('.js') || chunk.filePath?.endsWith('.jsx')) {
552
- languages.add('JavaScript');
553
- }
554
- });
555
- return Array.from(languages);
556
- }
557
-
558
- calculateFileRelevance(filePath, keywords) {
559
- let score = 0;
560
- const fileName = require('path').basename(filePath).toLowerCase();
561
- keywords.forEach(keyword => {
562
- if (fileName.includes(keyword.toLowerCase())) {
563
- score += 1;
564
- }
565
- });
566
- return score;
567
- }
568
-
569
- isFeatureRelated(featureDescription, bundleName, bundlePurpose) {
570
- const feature = featureDescription.toLowerCase();
571
- const name = bundleName.toLowerCase();
572
- const purpose = (bundlePurpose || '').toLowerCase();
573
-
574
- return name.includes(feature) || purpose.includes(feature) ||
575
- (feature.includes('ui') && (name.includes('component') || name.includes('ui'))) ||
576
- (feature.includes('api') && (name.includes('api') || name.includes('server')));
577
- }
578
-
579
- identifyRelatedPatterns(featureDescription, relatedBundles) {
580
- return relatedBundles.map(bundle => ({
581
- bundle: bundle.name,
582
- pattern: bundle.patterns,
583
- reason: `Similar naming/purpose pattern to ${featureDescription}`
584
- }));
585
- }
586
-
587
- suggestImplementationLocation(featureDescription) {
588
- const feature = featureDescription.toLowerCase();
589
-
590
- if (feature.includes('component') || feature.includes('ui')) {
591
- return 'web/src/components/';
592
- }
593
- if (feature.includes('hook')) {
594
- return 'web/src/hooks/';
595
- }
596
- if (feature.includes('api')) {
597
- return 'lib/';
598
- }
599
- if (feature.includes('util')) {
600
- return 'web/src/utils/';
601
- }
602
-
603
- return 'web/src/';
604
- }
605
-
606
- identifyOrganizationalPatterns(files) {
607
- const patterns = {
608
- directoryBased: files.some(f => f.path.includes('components/') || f.path.includes('utils/')),
609
- featureBased: files.some(f => f.path.split('/').length > 3),
610
- flatStructure: files.every(f => f.path.split('/').length <= 2)
611
- };
612
- return patterns;
613
- }
614
-
615
- identifyCodingPatterns(chunks) {
224
+ async analyzeArchitecture() {
616
225
  return {
617
- functionalComponents: chunks.filter(c => c.subtype === 'react_component').length,
618
- hooks: chunks.filter(c => c.name && c.name.startsWith('use')).length,
619
- asyncFunctions: chunks.filter(c => c.isAsync).length,
620
- exportedFunctions: chunks.filter(c => c.isExported).length
621
- };
622
- }
623
-
624
- identifyNamingPatterns(files) {
625
- const patterns = {
626
- camelCase: files.filter(f => /[a-z][A-Z]/.test(require('path').basename(f.path, require('path').extname(f.path)))).length,
627
- kebabCase: files.filter(f => /-/.test(require('path').basename(f.path, require('path').extname(f.path)))).length,
628
- pascalCase: files.filter(f => /^[A-Z]/.test(require('path').basename(f.path, require('path').extname(f.path)))).length
226
+ type: 'Dynamic Architecture',
227
+ timestamp: new Date().toISOString()
629
228
  };
630
- return patterns;
631
229
  }
632
230
 
633
- identifyStructuralPatterns(files) {
231
+ async identifyPatterns() {
634
232
  return {
635
- hasTests: files.some(f => f.path.includes('test') || f.path.includes('spec')),
636
- hasConfig: files.some(f => f.path.includes('config') || f.path.endsWith('.config.js')),
637
- hasDocumentation: files.some(f => f.path.endsWith('.md')),
638
- hasTypeDefinitions: files.some(f => f.path.endsWith('.d.ts'))
233
+ coding: 'Modern Node.js',
234
+ style: 'Functional / Modular'
639
235
  };
640
236
  }
641
237
 
642
238
  async getSemanticSummary() {
643
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 100 });
644
- return analysis.summary || { message: 'No semantic summary available' };
239
+ const chunks = this.db.db.prepare('SELECT COUNT(*) as count FROM semantic_chunks').get();
240
+ return { totalChunks: chunks.count };
645
241
  }
646
242
 
647
243
  async analyzeFileTypes() {
648
- const files = await this.tools.listFiles({ limit: 500 });
649
- return this.categorizeFiles(files.map(f => f.path));
244
+ const rows = this.db.db.prepare('SELECT file_path FROM semantic_chunks').all();
245
+ const exts = {};
246
+ rows.forEach(r => {
247
+ const ext = r.file_path.split('.').pop();
248
+ exts[ext] = (exts[ext] || 0) + 1;
249
+ });
250
+ return exts;
650
251
  }
651
252
 
652
253
  async analyzeComplexity() {
653
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 100 });
654
- if (!analysis.chunks) return { message: 'No complexity data available' };
655
-
656
- const complexity = { low: 0, medium: 0, high: 0 };
657
- analysis.chunks.forEach(chunk => {
658
- const level = chunk.complexity?.level || 'low';
659
- complexity[level]++;
254
+ const rows = this.db.db.prepare('SELECT complexity_score FROM semantic_chunks').all();
255
+ const scores = { low: 0, medium: 0, high: 0 };
256
+ rows.forEach(r => {
257
+ if (r.complexity_score < 5) scores.low++;
258
+ else if (r.complexity_score < 15) scores.medium++;
259
+ else scores.high++;
660
260
  });
661
-
662
- return complexity;
261
+ return scores;
663
262
  }
664
263
 
665
- async generateDiscoveryRecommendations(discovery) {
666
- const recommendations = [];
667
-
668
- if (discovery.bundles.length > 10) {
669
- recommendations.push({
670
- type: 'organization',
671
- message: 'Consider consolidating similar bundles for better organization'
672
- });
673
- }
674
-
675
- if (discovery.semanticSummary?.totalChunks > 100) {
676
- recommendations.push({
677
- type: 'performance',
678
- message: 'Large codebase detected - consider using bundle-scoped queries for better performance'
679
- });
680
- }
681
-
682
- return recommendations;
264
+ async generateDiscoveryRecommendations() {
265
+ return [{ type: 'info', message: 'Continue organizing by semantic purpose.' }];
683
266
  }
684
267
 
685
- async analyzeDependencies(featureDescription) {
686
- // Simple dependency analysis based on imports in semantic chunks
687
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 50 });
688
- const allImports = new Set();
689
-
690
- if (analysis.chunks) {
691
- analysis.chunks.forEach(chunk => {
692
- if (chunk.includes?.imports) {
693
- chunk.includes.imports.forEach(imp => allImports.add(imp));
694
- }
695
- });
696
- }
697
-
698
- return Array.from(allImports);
268
+ async findExistingImplementations(featureDescription) {
269
+ return await this.cntxServer.vectorStore.search(featureDescription, { limit: 5 });
699
270
  }
700
271
 
701
- async findIntegrationPoints(featureDescription) {
702
- const searchResults = await this.tools.searchSemanticChunks(featureDescription, { maxResults: 10 });
703
-
704
- return searchResults.chunks.map(chunk => ({
705
- file: chunk.filePath,
706
- function: chunk.name,
707
- reason: `Potential integration point based on ${chunk.purpose}`
708
- }));
272
+ async findRelatedCode(featureDescription) {
273
+ return [];
709
274
  }
710
275
 
711
- async identifyImplementationPatterns(featureDescription) {
712
- const bundles = await this.analyzeBundles('all');
713
- const patterns = [];
714
-
715
- bundles.forEach(bundle => {
716
- if (this.isFeatureRelated(featureDescription, bundle.name, bundle.purpose)) {
717
- patterns.push({
718
- bundle: bundle.name,
719
- fileTypes: bundle.fileTypes,
720
- organizationPattern: bundle.patterns
721
- });
722
- }
723
- });
724
-
725
- return patterns;
276
+ async findIntegrationPoints(featureDescription) {
277
+ return [];
726
278
  }
727
279
 
728
280
  async suggestImplementationApproach(investigation) {
729
- if (investigation.existing.found) {
730
- return {
731
- strategy: 'extend',
732
- description: 'Build upon existing implementation',
733
- files: investigation.existing.implementations.slice(0, 2).map(impl => impl.file)
734
- };
735
- }
736
-
737
- return {
738
- strategy: 'create',
739
- description: 'Create new implementation following project patterns',
740
- location: this.suggestImplementationLocation(investigation.feature)
741
- };
742
- }
743
-
744
- async analyzeDiscussionContext(userInput, context) {
745
- return {
746
- intent: this.classifyIntent(userInput),
747
- scope: context.scope || 'general',
748
- complexity: this.assessComplexity(userInput)
749
- };
750
- }
751
-
752
- async generateInsights(userInput) {
753
- return [
754
- 'Consider the existing bundle organization when planning changes',
755
- 'Use semantic search to find similar implementations',
756
- 'Check for related patterns in the codebase before implementing'
757
- ];
758
- }
759
-
760
- async identifyConsiderations(userInput) {
761
- return [
762
- 'Impact on existing bundles and organization',
763
- 'Compatibility with current architecture patterns',
764
- 'Testing strategy for new implementations'
765
- ];
766
- }
767
-
768
- async suggestAlternatives(userInput) {
769
- return [
770
- 'Extend existing functionality instead of creating new',
771
- 'Consider configuration-based approach for flexibility',
772
- 'Evaluate third-party solutions before custom implementation'
773
- ];
774
- }
775
-
776
- async generateClarifyingQuestions(userInput) {
777
- return [
778
- 'Which bundle or area of the codebase is most relevant?',
779
- 'Are there existing implementations we should build upon?',
780
- 'What are the specific requirements or constraints?'
781
- ];
782
- }
783
-
784
- classifyIntent(userInput) {
785
- const input = userInput.toLowerCase();
786
- if (input.includes('implement') || input.includes('add') || input.includes('create')) {
787
- return 'implementation';
788
- }
789
- if (input.includes('refactor') || input.includes('improve') || input.includes('optimize')) {
790
- return 'optimization';
791
- }
792
- if (input.includes('understand') || input.includes('explain') || input.includes('how')) {
793
- return 'understanding';
794
- }
795
- return 'discussion';
796
- }
797
-
798
- assessComplexity(userInput) {
799
- const words = userInput.split(' ').length;
800
- if (words > 20) return 'high';
801
- if (words > 10) return 'medium';
802
- return 'low';
803
- }
804
-
805
- // Project Organizer Mode Helper Methods
806
-
807
- async detectProjectState() {
808
- const bundles = await this.analyzeBundles('all');
809
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 10 });
810
-
811
- // Determine project maturity
812
- const state = {
813
- bundleCount: bundles.length,
814
- hasOnlyMaster: bundles.length === 1 && bundles[0].name === 'master',
815
- hasSemanticAnalysis: analysis && analysis.chunks && analysis.chunks.length > 0,
816
- totalFiles: bundles.reduce((sum, b) => sum + b.fileCount, 0),
817
- maturityLevel: 'unknown'
818
- };
819
-
820
- // Classify maturity
821
- if (state.hasOnlyMaster && !state.hasSemanticAnalysis) {
822
- state.maturityLevel = 'fresh'; // Brand new project
823
- } else if (state.hasOnlyMaster && state.hasSemanticAnalysis) {
824
- state.maturityLevel = 'analyzed'; // Ready for bundling
825
- } else if (state.bundleCount > 1 && state.bundleCount < 5) {
826
- state.maturityLevel = 'organized'; // Basic organization
827
- } else if (state.bundleCount >= 5) {
828
- state.maturityLevel = 'mature'; // Well-organized
829
- }
830
-
831
- return state;
832
- }
833
-
834
- suggestActivity(projectState) {
835
- switch (projectState.maturityLevel) {
836
- case 'fresh':
837
- return 'analyze'; // Need semantic analysis first
838
- case 'analyzed':
839
- return 'bundle'; // Ready to plan bundles
840
- case 'organized':
841
- return 'optimize'; // Can optimize existing organization
842
- case 'mature':
843
- return 'audit'; // Regular maintenance
844
- default:
845
- return 'detect';
846
- }
847
- }
848
-
849
- generateWorkflow(projectState) {
850
- const workflows = {
851
- fresh: [
852
- { step: 'analyze', description: 'Generate semantic analysis', required: true },
853
- { step: 'bundle', description: 'Plan intelligent bundles', required: true },
854
- { step: 'create', description: 'Create the planned bundles', required: true },
855
- { step: 'validate', description: 'Verify organization', required: false }
856
- ],
857
- analyzed: [
858
- { step: 'bundle', description: 'Plan intelligent bundles', required: true },
859
- { step: 'create', description: 'Create the planned bundles', required: true },
860
- { step: 'validate', description: 'Verify organization', required: false }
861
- ],
862
- organized: [
863
- { step: 'audit', description: 'Review current organization', required: false },
864
- { step: 'optimize', description: 'Improve bundle structure', required: false },
865
- { step: 'validate', description: 'Verify improvements', required: false }
866
- ],
867
- mature: [
868
- { step: 'audit', description: 'Regular maintenance check', required: false },
869
- { step: 'cleanup', description: 'Remove obsolete patterns', required: false }
870
- ]
871
- };
872
-
873
- return workflows[projectState.maturityLevel] || workflows.fresh;
874
- }
875
-
876
- async analyzeProjectMaturity() {
877
- const bundles = await this.analyzeBundles('all');
878
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 50 });
879
-
880
- return {
881
- bundles: {
882
- count: bundles.length,
883
- types: bundles.map(b => ({ name: b.name, purpose: b.purpose, files: b.fileCount })),
884
- coverage: this.calculateBundleCoverage(bundles)
885
- },
886
- codebase: {
887
- hasSemanticAnalysis: analysis && analysis.chunks && analysis.chunks.length > 0,
888
- chunkCount: analysis?.chunks?.length || 0,
889
- complexity: analysis?.summary?.complexity || this.assessDefaultComplexity(bundles)
890
- },
891
- recommendations: this.generateMaturityRecommendations(bundles, analysis)
892
- };
893
- }
894
-
895
- async generateSetupRecommendations(projectState) {
896
- const recommendations = [];
897
-
898
- if (projectState.maturityLevel === 'fresh') {
899
- recommendations.push({
900
- priority: 'high',
901
- action: 'Generate semantic analysis',
902
- reason: 'Required before intelligent bundle creation',
903
- command: 'Use activity "analyze" to generate code analysis'
904
- });
905
- }
906
-
907
- if (projectState.hasOnlyMaster) {
908
- recommendations.push({
909
- priority: 'high',
910
- action: 'Create logical bundles',
911
- reason: 'Only master bundle exists - organize code by purpose',
912
- command: 'Use activity "bundle" after semantic analysis'
913
- });
914
- }
915
-
916
- if (projectState.totalFiles > 50) {
917
- recommendations.push({
918
- priority: 'medium',
919
- action: 'Consider bundle size limits',
920
- reason: 'Large codebase may benefit from smaller, focused bundles'
921
- });
922
- }
923
-
924
- return recommendations;
925
- }
926
-
927
- async performSemanticAnalysis() {
928
- try {
929
- // Trigger semantic analysis if not available
930
- const existingAnalysis = await this.tools.getSemanticAnalysis({ maxChunks: 10 });
931
-
932
- if (!existingAnalysis.chunks || existingAnalysis.chunks.length === 0) {
933
- // Need to trigger analysis - this would typically call the cntx server's analysis
934
- return {
935
- status: 'analysis_needed',
936
- message: 'Semantic analysis needs to be generated first',
937
- instruction: 'Run semantic analysis before bundling'
938
- };
939
- }
940
-
941
- return {
942
- status: 'ready',
943
- chunkCount: existingAnalysis.totalChunks,
944
- summary: existingAnalysis.summary,
945
- readyForBundling: true
946
- };
947
- } catch (error) {
948
- return {
949
- status: 'error',
950
- message: error.message,
951
- readyForBundling: false
952
- };
953
- }
954
- }
955
-
956
- assessBundlingReadiness(semanticAnalysis) {
957
- if (semanticAnalysis.status === 'ready') {
958
- return {
959
- ready: true,
960
- confidence: 'high',
961
- reason: `${semanticAnalysis.chunkCount} semantic chunks available for intelligent bundling`
962
- };
963
- }
964
-
965
- return {
966
- ready: false,
967
- confidence: 'none',
968
- reason: semanticAnalysis.message || 'Semantic analysis required'
969
- };
970
- }
971
-
972
- async generateIntelligentBundles() {
973
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 100 });
974
-
975
- if (!analysis.chunks) {
976
- return {
977
- status: 'error',
978
- message: 'Semantic analysis required before bundle generation'
979
- };
980
- }
981
-
982
- const suggestions = {
983
- recommended: {},
984
- reasoning: [],
985
- stats: {
986
- totalChunks: analysis.chunks.length,
987
- totalFiles: new Set(analysis.chunks.map(c => c.filePath)).size
988
- }
989
- };
990
-
991
- // Group by semantic purpose
992
- const purposeGroups = this.groupChunksByPurpose(analysis.chunks);
993
-
994
- Object.entries(purposeGroups).forEach(([purpose, chunks]) => {
995
- if (chunks.length >= 3) { // Only suggest if substantial
996
- const bundleName = this.purposeToBundleName(purpose);
997
- const patterns = this.generatePatternsForChunks(chunks);
998
-
999
- suggestions.recommended[bundleName] = {
1000
- patterns,
1001
- chunkCount: chunks.length,
1002
- files: [...new Set(chunks.map(c => c.filePath))],
1003
- purpose: purpose
1004
- };
1005
-
1006
- suggestions.reasoning.push({
1007
- bundle: bundleName,
1008
- reason: `${chunks.length} functions with purpose: ${purpose}`,
1009
- confidence: chunks.length > 5 ? 'high' : 'medium'
1010
- });
1011
- }
1012
- });
1013
-
1014
- // Add standard bundles
1015
- this.addStandardBundles(suggestions, analysis.chunks);
1016
-
1017
- return suggestions;
1018
- }
1019
-
1020
- async previewBundleChanges(bundleSuggestions) {
1021
- const currentBundles = await this.analyzeBundles('all');
1022
-
1023
- return {
1024
- current: {
1025
- count: currentBundles.length,
1026
- bundles: currentBundles.map(b => ({ name: b.name, files: b.fileCount }))
1027
- },
1028
- proposed: {
1029
- count: Object.keys(bundleSuggestions.recommended).length,
1030
- bundles: Object.entries(bundleSuggestions.recommended).map(([name, bundle]) => ({
1031
- name,
1032
- files: bundle.files.length,
1033
- purpose: bundle.purpose
1034
- }))
1035
- },
1036
- impact: {
1037
- filesReorganized: bundleSuggestions.stats?.totalFiles || 0,
1038
- newBundles: Object.keys(bundleSuggestions.recommended).length,
1039
- recommendation: 'Review and approve bundle organization before applying'
1040
- }
1041
- };
1042
- }
1043
-
1044
- async createSuggestedBundles(bundleSuggestions) {
1045
- if (!bundleSuggestions || !bundleSuggestions.recommended) {
1046
- return {
1047
- status: 'error',
1048
- message: 'No bundle suggestions available'
1049
- };
1050
- }
1051
-
1052
- const results = {
1053
- status: 'success',
1054
- created: [],
1055
- failed: [],
1056
- total: Object.keys(bundleSuggestions.recommended).length
1057
- };
1058
-
1059
- // Create each suggested bundle
1060
- for (const [bundleName, bundleConfig] of Object.entries(bundleSuggestions.recommended)) {
1061
- try {
1062
- // Use the agent tools to create bundle via MCP
1063
- const createResult = await this.createBundleViaMCP(bundleName, bundleConfig.patterns, bundleConfig.purpose);
1064
-
1065
- if (createResult.success) {
1066
- results.created.push({
1067
- name: bundleName,
1068
- patterns: bundleConfig.patterns,
1069
- files: bundleConfig.files?.length || 0,
1070
- purpose: bundleConfig.purpose
1071
- });
1072
- } else {
1073
- results.failed.push({
1074
- name: bundleName,
1075
- error: createResult.error || 'Unknown error'
1076
- });
1077
- }
1078
- } catch (error) {
1079
- results.failed.push({
1080
- name: bundleName,
1081
- error: error.message
1082
- });
1083
- }
1084
- }
1085
-
1086
- // Update status based on results
1087
- if (results.failed.length > 0) {
1088
- results.status = results.created.length > 0 ? 'partial' : 'failed';
1089
- }
1090
-
1091
- results.summary = `Created ${results.created.length}/${results.total} bundles successfully`;
1092
-
1093
- if (results.failed.length > 0) {
1094
- results.summary += `. Failed to create ${results.failed.length} bundles.`;
1095
- }
1096
-
1097
- return results;
1098
- }
1099
-
1100
- async createBundleViaMCP(name, patterns, description) {
1101
- try {
1102
- // Check if bundle already exists
1103
- const existingBundles = await this.analyzeBundles('all');
1104
- if (existingBundles.some(b => b.name === name)) {
1105
- return {
1106
- success: false,
1107
- error: `Bundle '${name}' already exists`
1108
- };
1109
- }
1110
-
1111
- // Don't allow creating or overwriting master bundle
1112
- if (name === 'master') {
1113
- return {
1114
- success: false,
1115
- error: 'Cannot create or overwrite master bundle'
1116
- };
1117
- }
1118
-
1119
- // Use the existing bundle creation functionality through the server
1120
- // This simulates what the MCP create_bundle tool does
1121
- const { join } = await import('path');
1122
- const { existsSync, readFileSync, writeFileSync } = await import('fs');
1123
-
1124
- const configPath = join(this.cntxServer.CNTX_DIR, 'config.json');
1125
- let config = { bundles: {} };
1126
-
1127
- if (existsSync(configPath)) {
1128
- config = JSON.parse(readFileSync(configPath, 'utf8'));
1129
- }
1130
-
1131
- // Add new bundle
1132
- config.bundles[name] = patterns;
1133
-
1134
- // Save config
1135
- writeFileSync(configPath, JSON.stringify(config, null, 2));
1136
-
1137
- // Reload server config and regenerate bundles
1138
- this.cntxServer.loadConfig();
1139
- this.cntxServer.generateAllBundles();
1140
-
1141
- return {
1142
- success: true,
1143
- bundle: {
1144
- name,
1145
- patterns,
1146
- description,
1147
- created: new Date().toISOString()
1148
- }
1149
- };
1150
- } catch (error) {
1151
- return {
1152
- success: false,
1153
- error: error.message
1154
- };
1155
- }
281
+ return { strategy: 'TBD', description: 'Ready to plan' };
1156
282
  }
1157
283
 
1158
- async analyzeOptimizationOpportunities() {
1159
- const bundles = await this.analyzeBundles('all');
1160
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 50 });
1161
-
1162
- const opportunities = [];
1163
-
1164
- // Check for oversized bundles
1165
- bundles.forEach(bundle => {
1166
- if (bundle.fileCount > 50) {
1167
- opportunities.push({
1168
- type: 'split',
1169
- bundle: bundle.name,
1170
- issue: `Large bundle with ${bundle.fileCount} files`,
1171
- suggestion: 'Consider splitting by functionality'
1172
- });
1173
- }
1174
- });
1175
-
1176
- // Check for undersized bundles
1177
- bundles.forEach(bundle => {
1178
- if (bundle.fileCount < 3 && bundle.name !== 'master') {
1179
- opportunities.push({
1180
- type: 'merge',
1181
- bundle: bundle.name,
1182
- issue: `Small bundle with only ${bundle.fileCount} files`,
1183
- suggestion: 'Consider merging with related bundle'
1184
- });
1185
- }
1186
- });
1187
-
1188
- // Check for misaligned purposes
1189
- if (analysis.chunks) {
1190
- const misaligned = this.findMisalignedFiles(bundles, analysis.chunks);
1191
- misaligned.forEach(item => {
1192
- opportunities.push({
1193
- type: 'realign',
1194
- file: item.file,
1195
- issue: `File purpose '${item.purpose}' doesn't match bundle '${item.currentBundle}'`,
1196
- suggestion: `Consider moving to '${item.suggestedBundle}' bundle`
1197
- });
1198
- });
1199
- }
1200
-
1201
- return opportunities;
1202
- }
1203
-
1204
- async generateOptimizationPlan() {
1205
- const opportunities = await this.analyzeOptimizationOpportunities();
1206
-
1207
- const plan = {
1208
- priority_1: opportunities.filter(o => o.type === 'split'),
1209
- priority_2: opportunities.filter(o => o.type === 'realign'),
1210
- priority_3: opportunities.filter(o => o.type === 'merge'),
1211
- summary: `Found ${opportunities.length} optimization opportunities`
1212
- };
1213
-
1214
- return plan;
1215
- }
1216
-
1217
- async auditCurrentOrganization() {
1218
- const bundles = await this.analyzeBundles('all');
1219
- const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 50 });
1220
-
1221
- return {
1222
- bundleHealth: this.assessBundleHealth(bundles),
1223
- coverage: this.calculateBundleCoverage(bundles),
1224
- alignment: this.assessSemanticAlignment(bundles, analysis),
1225
- recommendations: this.generateAuditRecommendations(bundles, analysis)
1226
- };
1227
- }
1228
-
1229
- async identifyOrganizationalIssues() {
1230
- const bundles = await this.analyzeBundles('all');
1231
- const issues = [];
1232
-
1233
- // Check for naming inconsistencies
1234
- const namingPatterns = this.analyzeNamingPatterns(bundles);
1235
- if (namingPatterns.inconsistent) {
1236
- issues.push({
1237
- type: 'naming',
1238
- severity: 'medium',
1239
- description: 'Inconsistent bundle naming patterns detected'
1240
- });
1241
- }
1242
-
1243
- // Check for duplicate patterns
1244
- const duplicates = this.findDuplicatePatterns(bundles);
1245
- duplicates.forEach(dup => {
1246
- issues.push({
1247
- type: 'duplication',
1248
- severity: 'high',
1249
- description: `Pattern '${dup.pattern}' used in multiple bundles: ${dup.bundles.join(', ')}`
1250
- });
1251
- });
1252
-
1253
- return issues;
1254
- }
1255
-
1256
- generateNextSteps(organization, currentActivity) {
1257
- const state = organization.projectState;
1258
- const steps = [];
1259
-
1260
- switch (currentActivity) {
1261
- case 'detect':
1262
- if (state.maturityLevel === 'fresh') {
1263
- steps.push('Run activity "analyze" to generate semantic analysis');
1264
- } else if (state.maturityLevel === 'analyzed') {
1265
- steps.push('Run activity "bundle" to create intelligent bundles');
1266
- }
1267
- break;
1268
-
1269
- case 'analyze':
1270
- if (organization.semanticAnalysis?.readyForBundling) {
1271
- steps.push('Run activity "bundle" to create logical bundle organization');
1272
- }
1273
- break;
1274
-
1275
- case 'bundle':
1276
- if (organization.bundleSuggestions?.recommended) {
1277
- steps.push('Review suggested bundles and run activity "create" to implement them');
1278
- steps.push('After creation, run activity "validate" to verify organization');
1279
- }
1280
- break;
1281
-
1282
- case 'create':
1283
- if (organization.creation?.status === 'success') {
1284
- steps.push('Bundle creation completed successfully! Run activity "validate" to verify organization');
1285
- } else if (organization.creation?.failed?.length > 0) {
1286
- steps.push('Some bundles failed to create. Review errors and retry if needed');
1287
- }
1288
- break;
1289
-
1290
- case 'optimize':
1291
- if (organization.optimizations?.length > 0) {
1292
- steps.push('Review optimization opportunities and implement highest priority items');
1293
- }
1294
- break;
1295
- }
1296
-
1297
- if (steps.length === 0) {
1298
- steps.push('Project organization is up to date - consider periodic audits');
1299
- }
1300
-
1301
- return steps;
1302
- }
1303
-
1304
- // Additional helper methods
1305
-
1306
- purposeToBundleName(purpose) {
1307
- const purposeMap = {
1308
- 'React component': 'ui-components',
1309
- 'API endpoint': 'api-endpoints',
1310
- 'Utility function': 'utilities',
1311
- 'Configuration': 'configuration',
1312
- 'Type definition': 'types',
1313
- 'Test': 'tests'
1314
- };
1315
-
1316
- return purposeMap[purpose] || purpose.toLowerCase().replace(/\s+/g, '-');
1317
- }
1318
-
1319
- addStandardBundles(suggestions, chunks) {
1320
- // Add configuration bundle
1321
- suggestions.recommended.configuration = {
1322
- patterns: ['*.config.*', 'package.json', 'tsconfig*.json', '.env*'],
1323
- chunkCount: 0,
1324
- files: [],
1325
- purpose: 'Build and environment configuration'
1326
- };
1327
-
1328
- // Add tests bundle if test files exist
1329
- const testFiles = chunks.filter(c => c.filePath && (c.filePath.includes('test') || c.filePath.includes('spec')));
1330
- if (testFiles.length > 0) {
1331
- suggestions.recommended.tests = {
1332
- patterns: ['**/*.test.*', '**/*.spec.*', '**/__tests__/**'],
1333
- chunkCount: testFiles.length,
1334
- files: testFiles.map(c => c.filePath),
1335
- purpose: 'Test files and test utilities'
1336
- };
1337
- }
1338
- }
1339
-
1340
- calculateBundleCoverage(bundles) {
1341
- const totalFiles = bundles.reduce((sum, b) => sum + b.fileCount, 0);
1342
- const masterBundle = bundles.find(b => b.name === 'master');
1343
-
1344
- if (masterBundle && bundles.length === 1) {
1345
- return { coverage: 100, organized: false, recommendation: 'Create specific bundles for better organization' };
1346
- }
1347
-
1348
- return { coverage: 100, organized: true, recommendation: 'Bundle organization looks good' };
1349
- }
1350
-
1351
- assessBundleHealth(bundles) {
1352
- const health = {
1353
- score: 0,
1354
- issues: [],
1355
- strengths: []
1356
- };
1357
-
1358
- // Check bundle count
1359
- if (bundles.length === 1) {
1360
- health.issues.push('Only master bundle - needs organization');
1361
- health.score += 20;
1362
- } else if (bundles.length > 1 && bundles.length <= 8) {
1363
- health.strengths.push('Good bundle organization');
1364
- health.score += 80;
284
+ async generateContextualAnswer(question, results, includeCode) {
285
+ let response = `Based on the codebase analysis:\n\n`;
286
+ if (results.chunks.length > 0) {
287
+ const top = results.chunks[0];
288
+ response += `The most relevant implementation found is \`${top.name}\` in \`${top.filePath}\` (Purpose: ${top.purpose}).\n\n`;
1365
289
  } else {
1366
- health.issues.push('Too many bundles - consider consolidation');
1367
- health.score += 60;
1368
- }
1369
-
1370
- return health;
1371
- }
1372
-
1373
- assessDefaultComplexity(bundles) {
1374
- const totalFiles = bundles.reduce((sum, b) => sum + b.fileCount, 0);
1375
-
1376
- if (totalFiles < 20) return { level: 'low', score: 1 };
1377
- if (totalFiles < 100) return { level: 'medium', score: 2 };
1378
- return { level: 'high', score: 3 };
1379
- }
1380
-
1381
- generateMaturityRecommendations(bundles, analysis) {
1382
- const recommendations = [];
1383
-
1384
- if (bundles.length === 1) {
1385
- recommendations.push('Create semantic bundles for better code organization');
290
+ response += `No direct semantic matches found. Try refining your query.`;
1386
291
  }
1387
-
1388
- if (!analysis || !analysis.chunks) {
1389
- recommendations.push('Generate semantic analysis for intelligent bundling');
1390
- }
1391
-
1392
- return recommendations;
1393
- }
1394
-
1395
- findMisalignedFiles(bundles, chunks) {
1396
- // Simplified implementation - would need more sophisticated logic
1397
- return [];
1398
- }
1399
-
1400
- analyzeNamingPatterns(bundles) {
1401
- const patterns = bundles.map(b => b.name);
1402
- const hasConsistentNaming = patterns.every(name =>
1403
- name.includes('-') || name.toLowerCase() === name
1404
- );
1405
-
1406
- return { inconsistent: !hasConsistentNaming };
1407
- }
1408
-
1409
- findDuplicatePatterns(bundles) {
1410
- const patternMap = new Map();
1411
-
1412
- bundles.forEach(bundle => {
1413
- bundle.patterns.forEach(pattern => {
1414
- if (!patternMap.has(pattern)) {
1415
- patternMap.set(pattern, []);
1416
- }
1417
- patternMap.get(pattern).push(bundle.name);
1418
- });
1419
- });
1420
-
1421
- return Array.from(patternMap.entries())
1422
- .filter(([pattern, bundleNames]) => bundleNames.length > 1)
1423
- .map(([pattern, bundleNames]) => ({ pattern, bundles: bundleNames }));
1424
- }
1425
-
1426
- async suggestCleanupActions() {
1427
- return [
1428
- { action: 'Remove unused bundle patterns', impact: 'low', effort: 'low' },
1429
- { action: 'Consolidate similar bundles', impact: 'medium', effort: 'medium' }
1430
- ];
1431
- }
1432
-
1433
- async assessCleanupImpact() {
1434
- return {
1435
- estimatedTimeReduction: '10-15%',
1436
- maintainabilityImprovement: 'medium',
1437
- riskLevel: 'low'
1438
- };
1439
- }
1440
-
1441
- async validateCurrentOrganization() {
1442
- const bundles = await this.analyzeBundles('all');
1443
- return {
1444
- valid: bundles.length > 1,
1445
- score: bundles.length === 1 ? 30 : 85,
1446
- issues: bundles.length === 1 ? ['Only master bundle exists'] : []
1447
- };
1448
- }
1449
292
 
1450
- async calculateOrganizationHealth() {
1451
- const bundles = await this.analyzeBundles('all');
1452
- const health = this.assessBundleHealth(bundles);
1453
-
1454
293
  return {
1455
- overall: health.score > 70 ? 'good' : health.score > 40 ? 'fair' : 'poor',
1456
- score: health.score,
1457
- recommendations: health.issues
294
+ response,
295
+ evidence: results.chunks.slice(0, 3),
296
+ confidence: results.chunks.length > 0 ? 0.8 : 0.2
1458
297
  };
1459
298
  }
1460
-
1461
- assessSemanticAlignment(bundles, analysis) {
1462
- // Simplified - would analyze if files are in semantically appropriate bundles
1463
- return {
1464
- aligned: bundles.length > 1,
1465
- score: bundles.length > 1 ? 80 : 30
1466
- };
1467
- }
1468
-
1469
- generateAuditRecommendations(bundles, analysis) {
1470
- const recommendations = [];
1471
-
1472
- if (bundles.length === 1) {
1473
- recommendations.push('Create semantic bundles for better organization');
1474
- }
1475
-
1476
- return recommendations;
1477
- }
1478
299
  }
1479
300
 
1480
- export default AgentRuntime;
301
+ export default AgentRuntime;