prjct-cli 0.10.6 → 0.10.8

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.
@@ -15,6 +15,7 @@ const TaskAnalyzer = require('../domain/task-analyzer');
15
15
  const AgentMatcher = require('../domain/agent-matcher');
16
16
  const SmartCache = require('../domain/smart-cache');
17
17
  const AgentValidator = require('../domain/agent-validator');
18
+ const log = require('../utils/logger');
18
19
 
19
20
  class MandatoryAgentRouter {
20
21
  constructor() {
@@ -90,119 +91,24 @@ class MandatoryAgentRouter {
90
91
 
91
92
  /**
92
93
  * Analyze task to determine what type of expertise is needed
93
- * DEPRECATED: Now uses TaskAnalyzer for deep semantic analysis
94
- * Kept for backward compatibility
94
+ *
95
+ * 100% AGENTIC: Delegates to TaskAnalyzer which uses templates.
96
+ * NO hardcoded patterns or keyword lists.
95
97
  */
96
98
  async analyzeTask(task, projectPath = null) {
97
- const description = task.description?.toLowerCase() || '';
98
- const type = task.type?.toLowerCase() || '';
99
-
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
113
- const patterns = {
114
- frontend: [
115
- 'component', 'ui', 'user interface', 'frontend', 'client',
116
- 'style', 'css', 'layout', 'responsive', 'design',
117
- 'page', 'view', 'template', 'render', 'display'
118
- ],
119
- backend: [
120
- 'api', 'server', 'endpoint', 'route', 'middleware',
121
- 'auth', 'authentication', 'authorization', 'jwt', 'session',
122
- 'backend', 'service', 'controller', 'handler'
123
- ],
124
- database: [
125
- 'database', 'db', 'query', 'migration', 'schema', 'model',
126
- 'sql', 'data', 'table', 'collection', 'index', 'relation'
127
- ],
128
- devops: [
129
- 'deploy', 'deployment', 'docker', 'kubernetes', 'k8s',
130
- 'ci/cd', 'pipeline', 'build', 'ship', 'release',
131
- 'production', 'infrastructure', 'container', 'orchestration'
132
- ],
133
- qa: [
134
- 'test', 'testing', 'bug', 'error', 'fix', 'debug', 'issue',
135
- 'quality', 'coverage', 'unit test', 'integration test',
136
- 'e2e', 'spec', 'assertion', 'validation'
137
- ],
138
- architecture: [
139
- 'design', 'architecture', 'pattern', 'structure',
140
- 'refactor', 'refactoring', 'organize', 'plan',
141
- 'feature', 'system', 'module', 'component design'
142
- ]
143
- };
144
-
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
- }
155
-
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())
159
- );
160
- if (backendTech.length > 0) {
161
- patterns.backend.push(...backendTech.map(t => t.toLowerCase()));
162
- }
99
+ // Use TaskAnalyzer for semantic analysis (template-driven)
100
+ if (this.taskAnalyzer) {
101
+ return await this.taskAnalyzer.analyzeTask(task);
163
102
  }
164
103
 
165
- // Detect primary domain
166
- const { detectedDomain, matchedKeywords, confidence } = this.detectDomain(description, type, patterns);
167
-
104
+ // Fallback: Return minimal analysis, let Claude decide in prompt
168
105
  return {
169
- domain: detectedDomain,
170
- confidence: confidence > 0 ? Math.min(confidence / 3, 1.0) : 0.3,
171
- matchedKeywords,
172
- reason: `Detected ${detectedDomain} task based on: ${matchedKeywords.join(', ')}`,
173
- alternatives: this.getSimilarDomains(detectedDomain),
174
- projectTechnologies: projectTech
175
- };
176
- }
177
-
178
- /**
179
- * Detect domain based on patterns
180
- */
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 };
188
- });
189
-
190
- // Sort by count descending
191
- const sorted = matches.sort((a, b) => b.count - a.count);
192
- const bestMatch = sorted[0];
193
-
194
- if (bestMatch && bestMatch.count > 0) {
195
- return {
196
- detectedDomain: bestMatch.domain,
197
- matchedKeywords: bestMatch.keywords,
198
- confidence: bestMatch.count
199
- };
200
- }
201
-
202
- return {
203
- detectedDomain: 'generalist',
106
+ domain: 'generalist',
107
+ confidence: 0.5,
204
108
  matchedKeywords: [],
205
- confidence: 0.5
109
+ reason: 'Using generalist - Claude will analyze task in context',
110
+ alternatives: ['full-stack'],
111
+ projectTechnologies: null
206
112
  };
207
113
  }
208
114
 
@@ -282,7 +188,7 @@ class MandatoryAgentRouter {
282
188
  // Validate after generation
283
189
  const postValidation = this.agentValidator.validateAfterGeneration(agent);
284
190
  if (!postValidation.valid) {
285
- console.warn(`⚠️ Agent validation issues: ${postValidation.issues.join(', ')}`);
191
+ log.warn(`Agent validation issues: ${postValidation.issues.join(', ')}`);
286
192
  }
287
193
 
288
194
  // Cache for reuse
@@ -293,28 +199,27 @@ class MandatoryAgentRouter {
293
199
 
294
200
  /**
295
201
  * Build expertise string from tech stack
202
+ *
203
+ * 100% AGENTIC: No hardcoded framework lists.
204
+ * Returns ALL tech, Claude decides what's relevant.
296
205
  */
297
206
  buildExpertiseFromTech(projectTech, domain) {
298
207
  const parts = []
299
208
 
209
+ // Include ALL languages - no filtering
300
210
  if (projectTech.languages && projectTech.languages.length > 0) {
301
211
  parts.push(projectTech.languages.join(', '))
302
212
  }
303
213
 
214
+ // Include ALL frameworks - Claude decides relevance
215
+ // NO hardcoded lists like ['react', 'vue', 'angular']
304
216
  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
- }
217
+ parts.push(projectTech.frameworks.join(', '))
218
+ }
219
+
220
+ // Include tools if present
221
+ if (projectTech.tools && projectTech.tools.length > 0) {
222
+ parts.push(projectTech.tools.join(', '))
318
223
  }
319
224
 
320
225
  return parts.join(', ') || `${domain} development`
@@ -375,51 +280,23 @@ class MandatoryAgentRouter {
375
280
 
376
281
  /**
377
282
  * Filter context to only what's relevant for this agent
283
+ *
284
+ * 100% AGENTIC: No hardcoded directory/extension lists.
285
+ * Only excludes universal noise (node_modules, .git, dist).
286
+ * Claude decides relevance based on task.
378
287
  */
379
288
  async filterContextForAgent(agent, fullContext, taskAnalysis) {
380
- const { domain } = taskAnalysis;
381
-
382
- // Define what each agent type should see
383
- const contextPatterns = {
384
- frontend: {
385
- include: ['components', 'views', 'styles', 'pages', 'layouts'],
386
- exclude: ['node_modules', 'dist', 'build', 'migrations'],
387
- extensions: ['.jsx', '.tsx', '.vue', '.css', '.scss', '.styled.js']
388
- },
389
- backend: {
390
- include: ['routes', 'controllers', 'services', 'middleware', 'api'],
391
- exclude: ['node_modules', 'dist', 'public', 'styles'],
392
- extensions: ['.js', '.ts', '.py', '.rb', '.go', '.java']
393
- },
394
- database: {
395
- include: ['models', 'migrations', 'schemas', 'seeds', 'queries'],
396
- exclude: ['node_modules', 'public', 'styles', 'components'],
397
- extensions: ['.sql', '.js', '.ts', '.rb', '.py']
398
- },
399
- devops: {
400
- include: ['.github', '.gitlab', 'docker', 'k8s', 'terraform'],
401
- exclude: ['node_modules', 'src', 'public'],
402
- extensions: ['.yml', '.yaml', '.dockerfile', '.sh', '.tf']
403
- },
404
- qa: {
405
- include: ['tests', 'spec', '__tests__', 'test'],
406
- exclude: ['node_modules', 'dist', 'build'],
407
- extensions: ['.test.js', '.spec.js', '.test.ts', '.spec.ts']
408
- }
409
- };
289
+ // Universal exclusions that apply to ALL projects
290
+ const universalExclusions = ['node_modules', '.git', 'dist', 'build', '.next', 'target', 'vendor'];
410
291
 
411
- const pattern = contextPatterns[domain] || {
412
- include: [],
413
- exclude: ['node_modules', 'dist', 'build'],
414
- extensions: []
415
- };
416
-
417
- // Filter the context based on patterns
292
+ // Filter only universal noise - let Claude decide the rest
418
293
  const filtered = {
419
294
  ...fullContext,
420
- files: this.filterFiles(fullContext.files || [], pattern),
421
- relevantOnly: true,
422
- filterApplied: domain
295
+ files: (fullContext.files || []).filter(file =>
296
+ !universalExclusions.some(exc => file.includes(exc))
297
+ ),
298
+ relevantOnly: false, // Claude decides relevance, not us
299
+ filterApplied: 'universal-only'
423
300
  };
424
301
 
425
302
  return filtered;
@@ -498,25 +375,20 @@ class MandatoryAgentRouter {
498
375
  const logEntry = JSON.stringify(usage) + '\n';
499
376
  await fs.appendFile(logPath, logEntry);
500
377
  } catch (error) {
501
- // Log errors silently, don't break execution
502
- console.error('Failed to log agent usage:', error.message);
378
+ log.error('Failed to log agent usage:', error.message);
503
379
  }
504
380
  }
505
381
 
506
382
  /**
507
383
  * Get similar domains for fallback
384
+ *
385
+ * 100% AGENTIC: Returns generic fallback.
386
+ * Claude determines domain relationships based on context.
508
387
  */
509
388
  getSimilarDomains(domain) {
510
- const similarities = {
511
- frontend: ['fullstack', 'ui/ux'],
512
- backend: ['fullstack', 'api', 'services'],
513
- database: ['backend', 'data'],
514
- devops: ['infrastructure', 'platform'],
515
- qa: ['testing', 'quality'],
516
- architecture: ['design', 'planning']
517
- };
518
-
519
- return similarities[domain] || ['generalist'];
389
+ // No hardcoded domain relationships
390
+ // Claude decides what's similar based on actual project context
391
+ return ['full-stack', 'generalist'];
520
392
  }
521
393
 
522
394
  /**
@@ -168,29 +168,38 @@ class ContextBuilder {
168
168
  async loadStateForCommand(context, commandName) {
169
169
  // Command-specific file requirements
170
170
  // Minimizes context window usage
171
+ // CRITICAL: Always include 'analysis' for pattern detection
171
172
  const commandFileMap = {
172
173
  // Core workflow
173
- 'now': ['now', 'next'],
174
- 'done': ['now', 'next', 'metrics'],
175
- 'next': ['next'],
174
+ 'now': ['now', 'next', 'analysis'],
175
+ 'done': ['now', 'next', 'metrics', 'analysis'],
176
+ 'next': ['next', 'analysis'],
176
177
 
177
178
  // Progress
178
- 'ship': ['now', 'shipped', 'metrics'],
179
- 'recap': ['shipped', 'metrics', 'now'],
180
- 'progress': ['shipped', 'metrics'],
179
+ 'ship': ['now', 'shipped', 'metrics', 'analysis'],
180
+ 'recap': ['shipped', 'metrics', 'now', 'analysis'],
181
+ 'progress': ['shipped', 'metrics', 'analysis'],
181
182
 
182
183
  // Planning
183
- 'idea': ['ideas', 'next'],
184
- 'feature': ['roadmap', 'next', 'ideas'],
185
- 'roadmap': ['roadmap'],
186
- 'spec': ['roadmap', 'next', 'specs'],
184
+ 'idea': ['ideas', 'next', 'analysis'],
185
+ 'feature': ['roadmap', 'next', 'ideas', 'analysis'],
186
+ 'roadmap': ['roadmap', 'analysis'],
187
+ 'spec': ['roadmap', 'next', 'specs', 'analysis'],
187
188
 
188
189
  // Analysis
189
190
  'analyze': ['analysis', 'context'],
190
191
  'sync': ['analysis', 'context', 'now'],
191
192
 
193
+ // Code modification commands - ALWAYS need analysis for patterns
194
+ 'work': ['now', 'next', 'analysis', 'context'],
195
+ 'build': ['now', 'next', 'analysis', 'context'],
196
+ 'design': ['analysis', 'context'],
197
+ 'cleanup': ['analysis', 'context'],
198
+ 'fix': ['analysis', 'context'],
199
+ 'test': ['analysis', 'context'],
200
+
192
201
  // All files (fallback)
193
- 'default': null // null means load all
202
+ 'default': ['analysis'] // Always include analysis even for unknown commands
194
203
  }
195
204
 
196
205
  const requiredFiles = commandFileMap[commandName] || commandFileMap.default