@sugar-crash-studios/vibe-forge 0.4.0
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/.claude/commands/clear-attention.md +63 -0
- package/.claude/commands/compact-context.md +52 -0
- package/.claude/commands/configure-vcs.md +102 -0
- package/.claude/commands/forge.md +171 -0
- package/.claude/commands/need-help.md +77 -0
- package/.claude/commands/update-status.md +64 -0
- package/.claude/commands/worker-loop.md +106 -0
- package/.claude/hooks/worker-loop.js +198 -0
- package/.claude/scripts/setup-worker-loop.sh +45 -0
- package/.claude/settings.local.json +46 -0
- package/LICENSE +21 -0
- package/README.md +238 -0
- package/agents/aegis/personality.md +294 -0
- package/agents/anvil/personality.md +276 -0
- package/agents/architect/personality.md +258 -0
- package/agents/crucible/personality.md +360 -0
- package/agents/ember/personality.md +291 -0
- package/agents/forge-master/capabilities.md +144 -0
- package/agents/forge-master/context-template.md +128 -0
- package/agents/forge-master/personality.md +138 -0
- package/agents/furnace/personality.md +340 -0
- package/agents/herald/personality.md +247 -0
- package/agents/loki/personality.md +108 -0
- package/agents/oracle/personality.md +283 -0
- package/agents/pixel/personality.md +113 -0
- package/agents/planning-hub/personality.md +320 -0
- package/agents/scribe/personality.md +251 -0
- package/agents/temper/personality.md +218 -0
- package/bin/cli.js +375 -0
- package/bin/dashboard/api/agents.js +333 -0
- package/bin/dashboard/api/dispatch.js +483 -0
- package/bin/dashboard/api/tasks.js +416 -0
- package/bin/dashboard/frontend/index.html +13 -0
- package/bin/dashboard/frontend/package.json +16 -0
- package/bin/dashboard/frontend/src/App.svelte +222 -0
- package/bin/dashboard/frontend/src/app.css +1777 -0
- package/bin/dashboard/frontend/src/lib/components/AgentCard.svelte +60 -0
- package/bin/dashboard/frontend/src/lib/components/AgentsPanel.svelte +57 -0
- package/bin/dashboard/frontend/src/lib/components/DispatchModal.svelte +180 -0
- package/bin/dashboard/frontend/src/lib/components/Footer.svelte +33 -0
- package/bin/dashboard/frontend/src/lib/components/Header.svelte +84 -0
- package/bin/dashboard/frontend/src/lib/components/IssueCard.svelte +33 -0
- package/bin/dashboard/frontend/src/lib/components/IssuesPanel.svelte +73 -0
- package/bin/dashboard/frontend/src/lib/components/KeyboardShortcutsModal.svelte +108 -0
- package/bin/dashboard/frontend/src/lib/components/MobileTabs.svelte +52 -0
- package/bin/dashboard/frontend/src/lib/components/NotificationCard.svelte +60 -0
- package/bin/dashboard/frontend/src/lib/components/NotificationsPanel.svelte +44 -0
- package/bin/dashboard/frontend/src/lib/components/TaskCard.svelte +63 -0
- package/bin/dashboard/frontend/src/lib/components/TasksPanel.svelte +82 -0
- package/bin/dashboard/frontend/src/lib/components/Toast.svelte +45 -0
- package/bin/dashboard/frontend/src/lib/stores/agents.js +34 -0
- package/bin/dashboard/frontend/src/lib/stores/issues.js +54 -0
- package/bin/dashboard/frontend/src/lib/stores/notifications.js +48 -0
- package/bin/dashboard/frontend/src/lib/stores/tasks.js +63 -0
- package/bin/dashboard/frontend/src/lib/stores/theme.js +33 -0
- package/bin/dashboard/frontend/src/lib/stores/toast.js +35 -0
- package/bin/dashboard/frontend/src/lib/stores/ui.js +25 -0
- package/bin/dashboard/frontend/src/lib/stores/voice.js +275 -0
- package/bin/dashboard/frontend/src/lib/stores/websocket.js +295 -0
- package/bin/dashboard/frontend/src/lib/utils/api.js +101 -0
- package/bin/dashboard/frontend/src/lib/utils/formatters.js +54 -0
- package/bin/dashboard/frontend/src/main.js +9 -0
- package/bin/dashboard/frontend/svelte.config.js +5 -0
- package/bin/dashboard/frontend/vite.config.js +20 -0
- package/bin/dashboard/public/assets/index-DnfVj9Ce.css +1 -0
- package/bin/dashboard/public/assets/index-Ze5h0kXQ.js +2 -0
- package/bin/dashboard/public/index.html +14 -0
- package/bin/dashboard/server.js +566 -0
- package/bin/forge-daemon.sh +463 -0
- package/bin/forge-setup.sh +645 -0
- package/bin/forge-spawn.sh +164 -0
- package/bin/forge.cmd +83 -0
- package/bin/forge.sh +533 -0
- package/bin/lib/agents.sh +177 -0
- package/bin/lib/colors.sh +44 -0
- package/bin/lib/config.sh +347 -0
- package/bin/lib/constants.sh +241 -0
- package/bin/lib/daemon/display.sh +128 -0
- package/bin/lib/daemon/notifications.sh +263 -0
- package/bin/lib/daemon/routing.sh +77 -0
- package/bin/lib/daemon/state.sh +115 -0
- package/bin/lib/daemon/sync.sh +95 -0
- package/bin/lib/database.sh +310 -0
- package/bin/lib/heimdall-setup.js +113 -0
- package/bin/lib/heimdall.js +265 -0
- package/bin/lib/json.sh +264 -0
- package/bin/lib/terminal.js +451 -0
- package/bin/lib/util.sh +126 -0
- package/bin/lib/vcs.js +349 -0
- package/config/agent-manifest.yaml +203 -0
- package/config/agents.json +168 -0
- package/config/task-template.md +159 -0
- package/config/task-types.yaml +106 -0
- package/context/agent-status/aegis.json +7 -0
- package/context/agent-status/anvil.json +7 -0
- package/context/agent-status/architect.json +7 -0
- package/context/agent-status/crucible.json +7 -0
- package/context/agent-status/ember.json +7 -0
- package/context/agent-status/furnace.json +7 -0
- package/context/agent-status/loki.json +7 -0
- package/context/agent-status/oracle.json +7 -0
- package/context/agent-status/pixel.json +7 -0
- package/context/agent-status/planning-hub.json +7 -0
- package/context/agent-status/scribe.json +7 -0
- package/context/agent-status/temper.json +7 -0
- package/context/feature-brainstorm.md +426 -0
- package/context/forge-state.yaml +19 -0
- package/context/modern-conventions.md +129 -0
- package/context/project-context-template.md +122 -0
- package/context/project-context.md +122 -0
- package/docs/TODO.md +150 -0
- package/docs/agents.md +409 -0
- package/docs/architecture/decisions/ADR-001-daemon-modularization.md +122 -0
- package/docs/architecture/vibe-lab-integration.md +684 -0
- package/docs/architecture.md +194 -0
- package/docs/bmad-gap-analysis-2026-03-31.md +444 -0
- package/docs/cleanup-workflow.md +329 -0
- package/docs/commands.md +451 -0
- package/docs/dashboard-mockup.html +989 -0
- package/docs/getting-started.md +261 -0
- package/docs/integration/forge-ownership-policy.md +112 -0
- package/docs/npm-publishing.md +132 -0
- package/docs/roadmap-2026.md +519 -0
- package/docs/security.md +144 -0
- package/docs/wireframes/dashboard-mvp.md +1164 -0
- package/docs/workflows/README.md +32 -0
- package/docs/workflows/azure-devops.md +108 -0
- package/docs/workflows/bitbucket.md +104 -0
- package/docs/workflows/git-only.md +130 -0
- package/docs/workflows/gitea.md +168 -0
- package/docs/workflows/github.md +103 -0
- package/docs/workflows/gitlab.md +105 -0
- package/docs/workflows.md +454 -0
- package/package.json +73 -0
- package/tasks/completed/ARCH-001-duplicate-agent-config.md +121 -0
- package/tasks/completed/ARCH-002-mixed-bash-node-implementation.md +88 -0
- package/tasks/completed/ARCH-003-worker-loop-hook-duplication.md +77 -0
- package/tasks/completed/ARCH-009-test-organization.md +78 -0
- package/tasks/completed/ARCH-011-jq-vs-nodejs-json.md +94 -0
- package/tasks/completed/ARCH-012-tmp-files-in-root.md +71 -0
- package/tasks/completed/ARCH-013-exit-code-constants.md +65 -0
- package/tasks/completed/ARCH-014-sed-incompatibility.md +96 -0
- package/tasks/completed/ARCH-015-docs-todo-tracking.md +83 -0
- package/tasks/completed/BUG-dash-001-tasks-filter-error.md +31 -0
- package/tasks/completed/BUG-dash-002-agents-unknown.md +41 -0
- package/tasks/completed/CLEAN-001.md +38 -0
- package/tasks/completed/CLEAN-002.md +43 -0
- package/tasks/completed/CLEAN-003.md +47 -0
- package/tasks/completed/CLEAN-004.md +56 -0
- package/tasks/completed/CLEAN-005.md +75 -0
- package/tasks/completed/CLEAN-006.md +47 -0
- package/tasks/completed/CLEAN-007.md +34 -0
- package/tasks/completed/CLEAN-008.md +49 -0
- package/tasks/completed/CLEAN-012.md +58 -0
- package/tasks/completed/CLEAN-013.md +45 -0
- package/tasks/completed/FEATURE-001a-dashboard-wireframes.md +162 -0
- package/tasks/completed/IMPL-007a-daemon-notifications-module.md +82 -0
- package/tasks/completed/IMPL-007b-daemon-sync-module.md +71 -0
- package/tasks/completed/IMPL-007c-daemon-state-module.md +80 -0
- package/tasks/completed/IMPL-007d-daemon-routing-module.md +77 -0
- package/tasks/completed/IMPL-007e-daemon-display-module.md +77 -0
- package/tasks/completed/IMPL-007f-daemon-integration.md +124 -0
- package/tasks/completed/PLAT-1-heimdall.md +420 -0
- package/tasks/completed/SEC-001-sql-injection-fix.md +58 -0
- package/tasks/completed/SEC-002-notification-injection-fix.md +45 -0
- package/tasks/completed/SEC-003-eval-injection-fix.md +54 -0
- package/tasks/completed/SEC-004-pid-race-condition-fix.md +49 -0
- package/tasks/completed/SEC-005-worker-loop-path-fix.md +51 -0
- package/tasks/completed/SEC-006-eval-agent-names.md +55 -0
- package/tasks/completed/SEC-007-spawn-escaping.md +67 -0
- package/tasks/completed/TASK-DASH-001-server-infrastructure.md +185 -0
- package/tasks/completed/TASK-anvil-001-dashboard-frontend.md +133 -0
- package/tasks/completed/review-bmad-aegis.md +89 -0
- package/tasks/completed/review-bmad-anvil.md +80 -0
- package/tasks/completed/review-bmad-crucible.md +81 -0
- package/tasks/completed/review-bmad-ember.md +90 -0
- package/tasks/completed/review-bmad-furnace.md +79 -0
- package/tasks/completed/review-bmad-pixel.md +82 -0
- package/tasks/completed/review-bmad-scribe.md +92 -0
- package/tasks/completed/review-bmad-sentinel.md +83 -0
- package/tasks/pending/ARCH-004-git-bash-detection-duplication.md +72 -0
- package/tasks/pending/ARCH-005-missing-src-directory.md +95 -0
- package/tasks/pending/ARCH-006-task-template-location.md +64 -0
- package/tasks/pending/ARCH-008-forge-master-vs-hub.md +81 -0
- package/tasks/pending/ARCH-010-missing-index-files.md +84 -0
- package/tasks/pending/CLEAN-009.md +31 -0
- package/tasks/pending/CLEAN-010.md +30 -0
- package/tasks/pending/CLEAN-011.md +30 -0
- package/tasks/pending/CLEAN-014.md +32 -0
- package/tasks/pending/DESIGN-dash-001-layout-review.md +45 -0
- package/tasks/pending/FEATURE-001-dashboard-mvp.md +268 -0
- package/tasks/review/ARCH-007-daemon-monolith.md +162 -0
- package/tasks/review/bmad-review-aegis.md +349 -0
- package/tasks/review/bmad-review-anvil.md +259 -0
- package/tasks/review/bmad-review-crucible.md +277 -0
- package/tasks/review/bmad-review-ember.md +307 -0
- package/tasks/review/bmad-review-furnace.md +285 -0
- package/tasks/review/bmad-review-pixel.md +329 -0
- package/tasks/review/bmad-review-scribe.md +361 -0
- package/tasks/review/bmad-review-sentinel.md +242 -0
- package/tasks/review/task-001.md +78 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents API - List and query agent status
|
|
3
|
+
*
|
|
4
|
+
* Reads agent status from:
|
|
5
|
+
* 1. SQLite database (if available) - daemon's aggregated view
|
|
6
|
+
* 2. JSON files in context/agent-status/ - direct file read fallback
|
|
7
|
+
*
|
|
8
|
+
* Endpoints:
|
|
9
|
+
* GET /api/agents - List all agents with their status
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// Agent status directory
|
|
16
|
+
const AGENT_STATUS_DIR = 'context/agent-status';
|
|
17
|
+
|
|
18
|
+
// Known agents (for complete listing even if status file missing)
|
|
19
|
+
const KNOWN_AGENTS = [
|
|
20
|
+
'planning-hub',
|
|
21
|
+
'architect',
|
|
22
|
+
'furnace',
|
|
23
|
+
'anvil',
|
|
24
|
+
'crucible',
|
|
25
|
+
'temper',
|
|
26
|
+
'ember',
|
|
27
|
+
'scribe',
|
|
28
|
+
'aegis',
|
|
29
|
+
'pixel',
|
|
30
|
+
'oracle',
|
|
31
|
+
'loki'
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Status display order
|
|
35
|
+
const STATUS_ORDER = {
|
|
36
|
+
'working': 0,
|
|
37
|
+
'testing': 1,
|
|
38
|
+
'blocked': 2,
|
|
39
|
+
'attention': 3,
|
|
40
|
+
'idle': 4,
|
|
41
|
+
'unknown': 5,
|
|
42
|
+
'offline': 6
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Try to read agent status from SQLite database
|
|
47
|
+
* @param {string} projectRoot - Project root directory
|
|
48
|
+
* @returns {Array|null} Agent statuses or null if DB not available
|
|
49
|
+
*/
|
|
50
|
+
async function readFromDatabase(projectRoot) {
|
|
51
|
+
try {
|
|
52
|
+
// Check for better-sqlite3 (synchronous)
|
|
53
|
+
const Database = require('better-sqlite3');
|
|
54
|
+
const dbPath = path.join(projectRoot, '.forge', 'forge.db');
|
|
55
|
+
|
|
56
|
+
if (!fs.existsSync(dbPath)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const db = new Database(dbPath, { readonly: true });
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const rows = db.prepare(`
|
|
64
|
+
SELECT agent, status, task, message, updated_at
|
|
65
|
+
FROM agent_status
|
|
66
|
+
ORDER BY agent
|
|
67
|
+
`).all();
|
|
68
|
+
|
|
69
|
+
return rows.map(row => ({
|
|
70
|
+
agent: row.agent,
|
|
71
|
+
status: row.status || 'unknown',
|
|
72
|
+
task: row.task || null,
|
|
73
|
+
message: row.message || null,
|
|
74
|
+
updatedAt: row.updated_at || null,
|
|
75
|
+
source: 'database'
|
|
76
|
+
}));
|
|
77
|
+
} finally {
|
|
78
|
+
db.close();
|
|
79
|
+
}
|
|
80
|
+
} catch (err) {
|
|
81
|
+
// better-sqlite3 not available or DB error, fall back to files
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Read agent status from JSON file
|
|
88
|
+
* @param {string} filePath - Path to status file
|
|
89
|
+
* @returns {Object|null} Agent status or null
|
|
90
|
+
*/
|
|
91
|
+
function readStatusFile(filePath) {
|
|
92
|
+
try {
|
|
93
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
94
|
+
const data = JSON.parse(content);
|
|
95
|
+
const stats = fs.statSync(filePath);
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
agent: data.agent || path.basename(filePath, '.json'),
|
|
99
|
+
status: data.status || 'unknown',
|
|
100
|
+
task: data.task || null,
|
|
101
|
+
message: data.message || null,
|
|
102
|
+
updatedAt: data.updated || stats.mtime.toISOString(),
|
|
103
|
+
source: 'file'
|
|
104
|
+
};
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error(`[Agents] Error reading ${filePath}: ${err.message}`);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Read all agent statuses from files
|
|
113
|
+
* @param {string} projectRoot - Project root directory
|
|
114
|
+
* @returns {Array} Agent statuses
|
|
115
|
+
*/
|
|
116
|
+
function readFromFiles(projectRoot) {
|
|
117
|
+
const statusDir = path.join(projectRoot, AGENT_STATUS_DIR);
|
|
118
|
+
const agents = [];
|
|
119
|
+
|
|
120
|
+
if (!fs.existsSync(statusDir)) {
|
|
121
|
+
return agents;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const files = fs.readdirSync(statusDir).filter(f => f.endsWith('.json'));
|
|
125
|
+
|
|
126
|
+
for (const file of files) {
|
|
127
|
+
const filePath = path.join(statusDir, file);
|
|
128
|
+
const status = readStatusFile(filePath);
|
|
129
|
+
|
|
130
|
+
if (status) {
|
|
131
|
+
agents.push(status);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return agents;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get agent metadata (icon, role) from personality files
|
|
140
|
+
* @param {string} projectRoot - Project root directory
|
|
141
|
+
* @param {string} agentName - Agent name
|
|
142
|
+
* @returns {Object} Agent metadata
|
|
143
|
+
*/
|
|
144
|
+
function getAgentMetadata(projectRoot, agentName) {
|
|
145
|
+
const personalityPath = path.join(projectRoot, 'agents', agentName, 'personality.md');
|
|
146
|
+
|
|
147
|
+
const defaults = {
|
|
148
|
+
icon: getDefaultIcon(agentName),
|
|
149
|
+
role: 'Agent',
|
|
150
|
+
color: getDefaultColor(agentName)
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (!fs.existsSync(personalityPath)) {
|
|
154
|
+
return defaults;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const content = fs.readFileSync(personalityPath, 'utf8');
|
|
159
|
+
|
|
160
|
+
// Extract icon from first line or **Icon:** field
|
|
161
|
+
const iconMatch = content.match(/\*\*Icon:\*\*\s*(.+)/);
|
|
162
|
+
if (iconMatch) {
|
|
163
|
+
defaults.icon = iconMatch[1].trim();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Extract role from **Role:** field
|
|
167
|
+
const roleMatch = content.match(/\*\*Role:\*\*\s*(.+)/);
|
|
168
|
+
if (roleMatch) {
|
|
169
|
+
defaults.role = roleMatch[1].trim();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return defaults;
|
|
173
|
+
} catch (err) {
|
|
174
|
+
return defaults;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get default icon for agent
|
|
180
|
+
* @param {string} agentName - Agent name
|
|
181
|
+
* @returns {string} Icon emoji
|
|
182
|
+
*/
|
|
183
|
+
function getDefaultIcon(agentName) {
|
|
184
|
+
const icons = {
|
|
185
|
+
'planning-hub': 'โ๏ธ',
|
|
186
|
+
'architect': '๐๏ธ',
|
|
187
|
+
'furnace': '๐ฅ',
|
|
188
|
+
'anvil': '๐จ',
|
|
189
|
+
'crucible': '๐งช',
|
|
190
|
+
'temper': 'โ๏ธ',
|
|
191
|
+
'ember': 'โจ',
|
|
192
|
+
'scribe': '๐',
|
|
193
|
+
'aegis': '๐',
|
|
194
|
+
'pixel': '๐จ',
|
|
195
|
+
'oracle': '๐ฎ',
|
|
196
|
+
'loki': '๐ญ'
|
|
197
|
+
};
|
|
198
|
+
return icons[agentName] || '๐ค';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get default color for agent (for UI)
|
|
203
|
+
* @param {string} agentName - Agent name
|
|
204
|
+
* @returns {string} Color code
|
|
205
|
+
*/
|
|
206
|
+
function getDefaultColor(agentName) {
|
|
207
|
+
const colors = {
|
|
208
|
+
'planning-hub': '#6366f1',
|
|
209
|
+
'architect': '#8b5cf6',
|
|
210
|
+
'furnace': '#ef4444',
|
|
211
|
+
'anvil': '#f97316',
|
|
212
|
+
'crucible': '#22c55e',
|
|
213
|
+
'temper': '#8b5cf6',
|
|
214
|
+
'ember': '#eab308',
|
|
215
|
+
'scribe': '#06b6d4',
|
|
216
|
+
'aegis': '#14b8a6',
|
|
217
|
+
'pixel': '#ec4899',
|
|
218
|
+
'oracle': '#FBBF24',
|
|
219
|
+
'loki': '#7C3AED'
|
|
220
|
+
};
|
|
221
|
+
return colors[agentName] || '#6b7280';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* List all agents with their status
|
|
226
|
+
* @param {string} projectRoot - Project root directory
|
|
227
|
+
* @returns {Object} Agents listing with summary
|
|
228
|
+
*/
|
|
229
|
+
async function listAgents(projectRoot) {
|
|
230
|
+
// Try database first, then fall back to files
|
|
231
|
+
let statuses = await readFromDatabase(projectRoot);
|
|
232
|
+
|
|
233
|
+
if (!statuses) {
|
|
234
|
+
statuses = readFromFiles(projectRoot);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Create map for quick lookup
|
|
238
|
+
const statusMap = new Map();
|
|
239
|
+
for (const status of statuses) {
|
|
240
|
+
statusMap.set(status.agent, status);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Build complete agent list
|
|
244
|
+
const agents = [];
|
|
245
|
+
|
|
246
|
+
for (const agentName of KNOWN_AGENTS) {
|
|
247
|
+
const status = statusMap.get(agentName);
|
|
248
|
+
const metadata = getAgentMetadata(projectRoot, agentName);
|
|
249
|
+
|
|
250
|
+
agents.push({
|
|
251
|
+
agent: agentName,
|
|
252
|
+
displayName: agentName.split('-').map(w =>
|
|
253
|
+
w.charAt(0).toUpperCase() + w.slice(1)
|
|
254
|
+
).join(' '),
|
|
255
|
+
icon: metadata.icon,
|
|
256
|
+
role: metadata.role,
|
|
257
|
+
color: metadata.color,
|
|
258
|
+
status: status?.status || 'offline',
|
|
259
|
+
task: status?.task || null,
|
|
260
|
+
message: status?.message || null,
|
|
261
|
+
updatedAt: status?.updatedAt || null,
|
|
262
|
+
isOnline: status != null
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Add any unknown agents found in status files
|
|
267
|
+
for (const status of statuses) {
|
|
268
|
+
if (!KNOWN_AGENTS.includes(status.agent)) {
|
|
269
|
+
const metadata = getAgentMetadata(projectRoot, status.agent);
|
|
270
|
+
agents.push({
|
|
271
|
+
agent: status.agent,
|
|
272
|
+
displayName: status.agent.split('-').map(w =>
|
|
273
|
+
w.charAt(0).toUpperCase() + w.slice(1)
|
|
274
|
+
).join(' '),
|
|
275
|
+
icon: metadata.icon,
|
|
276
|
+
role: metadata.role,
|
|
277
|
+
color: metadata.color,
|
|
278
|
+
status: status.status,
|
|
279
|
+
task: status.task,
|
|
280
|
+
message: status.message,
|
|
281
|
+
updatedAt: status.updatedAt,
|
|
282
|
+
isOnline: true
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Sort by status priority, then by name
|
|
288
|
+
agents.sort((a, b) => {
|
|
289
|
+
const sa = STATUS_ORDER[a.status] ?? 5;
|
|
290
|
+
const sb = STATUS_ORDER[b.status] ?? 5;
|
|
291
|
+
if (sa !== sb) return sa - sb;
|
|
292
|
+
return a.agent.localeCompare(b.agent);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Build summary
|
|
296
|
+
const summary = {
|
|
297
|
+
total: agents.length,
|
|
298
|
+
online: agents.filter(a => a.isOnline).length,
|
|
299
|
+
working: agents.filter(a => a.status === 'working').length,
|
|
300
|
+
idle: agents.filter(a => a.status === 'idle').length,
|
|
301
|
+
blocked: agents.filter(a => a.status === 'blocked').length,
|
|
302
|
+
attention: agents.filter(a => a.status === 'attention').length
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
agents,
|
|
307
|
+
summary,
|
|
308
|
+
dataSource: statuses.length > 0 && statuses[0].source || 'none'
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get single agent details
|
|
314
|
+
* @param {string} projectRoot - Project root directory
|
|
315
|
+
* @param {string} agentName - Agent name
|
|
316
|
+
* @returns {Object|null} Agent details or null
|
|
317
|
+
*/
|
|
318
|
+
async function getAgent(projectRoot, agentName) {
|
|
319
|
+
// Sanitize agent name
|
|
320
|
+
const safeName = agentName.toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
321
|
+
|
|
322
|
+
const result = await listAgents(projectRoot);
|
|
323
|
+
return result.agents.find(a => a.agent === safeName) || null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
module.exports = {
|
|
327
|
+
listAgents,
|
|
328
|
+
getAgent,
|
|
329
|
+
readFromFiles,
|
|
330
|
+
readFromDatabase,
|
|
331
|
+
getAgentMetadata,
|
|
332
|
+
KNOWN_AGENTS
|
|
333
|
+
};
|