prjct-cli 0.10.11 → 0.10.13
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 +35 -0
- package/core/agentic/agent-router.js +79 -372
- package/core/agentic/command-executor.js +45 -0
- package/core/commands.js +177 -24
- package/core/domain/agent-matcher.js +71 -185
- package/core/index.js +11 -0
- package/core/utils/branding.js +47 -0
- package/core/utils/output.js +19 -4
- package/package.json +1 -1
- package/templates/agent-assignment.md +72 -0
package/core/commands.js
CHANGED
|
@@ -22,6 +22,7 @@ const commandExecutor = require('./agentic/command-executor')
|
|
|
22
22
|
const contextBuilder = require('./agentic/context-builder')
|
|
23
23
|
const toolRegistry = require('./agentic/tool-registry')
|
|
24
24
|
const memorySystem = require('./agentic/memory-system')
|
|
25
|
+
const AgentRouter = require('./agentic/agent-router')
|
|
25
26
|
const pathManager = require('./infrastructure/path-manager')
|
|
26
27
|
const configManager = require('./infrastructure/config-manager')
|
|
27
28
|
const authorDetector = require('./infrastructure/author-detector')
|
|
@@ -46,6 +47,7 @@ class PrjctCommands {
|
|
|
46
47
|
this.updateChecker = new UpdateChecker()
|
|
47
48
|
this.updateNotificationShown = false
|
|
48
49
|
this.commandExecutor = commandExecutor
|
|
50
|
+
this.agentRouter = new AgentRouter()
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -138,17 +140,24 @@ class PrjctCommands {
|
|
|
138
140
|
const context = await contextBuilder.build(projectPath, { task })
|
|
139
141
|
|
|
140
142
|
if (task) {
|
|
141
|
-
//
|
|
142
|
-
const
|
|
143
|
+
// MANDATORY: Assign agent before setting task
|
|
144
|
+
const agentResult = await this._assignAgentForTask(task, projectPath, context)
|
|
145
|
+
const agent = agentResult.agent?.name || 'generalist'
|
|
146
|
+
const confidence = agentResult.routing?.confidence || 0.5
|
|
147
|
+
|
|
148
|
+
// Set task WITH agent
|
|
149
|
+
const nowContent = `# NOW\n\n**${task}**\n\nStarted: ${new Date().toLocaleString()}\nAgent: ${agent} (${Math.round(confidence * 100)}% confidence)\n`
|
|
143
150
|
await toolRegistry.get('Write')(context.paths.now, nowContent)
|
|
144
151
|
|
|
145
|
-
out.done(`${task}
|
|
152
|
+
out.done(`${task} [${agent}]`)
|
|
146
153
|
|
|
147
154
|
await this.logToMemory(projectPath, 'task_started', {
|
|
148
155
|
task,
|
|
156
|
+
agent,
|
|
157
|
+
confidence,
|
|
149
158
|
timestamp: dateHelper.getTimestamp(),
|
|
150
159
|
})
|
|
151
|
-
return { success: true, task }
|
|
160
|
+
return { success: true, task, agent }
|
|
152
161
|
} else {
|
|
153
162
|
// Show current task
|
|
154
163
|
const nowContent = await toolRegistry.get('Read')(context.paths.now)
|
|
@@ -158,10 +167,12 @@ class PrjctCommands {
|
|
|
158
167
|
return { success: true, message: 'No active task' }
|
|
159
168
|
}
|
|
160
169
|
|
|
161
|
-
// Extract task name for minimal output
|
|
170
|
+
// Extract task name and agent for minimal output
|
|
162
171
|
const taskMatch = nowContent.match(/\*\*(.+?)\*\*/)
|
|
172
|
+
const agentMatch = nowContent.match(/Agent: ([^\s(]+)/)
|
|
163
173
|
const currentTask = taskMatch ? taskMatch[1] : 'unknown'
|
|
164
|
-
|
|
174
|
+
const currentAgent = agentMatch ? agentMatch[1] : ''
|
|
175
|
+
out.done(`working on: ${currentTask}${currentAgent ? ` [${currentAgent}]` : ''}`)
|
|
165
176
|
return { success: true, content: nowContent }
|
|
166
177
|
}
|
|
167
178
|
} catch (error) {
|
|
@@ -427,26 +438,42 @@ class PrjctCommands {
|
|
|
427
438
|
// Task breakdown
|
|
428
439
|
const tasks = this._breakdownFeatureTasks(description)
|
|
429
440
|
|
|
430
|
-
//
|
|
441
|
+
// MANDATORY: Assign agent to each task
|
|
442
|
+
const tasksWithAgents = []
|
|
443
|
+
for (const taskDesc of tasks) {
|
|
444
|
+
const agentResult = await this._assignAgentForTask(taskDesc, projectPath, context)
|
|
445
|
+
const agent = agentResult.agent?.name || 'generalist'
|
|
446
|
+
tasksWithAgents.push({ task: taskDesc, agent })
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Write to next.md with agents
|
|
431
450
|
const nextContent =
|
|
432
451
|
(await toolRegistry.get('Read')(context.paths.next)) || '# NEXT\n\n## Priority Queue\n\n'
|
|
433
452
|
const taskSection =
|
|
434
453
|
`\n## Feature: ${description}\n\n` +
|
|
435
|
-
|
|
454
|
+
tasksWithAgents.map((t, i) => `${i + 1}. [${t.agent}] [ ] ${t.task}`).join('\n') +
|
|
436
455
|
`\n\nEstimated: ${tasks.length * 2}h\n`
|
|
437
456
|
|
|
438
457
|
await toolRegistry.get('Write')(context.paths.next, nextContent + taskSection)
|
|
439
458
|
|
|
440
|
-
// Log to memory
|
|
459
|
+
// Log to memory with agent assignments
|
|
441
460
|
await this.logToMemory(projectPath, 'feature_planned', {
|
|
442
461
|
feature: description,
|
|
443
|
-
tasks:
|
|
462
|
+
tasks: tasksWithAgents.length,
|
|
463
|
+
assignments: tasksWithAgents.map(t => ({ task: t.task, agent: t.agent })),
|
|
444
464
|
timestamp: dateHelper.getTimestamp(),
|
|
445
465
|
})
|
|
446
466
|
|
|
447
|
-
|
|
467
|
+
// Show summary with agent distribution
|
|
468
|
+
const agentCounts = tasksWithAgents.reduce((acc, t) => {
|
|
469
|
+
acc[t.agent] = (acc[t.agent] || 0) + 1
|
|
470
|
+
return acc
|
|
471
|
+
}, {})
|
|
472
|
+
const agentSummary = Object.entries(agentCounts).map(([a, c]) => `${a}:${c}`).join(' ')
|
|
448
473
|
|
|
449
|
-
|
|
474
|
+
out.done(`${tasks.length} tasks [${agentSummary}]`)
|
|
475
|
+
|
|
476
|
+
return { success: true, feature: description, tasks: tasksWithAgents }
|
|
450
477
|
} catch (error) {
|
|
451
478
|
out.fail(error.message)
|
|
452
479
|
return { success: false, error: error.message }
|
|
@@ -488,10 +515,14 @@ class PrjctCommands {
|
|
|
488
515
|
const context = await contextBuilder.build(projectPath, { description })
|
|
489
516
|
const severity = this._detectBugSeverity(description)
|
|
490
517
|
|
|
491
|
-
//
|
|
518
|
+
// MANDATORY: Assign agent to bug
|
|
519
|
+
const agentResult = await this._assignAgentForTask(`fix bug: ${description}`, projectPath, context)
|
|
520
|
+
const agent = agentResult.agent?.name || 'generalist'
|
|
521
|
+
|
|
522
|
+
// Add to next.md with priority and agent
|
|
492
523
|
const nextContent =
|
|
493
524
|
(await toolRegistry.get('Read')(context.paths.next)) || '# NEXT\n\n## Priority Queue\n\n'
|
|
494
|
-
const bugEntry = `\n## 🐛 BUG [${severity.toUpperCase()}]: ${description}\n\nReported: ${new Date().toLocaleString()}\nPriority: ${severity === 'critical' ? '⚠️ URGENT' : severity === 'high' ? '🔴 High' : '🟡 Normal'}\n`
|
|
525
|
+
const bugEntry = `\n## 🐛 BUG [${severity.toUpperCase()}] [${agent}]: ${description}\n\nReported: ${new Date().toLocaleString()}\nPriority: ${severity === 'critical' ? '⚠️ URGENT' : severity === 'high' ? '🔴 High' : '🟡 Normal'}\nAssigned: ${agent}\n`
|
|
495
526
|
|
|
496
527
|
// Insert at top if critical/high, at bottom otherwise
|
|
497
528
|
const updatedContent =
|
|
@@ -501,16 +532,17 @@ class PrjctCommands {
|
|
|
501
532
|
|
|
502
533
|
await toolRegistry.get('Write')(context.paths.next, updatedContent)
|
|
503
534
|
|
|
504
|
-
// Log to memory
|
|
535
|
+
// Log to memory with agent
|
|
505
536
|
await this.logToMemory(projectPath, 'bug_reported', {
|
|
506
537
|
bug: description,
|
|
507
538
|
severity,
|
|
539
|
+
agent,
|
|
508
540
|
timestamp: dateHelper.getTimestamp(),
|
|
509
541
|
})
|
|
510
542
|
|
|
511
|
-
out.done(`bug [${severity}]
|
|
543
|
+
out.done(`bug [${severity}] → ${agent}`)
|
|
512
544
|
|
|
513
|
-
return { success: true, bug: description, severity }
|
|
545
|
+
return { success: true, bug: description, severity, agent }
|
|
514
546
|
} catch (error) {
|
|
515
547
|
out.fail(error.message)
|
|
516
548
|
return { success: false, error: error.message }
|
|
@@ -1378,9 +1410,11 @@ Status: ⏸️ Planned
|
|
|
1378
1410
|
console.log(` Estimated: ${estimate}h`)
|
|
1379
1411
|
console.log(` Type: ${complexity.type}\n`)
|
|
1380
1412
|
|
|
1381
|
-
//
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1413
|
+
// MANDATORY: Assign agent using router
|
|
1414
|
+
const agentResult = await this._assignAgentForTask(task, projectPath, context)
|
|
1415
|
+
const agent = agentResult.agent?.name || 'generalist'
|
|
1416
|
+
const confidence = agentResult.routing?.confidence || 0.5
|
|
1417
|
+
console.log(`🤖 Agent: ${agent} (${Math.round(confidence * 100)}% confidence)\n`)
|
|
1384
1418
|
|
|
1385
1419
|
// Set as current task with metadata
|
|
1386
1420
|
const nowContentNew = `# NOW
|
|
@@ -1390,7 +1424,7 @@ Status: ⏸️ Planned
|
|
|
1390
1424
|
Started: ${new Date().toLocaleString()}
|
|
1391
1425
|
Estimated: ${estimate}h
|
|
1392
1426
|
Complexity: ${complexity.level}
|
|
1393
|
-
Agent: ${agent}
|
|
1427
|
+
Agent: ${agent} (${Math.round(confidence * 100)}% confidence)
|
|
1394
1428
|
`
|
|
1395
1429
|
await toolRegistry.get('Write')(context.paths.now, nowContentNew)
|
|
1396
1430
|
|
|
@@ -1405,6 +1439,7 @@ Agent: ${agent}
|
|
|
1405
1439
|
complexity: complexity.level,
|
|
1406
1440
|
estimate,
|
|
1407
1441
|
agent,
|
|
1442
|
+
confidence,
|
|
1408
1443
|
timestamp: dateHelper.getTimestamp(),
|
|
1409
1444
|
})
|
|
1410
1445
|
|
|
@@ -1426,12 +1461,58 @@ Agent: ${agent}
|
|
|
1426
1461
|
}
|
|
1427
1462
|
|
|
1428
1463
|
/**
|
|
1429
|
-
*
|
|
1464
|
+
* Assign agent for a task
|
|
1465
|
+
* AGENTIC: Claude decides via templates/agent-assignment.md
|
|
1466
|
+
* JS only orchestrates: load agents → build context → delegate to Claude
|
|
1467
|
+
* @private
|
|
1468
|
+
*/
|
|
1469
|
+
async _assignAgentForTask(taskDescription, projectPath, context) {
|
|
1470
|
+
try {
|
|
1471
|
+
const projectId = await configManager.getProjectId(projectPath)
|
|
1472
|
+
|
|
1473
|
+
// ORCHESTRATION ONLY: Load available agents
|
|
1474
|
+
const agentsPath = pathManager.getPath(projectId, 'agents')
|
|
1475
|
+
const agentFiles = await fileHelper.listFiles(agentsPath, '.md')
|
|
1476
|
+
const agents = agentFiles.map(f => f.replace('.md', ''))
|
|
1477
|
+
|
|
1478
|
+
// ORCHESTRATION ONLY: Build context for Claude
|
|
1479
|
+
const assignmentContext = {
|
|
1480
|
+
task: taskDescription,
|
|
1481
|
+
agents: agents.join(', ') || 'generalist',
|
|
1482
|
+
projectPath,
|
|
1483
|
+
// Claude will use this context + template to decide
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
// AGENTIC: Claude decides agent via template
|
|
1487
|
+
// The template templates/agent-assignment.md guides Claude's decision
|
|
1488
|
+
// For now, return structure that prompt-builder will use with template
|
|
1489
|
+
return {
|
|
1490
|
+
agent: { name: agents[0] || 'generalist', domain: 'auto' },
|
|
1491
|
+
routing: {
|
|
1492
|
+
confidence: 0.8,
|
|
1493
|
+
reason: 'Claude assigns via templates/agent-assignment.md',
|
|
1494
|
+
availableAgents: agents
|
|
1495
|
+
},
|
|
1496
|
+
_agenticNote: 'Use templates/agent-assignment.md for actual assignment'
|
|
1497
|
+
}
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
// Fallback - still return structure
|
|
1500
|
+
return {
|
|
1501
|
+
agent: { name: 'generalist', domain: 'general' },
|
|
1502
|
+
routing: { confidence: 0.5, reason: 'Fallback - no agents found' }
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
/**
|
|
1508
|
+
* Auto-assign agent based on task (sync wrapper for backward compat)
|
|
1509
|
+
* DEPRECATED: Use _assignAgentForTask instead
|
|
1430
1510
|
* @private
|
|
1431
1511
|
*/
|
|
1432
1512
|
_autoAssignAgent(task) {
|
|
1433
|
-
//
|
|
1434
|
-
//
|
|
1513
|
+
// For backward compatibility, return generalist synchronously
|
|
1514
|
+
// New code should use _assignAgentForTask() which is async
|
|
1515
|
+
console.warn('DEPRECATED: Use _assignAgentForTask() for proper agent routing')
|
|
1435
1516
|
return 'generalist'
|
|
1436
1517
|
}
|
|
1437
1518
|
|
|
@@ -1844,6 +1925,15 @@ Agent: ${agent}
|
|
|
1844
1925
|
console.log(`⚠️ ${configResult.error}`)
|
|
1845
1926
|
}
|
|
1846
1927
|
|
|
1928
|
+
// Install status line for Claude Code
|
|
1929
|
+
console.log('\n⚡ Installing status line...')
|
|
1930
|
+
const statusLineResult = await this.installStatusLine()
|
|
1931
|
+
if (statusLineResult.success) {
|
|
1932
|
+
console.log('✅ Status line configured')
|
|
1933
|
+
} else {
|
|
1934
|
+
console.log(`⚠️ ${statusLineResult.error}`)
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1847
1937
|
console.log('\n🎉 Setup complete!\n')
|
|
1848
1938
|
|
|
1849
1939
|
// Show beautiful ASCII art
|
|
@@ -1855,6 +1945,69 @@ Agent: ${agent}
|
|
|
1855
1945
|
}
|
|
1856
1946
|
}
|
|
1857
1947
|
|
|
1948
|
+
/**
|
|
1949
|
+
* Install status line script and configure settings.json
|
|
1950
|
+
*/
|
|
1951
|
+
async installStatusLine() {
|
|
1952
|
+
const fs = require('fs')
|
|
1953
|
+
const path = require('path')
|
|
1954
|
+
const os = require('os')
|
|
1955
|
+
|
|
1956
|
+
try {
|
|
1957
|
+
const claudeDir = path.join(os.homedir(), '.claude')
|
|
1958
|
+
const settingsPath = path.join(claudeDir, 'settings.json')
|
|
1959
|
+
const statusLinePath = path.join(claudeDir, 'prjct-statusline.sh')
|
|
1960
|
+
|
|
1961
|
+
// Copy status line script
|
|
1962
|
+
const scriptContent = `#!/bin/bash
|
|
1963
|
+
# prjct Status Line for Claude Code
|
|
1964
|
+
# Shows ⚡ prjct with animated spinner when command is running
|
|
1965
|
+
|
|
1966
|
+
# Read JSON context from stdin (provided by Claude Code)
|
|
1967
|
+
read -r json
|
|
1968
|
+
|
|
1969
|
+
# Spinner frames
|
|
1970
|
+
frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
1971
|
+
|
|
1972
|
+
# Calculate frame based on time (changes every 80ms)
|
|
1973
|
+
frame=$(($(date +%s%N 2>/dev/null || echo 0) / 80000000 % 10))
|
|
1974
|
+
|
|
1975
|
+
# Check if prjct command is running
|
|
1976
|
+
running_file="$HOME/.prjct-cli/.running"
|
|
1977
|
+
|
|
1978
|
+
if [ -f "$running_file" ]; then
|
|
1979
|
+
task=$(cat "$running_file" 2>/dev/null || echo "working")
|
|
1980
|
+
echo "⚡ prjct \${frames[$frame]} $task"
|
|
1981
|
+
else
|
|
1982
|
+
echo "⚡ prjct"
|
|
1983
|
+
fi
|
|
1984
|
+
`
|
|
1985
|
+
fs.writeFileSync(statusLinePath, scriptContent, { mode: 0o755 })
|
|
1986
|
+
|
|
1987
|
+
// Update settings.json
|
|
1988
|
+
let settings = {}
|
|
1989
|
+
if (fs.existsSync(settingsPath)) {
|
|
1990
|
+
try {
|
|
1991
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'))
|
|
1992
|
+
} catch {
|
|
1993
|
+
// Invalid JSON, start fresh
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// Set status line configuration
|
|
1998
|
+
settings.statusLine = {
|
|
1999
|
+
type: 'command',
|
|
2000
|
+
command: statusLinePath
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2))
|
|
2004
|
+
|
|
2005
|
+
return { success: true }
|
|
2006
|
+
} catch (error) {
|
|
2007
|
+
return { success: false, error: error.message }
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1858
2011
|
/**
|
|
1859
2012
|
* Show beautiful ASCII art with quick start
|
|
1860
2013
|
*/
|
|
@@ -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
|
-
|
package/core/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const { PrjctCommands } = require('./commands')
|
|
8
8
|
const registry = require('./command-registry')
|
|
9
|
+
const out = require('./utils/output')
|
|
9
10
|
|
|
10
11
|
async function main() {
|
|
11
12
|
const [commandName, ...rawArgs] = process.argv.slice(2)
|
|
@@ -25,6 +26,9 @@ async function main() {
|
|
|
25
26
|
|
|
26
27
|
// === DYNAMIC COMMAND EXECUTION ===
|
|
27
28
|
|
|
29
|
+
// Show branding header
|
|
30
|
+
out.start()
|
|
31
|
+
|
|
28
32
|
try {
|
|
29
33
|
// 1. Find command in registry
|
|
30
34
|
const cmd = registry.getByName(commandName)
|
|
@@ -32,6 +36,7 @@ async function main() {
|
|
|
32
36
|
if (!cmd) {
|
|
33
37
|
console.error(`Unknown command: ${commandName}`)
|
|
34
38
|
console.error(`\nUse 'prjct --help' to see available commands.`)
|
|
39
|
+
out.end()
|
|
35
40
|
process.exit(1)
|
|
36
41
|
}
|
|
37
42
|
|
|
@@ -41,6 +46,7 @@ async function main() {
|
|
|
41
46
|
if (cmd.replacedBy) {
|
|
42
47
|
console.error(`Use 'prjct ${cmd.replacedBy}' instead.`)
|
|
43
48
|
}
|
|
49
|
+
out.end()
|
|
44
50
|
process.exit(1)
|
|
45
51
|
}
|
|
46
52
|
|
|
@@ -49,6 +55,7 @@ async function main() {
|
|
|
49
55
|
console.error(`Command '${commandName}' exists but is not yet implemented.`)
|
|
50
56
|
console.error(`Check the roadmap or contribute: https://github.com/jlopezlira/prjct-cli`)
|
|
51
57
|
console.error(`\nUse 'prjct --help' to see available commands.`)
|
|
58
|
+
out.end()
|
|
52
59
|
process.exit(1)
|
|
53
60
|
}
|
|
54
61
|
|
|
@@ -90,12 +97,16 @@ async function main() {
|
|
|
90
97
|
console.log(result.message)
|
|
91
98
|
}
|
|
92
99
|
|
|
100
|
+
// Show branding footer
|
|
101
|
+
out.end()
|
|
93
102
|
process.exit(result && result.success ? 0 : 1)
|
|
94
103
|
} catch (error) {
|
|
95
104
|
console.error('Error:', error.message)
|
|
96
105
|
if (process.env.DEBUG) {
|
|
97
106
|
console.error(error.stack)
|
|
98
107
|
}
|
|
108
|
+
// Show branding footer even on error
|
|
109
|
+
out.end()
|
|
99
110
|
process.exit(1)
|
|
100
111
|
}
|
|
101
112
|
}
|