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
|
@@ -1,217 +1,103 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AgentMatcher -
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @version 1.0.0
|
|
2
|
+
* AgentMatcher - Orchestration Only
|
|
3
|
+
*
|
|
4
|
+
* AGENTIC: All matching decisions made by Claude via templates/agent-assignment.md
|
|
5
|
+
* JS only orchestrates: format data, pass to Claude, return result
|
|
6
|
+
*
|
|
7
|
+
* NO scoring logic, NO algorithms, NO hardcoded weights
|
|
8
|
+
*
|
|
9
|
+
* @version 2.0.0
|
|
11
10
|
*/
|
|
12
11
|
|
|
12
|
+
const fs = require('fs').promises
|
|
13
|
+
const path = require('path')
|
|
14
|
+
|
|
13
15
|
class AgentMatcher {
|
|
14
16
|
constructor() {
|
|
15
|
-
this.
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Find best agent for a task using intelligent scoring
|
|
20
|
-
* @param {Array<Object>} availableAgents - All available agents
|
|
21
|
-
* @param {Object} taskAnalysis - Task analysis result
|
|
22
|
-
* @returns {Object|null} Best matching agent with score
|
|
23
|
-
*/
|
|
24
|
-
findBestAgent(availableAgents, taskAnalysis) {
|
|
25
|
-
if (!availableAgents || availableAgents.length === 0) {
|
|
26
|
-
return null
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Score each agent
|
|
30
|
-
const scored = availableAgents.map(agent => {
|
|
31
|
-
const score = this.scoreAgent(agent, taskAnalysis)
|
|
32
|
-
return { agent, score }
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// Sort by score descending
|
|
36
|
-
scored.sort((a, b) => b.score - a.score)
|
|
37
|
-
|
|
38
|
-
// Return best match if score is above threshold
|
|
39
|
-
const best = scored[0]
|
|
40
|
-
if (best && best.score > 0.3) {
|
|
41
|
-
return {
|
|
42
|
-
agent: best.agent,
|
|
43
|
-
score: best.score,
|
|
44
|
-
alternatives: scored.slice(1, 3).map(s => ({
|
|
45
|
-
agent: s.agent,
|
|
46
|
-
score: s.score
|
|
47
|
-
}))
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return null
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Score an agent for a specific task
|
|
56
|
-
* Multi-factor scoring system
|
|
57
|
-
*/
|
|
58
|
-
scoreAgent(agent, taskAnalysis) {
|
|
59
|
-
let score = 0
|
|
60
|
-
|
|
61
|
-
// Factor 1: Domain Match (40% weight)
|
|
62
|
-
const domainScore = this.scoreDomainMatch(agent, taskAnalysis)
|
|
63
|
-
score += domainScore * 0.4
|
|
64
|
-
|
|
65
|
-
// Factor 2: Skills Match (30% weight)
|
|
66
|
-
const skillsScore = this.scoreSkillsMatch(agent, taskAnalysis)
|
|
67
|
-
score += skillsScore * 0.3
|
|
68
|
-
|
|
69
|
-
// Factor 3: Historical Success (20% weight)
|
|
70
|
-
const historyScore = this.scoreHistoricalSuccess(agent, taskAnalysis)
|
|
71
|
-
score += historyScore * 0.2
|
|
72
|
-
|
|
73
|
-
// Factor 4: Complexity Match (10% weight)
|
|
74
|
-
const complexityScore = this.scoreComplexityMatch(agent, taskAnalysis)
|
|
75
|
-
score += complexityScore * 0.1
|
|
76
|
-
|
|
77
|
-
return Math.min(score, 1.0)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Score domain match
|
|
82
|
-
*/
|
|
83
|
-
scoreDomainMatch(agent, taskAnalysis) {
|
|
84
|
-
const agentDomain = agent.domain || ''
|
|
85
|
-
const taskDomain = taskAnalysis.primaryDomain || ''
|
|
86
|
-
|
|
87
|
-
// Exact match
|
|
88
|
-
if (agentDomain === taskDomain) {
|
|
89
|
-
return 1.0
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Partial match (agent name contains domain)
|
|
93
|
-
if (agent.name && agent.name.includes(taskDomain)) {
|
|
94
|
-
return 0.7
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Check alternatives
|
|
98
|
-
if (taskAnalysis.alternatives && taskAnalysis.alternatives.includes(agentDomain)) {
|
|
99
|
-
return 0.5
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return 0.1
|
|
17
|
+
this.historyPath = null
|
|
103
18
|
}
|
|
104
19
|
|
|
105
20
|
/**
|
|
106
|
-
*
|
|
21
|
+
* Set history path for logging
|
|
22
|
+
* ORCHESTRATION: Path setup only
|
|
107
23
|
*/
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
...(projectTech.frameworks || []),
|
|
117
|
-
...(projectTech.tools || [])
|
|
118
|
-
]
|
|
119
|
-
|
|
120
|
-
// Count matching skills
|
|
121
|
-
const matchingSkills = agent.skills.filter(skill => {
|
|
122
|
-
const skillLower = skill.toLowerCase()
|
|
123
|
-
return allTech.some(tech => tech.toLowerCase().includes(skillLower) ||
|
|
124
|
-
skillLower.includes(tech.toLowerCase()))
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
if (matchingSkills.length === 0) {
|
|
128
|
-
return 0.1 // No matching skills
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Score based on match ratio
|
|
132
|
-
const matchRatio = matchingSkills.length / Math.max(agent.skills.length, allTech.length)
|
|
133
|
-
return Math.min(matchRatio * 2, 1.0) // Boost for good matches
|
|
24
|
+
setHistoryPath(projectId) {
|
|
25
|
+
this.historyPath = path.join(
|
|
26
|
+
process.env.HOME,
|
|
27
|
+
'.prjct-cli',
|
|
28
|
+
'projects',
|
|
29
|
+
projectId,
|
|
30
|
+
'agent-history.jsonl'
|
|
31
|
+
)
|
|
134
32
|
}
|
|
135
33
|
|
|
136
34
|
/**
|
|
137
|
-
*
|
|
35
|
+
* Format agents for Claude
|
|
36
|
+
* ORCHESTRATION: Data formatting only
|
|
138
37
|
*/
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (history) {
|
|
146
|
-
// Success rate from history
|
|
147
|
-
return history.successRate || 0.5
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return 0.5 // Neutral - no history
|
|
38
|
+
formatAgentsForTemplate(agents) {
|
|
39
|
+
return agents.map(a => ({
|
|
40
|
+
name: a.name,
|
|
41
|
+
domain: a.domain || 'general',
|
|
42
|
+
hasContent: !!a.content
|
|
43
|
+
}))
|
|
151
44
|
}
|
|
152
45
|
|
|
153
46
|
/**
|
|
154
|
-
*
|
|
47
|
+
* Format task for Claude
|
|
48
|
+
* ORCHESTRATION: Data formatting only
|
|
155
49
|
*/
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// Specialized agents are better for complex tasks
|
|
161
|
-
const isGeneric = !agent.skills || agent.skills.length === 0
|
|
162
|
-
|
|
163
|
-
if (taskComplexity === 'low' && isGeneric) {
|
|
164
|
-
return 0.8
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (taskComplexity === 'high' && !isGeneric) {
|
|
168
|
-
return 0.9
|
|
50
|
+
formatTaskForTemplate(task) {
|
|
51
|
+
return {
|
|
52
|
+
description: typeof task === 'string' ? task : task.description,
|
|
53
|
+
type: task.type || 'unknown'
|
|
169
54
|
}
|
|
170
|
-
|
|
171
|
-
return 0.5 // Neutral
|
|
172
55
|
}
|
|
173
56
|
|
|
174
57
|
/**
|
|
175
|
-
* Record agent
|
|
58
|
+
* Record agent usage
|
|
59
|
+
* ORCHESTRATION: File I/O only
|
|
176
60
|
*/
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
61
|
+
async recordUsage(agent, task) {
|
|
62
|
+
if (!this.historyPath) return
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const entry = JSON.stringify({
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
agent: agent.name || agent,
|
|
68
|
+
task: typeof task === 'string' ? task : task.description
|
|
69
|
+
}) + '\n'
|
|
70
|
+
|
|
71
|
+
await fs.appendFile(this.historyPath, entry)
|
|
72
|
+
} catch {
|
|
73
|
+
// Silent fail
|
|
188
74
|
}
|
|
189
|
-
history.successRate = history.successes / history.attempts
|
|
190
|
-
|
|
191
|
-
this.historyCache.set(cacheKey, history)
|
|
192
75
|
}
|
|
193
76
|
|
|
194
77
|
/**
|
|
195
|
-
*
|
|
78
|
+
* Load usage history
|
|
79
|
+
* ORCHESTRATION: File I/O only
|
|
196
80
|
*/
|
|
197
|
-
|
|
198
|
-
if (!
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
81
|
+
async loadHistory() {
|
|
82
|
+
if (!this.historyPath) return []
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const content = await fs.readFile(this.historyPath, 'utf-8')
|
|
86
|
+
return content
|
|
87
|
+
.split('\n')
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.map(line => {
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(line)
|
|
92
|
+
} catch {
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
} catch {
|
|
98
|
+
return []
|
|
210
99
|
}
|
|
211
|
-
|
|
212
|
-
return reasons.join(', ')
|
|
213
100
|
}
|
|
214
101
|
}
|
|
215
102
|
|
|
216
103
|
module.exports = AgentMatcher
|
|
217
|
-
|