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.
- package/README.md +40 -344
- package/bin/cntx-ui-mcp.sh +3 -0
- package/bin/cntx-ui.js +2 -1
- package/lib/agent-runtime.js +161 -1340
- package/lib/agent-tools.js +9 -7
- package/lib/api-router.js +262 -79
- package/lib/bundle-manager.js +172 -407
- package/lib/configuration-manager.js +94 -59
- package/lib/database-manager.js +397 -0
- package/lib/file-system-manager.js +17 -0
- package/lib/heuristics-manager.js +119 -17
- package/lib/mcp-server.js +125 -55
- package/lib/semantic-splitter.js +222 -481
- package/lib/simple-vector-store.js +69 -300
- package/package.json +18 -31
- package/server.js +151 -73
- package/templates/TOOLS.md +41 -0
- package/templates/activities/activities/create-project-bundles/README.md +4 -3
- package/templates/activities/activities/create-project-bundles/notes.md +15 -19
- package/templates/activities/activities/create-project-bundles/tasks.md +4 -4
- package/templates/activities/activities.json +1 -1
- package/templates/agent-config.yaml +0 -13
- package/templates/agent-instructions.md +22 -6
- package/templates/agent-rules/capabilities/bundle-system.md +1 -1
- package/templates/agent-rules/project-specific/architecture.md +1 -1
- package/web/dist/assets/index-B2OdTzzI.css +1 -0
- package/web/dist/assets/index-D0tBsKiR.js +2016 -0
- package/web/dist/index.html +2 -2
- package/mcp-config-example.json +0 -9
- package/web/dist/assets/heuristics-manager-browser-DfonOP5I.js +0 -1
- package/web/dist/assets/index-dF3qg-y_.js +0 -2486
- package/web/dist/assets/index-h5FGSg_P.css +0 -1
package/lib/agent-runtime.js
CHANGED
|
@@ -1,24 +1,109 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agent Runtime for Codebase Exploration and Development
|
|
3
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
54
|
-
const searchTerms = this.extractSearchTerms(question);
|
|
140
|
+
await this.logInteraction('user', question);
|
|
55
141
|
|
|
56
|
-
// Perform semantic search
|
|
57
|
-
const
|
|
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
|
-
|
|
148
|
+
const response = {
|
|
72
149
|
question,
|
|
73
150
|
answer: answer.response,
|
|
74
151
|
evidence: answer.evidence,
|
|
75
152
|
confidence: answer.confidence,
|
|
76
|
-
|
|
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:
|
|
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
|
-
|
|
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.
|
|
213
|
-
const totalFiles = bundles.reduce((sum,
|
|
214
|
-
const totalSize = bundles.reduce((sum,
|
|
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
|
-
|
|
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
|
|
228
|
-
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
618
|
-
|
|
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
|
-
|
|
231
|
+
async identifyPatterns() {
|
|
634
232
|
return {
|
|
635
|
-
|
|
636
|
-
|
|
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
|
|
644
|
-
return
|
|
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
|
|
649
|
-
|
|
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
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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(
|
|
666
|
-
|
|
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
|
|
686
|
-
|
|
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
|
|
702
|
-
|
|
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
|
|
712
|
-
|
|
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
|
-
|
|
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
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
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;
|