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.
@@ -1,217 +1,103 @@
1
1
  /**
2
- * AgentMatcher - Intelligent Agent Matching with Scoring
3
- *
4
- * Matches tasks to agents using multi-factor scoring:
5
- * - Agent skills and capabilities
6
- * - Historical success rates
7
- * - Project technologies
8
- * - Task complexity
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.historyCache = new Map()
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
- * Score skills match
21
+ * Set history path for logging
22
+ * ORCHESTRATION: Path setup only
107
23
  */
108
- scoreSkillsMatch(agent, taskAnalysis) {
109
- if (!agent.skills || agent.skills.length === 0) {
110
- return 0.2 // Generic agent penalty
111
- }
112
-
113
- const projectTech = taskAnalysis.projectTechnologies || {}
114
- const allTech = [
115
- ...(projectTech.languages || []),
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
- * Score historical success
35
+ * Format agents for Claude
36
+ * ORCHESTRATION: Data formatting only
138
37
  */
139
- scoreHistoricalSuccess(agent, taskAnalysis) {
140
- // TODO: Load from persistent history
141
- // For now, return neutral score
142
- const cacheKey = `${agent.name}-${taskAnalysis.primaryDomain}`
143
- const history = this.historyCache.get(cacheKey)
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
- * Score complexity match
47
+ * Format task for Claude
48
+ * ORCHESTRATION: Data formatting only
155
49
  */
156
- scoreComplexityMatch(agent, taskAnalysis) {
157
- const taskComplexity = taskAnalysis.complexity || 'medium'
158
-
159
- // Generic agents are better for simple tasks
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 success for learning
58
+ * Record agent usage
59
+ * ORCHESTRATION: File I/O only
176
60
  */
177
- recordSuccess(agent, taskAnalysis, success = true) {
178
- const cacheKey = `${agent.name}-${taskAnalysis.primaryDomain}`
179
- const history = this.historyCache.get(cacheKey) || {
180
- attempts: 0,
181
- successes: 0,
182
- successRate: 0.5
183
- }
184
-
185
- history.attempts++
186
- if (success) {
187
- history.successes++
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
- * Get match explanation
78
+ * Load usage history
79
+ * ORCHESTRATION: File I/O only
196
80
  */
197
- explainMatch(match) {
198
- if (!match) {
199
- return 'No suitable agent found'
200
- }
201
-
202
- const reasons = []
203
-
204
- if (match.score > 0.8) {
205
- reasons.push('Excellent match')
206
- } else if (match.score > 0.6) {
207
- reasons.push('Good match')
208
- } else {
209
- reasons.push('Acceptable match')
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
-