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/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
- // Set task
142
- const nowContent = `# NOW\n\n**${task}**\n\nStarted: ${new Date().toLocaleString()}\n`
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} (started)`)
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
- out.done(`working on: ${currentTask}`)
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
- // Write to next.md
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
- tasks.map((t, i) => `${i + 1}. [ ] ${t}`).join('\n') +
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: tasks.length,
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
- out.done(`${tasks.length} tasks created`)
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
- return { success: true, feature: description, tasks }
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
- // Add to next.md with priority
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}] tracked`)
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
- // Auto-assign agent (simplified)
1382
- const agent = this._autoAssignAgent(task)
1383
- console.log(`🤖 Agent: ${agent}\n`)
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
- * Auto-assign agent based on task
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
- // AGENTIC: Agent assignment handled by agent-router.js with semantic analysis
1434
- // Returns default - real routing happens via MandatoryAgentRouter
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 - 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
-
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
  }