prjct-cli 0.10.10 → 0.10.12
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 +88 -0
- package/core/__tests__/agentic/prompt-builder.test.js +7 -3
- package/core/agentic/agent-router.js +79 -372
- package/core/commands.js +128 -470
- package/core/domain/agent-matcher.js +71 -185
- package/core/domain/architecture-generator.js +51 -519
- package/core/domain/task-analyzer.js +11 -36
- package/package.json +1 -1
- package/templates/agent-assignment.md +72 -0
- package/templates/analysis/bug-severity.md +74 -0
- package/templates/analysis/complexity.md +54 -0
- package/templates/analysis/health.md +66 -0
- package/templates/analysis/intent.md +66 -0
- package/templates/analysis/task-breakdown.md +53 -0
- package/templates/architect/discovery.md +67 -0
- package/templates/architect/phases.md +59 -0
- package/templates/design/api.md +95 -0
- package/templates/design/architecture.md +77 -0
- package/templates/design/component.md +89 -0
- package/templates/design/database.md +78 -0
- package/templates/design/flow.md +94 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,93 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.12] - 2025-11-29
|
|
4
|
+
|
|
5
|
+
### Refactored - Mandatory Agent Assignment (100% Agentic)
|
|
6
|
+
|
|
7
|
+
All agent assignment decisions now delegated to Claude via templates. JS code is pure orchestration.
|
|
8
|
+
|
|
9
|
+
- **New Template**: `templates/agent-assignment.md`
|
|
10
|
+
- Claude decides which agent based on task + available agents + context
|
|
11
|
+
- No hardcoded scoring weights or matching algorithms
|
|
12
|
+
- Semantic understanding replaces keyword matching
|
|
13
|
+
|
|
14
|
+
- **Simplified `agent-router.js`**: 419 → 128 lines (69% reduction)
|
|
15
|
+
- Removed: scoring logic, domain mappings, caching algorithms
|
|
16
|
+
- Kept: load agents, build context, log usage (I/O only)
|
|
17
|
+
- Class renamed: `MandatoryAgentRouter` → `AgentRouter`
|
|
18
|
+
|
|
19
|
+
- **Simplified `agent-matcher.js`**: 218 → 103 lines (53% reduction)
|
|
20
|
+
- Removed: multi-factor scoring (40% domain, 30% skills, etc.)
|
|
21
|
+
- Removed: all if/else matching logic
|
|
22
|
+
- Kept: format data, record usage, load history (I/O only)
|
|
23
|
+
|
|
24
|
+
- **Updated commands.js**:
|
|
25
|
+
- `/p:now` - Assigns agent before setting task, shows `[agent]`
|
|
26
|
+
- `/p:feature` - Assigns agent to each task, format: `[agent] [ ] task`
|
|
27
|
+
- `/p:bug` - Assigns agent, shows `→ agent`
|
|
28
|
+
- `/p:build` - Uses async `_assignAgentForTask()`
|
|
29
|
+
- New method: `_assignAgentForTask()` - orchestrates agent assignment
|
|
30
|
+
|
|
31
|
+
### Architecture Principle
|
|
32
|
+
|
|
33
|
+
**JS = Orchestrator** (load files, build context, format data, I/O)
|
|
34
|
+
**Claude = Decision Maker** (via templates for all logic)
|
|
35
|
+
|
|
36
|
+
No scoring algorithms, no matching weights, no domain mappings in code.
|
|
37
|
+
|
|
38
|
+
## [0.10.11] - 2025-11-29
|
|
39
|
+
|
|
40
|
+
### Refactored - 100% Agentic System
|
|
41
|
+
|
|
42
|
+
Complete elimination of procedural keyword-based logic. Claude now decides everything via templates:
|
|
43
|
+
|
|
44
|
+
- **Templates Created (12 new files)**
|
|
45
|
+
- `templates/analysis/complexity.md` - Replaces `_detectComplexity()` keyword lists
|
|
46
|
+
- `templates/analysis/task-breakdown.md` - Replaces `_breakdownFeatureTasks()` hardcoded patterns
|
|
47
|
+
- `templates/analysis/bug-severity.md` - Replaces `_detectBugSeverity()` keyword detection
|
|
48
|
+
- `templates/analysis/health.md` - Replaces `_calculateHealth()` scoring logic
|
|
49
|
+
- `templates/analysis/intent.md` - Replaces `analyzeSemantics()` regex patterns
|
|
50
|
+
- `templates/design/architecture.md` - Architecture design guidance for Claude
|
|
51
|
+
- `templates/design/api.md` - API design guidance for Claude
|
|
52
|
+
- `templates/design/component.md` - Component design guidance for Claude
|
|
53
|
+
- `templates/design/database.md` - Database design guidance for Claude
|
|
54
|
+
- `templates/design/flow.md` - User flow design guidance for Claude
|
|
55
|
+
- `templates/architect/discovery.md` - Discovery phase template
|
|
56
|
+
- `templates/architect/phases.md` - Phase selection template
|
|
57
|
+
|
|
58
|
+
- **commands.js** - Simplified 11 helper methods
|
|
59
|
+
- `_breakdownFeatureTasks()` - Returns placeholder, Claude generates via template
|
|
60
|
+
- `_detectBugSeverity()` - Returns 'medium', Claude assesses via template
|
|
61
|
+
- `_calculateHealth()` - Simple activity check, Claude evaluates via template
|
|
62
|
+
- `_detectComplexity()` - Returns default, Claude analyzes via template
|
|
63
|
+
- `_autoAssignAgent()` - Returns 'generalist', routing via agent-router.js
|
|
64
|
+
- `_generateArchitectureDesign()` - 60 → 2 lines
|
|
65
|
+
- `_generateApiDesign()` - 75 → 2 lines
|
|
66
|
+
- `_generateComponentDesign()` - 75 → 2 lines
|
|
67
|
+
- `_generateDatabaseDesign()` - 65 → 2 lines
|
|
68
|
+
- `_generateFlowDesign()` - 58 → 2 lines
|
|
69
|
+
|
|
70
|
+
- **architecture-generator.js** - Reduced from 561 to 93 lines (83% reduction)
|
|
71
|
+
- Removed all phase-specific generation logic
|
|
72
|
+
- Claude generates content via architect templates
|
|
73
|
+
|
|
74
|
+
- **task-analyzer.js** - Simplified semantic analysis
|
|
75
|
+
- `analyzeSemantics()` - Removed all regex patterns
|
|
76
|
+
- `estimateComplexity()` - Removed keyword lists
|
|
77
|
+
- `detectDomains()` - Returns empty, Claude decides domain
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- **prompt-builder.test.js** - Updated 3 tests to match compressed format
|
|
82
|
+
- Tests now expect `## FILES:` instead of `AVAILABLE PROJECT FILES`
|
|
83
|
+
- Tests now expect `## PROJECT:` instead of `PROJECT FILES`
|
|
84
|
+
|
|
85
|
+
### Impact
|
|
86
|
+
- Zero keyword-based logic remaining
|
|
87
|
+
- All decisions delegated to Claude via templates
|
|
88
|
+
- Easier to extend (just add/edit templates)
|
|
89
|
+
- More accurate analysis (semantic understanding vs pattern matching)
|
|
90
|
+
|
|
3
91
|
## [0.10.10] - 2025-11-28
|
|
4
92
|
|
|
5
93
|
### Refactored - Agentic Architecture Optimization
|
|
@@ -143,7 +143,9 @@ describe('PromptBuilder', () => {
|
|
|
143
143
|
|
|
144
144
|
const prompt = builder.build(template, context, state)
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
// OPTIMIZED: New compressed format uses ## FILES:
|
|
147
|
+
expect(prompt).toContain('## FILES:')
|
|
148
|
+
expect(prompt).toContain('3 available')
|
|
147
149
|
expect(prompt).toContain('file1.js')
|
|
148
150
|
expect(prompt).toContain('Read')
|
|
149
151
|
})
|
|
@@ -159,7 +161,8 @@ describe('PromptBuilder', () => {
|
|
|
159
161
|
|
|
160
162
|
const prompt = builder.build(template, context, state)
|
|
161
163
|
|
|
162
|
-
|
|
164
|
+
// OPTIMIZED: New compressed format uses ## PROJECT:
|
|
165
|
+
expect(prompt).toContain('## PROJECT:')
|
|
163
166
|
expect(prompt).toContain('/test/project')
|
|
164
167
|
})
|
|
165
168
|
})
|
|
@@ -188,7 +191,8 @@ describe('PromptBuilder', () => {
|
|
|
188
191
|
expect(prompt).toContain('TOOLS:')
|
|
189
192
|
expect(prompt).toContain('Flow')
|
|
190
193
|
expect(prompt).toContain('RULES (CRITICAL)')
|
|
191
|
-
|
|
194
|
+
// OPTIMIZED: New compressed format uses ## FILES:
|
|
195
|
+
expect(prompt).toContain('## FILES:')
|
|
192
196
|
})
|
|
193
197
|
|
|
194
198
|
it('should be concise (under 2000 chars for simple prompt)', () => {
|
|
@@ -1,421 +1,128 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Agent Router - Orchestration Only
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* AGENTIC: All decisions made by Claude via templates/agent-assignment.md
|
|
5
|
+
* JS only orchestrates: load agents, build context, delegate to Claude
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* NO scoring logic, NO matching algorithms, NO hardcoded mappings
|
|
8
|
+
*
|
|
9
|
+
* @version 2.0.0
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
|
-
const fs = require('fs').promises
|
|
11
|
-
const path = require('path')
|
|
12
|
-
const
|
|
13
|
-
const
|
|
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');
|
|
18
|
-
const log = require('../utils/logger');
|
|
12
|
+
const fs = require('fs').promises
|
|
13
|
+
const path = require('path')
|
|
14
|
+
const configManager = require('../infrastructure/config-manager')
|
|
15
|
+
const pathManager = require('../infrastructure/path-manager')
|
|
19
16
|
|
|
20
|
-
class
|
|
17
|
+
class AgentRouter {
|
|
21
18
|
constructor() {
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
24
|
-
this.usageLog = [];
|
|
25
|
-
this.projectId = null;
|
|
26
|
-
this.taskAnalyzer = null;
|
|
27
|
-
this.agentMatcher = new AgentMatcher();
|
|
28
|
-
this.agentValidator = new AgentValidator();
|
|
19
|
+
this.projectId = null
|
|
20
|
+
this.agentsPath = null
|
|
29
21
|
}
|
|
30
22
|
|
|
31
23
|
/**
|
|
32
24
|
* Initialize with project context
|
|
33
|
-
*
|
|
25
|
+
* ORCHESTRATION: Just sets up paths
|
|
34
26
|
*/
|
|
35
27
|
async initialize(projectPath) {
|
|
36
|
-
this.projectId = await configManager.getProjectId(projectPath)
|
|
37
|
-
this.
|
|
38
|
-
this.agentCache = new SmartCache(this.projectId);
|
|
39
|
-
await this.agentCache.initialize();
|
|
40
|
-
this.taskAnalyzer = new TaskAnalyzer(projectPath);
|
|
41
|
-
await this.taskAnalyzer.initialize();
|
|
28
|
+
this.projectId = await configManager.getProjectId(projectPath)
|
|
29
|
+
this.agentsPath = pathManager.getPath(this.projectId, 'agents')
|
|
42
30
|
}
|
|
43
31
|
|
|
44
32
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
33
|
+
* Load all available agents from project
|
|
34
|
+
* ORCHESTRATION: File I/O only, no logic
|
|
47
35
|
*/
|
|
48
|
-
async
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
throw new Error(
|
|
63
|
-
`CRITICAL: No agent assigned for task "${task.description}".
|
|
64
|
-
System requires ALL tasks to use specialized agents.`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// STEP 4: Filter context for this specific agent
|
|
69
|
-
const filteredContext = await this.filterContextForAgent(
|
|
70
|
-
agent,
|
|
71
|
-
context,
|
|
72
|
-
taskAnalysis
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
// STEP 5: Log agent usage for tracking and learning
|
|
76
|
-
this.logAgentUsage(task, agent, filteredContext);
|
|
77
|
-
this.agentMatcher.recordSuccess(agent, taskAnalysis, true); // Learn from assignment
|
|
78
|
-
|
|
79
|
-
// STEP 6: Return agent with filtered context
|
|
80
|
-
return {
|
|
81
|
-
agent,
|
|
82
|
-
context: filteredContext,
|
|
83
|
-
taskAnalysis,
|
|
84
|
-
routing: {
|
|
85
|
-
reason: taskAnalysis.reason,
|
|
86
|
-
confidence: taskAnalysis.confidence,
|
|
87
|
-
alternativeAgents: taskAnalysis.alternatives
|
|
36
|
+
async loadAvailableAgents() {
|
|
37
|
+
try {
|
|
38
|
+
const files = await fs.readdir(this.agentsPath)
|
|
39
|
+
const agents = []
|
|
40
|
+
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
if (file.endsWith('.md')) {
|
|
43
|
+
const name = file.replace('.md', '')
|
|
44
|
+
const content = await fs.readFile(
|
|
45
|
+
path.join(this.agentsPath, file),
|
|
46
|
+
'utf-8'
|
|
47
|
+
)
|
|
48
|
+
agents.push({ name, content })
|
|
49
|
+
}
|
|
88
50
|
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
51
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* 100% AGENTIC: Delegates to TaskAnalyzer which uses templates.
|
|
96
|
-
* NO hardcoded patterns or keyword lists.
|
|
97
|
-
*/
|
|
98
|
-
async analyzeTask(task, projectPath = null) {
|
|
99
|
-
// Use TaskAnalyzer for semantic analysis (template-driven)
|
|
100
|
-
if (this.taskAnalyzer) {
|
|
101
|
-
return await this.taskAnalyzer.analyzeTask(task);
|
|
52
|
+
return agents
|
|
53
|
+
} catch {
|
|
54
|
+
return []
|
|
102
55
|
}
|
|
103
|
-
|
|
104
|
-
// Fallback: Return minimal analysis, let Claude decide in prompt
|
|
105
|
-
return {
|
|
106
|
-
domain: 'generalist',
|
|
107
|
-
confidence: 0.5,
|
|
108
|
-
matchedKeywords: [],
|
|
109
|
-
reason: 'Using generalist - Claude will analyze task in context',
|
|
110
|
-
alternatives: ['full-stack'],
|
|
111
|
-
projectTechnologies: null
|
|
112
|
-
};
|
|
113
56
|
}
|
|
114
57
|
|
|
115
58
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
59
|
+
* Get agent names list
|
|
60
|
+
* ORCHESTRATION: Simple extraction
|
|
118
61
|
*/
|
|
119
|
-
async
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const existing = await this.agentGenerator.loadAgent(overrideAgent);
|
|
123
|
-
if (existing) {
|
|
124
|
-
return existing;
|
|
125
|
-
}
|
|
126
|
-
return this.generateSpecializedAgent(overrideAgent, {}, context);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const primaryDomain = taskAnalysis.primaryDomain;
|
|
130
|
-
const projectTech = taskAnalysis.projectTechnologies || {};
|
|
131
|
-
|
|
132
|
-
// Generate cache key with tech stack
|
|
133
|
-
const techStack = {
|
|
134
|
-
languages: projectTech.languages || [],
|
|
135
|
-
frameworks: projectTech.frameworks || []
|
|
136
|
-
};
|
|
137
|
-
const cacheKey = this.agentCache.generateKey(this.projectId, primaryDomain, techStack);
|
|
138
|
-
|
|
139
|
-
// Check smart cache first
|
|
140
|
-
const cached = await this.agentCache.get(cacheKey);
|
|
141
|
-
if (cached) {
|
|
142
|
-
return cached;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// STEP 1: Load all existing agents
|
|
146
|
-
const allAgents = await this.agentGenerator.loadAllAgents();
|
|
147
|
-
|
|
148
|
-
// STEP 2: Use intelligent matching to find best agent
|
|
149
|
-
const match = this.agentMatcher.findBestAgent(allAgents, taskAnalysis);
|
|
150
|
-
|
|
151
|
-
if (match && match.score > 0.5) {
|
|
152
|
-
// Good match found - use it
|
|
153
|
-
await this.agentCache.set(cacheKey, match.agent);
|
|
154
|
-
return match.agent;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// STEP 3: Try to load domain-specific agent
|
|
158
|
-
const agentType = this.getAgentTypeForDomain(primaryDomain);
|
|
159
|
-
const existingAgent = await this.agentGenerator.loadAgent(agentType);
|
|
160
|
-
|
|
161
|
-
if (existingAgent) {
|
|
162
|
-
await this.agentCache.set(cacheKey, existingAgent);
|
|
163
|
-
return existingAgent;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// STEP 4: Validate before generating new agent
|
|
167
|
-
const config = {
|
|
168
|
-
domain: primaryDomain,
|
|
169
|
-
projectContext: context.projectSummary || context.projectContext || '',
|
|
170
|
-
expertise: this.buildExpertiseFromTech(projectTech, primaryDomain)
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const validation = this.agentValidator.validateBeforeGeneration(
|
|
174
|
-
agentType,
|
|
175
|
-
config,
|
|
176
|
-
allAgents
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
if (!validation.valid && validation.similarAgent) {
|
|
180
|
-
// Similar agent exists - use it instead
|
|
181
|
-
await this.agentCache.set(cacheKey, validation.similarAgent);
|
|
182
|
-
return validation.similarAgent;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// STEP 5: Generate new agent only if validated
|
|
186
|
-
const agent = await this.generateSpecializedAgent(primaryDomain, techStack, context);
|
|
187
|
-
|
|
188
|
-
// Validate after generation
|
|
189
|
-
const postValidation = this.agentValidator.validateAfterGeneration(agent);
|
|
190
|
-
if (!postValidation.valid) {
|
|
191
|
-
log.warn(`Agent validation issues: ${postValidation.issues.join(', ')}`);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Cache for reuse
|
|
195
|
-
await this.agentCache.set(cacheKey, agent);
|
|
196
|
-
|
|
197
|
-
return agent;
|
|
62
|
+
async getAgentNames() {
|
|
63
|
+
const agents = await this.loadAvailableAgents()
|
|
64
|
+
return agents.map(a => a.name)
|
|
198
65
|
}
|
|
199
66
|
|
|
200
67
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* 100% AGENTIC: No hardcoded framework lists.
|
|
204
|
-
* Returns ALL tech, Claude decides what's relevant.
|
|
68
|
+
* Load specific agent by name
|
|
69
|
+
* ORCHESTRATION: File I/O only
|
|
205
70
|
*/
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
// Include ALL frameworks - Claude decides relevance
|
|
215
|
-
// NO hardcoded lists like ['react', 'vue', 'angular']
|
|
216
|
-
if (projectTech.frameworks && projectTech.frameworks.length > 0) {
|
|
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(', '))
|
|
71
|
+
async loadAgent(name) {
|
|
72
|
+
try {
|
|
73
|
+
const filePath = path.join(this.agentsPath, `${name}.md`)
|
|
74
|
+
const content = await fs.readFile(filePath, 'utf-8')
|
|
75
|
+
return { name, content }
|
|
76
|
+
} catch {
|
|
77
|
+
return null
|
|
223
78
|
}
|
|
224
|
-
|
|
225
|
-
return parts.join(', ') || `${domain} development`
|
|
226
79
|
}
|
|
227
80
|
|
|
228
81
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*/
|
|
232
|
-
getAgentTypeForDomain(domain) {
|
|
233
|
-
const agentTypes = {
|
|
234
|
-
frontend: 'frontend-specialist',
|
|
235
|
-
backend: 'backend-specialist',
|
|
236
|
-
database: 'database-specialist',
|
|
237
|
-
devops: 'devops-specialist',
|
|
238
|
-
qa: 'qa-specialist',
|
|
239
|
-
architecture: 'architect',
|
|
240
|
-
generalist: 'full-stack'
|
|
241
|
-
};
|
|
242
|
-
return agentTypes[domain] || 'full-stack';
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Find similar agent from existing agents
|
|
247
|
-
* DEPRECATED: Now uses AgentMatcher for intelligent matching
|
|
248
|
-
* @private
|
|
249
|
-
*/
|
|
250
|
-
findSimilarAgent(allAgents, domain, taskAnalysis) {
|
|
251
|
-
// Use AgentMatcher instead
|
|
252
|
-
const match = this.agentMatcher.findBestAgent(allAgents, taskAnalysis);
|
|
253
|
-
return match ? match.agent : null;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Generate a specialized agent for the detected domain
|
|
258
|
-
* Only called when no existing agent is found
|
|
259
|
-
*/
|
|
260
|
-
async generateSpecializedAgent(domain, techStack, context) {
|
|
261
|
-
// Map domain to agent type
|
|
262
|
-
const agentType = this.getAgentTypeForDomain(domain);
|
|
263
|
-
|
|
264
|
-
// Generate with minimal config - let the Agent figure it out
|
|
265
|
-
const config = {
|
|
266
|
-
domain,
|
|
267
|
-
projectContext: context.projectSummary || context.projectContext || '',
|
|
268
|
-
// No hardcoded best practices passed here
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
// Generate the agent file
|
|
272
|
-
await this.agentGenerator.generateDynamicAgent(agentType, config);
|
|
273
|
-
|
|
274
|
-
// Load it immediately so we return the full agent object
|
|
275
|
-
const agent = await this.agentGenerator.loadAgent(agentType);
|
|
276
|
-
|
|
277
|
-
// If loading failed, return minimal object
|
|
278
|
-
return agent || { name: agentType, content: '', domain };
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Filter context to only what's relevant for this agent
|
|
82
|
+
* Build context for agent assignment
|
|
83
|
+
* ORCHESTRATION: Data gathering only
|
|
283
84
|
*
|
|
284
|
-
*
|
|
285
|
-
* Only excludes universal noise (node_modules, .git, dist).
|
|
286
|
-
* Claude decides relevance based on task.
|
|
287
|
-
*/
|
|
288
|
-
async filterContextForAgent(agent, fullContext, taskAnalysis) {
|
|
289
|
-
// Universal exclusions that apply to ALL projects
|
|
290
|
-
const universalExclusions = ['node_modules', '.git', 'dist', 'build', '.next', 'target', 'vendor'];
|
|
291
|
-
|
|
292
|
-
// Filter only universal noise - let Claude decide the rest
|
|
293
|
-
const filtered = {
|
|
294
|
-
...fullContext,
|
|
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'
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
return filtered;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Filter files based on patterns
|
|
85
|
+
* Claude uses this context + templates/agent-assignment.md to decide
|
|
307
86
|
*/
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
// Check if file should be excluded
|
|
311
|
-
const isExcluded = pattern.exclude.some(exclude => file.includes(exclude));
|
|
312
|
-
if (isExcluded) return false;
|
|
313
|
-
|
|
314
|
-
// Check if file matches include patterns
|
|
315
|
-
if (pattern.include.length > 0) {
|
|
316
|
-
const isIncluded = pattern.include.some(include => file.includes(include));
|
|
317
|
-
if (!isIncluded) return false;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Check extensions if specified
|
|
321
|
-
if (pattern.extensions.length > 0) {
|
|
322
|
-
const hasValidExtension = pattern.extensions.some(ext => file.endsWith(ext));
|
|
323
|
-
if (!hasValidExtension) return false;
|
|
324
|
-
}
|
|
87
|
+
async buildAssignmentContext(task, projectPath) {
|
|
88
|
+
const agents = await this.getAgentNames()
|
|
325
89
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
logAgentUsage(task, agent, context) {
|
|
334
|
-
const usage = {
|
|
335
|
-
timestamp: new Date().toISOString(),
|
|
336
|
-
task: task.description,
|
|
337
|
-
agent: agent.name,
|
|
338
|
-
domain: agent.domain || 'unknown',
|
|
339
|
-
contextSize: context.files?.length || 0,
|
|
340
|
-
contextReduction: this.calculateContextReduction(context),
|
|
341
|
-
confidence: agent.confidence || 1.0
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
this.usageLog.push(usage);
|
|
345
|
-
|
|
346
|
-
// Also append to a log file for persistence
|
|
347
|
-
this.appendToLogFile(usage);
|
|
348
|
-
|
|
349
|
-
return usage;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Calculate how much context was reduced
|
|
354
|
-
*/
|
|
355
|
-
calculateContextReduction(filteredContext) {
|
|
356
|
-
// This would compare against full context
|
|
357
|
-
// For now, estimate based on filtering
|
|
358
|
-
if (filteredContext.relevantOnly) {
|
|
359
|
-
return '70-90%'; // Typical reduction when filtering
|
|
90
|
+
return {
|
|
91
|
+
task: task.description || task,
|
|
92
|
+
availableAgents: agents,
|
|
93
|
+
projectPath,
|
|
94
|
+
projectId: this.projectId,
|
|
95
|
+
// Claude reads this and decides via template
|
|
96
|
+
_template: 'templates/agent-assignment.md'
|
|
360
97
|
}
|
|
361
|
-
return '0%';
|
|
362
98
|
}
|
|
363
99
|
|
|
364
100
|
/**
|
|
365
|
-
*
|
|
101
|
+
* Log agent usage
|
|
102
|
+
* ORCHESTRATION: File I/O only
|
|
366
103
|
*/
|
|
367
|
-
async
|
|
104
|
+
async logUsage(task, agent, projectPath) {
|
|
368
105
|
try {
|
|
369
106
|
const logPath = path.join(
|
|
370
107
|
process.env.HOME,
|
|
371
108
|
'.prjct-cli',
|
|
109
|
+
'projects',
|
|
110
|
+
this.projectId,
|
|
372
111
|
'agent-usage.jsonl'
|
|
373
|
-
)
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
const entry = JSON.stringify({
|
|
115
|
+
timestamp: new Date().toISOString(),
|
|
116
|
+
task: typeof task === 'string' ? task : task.description,
|
|
117
|
+
agent: agent.name || agent,
|
|
118
|
+
projectId: this.projectId
|
|
119
|
+
}) + '\n'
|
|
120
|
+
|
|
121
|
+
await fs.appendFile(logPath, entry)
|
|
122
|
+
} catch {
|
|
123
|
+
// Silent fail for logging
|
|
379
124
|
}
|
|
380
125
|
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Get similar domains for fallback
|
|
384
|
-
*
|
|
385
|
-
* 100% AGENTIC: Returns generic fallback.
|
|
386
|
-
* Claude determines domain relationships based on context.
|
|
387
|
-
*/
|
|
388
|
-
getSimilarDomains(domain) {
|
|
389
|
-
// No hardcoded domain relationships
|
|
390
|
-
// Claude decides what's similar based on actual project context
|
|
391
|
-
return ['full-stack', 'generalist'];
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Get usage statistics
|
|
396
|
-
*/
|
|
397
|
-
getUsageStats() {
|
|
398
|
-
const stats = {
|
|
399
|
-
totalTasks: this.usageLog.length,
|
|
400
|
-
byAgent: {},
|
|
401
|
-
avgContextReduction: '0%',
|
|
402
|
-
mostUsedAgent: null
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
// Calculate stats from usage log
|
|
406
|
-
this.usageLog.forEach(log => {
|
|
407
|
-
stats.byAgent[log.agent] = (stats.byAgent[log.agent] || 0) + 1;
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
// Find most used agent
|
|
411
|
-
const mostUsed = Object.entries(stats.byAgent).reduce((max, [agent, count]) => {
|
|
412
|
-
return count > max.count ? { agent, count } : max;
|
|
413
|
-
}, { agent: null, count: 0 });
|
|
414
|
-
|
|
415
|
-
stats.mostUsedAgent = mostUsed.agent;
|
|
416
|
-
|
|
417
|
-
return stats;
|
|
418
|
-
}
|
|
419
126
|
}
|
|
420
127
|
|
|
421
|
-
module.exports =
|
|
128
|
+
module.exports = AgentRouter
|