prjct-cli 0.9.2 → 0.10.0
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/CHANGELOG.md +111 -0
- package/core/__tests__/agentic/context-filter.test.js +2 -2
- package/core/__tests__/agentic/prompt-builder.test.js +39 -47
- package/core/__tests__/domain/agent-generator.test.js +29 -36
- package/core/__tests__/domain/agent-loader.test.js +179 -0
- package/core/agentic/agent-router.js +253 -186
- package/core/agentic/command-executor.js +61 -13
- package/core/agentic/context-filter.js +83 -83
- package/core/agentic/prompt-builder.js +51 -1
- package/core/commands.js +85 -59
- package/core/domain/agent-generator.js +77 -46
- package/core/domain/agent-loader.js +183 -0
- package/core/domain/agent-matcher.js +217 -0
- package/core/domain/agent-validator.js +217 -0
- package/core/domain/context-estimator.js +175 -0
- package/core/domain/product-standards.js +92 -0
- package/core/domain/smart-cache.js +157 -0
- package/core/domain/task-analyzer.js +353 -0
- package/core/domain/tech-detector.js +365 -0
- package/package.json +2 -2
|
@@ -10,12 +10,34 @@
|
|
|
10
10
|
const fs = require('fs').promises;
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const AgentGenerator = require('../domain/agent-generator');
|
|
13
|
+
const configManager = require('../infrastructure/config-manager');
|
|
14
|
+
const TaskAnalyzer = require('../domain/task-analyzer');
|
|
15
|
+
const AgentMatcher = require('../domain/agent-matcher');
|
|
16
|
+
const SmartCache = require('../domain/smart-cache');
|
|
17
|
+
const AgentValidator = require('../domain/agent-validator');
|
|
13
18
|
|
|
14
19
|
class MandatoryAgentRouter {
|
|
15
20
|
constructor() {
|
|
16
|
-
this.agentGenerator =
|
|
17
|
-
this.agentCache =
|
|
21
|
+
this.agentGenerator = null; // Will be initialized with projectId
|
|
22
|
+
this.agentCache = null; // SmartCache instance
|
|
18
23
|
this.usageLog = [];
|
|
24
|
+
this.projectId = null;
|
|
25
|
+
this.taskAnalyzer = null;
|
|
26
|
+
this.agentMatcher = new AgentMatcher();
|
|
27
|
+
this.agentValidator = new AgentValidator();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Initialize with project context
|
|
32
|
+
* @param {string} projectPath - Path to the project
|
|
33
|
+
*/
|
|
34
|
+
async initialize(projectPath) {
|
|
35
|
+
this.projectId = await configManager.getProjectId(projectPath);
|
|
36
|
+
this.agentGenerator = new AgentGenerator(this.projectId);
|
|
37
|
+
this.agentCache = new SmartCache(this.projectId);
|
|
38
|
+
await this.agentCache.initialize();
|
|
39
|
+
this.taskAnalyzer = new TaskAnalyzer(projectPath);
|
|
40
|
+
await this.taskAnalyzer.initialize();
|
|
19
41
|
}
|
|
20
42
|
|
|
21
43
|
/**
|
|
@@ -23,11 +45,16 @@ class MandatoryAgentRouter {
|
|
|
23
45
|
* @throws {Error} If no agent can be assigned
|
|
24
46
|
*/
|
|
25
47
|
async executeTask(task, context, projectPath) {
|
|
26
|
-
//
|
|
27
|
-
|
|
48
|
+
// Initialize if needed
|
|
49
|
+
if (!this.agentGenerator) {
|
|
50
|
+
await this.initialize(projectPath);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// STEP 1: Deep semantic task analysis (NEW)
|
|
54
|
+
const taskAnalysis = await this.taskAnalyzer.analyzeTask(task);
|
|
28
55
|
|
|
29
|
-
// STEP 2: Select or generate specialized agent (MANDATORY)
|
|
30
|
-
const agent = await this.assignAgent(taskAnalysis, context);
|
|
56
|
+
// STEP 2: Select or generate specialized agent (MANDATORY) - with intelligent matching
|
|
57
|
+
const agent = await this.assignAgent(taskAnalysis, context, projectPath);
|
|
31
58
|
|
|
32
59
|
// STEP 3: Validate agent assignment
|
|
33
60
|
if (!agent || !agent.name) {
|
|
@@ -44,8 +71,9 @@ class MandatoryAgentRouter {
|
|
|
44
71
|
taskAnalysis
|
|
45
72
|
);
|
|
46
73
|
|
|
47
|
-
// STEP 5: Log agent usage for tracking
|
|
74
|
+
// STEP 5: Log agent usage for tracking and learning
|
|
48
75
|
this.logAgentUsage(task, agent, filteredContext);
|
|
76
|
+
this.agentMatcher.recordSuccess(agent, taskAnalysis, true); // Learn from assignment
|
|
49
77
|
|
|
50
78
|
// STEP 6: Return agent with filtered context
|
|
51
79
|
return {
|
|
@@ -62,145 +90,241 @@ class MandatoryAgentRouter {
|
|
|
62
90
|
|
|
63
91
|
/**
|
|
64
92
|
* Analyze task to determine what type of expertise is needed
|
|
93
|
+
* DEPRECATED: Now uses TaskAnalyzer for deep semantic analysis
|
|
94
|
+
* Kept for backward compatibility
|
|
65
95
|
*/
|
|
66
|
-
analyzeTask(task) {
|
|
96
|
+
async analyzeTask(task, projectPath = null) {
|
|
67
97
|
const description = task.description?.toLowerCase() || '';
|
|
68
98
|
const type = task.type?.toLowerCase() || '';
|
|
69
99
|
|
|
70
|
-
//
|
|
100
|
+
// Get project technologies for better matching
|
|
101
|
+
let projectTech = null
|
|
102
|
+
if (projectPath) {
|
|
103
|
+
try {
|
|
104
|
+
const TechDetector = require('../domain/tech-detector');
|
|
105
|
+
const detector = new TechDetector(projectPath);
|
|
106
|
+
projectTech = await detector.detectAll();
|
|
107
|
+
} catch (error) {
|
|
108
|
+
// If detection fails, continue with keyword-based analysis
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Semantic patterns - broader, more flexible
|
|
71
113
|
const patterns = {
|
|
72
114
|
frontend: [
|
|
73
|
-
'component', 'ui', '
|
|
74
|
-
'css', 'layout', 'responsive', '
|
|
115
|
+
'component', 'ui', 'user interface', 'frontend', 'client',
|
|
116
|
+
'style', 'css', 'layout', 'responsive', 'design',
|
|
117
|
+
'page', 'view', 'template', 'render', 'display'
|
|
75
118
|
],
|
|
76
119
|
backend: [
|
|
77
120
|
'api', 'server', 'endpoint', 'route', 'middleware',
|
|
78
|
-
'auth', 'authentication', '
|
|
121
|
+
'auth', 'authentication', 'authorization', 'jwt', 'session',
|
|
122
|
+
'backend', 'service', 'controller', 'handler'
|
|
79
123
|
],
|
|
80
124
|
database: [
|
|
81
|
-
'database', 'query', 'migration', 'schema', 'model',
|
|
82
|
-
'sql', '
|
|
125
|
+
'database', 'db', 'query', 'migration', 'schema', 'model',
|
|
126
|
+
'sql', 'data', 'table', 'collection', 'index', 'relation'
|
|
83
127
|
],
|
|
84
128
|
devops: [
|
|
85
|
-
'deploy', '
|
|
86
|
-
'build', 'ship', 'release',
|
|
129
|
+
'deploy', 'deployment', 'docker', 'kubernetes', 'k8s',
|
|
130
|
+
'ci/cd', 'pipeline', 'build', 'ship', 'release',
|
|
131
|
+
'production', 'infrastructure', 'container', 'orchestration'
|
|
87
132
|
],
|
|
88
133
|
qa: [
|
|
89
|
-
'test', 'bug', 'error', 'fix', 'debug', 'issue',
|
|
90
|
-
'quality', 'coverage', 'unit test', 'integration'
|
|
134
|
+
'test', 'testing', 'bug', 'error', 'fix', 'debug', 'issue',
|
|
135
|
+
'quality', 'coverage', 'unit test', 'integration test',
|
|
136
|
+
'e2e', 'spec', 'assertion', 'validation'
|
|
91
137
|
],
|
|
92
138
|
architecture: [
|
|
93
139
|
'design', 'architecture', 'pattern', 'structure',
|
|
94
|
-
'refactor', '
|
|
140
|
+
'refactor', 'refactoring', 'organize', 'plan',
|
|
141
|
+
'feature', 'system', 'module', 'component design'
|
|
95
142
|
]
|
|
96
143
|
};
|
|
97
144
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
145
|
+
// If we have project tech, enhance patterns with actual technologies
|
|
146
|
+
if (projectTech) {
|
|
147
|
+
// Add detected frontend frameworks to frontend patterns
|
|
148
|
+
const frontendTech = [
|
|
149
|
+
...projectTech.frameworks.filter(f => ['react', 'vue', 'angular', 'svelte', 'next', 'nuxt'].includes(f.toLowerCase())),
|
|
150
|
+
...projectTech.buildTools.filter(t => ['vite', 'webpack'].includes(t.toLowerCase()))
|
|
151
|
+
];
|
|
152
|
+
if (frontendTech.length > 0) {
|
|
153
|
+
patterns.frontend.push(...frontendTech.map(t => t.toLowerCase()));
|
|
154
|
+
}
|
|
102
155
|
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
156
|
+
// Add detected backend frameworks to backend patterns
|
|
157
|
+
const backendTech = projectTech.frameworks.filter(f =>
|
|
158
|
+
['express', 'fastify', 'django', 'flask', 'rails', 'phoenix'].includes(f.toLowerCase())
|
|
106
159
|
);
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
confidence = matches.length;
|
|
110
|
-
detectedDomain = domain;
|
|
111
|
-
matchedKeywords = matches;
|
|
160
|
+
if (backendTech.length > 0) {
|
|
161
|
+
patterns.backend.push(...backendTech.map(t => t.toLowerCase()));
|
|
112
162
|
}
|
|
113
163
|
}
|
|
114
164
|
|
|
115
|
-
// Detect
|
|
116
|
-
const
|
|
165
|
+
// Detect primary domain
|
|
166
|
+
const { detectedDomain, matchedKeywords, confidence } = this.detectDomain(description, type, patterns);
|
|
117
167
|
|
|
118
168
|
return {
|
|
119
169
|
domain: detectedDomain,
|
|
120
|
-
confidence: confidence > 0 ? (confidence / 3) : 0.3,
|
|
170
|
+
confidence: confidence > 0 ? Math.min(confidence / 3, 1.0) : 0.3,
|
|
121
171
|
matchedKeywords,
|
|
122
|
-
techStack,
|
|
123
172
|
reason: `Detected ${detectedDomain} task based on: ${matchedKeywords.join(', ')}`,
|
|
124
|
-
alternatives: this.getSimilarDomains(detectedDomain)
|
|
173
|
+
alternatives: this.getSimilarDomains(detectedDomain),
|
|
174
|
+
projectTechnologies: projectTech
|
|
125
175
|
};
|
|
126
176
|
}
|
|
127
177
|
|
|
128
178
|
/**
|
|
129
|
-
* Detect
|
|
179
|
+
* Detect domain based on patterns
|
|
130
180
|
*/
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// Language detection
|
|
140
|
-
const languages = [
|
|
141
|
-
'javascript', 'typescript', 'python', 'ruby', 'go',
|
|
142
|
-
'rust', 'java', 'csharp', 'php', 'elixir', 'swift'
|
|
143
|
-
];
|
|
144
|
-
|
|
145
|
-
const frameworks = [
|
|
146
|
-
'react', 'vue', 'angular', 'express', 'django', 'rails',
|
|
147
|
-
'spring', 'laravel', 'phoenix', 'gin', 'fastapi', 'nextjs'
|
|
148
|
-
];
|
|
149
|
-
|
|
150
|
-
const databases = [
|
|
151
|
-
'postgres', 'mysql', 'mongodb', 'redis', 'elasticsearch',
|
|
152
|
-
'dynamodb', 'firebase', 'supabase', 'sqlite'
|
|
153
|
-
];
|
|
154
|
-
|
|
155
|
-
// Check for each technology
|
|
156
|
-
languages.forEach(lang => {
|
|
157
|
-
if (description.includes(lang)) {
|
|
158
|
-
technologies.languages.push(lang);
|
|
159
|
-
}
|
|
181
|
+
detectDomain(description, type, patterns) {
|
|
182
|
+
// Simple domain detection based on keywords
|
|
183
|
+
const matches = Object.entries(patterns).map(([domain, keywords]) => {
|
|
184
|
+
const found = keywords.filter(keyword =>
|
|
185
|
+
description.includes(keyword) || type.includes(keyword)
|
|
186
|
+
);
|
|
187
|
+
return { domain, keywords: found, count: found.length };
|
|
160
188
|
});
|
|
161
189
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
});
|
|
190
|
+
// Sort by count descending
|
|
191
|
+
const sorted = matches.sort((a, b) => b.count - a.count);
|
|
192
|
+
const bestMatch = sorted[0];
|
|
167
193
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
194
|
+
if (bestMatch && bestMatch.count > 0) {
|
|
195
|
+
return {
|
|
196
|
+
detectedDomain: bestMatch.domain,
|
|
197
|
+
matchedKeywords: bestMatch.keywords,
|
|
198
|
+
confidence: bestMatch.count
|
|
199
|
+
};
|
|
200
|
+
}
|
|
173
201
|
|
|
174
|
-
return
|
|
202
|
+
return {
|
|
203
|
+
detectedDomain: 'generalist',
|
|
204
|
+
matchedKeywords: [],
|
|
205
|
+
confidence: 0.5
|
|
206
|
+
};
|
|
175
207
|
}
|
|
176
208
|
|
|
177
209
|
/**
|
|
178
210
|
* Assign the best agent for the task
|
|
179
|
-
*
|
|
211
|
+
* IMPROVED: Uses intelligent matching with scoring
|
|
180
212
|
*/
|
|
181
|
-
async assignAgent(taskAnalysis, context) {
|
|
182
|
-
|
|
213
|
+
async assignAgent(taskAnalysis, context, projectPath, overrideAgent = null) {
|
|
214
|
+
// Respect override
|
|
215
|
+
if (overrideAgent) {
|
|
216
|
+
const existing = await this.agentGenerator.loadAgent(overrideAgent);
|
|
217
|
+
if (existing) {
|
|
218
|
+
return existing;
|
|
219
|
+
}
|
|
220
|
+
return this.generateSpecializedAgent(overrideAgent, {}, context);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const primaryDomain = taskAnalysis.primaryDomain;
|
|
224
|
+
const projectTech = taskAnalysis.projectTechnologies || {};
|
|
225
|
+
|
|
226
|
+
// Generate cache key with tech stack
|
|
227
|
+
const techStack = {
|
|
228
|
+
languages: projectTech.languages || [],
|
|
229
|
+
frameworks: projectTech.frameworks || []
|
|
230
|
+
};
|
|
231
|
+
const cacheKey = this.agentCache.generateKey(this.projectId, primaryDomain, techStack);
|
|
232
|
+
|
|
233
|
+
// Check smart cache first
|
|
234
|
+
const cached = await this.agentCache.get(cacheKey);
|
|
235
|
+
if (cached) {
|
|
236
|
+
return cached;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// STEP 1: Load all existing agents
|
|
240
|
+
const allAgents = await this.agentGenerator.loadAllAgents();
|
|
241
|
+
|
|
242
|
+
// STEP 2: Use intelligent matching to find best agent
|
|
243
|
+
const match = this.agentMatcher.findBestAgent(allAgents, taskAnalysis);
|
|
244
|
+
|
|
245
|
+
if (match && match.score > 0.5) {
|
|
246
|
+
// Good match found - use it
|
|
247
|
+
await this.agentCache.set(cacheKey, match.agent);
|
|
248
|
+
return match.agent;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// STEP 3: Try to load domain-specific agent
|
|
252
|
+
const agentType = this.getAgentTypeForDomain(primaryDomain);
|
|
253
|
+
const existingAgent = await this.agentGenerator.loadAgent(agentType);
|
|
254
|
+
|
|
255
|
+
if (existingAgent) {
|
|
256
|
+
await this.agentCache.set(cacheKey, existingAgent);
|
|
257
|
+
return existingAgent;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// STEP 4: Validate before generating new agent
|
|
261
|
+
const config = {
|
|
262
|
+
domain: primaryDomain,
|
|
263
|
+
projectContext: context.projectSummary || context.projectContext || '',
|
|
264
|
+
expertise: this.buildExpertiseFromTech(projectTech, primaryDomain)
|
|
265
|
+
};
|
|
183
266
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
267
|
+
const validation = this.agentValidator.validateBeforeGeneration(
|
|
268
|
+
agentType,
|
|
269
|
+
config,
|
|
270
|
+
allAgents
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
if (!validation.valid && validation.similarAgent) {
|
|
274
|
+
// Similar agent exists - use it instead
|
|
275
|
+
await this.agentCache.set(cacheKey, validation.similarAgent);
|
|
276
|
+
return validation.similarAgent;
|
|
188
277
|
}
|
|
189
278
|
|
|
190
|
-
// Generate
|
|
191
|
-
const agent = await this.generateSpecializedAgent(
|
|
279
|
+
// STEP 5: Generate new agent only if validated
|
|
280
|
+
const agent = await this.generateSpecializedAgent(primaryDomain, techStack, context);
|
|
281
|
+
|
|
282
|
+
// Validate after generation
|
|
283
|
+
const postValidation = this.agentValidator.validateAfterGeneration(agent);
|
|
284
|
+
if (!postValidation.valid) {
|
|
285
|
+
console.warn(`⚠️ Agent validation issues: ${postValidation.issues.join(', ')}`);
|
|
286
|
+
}
|
|
192
287
|
|
|
193
288
|
// Cache for reuse
|
|
194
|
-
this.agentCache.set(cacheKey, agent);
|
|
289
|
+
await this.agentCache.set(cacheKey, agent);
|
|
195
290
|
|
|
196
291
|
return agent;
|
|
197
292
|
}
|
|
198
293
|
|
|
199
294
|
/**
|
|
200
|
-
*
|
|
295
|
+
* Build expertise string from tech stack
|
|
201
296
|
*/
|
|
202
|
-
|
|
203
|
-
|
|
297
|
+
buildExpertiseFromTech(projectTech, domain) {
|
|
298
|
+
const parts = []
|
|
299
|
+
|
|
300
|
+
if (projectTech.languages && projectTech.languages.length > 0) {
|
|
301
|
+
parts.push(projectTech.languages.join(', '))
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (projectTech.frameworks && projectTech.frameworks.length > 0) {
|
|
305
|
+
const relevantFrameworks = projectTech.frameworks.filter(f => {
|
|
306
|
+
const fLower = f.toLowerCase()
|
|
307
|
+
if (domain === 'frontend') {
|
|
308
|
+
return ['react', 'vue', 'angular', 'svelte', 'next', 'nuxt'].some(tech => fLower.includes(tech))
|
|
309
|
+
}
|
|
310
|
+
if (domain === 'backend') {
|
|
311
|
+
return ['express', 'fastify', 'django', 'flask', 'rails', 'phoenix'].some(tech => fLower.includes(tech))
|
|
312
|
+
}
|
|
313
|
+
return true
|
|
314
|
+
})
|
|
315
|
+
if (relevantFrameworks.length > 0) {
|
|
316
|
+
parts.push(relevantFrameworks.join(', '))
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return parts.join(', ') || `${domain} development`
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get agent type name for a domain
|
|
325
|
+
* @private
|
|
326
|
+
*/
|
|
327
|
+
getAgentTypeForDomain(domain) {
|
|
204
328
|
const agentTypes = {
|
|
205
329
|
frontend: 'frontend-specialist',
|
|
206
330
|
backend: 'backend-specialist',
|
|
@@ -210,85 +334,43 @@ class MandatoryAgentRouter {
|
|
|
210
334
|
architecture: 'architect',
|
|
211
335
|
generalist: 'full-stack'
|
|
212
336
|
};
|
|
337
|
+
return agentTypes[domain] || 'full-stack';
|
|
338
|
+
}
|
|
213
339
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return this.agentGenerator.generateDynamicAgent(agentType, config);
|
|
340
|
+
/**
|
|
341
|
+
* Find similar agent from existing agents
|
|
342
|
+
* DEPRECATED: Now uses AgentMatcher for intelligent matching
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
findSimilarAgent(allAgents, domain, taskAnalysis) {
|
|
346
|
+
// Use AgentMatcher instead
|
|
347
|
+
const match = this.agentMatcher.findBestAgent(allAgents, taskAnalysis);
|
|
348
|
+
return match ? match.agent : null;
|
|
225
349
|
}
|
|
226
350
|
|
|
227
351
|
/**
|
|
228
|
-
*
|
|
352
|
+
* Generate a specialized agent for the detected domain
|
|
353
|
+
* Only called when no existing agent is found
|
|
229
354
|
*/
|
|
230
|
-
async
|
|
231
|
-
|
|
355
|
+
async generateSpecializedAgent(domain, techStack, context) {
|
|
356
|
+
// Map domain to agent type
|
|
357
|
+
const agentType = this.getAgentTypeForDomain(domain);
|
|
232
358
|
|
|
233
|
-
//
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
'Responsive design principles',
|
|
239
|
-
'Accessibility standards',
|
|
240
|
-
'Performance optimization (lazy loading, memoization)'
|
|
241
|
-
],
|
|
242
|
-
backend: [
|
|
243
|
-
'RESTful API design principles',
|
|
244
|
-
'Authentication and authorization patterns',
|
|
245
|
-
'Error handling and logging',
|
|
246
|
-
'Rate limiting and caching',
|
|
247
|
-
'Database connection pooling'
|
|
248
|
-
],
|
|
249
|
-
database: [
|
|
250
|
-
'Normalization principles',
|
|
251
|
-
'Index optimization',
|
|
252
|
-
'Query performance tuning',
|
|
253
|
-
'Transaction management',
|
|
254
|
-
'Backup and recovery strategies'
|
|
255
|
-
],
|
|
256
|
-
devops: [
|
|
257
|
-
'CI/CD pipeline best practices',
|
|
258
|
-
'Container orchestration',
|
|
259
|
-
'Infrastructure as Code',
|
|
260
|
-
'Monitoring and alerting',
|
|
261
|
-
'Security scanning'
|
|
262
|
-
],
|
|
263
|
-
qa: [
|
|
264
|
-
'Test pyramid (unit, integration, e2e)',
|
|
265
|
-
'Test coverage standards',
|
|
266
|
-
'Mocking and stubbing',
|
|
267
|
-
'Performance testing',
|
|
268
|
-
'Security testing'
|
|
269
|
-
]
|
|
359
|
+
// Generate with minimal config - let the Agent figure it out
|
|
360
|
+
const config = {
|
|
361
|
+
domain,
|
|
362
|
+
projectContext: context.projectSummary || context.projectContext || '',
|
|
363
|
+
// No hardcoded best practices passed here
|
|
270
364
|
};
|
|
271
365
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
practices.push('Hooks patterns', 'Context API', 'Component lifecycle');
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (techStack.languages.includes('ruby')) {
|
|
284
|
-
practices.push('Ruby idioms', 'Metaprogramming carefully', 'Convention over configuration');
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (techStack.languages.includes('go')) {
|
|
288
|
-
practices.push('Goroutines and channels', 'Error handling patterns', 'Interface design');
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return practices;
|
|
366
|
+
// Generate the agent file
|
|
367
|
+
await this.agentGenerator.generateDynamicAgent(agentType, config);
|
|
368
|
+
|
|
369
|
+
// Load it immediately so we return the full agent object
|
|
370
|
+
const agent = await this.agentGenerator.loadAgent(agentType);
|
|
371
|
+
|
|
372
|
+
// If loading failed, return minimal object
|
|
373
|
+
return agent || { name: agentType, content: '', domain };
|
|
292
374
|
}
|
|
293
375
|
|
|
294
376
|
/**
|
|
@@ -349,31 +431,18 @@ class MandatoryAgentRouter {
|
|
|
349
431
|
filterFiles(files, pattern) {
|
|
350
432
|
return files.filter(file => {
|
|
351
433
|
// Check if file should be excluded
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
434
|
+
const isExcluded = pattern.exclude.some(exclude => file.includes(exclude));
|
|
435
|
+
if (isExcluded) return false;
|
|
355
436
|
|
|
356
437
|
// Check if file matches include patterns
|
|
357
438
|
if (pattern.include.length > 0) {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if (file.includes(include)) {
|
|
361
|
-
matches = true;
|
|
362
|
-
break;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (!matches) return false;
|
|
439
|
+
const isIncluded = pattern.include.some(include => file.includes(include));
|
|
440
|
+
if (!isIncluded) return false;
|
|
366
441
|
}
|
|
367
442
|
|
|
368
443
|
// Check extensions if specified
|
|
369
444
|
if (pattern.extensions.length > 0) {
|
|
370
|
-
|
|
371
|
-
for (const ext of pattern.extensions) {
|
|
372
|
-
if (file.endsWith(ext)) {
|
|
373
|
-
hasValidExtension = true;
|
|
374
|
-
break;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
445
|
+
const hasValidExtension = pattern.extensions.some(ext => file.endsWith(ext));
|
|
377
446
|
if (!hasValidExtension) return false;
|
|
378
447
|
}
|
|
379
448
|
|
|
@@ -467,13 +536,11 @@ class MandatoryAgentRouter {
|
|
|
467
536
|
});
|
|
468
537
|
|
|
469
538
|
// Find most used agent
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
}
|
|
539
|
+
const mostUsed = Object.entries(stats.byAgent).reduce((max, [agent, count]) => {
|
|
540
|
+
return count > max.count ? { agent, count } : max;
|
|
541
|
+
}, { agent: null, count: 0 });
|
|
542
|
+
|
|
543
|
+
stats.mostUsedAgent = mostUsed.agent;
|
|
477
544
|
|
|
478
545
|
return stats;
|
|
479
546
|
}
|