cntx-ui 2.0.13 → 2.0.15
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/bin/cntx-ui.js +137 -55
- package/lib/agent-runtime.js +1480 -0
- package/lib/agent-tools.js +368 -0
- package/lib/api-router.js +978 -0
- package/lib/bundle-manager.js +471 -0
- package/lib/configuration-manager.js +725 -0
- package/lib/file-system-manager.js +472 -0
- package/lib/heuristics-manager.js +425 -0
- package/lib/mcp-server.js +1054 -1
- package/lib/semantic-splitter.js +7 -14
- package/lib/simple-vector-store.js +329 -0
- package/lib/websocket-manager.js +470 -0
- package/package.json +10 -3
- package/server.js +662 -1933
- package/templates/activities/README.md +67 -0
- package/templates/activities/activities/create-project-bundles/README.md +83 -0
- package/templates/activities/activities/create-project-bundles/notes.md +102 -0
- package/templates/activities/activities/create-project-bundles/progress.md +63 -0
- package/templates/activities/activities/create-project-bundles/tasks.md +39 -0
- package/templates/activities/activities.json +219 -0
- package/templates/activities/lib/.markdownlint.jsonc +18 -0
- package/templates/activities/lib/create-activity.mdc +63 -0
- package/templates/activities/lib/generate-tasks.mdc +64 -0
- package/templates/activities/lib/process-task-list.mdc +52 -0
- package/templates/agent-config.yaml +78 -0
- package/templates/agent-instructions.md +218 -0
- package/templates/agent-rules/capabilities/activities-system.md +147 -0
- package/templates/agent-rules/capabilities/bundle-system.md +131 -0
- package/templates/agent-rules/capabilities/vector-search.md +135 -0
- package/templates/agent-rules/core/codebase-navigation.md +91 -0
- package/templates/agent-rules/core/performance-hierarchy.md +48 -0
- package/templates/agent-rules/core/response-formatting.md +120 -0
- package/templates/agent-rules/project-specific/architecture.md +145 -0
- package/templates/config.json +76 -0
- package/templates/hidden-files.json +14 -0
- package/web/dist/assets/heuristics-manager-browser-DfonOP5I.js +1 -0
- package/web/dist/assets/index-dF3qg-y_.js +2486 -0
- package/web/dist/assets/index-h5FGSg_P.css +1 -0
- package/web/dist/cntx-ui.svg +18 -0
- package/web/dist/index.html +25 -8
- package/lib/semantic-integration.js +0 -441
- package/web/dist/assets/index-Ci1Q-YrQ.js +0 -611
- package/web/dist/assets/index-IUp4q_fr.css +0 -1
- package/web/dist/vite.svg +0 -21
|
@@ -0,0 +1,1480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Runtime for Codebase Exploration and Development
|
|
3
|
+
* Implements the four behavior modes: Discovery, Query, Feature Investigation, Passive
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import AgentTools from './agent-tools.js';
|
|
7
|
+
|
|
8
|
+
export class AgentRuntime {
|
|
9
|
+
constructor(cntxServer) {
|
|
10
|
+
this.cntxServer = cntxServer;
|
|
11
|
+
this.tools = new AgentTools(cntxServer);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Discovery Mode: "Tell me about this codebase"
|
|
16
|
+
* Summarize bundles, architectural patterns, and code organization
|
|
17
|
+
*/
|
|
18
|
+
async discoverCodebase(options = {}) {
|
|
19
|
+
const { scope = 'all', includeDetails = true } = options;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const discovery = {
|
|
23
|
+
overview: await this.getCodebaseOverview(),
|
|
24
|
+
bundles: await this.analyzeBundles(scope),
|
|
25
|
+
architecture: await this.analyzeArchitecture(),
|
|
26
|
+
patterns: await this.identifyPatterns(),
|
|
27
|
+
recommendations: []
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (includeDetails) {
|
|
31
|
+
discovery.semanticSummary = await this.getSemanticSummary();
|
|
32
|
+
discovery.fileTypes = await this.analyzeFileTypes();
|
|
33
|
+
discovery.complexity = await this.analyzeComplexity();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Generate recommendations
|
|
37
|
+
discovery.recommendations = await this.generateDiscoveryRecommendations(discovery);
|
|
38
|
+
|
|
39
|
+
return discovery;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
throw new Error(`Discovery failed: ${error.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Query Mode: "Where is the user authentication handled?"
|
|
47
|
+
* Use semantic search and AST analysis for precise answers
|
|
48
|
+
*/
|
|
49
|
+
async answerQuery(question, options = {}) {
|
|
50
|
+
const { scope = null, maxResults = 10, includeCode = false } = options;
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
// Extract key terms from question
|
|
54
|
+
const searchTerms = this.extractSearchTerms(question);
|
|
55
|
+
|
|
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);
|
|
67
|
+
|
|
68
|
+
// Generate contextual answer
|
|
69
|
+
const answer = await this.generateContextualAnswer(question, combinedResults, includeCode);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
question,
|
|
73
|
+
answer: answer.response,
|
|
74
|
+
evidence: answer.evidence,
|
|
75
|
+
confidence: answer.confidence,
|
|
76
|
+
suggestions: answer.suggestions,
|
|
77
|
+
relatedFiles: combinedResults.files.slice(0, 5),
|
|
78
|
+
totalMatches: combinedResults.totalMatches
|
|
79
|
+
};
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new Error(`Query failed: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Feature Investigation Mode: "I want to add dark mode—what already exists?"
|
|
87
|
+
* Search for existing implementations and identify integration points
|
|
88
|
+
*/
|
|
89
|
+
async investigateFeature(featureDescription, options = {}) {
|
|
90
|
+
const { includeRecommendations = true } = options;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const investigation = {
|
|
94
|
+
feature: featureDescription,
|
|
95
|
+
existing: await this.findExistingImplementations(featureDescription),
|
|
96
|
+
related: await this.findRelatedCode(featureDescription),
|
|
97
|
+
dependencies: await this.analyzeDependencies(featureDescription),
|
|
98
|
+
integration: await this.findIntegrationPoints(featureDescription),
|
|
99
|
+
patterns: await this.identifyImplementationPatterns(featureDescription)
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
if (includeRecommendations) {
|
|
103
|
+
investigation.recommendations = await this.generateImplementationRecommendations(investigation);
|
|
104
|
+
investigation.approach = await this.suggestImplementationApproach(investigation);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return investigation;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
throw new Error(`Feature investigation failed: ${error.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
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
|
|
210
|
+
|
|
211
|
+
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);
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
projectPath: this.cntxServer.CWD,
|
|
218
|
+
totalBundles: bundles.length,
|
|
219
|
+
totalFiles,
|
|
220
|
+
totalSize,
|
|
221
|
+
formattedSize: this.formatBytes(totalSize),
|
|
222
|
+
bundleNames: bundles.map(([name]) => name)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
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 });
|
|
411
|
+
|
|
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;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
inferBundlePurpose(name, files, fileTypes) {
|
|
487
|
+
if (name.includes('component') || name.includes('ui')) return 'UI Components';
|
|
488
|
+
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
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
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) {
|
|
616
|
+
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
|
|
629
|
+
};
|
|
630
|
+
return patterns;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
identifyStructuralPatterns(files) {
|
|
634
|
+
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'))
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
async getSemanticSummary() {
|
|
643
|
+
const analysis = await this.tools.getSemanticAnalysis({ maxChunks: 100 });
|
|
644
|
+
return analysis.summary || { message: 'No semantic summary available' };
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async analyzeFileTypes() {
|
|
648
|
+
const files = await this.tools.listFiles({ limit: 500 });
|
|
649
|
+
return this.categorizeFiles(files.map(f => f.path));
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
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]++;
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
return complexity;
|
|
663
|
+
}
|
|
664
|
+
|
|
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;
|
|
683
|
+
}
|
|
684
|
+
|
|
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);
|
|
699
|
+
}
|
|
700
|
+
|
|
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
|
+
}));
|
|
709
|
+
}
|
|
710
|
+
|
|
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;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
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
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
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;
|
|
1365
|
+
} 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');
|
|
1386
|
+
}
|
|
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
|
+
|
|
1450
|
+
async calculateOrganizationHealth() {
|
|
1451
|
+
const bundles = await this.analyzeBundles('all');
|
|
1452
|
+
const health = this.assessBundleHealth(bundles);
|
|
1453
|
+
|
|
1454
|
+
return {
|
|
1455
|
+
overall: health.score > 70 ? 'good' : health.score > 40 ? 'fair' : 'poor',
|
|
1456
|
+
score: health.score,
|
|
1457
|
+
recommendations: health.issues
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
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
|
+
}
|
|
1479
|
+
|
|
1480
|
+
export default AgentRuntime;
|