prjct-cli 0.10.6 → 0.10.9

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
  /**
@@ -67,6 +67,7 @@ class ContextBuilder {
67
67
  memory: pathManager.getFilePath(projectId, 'memory', 'context.jsonl'),
68
68
  patterns: pathManager.getFilePath(projectId, 'memory', 'patterns.json'),
69
69
  analysis: pathManager.getFilePath(projectId, 'analysis', 'repo-summary.md'),
70
+ codePatterns: pathManager.getFilePath(projectId, 'analysis', 'patterns.md'),
70
71
  },
71
72
 
72
73
  // Command parameters
@@ -168,29 +169,38 @@ class ContextBuilder {
168
169
  async loadStateForCommand(context, commandName) {
169
170
  // Command-specific file requirements
170
171
  // Minimizes context window usage
172
+ // CRITICAL: Include 'codePatterns' for ALL code-modifying commands
171
173
  const commandFileMap = {
172
174
  // Core workflow
173
- 'now': ['now', 'next'],
174
- 'done': ['now', 'next', 'metrics'],
175
- 'next': ['next'],
175
+ 'now': ['now', 'next', 'analysis', 'codePatterns'],
176
+ 'done': ['now', 'next', 'metrics', 'analysis'],
177
+ 'next': ['next', 'analysis'],
176
178
 
177
179
  // Progress
178
- 'ship': ['now', 'shipped', 'metrics'],
179
- 'recap': ['shipped', 'metrics', 'now'],
180
- 'progress': ['shipped', 'metrics'],
180
+ 'ship': ['now', 'shipped', 'metrics', 'analysis'],
181
+ 'recap': ['shipped', 'metrics', 'now', 'analysis'],
182
+ 'progress': ['shipped', 'metrics', 'analysis'],
181
183
 
182
184
  // Planning
183
- 'idea': ['ideas', 'next'],
184
- 'feature': ['roadmap', 'next', 'ideas'],
185
- 'roadmap': ['roadmap'],
186
- 'spec': ['roadmap', 'next', 'specs'],
185
+ 'idea': ['ideas', 'next', 'analysis'],
186
+ 'feature': ['roadmap', 'next', 'ideas', 'analysis', 'codePatterns'],
187
+ 'roadmap': ['roadmap', 'analysis'],
188
+ 'spec': ['roadmap', 'next', 'specs', 'analysis', 'codePatterns'],
187
189
 
188
190
  // Analysis
189
- 'analyze': ['analysis', 'context'],
190
- 'sync': ['analysis', 'context', 'now'],
191
-
192
- // All files (fallback)
193
- 'default': null // null means load all
191
+ 'analyze': ['analysis', 'context', 'codePatterns'],
192
+ 'sync': ['analysis', 'context', 'now', 'codePatterns'],
193
+
194
+ // Code modification commands - ALWAYS need codePatterns
195
+ 'work': ['now', 'next', 'analysis', 'context', 'codePatterns'],
196
+ 'build': ['now', 'next', 'analysis', 'context', 'codePatterns'],
197
+ 'design': ['analysis', 'context', 'codePatterns'],
198
+ 'cleanup': ['analysis', 'context', 'codePatterns'],
199
+ 'fix': ['analysis', 'context', 'codePatterns'],
200
+ 'test': ['analysis', 'context', 'codePatterns'],
201
+
202
+ // All files (fallback) - include codePatterns for any code work
203
+ 'default': ['analysis', 'codePatterns']
194
204
  }
195
205
 
196
206
  const requiredFiles = commandFileMap[commandName] || commandFileMap.default