@leejungkiin/awkit 1.1.7 โ 1.2.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/README.md +36 -4
- package/bin/awk.js +2 -2
- package/package.json +6 -3
- package/skill-packs/neural-memory/skills/nm-memory-sync/SKILL.md +14 -1
- package/skills/gitnexus-intelligence/SKILL.md +224 -0
- package/skills/orchestrator/SKILL.md +9 -0
- package/skills/symphony-orchestrator/SKILL.md +9 -7
- package/workflows/gitnexus.md +123 -0
- package/symphony/LICENSE +0 -21
- package/symphony/README.md +0 -178
- package/symphony/app/api/agents/route.js +0 -152
- package/symphony/app/api/events/route.js +0 -22
- package/symphony/app/api/knowledge/route.js +0 -253
- package/symphony/app/api/locks/route.js +0 -29
- package/symphony/app/api/notes/route.js +0 -125
- package/symphony/app/api/preflight/route.js +0 -23
- package/symphony/app/api/projects/route.js +0 -116
- package/symphony/app/api/roles/route.js +0 -134
- package/symphony/app/api/skills/route.js +0 -82
- package/symphony/app/api/status/route.js +0 -18
- package/symphony/app/api/tasks/route.js +0 -157
- package/symphony/app/api/workflows/route.js +0 -61
- package/symphony/app/api/workspaces/route.js +0 -15
- package/symphony/app/globals.css +0 -2605
- package/symphony/app/layout.js +0 -20
- package/symphony/app/page.js +0 -2122
- package/symphony/cli/index.js +0 -1060
- package/symphony/core/agent-manager.js +0 -357
- package/symphony/core/context-bus.js +0 -100
- package/symphony/core/db.js +0 -223
- package/symphony/core/file-lock-manager.js +0 -154
- package/symphony/core/merge-pipeline.js +0 -234
- package/symphony/core/orchestrator.js +0 -236
- package/symphony/core/task-manager.js +0 -335
- package/symphony/core/workspace-manager.js +0 -168
- package/symphony/jsconfig.json +0 -7
- package/symphony/lib/core.mjs +0 -1034
- package/symphony/mcp/index.js +0 -29
- package/symphony/mcp/server.js +0 -110
- package/symphony/mcp/tools/context.js +0 -80
- package/symphony/mcp/tools/locks.js +0 -99
- package/symphony/mcp/tools/status.js +0 -82
- package/symphony/mcp/tools/tasks.js +0 -216
- package/symphony/mcp/tools/workspace.js +0 -143
- package/symphony/next.config.mjs +0 -7
- package/symphony/package.json +0 -53
- package/symphony/scripts/postinstall.js +0 -49
- package/symphony/symphony.config.js +0 -41
package/symphony/cli/index.js
DELETED
|
@@ -1,1060 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Symphony CLI
|
|
5
|
-
* Command-line interface for AWKit Symphony orchestration platform.
|
|
6
|
-
*/
|
|
7
|
-
const { Command } = require('commander');
|
|
8
|
-
const path = require('path');
|
|
9
|
-
|
|
10
|
-
// Ensure core modules resolve relative to this file
|
|
11
|
-
const SYMPHONY_ROOT = path.join(__dirname, '..');
|
|
12
|
-
process.env.SYMPHONY_ROOT = SYMPHONY_ROOT;
|
|
13
|
-
|
|
14
|
-
const program = new Command();
|
|
15
|
-
|
|
16
|
-
program
|
|
17
|
-
.name('symphony')
|
|
18
|
-
.description('๐ผ AWKit Symphony โ Multi-Agent Orchestration')
|
|
19
|
-
.version('0.1.0');
|
|
20
|
-
|
|
21
|
-
// โโโ Task Commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
22
|
-
|
|
23
|
-
const taskCmd = program.command('task').description('Task management');
|
|
24
|
-
|
|
25
|
-
taskCmd
|
|
26
|
-
.command('list')
|
|
27
|
-
.description('List tasks')
|
|
28
|
-
.option('-s, --status <status>', 'Filter by status (ready|claimed|in_progress|review|done)')
|
|
29
|
-
.option('-P, --project <id>', 'Filter by project ID')
|
|
30
|
-
.option('-l, --limit <n>', 'Limit results', '20')
|
|
31
|
-
.action((opts) => {
|
|
32
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
33
|
-
const tasks = tm.listTasks({
|
|
34
|
-
status: opts.status || undefined,
|
|
35
|
-
project: opts.project || undefined,
|
|
36
|
-
limit: parseInt(opts.limit),
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
if (tasks.length === 0) {
|
|
40
|
-
console.log('No tasks found.');
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const statusIcon = {
|
|
45
|
-
ready: 'โฌ', claimed: '๐ก', in_progress: '๐ต',
|
|
46
|
-
review: '๐ฃ', done: 'โ
', abandoned: 'โซ',
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
console.log(`\n${'ID'.padEnd(14)} ${'Status'.padEnd(14)} ${'Pri'.padEnd(6)} ${'Project'.padEnd(12)} Title`);
|
|
50
|
-
console.log('โ'.repeat(80));
|
|
51
|
-
for (const t of tasks) {
|
|
52
|
-
const icon = statusIcon[t.status] || 'โ';
|
|
53
|
-
const proj = (t.project_id || '-').substring(0, 10).padEnd(12);
|
|
54
|
-
const pri = (typeof t.priority === 'number' ? `P${t.priority}` : (t.priority || '-')).padEnd(6);
|
|
55
|
-
console.log(
|
|
56
|
-
`${t.id.padEnd(14)} ${(icon + ' ' + t.status).padEnd(14)} ${pri} ${proj} ${t.title}`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
console.log(`\nTotal: ${tasks.length} task(s)\n`);
|
|
60
|
-
closeDb();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
taskCmd
|
|
64
|
-
.command('create <title>')
|
|
65
|
-
.description('Create a new task')
|
|
66
|
-
.option('-p, --priority <n>', 'Priority (1=high, 3=low)', '2')
|
|
67
|
-
.option('-d, --description <text>', 'Task description')
|
|
68
|
-
.option('-a, --acceptance <text>', 'Acceptance criteria')
|
|
69
|
-
.option('-P, --project <id>', 'Project ID')
|
|
70
|
-
.option('--phase <phase>', 'Phase/group')
|
|
71
|
-
.option('--skills <list>', 'Required skills (comma-separated, e.g. "code,debug")')
|
|
72
|
-
.action((title, opts) => {
|
|
73
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
74
|
-
const taskOpts = {
|
|
75
|
-
priority: parseInt(opts.priority),
|
|
76
|
-
description: opts.description,
|
|
77
|
-
acceptance: opts.acceptance,
|
|
78
|
-
phase: opts.phase,
|
|
79
|
-
projectId: opts.project,
|
|
80
|
-
};
|
|
81
|
-
if (opts.skills) {
|
|
82
|
-
taskOpts.requiredSkills = opts.skills.split(',').map(s => s.trim());
|
|
83
|
-
}
|
|
84
|
-
const task = tm.createTask(title, taskOpts);
|
|
85
|
-
console.log(`\nโ
Task created: ${task.id}`);
|
|
86
|
-
console.log(` Title: ${task.title}`);
|
|
87
|
-
console.log(` Priority: P${task.priority}`);
|
|
88
|
-
console.log(` Project: ${task.project_id || '(none)'}`);
|
|
89
|
-
if (opts.skills) console.log(` Skills: ${opts.skills}`);
|
|
90
|
-
console.log(` Status: ${task.status}\n`);
|
|
91
|
-
closeDb();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
taskCmd
|
|
95
|
-
.command('show <id>')
|
|
96
|
-
.description('Show task details')
|
|
97
|
-
.action((id) => {
|
|
98
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
99
|
-
const task = tm.getTask(id);
|
|
100
|
-
if (!task) {
|
|
101
|
-
console.log(`โ Task not found: ${id}`);
|
|
102
|
-
closeDb();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
console.log(`\n๐ซ Task: ${task.id}`);
|
|
106
|
-
console.log(` Title: ${task.title}`);
|
|
107
|
-
console.log(` Status: ${task.status}`);
|
|
108
|
-
console.log(` Priority: P${task.priority}`);
|
|
109
|
-
console.log(` Agent: ${task.agent_id || '(unassigned)'}`);
|
|
110
|
-
console.log(` Progress: ${task.progress}%`);
|
|
111
|
-
if (task.description) console.log(` Description: ${task.description}`);
|
|
112
|
-
if (task.acceptance) console.log(` Acceptance: ${task.acceptance}`);
|
|
113
|
-
if (task.phase) console.log(` Phase: ${task.phase}`);
|
|
114
|
-
if (task.branch) console.log(` Branch: ${task.branch}`);
|
|
115
|
-
console.log(` Created: ${task.created_at}`);
|
|
116
|
-
if (task.claimed_at) console.log(` Claimed: ${task.claimed_at}`);
|
|
117
|
-
if (task.completed_at) console.log(` Completed: ${task.completed_at}`);
|
|
118
|
-
if (task.summary) console.log(` Summary: ${task.summary}`);
|
|
119
|
-
console.log('');
|
|
120
|
-
closeDb();
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
taskCmd
|
|
124
|
-
.command('claim <id>')
|
|
125
|
-
.description('Claim a task (ready โ claimed)')
|
|
126
|
-
.option('-a, --agent <agent>', 'Agent ID', 'cli-user')
|
|
127
|
-
.action((id, opts) => {
|
|
128
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
129
|
-
try {
|
|
130
|
-
const task = tm.claimTask(id, opts.agent);
|
|
131
|
-
console.log(`\n๐ก Task claimed: ${task.id}`);
|
|
132
|
-
console.log(` Title: ${task.title}`);
|
|
133
|
-
console.log(` Agent: ${task.agent_id}`);
|
|
134
|
-
console.log(` Status: ${task.status}\n`);
|
|
135
|
-
} catch (e) {
|
|
136
|
-
console.error(`\nโ ${e.message}\n`);
|
|
137
|
-
}
|
|
138
|
-
closeDb();
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
taskCmd
|
|
142
|
-
.command('start <id>')
|
|
143
|
-
.description('Start working on a task (โ in_progress)')
|
|
144
|
-
.action((id) => {
|
|
145
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
146
|
-
try {
|
|
147
|
-
// Claim first if still ready
|
|
148
|
-
const task = tm.getTask(id);
|
|
149
|
-
if (!task) { console.error(`\nโ Task not found: ${id}\n`); closeDb(); return; }
|
|
150
|
-
if (task.status === 'ready') {
|
|
151
|
-
tm.claimTask(id, 'cli-user');
|
|
152
|
-
}
|
|
153
|
-
const updated = tm.updateProgress(id, 1);
|
|
154
|
-
console.log(`\n๐ต Task started: ${updated.id}`);
|
|
155
|
-
console.log(` Title: ${updated.title}`);
|
|
156
|
-
console.log(` Status: ${updated.status}\n`);
|
|
157
|
-
} catch (e) {
|
|
158
|
-
console.error(`\nโ ${e.message}\n`);
|
|
159
|
-
}
|
|
160
|
-
closeDb();
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
taskCmd
|
|
164
|
-
.command('done <id>')
|
|
165
|
-
.description('Complete a task (โ done)')
|
|
166
|
-
.option('-m, --message <text>', 'Completion summary', '')
|
|
167
|
-
.action((id, opts) => {
|
|
168
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
169
|
-
try {
|
|
170
|
-
const task = tm.getTask(id);
|
|
171
|
-
if (!task) { console.error(`\nโ Task not found: ${id}\n`); closeDb(); return; }
|
|
172
|
-
// Auto-claim if still ready
|
|
173
|
-
if (task.status === 'ready') {
|
|
174
|
-
tm.claimTask(id, 'cli-user');
|
|
175
|
-
}
|
|
176
|
-
const updated = tm.completeTask(id, opts.message || 'Completed via CLI');
|
|
177
|
-
console.log(`\nโ
Task completed: ${updated.id}`);
|
|
178
|
-
console.log(` Title: ${updated.title}`);
|
|
179
|
-
console.log(` Status: ${updated.status}\n`);
|
|
180
|
-
} catch (e) {
|
|
181
|
-
console.error(`\nโ ${e.message}\n`);
|
|
182
|
-
}
|
|
183
|
-
closeDb();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
taskCmd
|
|
187
|
-
.command('approve <id>')
|
|
188
|
-
.description('Approve a draft task (draft โ ready)')
|
|
189
|
-
.action((id) => {
|
|
190
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
191
|
-
try {
|
|
192
|
-
const task = tm.approveTask(id);
|
|
193
|
-
console.log(`\nโฌ Task approved: ${task.id}`);
|
|
194
|
-
console.log(` Title: ${task.title}`);
|
|
195
|
-
console.log(` Status: ${task.status}\n`);
|
|
196
|
-
} catch (e) {
|
|
197
|
-
console.error(`\nโ ${e.message}\n`);
|
|
198
|
-
}
|
|
199
|
-
closeDb();
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
taskCmd
|
|
203
|
-
.command('reopen <id>')
|
|
204
|
-
.description('Reopen a completed task (done โ ready)')
|
|
205
|
-
.action((id) => {
|
|
206
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
207
|
-
try {
|
|
208
|
-
const task = tm.reopenTask(id);
|
|
209
|
-
console.log(`\n๐ Task reopened: ${task.id}`);
|
|
210
|
-
console.log(` Title: ${task.title}`);
|
|
211
|
-
console.log(` Status: ${task.status}\n`);
|
|
212
|
-
} catch (e) {
|
|
213
|
-
console.error(`\nโ ${e.message}\n`);
|
|
214
|
-
}
|
|
215
|
-
closeDb();
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
taskCmd
|
|
219
|
-
.command('abandon <id>')
|
|
220
|
-
.description('Abandon a task (back to ready)')
|
|
221
|
-
.option('-r, --reason <text>', 'Reason for abandoning', 'No reason given')
|
|
222
|
-
.action((id, opts) => {
|
|
223
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
224
|
-
try {
|
|
225
|
-
const task = tm.abandonTask(id, opts.reason);
|
|
226
|
-
console.log(`\nโซ Task abandoned: ${task.id}`);
|
|
227
|
-
console.log(` Title: ${task.title}`);
|
|
228
|
-
console.log(` Status: ${task.status}\n`);
|
|
229
|
-
} catch (e) {
|
|
230
|
-
console.error(`\nโ ${e.message}\n`);
|
|
231
|
-
}
|
|
232
|
-
closeDb();
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
taskCmd
|
|
236
|
-
.command('delete <id>')
|
|
237
|
-
.description('Delete a task (draft/ready only)')
|
|
238
|
-
.action((id) => {
|
|
239
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
240
|
-
try {
|
|
241
|
-
tm.deleteTask(id);
|
|
242
|
-
console.log(`\n๐๏ธ Task deleted: ${id}\n`);
|
|
243
|
-
} catch (e) {
|
|
244
|
-
console.error(`\nโ ${e.message}\n`);
|
|
245
|
-
}
|
|
246
|
-
closeDb();
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
taskCmd
|
|
250
|
-
.command('update <id>')
|
|
251
|
-
.description('Update task fields')
|
|
252
|
-
.option('-t, --title <text>', 'New title')
|
|
253
|
-
.option('-d, --description <text>', 'New description')
|
|
254
|
-
.option('-p, --priority <n>', 'New priority (1=high, 3=low)')
|
|
255
|
-
.option('--phase <phase>', 'New phase')
|
|
256
|
-
.option('-a, --acceptance <text>', 'Acceptance criteria')
|
|
257
|
-
.action((id, opts) => {
|
|
258
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
259
|
-
try {
|
|
260
|
-
const fields = {};
|
|
261
|
-
if (opts.title) fields.title = opts.title;
|
|
262
|
-
if (opts.description) fields.description = opts.description;
|
|
263
|
-
if (opts.priority) fields.priority = parseInt(opts.priority);
|
|
264
|
-
if (opts.phase) fields.phase = opts.phase;
|
|
265
|
-
if (opts.acceptance) fields.acceptance = opts.acceptance;
|
|
266
|
-
|
|
267
|
-
if (Object.keys(fields).length === 0) {
|
|
268
|
-
console.log('\nโ ๏ธ No fields specified. Use --title, --description, --priority, --phase, or --acceptance\n');
|
|
269
|
-
closeDb();
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const task = tm.updateTask(id, fields);
|
|
274
|
-
console.log(`\n๐ Task updated: ${task.id}`);
|
|
275
|
-
console.log(` Title: ${task.title}`);
|
|
276
|
-
console.log(` Priority: P${task.priority}`);
|
|
277
|
-
console.log(` Status: ${task.status}\n`);
|
|
278
|
-
} catch (e) {
|
|
279
|
-
console.error(`\nโ ${e.message}\n`);
|
|
280
|
-
}
|
|
281
|
-
closeDb();
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// โโโ Agent Commands (Project-Scoped) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
285
|
-
|
|
286
|
-
const agentCmd = program.command('agent').description('Agent management (project-scoped)');
|
|
287
|
-
|
|
288
|
-
agentCmd
|
|
289
|
-
.command('create <id>')
|
|
290
|
-
.description('Create a project-scoped agent with skills')
|
|
291
|
-
.requiredOption('-P, --project <project>', 'Project ID (required)')
|
|
292
|
-
.option('-n, --name <name>', 'Agent display name')
|
|
293
|
-
.option('-s, --skills <list>', 'Comma-separated skills (e.g. "code,debug,refactor")')
|
|
294
|
-
.option('--icon <icon>', 'Agent icon', '๐ค')
|
|
295
|
-
.option('-c, --color <hex>', 'Agent color (hex)', '#8888a0')
|
|
296
|
-
.action((id, opts) => {
|
|
297
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
298
|
-
try {
|
|
299
|
-
const skills = opts.skills ? opts.skills.split(',').map(s => s.trim()) : [];
|
|
300
|
-
const agent = am.createAgent(id, opts.project, opts.name || id, skills, {
|
|
301
|
-
icon: opts.icon,
|
|
302
|
-
color: opts.color,
|
|
303
|
-
});
|
|
304
|
-
console.log(`\nโ
Agent created: ${agent.icon} ${agent.name} (${agent.id})`);
|
|
305
|
-
console.log(` Project: ${agent.projectId}`);
|
|
306
|
-
console.log(` Skills: ${agent.skills.length > 0 ? agent.skills.join(', ') : '(none)'}`);
|
|
307
|
-
console.log(` Status: ${agent.status}\n`);
|
|
308
|
-
} catch (e) {
|
|
309
|
-
console.error(`\nโ ${e.message}\n`);
|
|
310
|
-
}
|
|
311
|
-
closeDb();
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
agentCmd
|
|
315
|
-
.command('list')
|
|
316
|
-
.description('List agents (optionally filter by project)')
|
|
317
|
-
.option('-P, --project <project>', 'Filter by project ID')
|
|
318
|
-
.option('-s, --status <status>', 'Filter by status (offline|idle|working)')
|
|
319
|
-
.action((opts) => {
|
|
320
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
321
|
-
const agents = am.listAgents({
|
|
322
|
-
project: opts.project || undefined,
|
|
323
|
-
status: opts.status || undefined,
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
if (agents.length === 0) {
|
|
327
|
-
console.log('\n๐ค No agents found\n');
|
|
328
|
-
closeDb();
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const statusIcon = { offline: 'โซ', idle: '๐ค', working: '๐ข' };
|
|
333
|
-
console.log(`\n๐ค Agents (${agents.length}):\n`);
|
|
334
|
-
|
|
335
|
-
let currentProject = '';
|
|
336
|
-
for (const a of agents) {
|
|
337
|
-
if (a.projectId !== currentProject) {
|
|
338
|
-
currentProject = a.projectId;
|
|
339
|
-
console.log(` ๐ ${currentProject}`);
|
|
340
|
-
}
|
|
341
|
-
const icon = statusIcon[a.status] || 'โ';
|
|
342
|
-
const task = a.currentTask ? ` โ ${a.currentTask}` : '';
|
|
343
|
-
const skills = a.skills.length > 0 ? ` [${a.skills.join(', ')}]` : '';
|
|
344
|
-
const session = a.sessionId ? ` ๐` : '';
|
|
345
|
-
console.log(` ${icon} ${a.icon} ${a.name} (${a.id}) โ ${a.status}${task}${skills}${session}`);
|
|
346
|
-
}
|
|
347
|
-
console.log('');
|
|
348
|
-
closeDb();
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
agentCmd
|
|
352
|
-
.command('show <id>')
|
|
353
|
-
.description('Show agent details')
|
|
354
|
-
.action((id) => {
|
|
355
|
-
// Try project_agents first, fallback to legacy agents
|
|
356
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
357
|
-
const agent = am.getAgent(id);
|
|
358
|
-
if (!agent) {
|
|
359
|
-
// Fallback to legacy orchestrator
|
|
360
|
-
const orchestrator = require(path.join(SYMPHONY_ROOT, 'core', 'orchestrator'));
|
|
361
|
-
const legacy = orchestrator.getAgent(id);
|
|
362
|
-
if (!legacy) {
|
|
363
|
-
console.error(`\nโ Agent not found: ${id}\n`);
|
|
364
|
-
closeDb();
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
const specs = legacy.specialties ? JSON.parse(legacy.specialties || '[]') : [];
|
|
368
|
-
console.log(`\n๐ค Agent (legacy): ${legacy.name} (${legacy.id})`);
|
|
369
|
-
console.log(` Status: ${legacy.status}`);
|
|
370
|
-
console.log(` Specialties: ${specs.length > 0 ? specs.join(', ') : '(none)'}`);
|
|
371
|
-
console.log(` Current Task: ${legacy.current_task_id || '(none)'}`);
|
|
372
|
-
console.log('');
|
|
373
|
-
closeDb();
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
console.log(`\n${agent.icon} Agent: ${agent.name} (${agent.id})`);
|
|
377
|
-
console.log(` Project: ${agent.projectId}`);
|
|
378
|
-
console.log(` Status: ${agent.status}`);
|
|
379
|
-
console.log(` Skills: ${agent.skills.length > 0 ? agent.skills.join(', ') : '(none)'}`);
|
|
380
|
-
console.log(` Current Task: ${agent.currentTask || '(none)'}`);
|
|
381
|
-
console.log(` Session: ${agent.sessionId || '(detached)'}`);
|
|
382
|
-
console.log(` Last Active: ${agent.lastActiveAt || '(never)'}`);
|
|
383
|
-
console.log(` Idle Since: ${agent.idleSince || '-'}`);
|
|
384
|
-
console.log(` Icon: ${agent.icon}`);
|
|
385
|
-
console.log(` Color: ${agent.color}`);
|
|
386
|
-
console.log(` Created: ${agent.createdAt}`);
|
|
387
|
-
console.log('');
|
|
388
|
-
closeDb();
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
agentCmd
|
|
392
|
-
.command('update <id>')
|
|
393
|
-
.description('Update agent config')
|
|
394
|
-
.option('-n, --name <name>', 'New display name')
|
|
395
|
-
.option('-s, --skills <list>', 'Comma-separated skills')
|
|
396
|
-
.option('--icon <icon>', 'Agent icon')
|
|
397
|
-
.option('-c, --color <hex>', 'Agent color (hex)')
|
|
398
|
-
.action((id, opts) => {
|
|
399
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
400
|
-
try {
|
|
401
|
-
const fields = {};
|
|
402
|
-
if (opts.name) fields.name = opts.name;
|
|
403
|
-
if (opts.skills) fields.skills = opts.skills.split(',').map(s => s.trim());
|
|
404
|
-
if (opts.icon) fields.icon = opts.icon;
|
|
405
|
-
if (opts.color) fields.color = opts.color;
|
|
406
|
-
|
|
407
|
-
if (Object.keys(fields).length === 0) {
|
|
408
|
-
console.log('\nโ ๏ธ No fields specified. Use --name, --skills, --icon, or --color\n');
|
|
409
|
-
closeDb();
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
const agent = am.updateAgent(id, fields);
|
|
414
|
-
console.log(`\n๐ Agent updated: ${agent.icon} ${agent.name} (${agent.id})`);
|
|
415
|
-
console.log(` Skills: ${agent.skills.join(', ')}`);
|
|
416
|
-
console.log(` Status: ${agent.status}\n`);
|
|
417
|
-
} catch (e) {
|
|
418
|
-
console.error(`\nโ ${e.message}\n`);
|
|
419
|
-
}
|
|
420
|
-
closeDb();
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
agentCmd
|
|
424
|
-
.command('remove <id>')
|
|
425
|
-
.description('Remove an agent config (must be offline)')
|
|
426
|
-
.action((id) => {
|
|
427
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
428
|
-
try {
|
|
429
|
-
am.removeAgent(id);
|
|
430
|
-
console.log(`\n๐๏ธ Agent removed: ${id}\n`);
|
|
431
|
-
} catch (e) {
|
|
432
|
-
console.error(`\nโ ${e.message}\n`);
|
|
433
|
-
}
|
|
434
|
-
closeDb();
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
agentCmd
|
|
438
|
-
.command('attach <id>')
|
|
439
|
-
.description('Attach this window (session) to an agent')
|
|
440
|
-
.option('--session <session>', 'Session/conversation ID', `cli-${Date.now()}`)
|
|
441
|
-
.action((id, opts) => {
|
|
442
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
443
|
-
try {
|
|
444
|
-
const agent = am.attachSession(id, opts.session);
|
|
445
|
-
console.log(`\n๐ Attached to agent: ${agent.icon} ${agent.name}`);
|
|
446
|
-
console.log(` Project: ${agent.projectId}`);
|
|
447
|
-
console.log(` Skills: ${agent.skills.join(', ')}`);
|
|
448
|
-
console.log(` Status: ${agent.status}`);
|
|
449
|
-
console.log(` Session: ${agent.sessionId}\n`);
|
|
450
|
-
} catch (e) {
|
|
451
|
-
console.error(`\nโ ${e.message}\n`);
|
|
452
|
-
}
|
|
453
|
-
closeDb();
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
agentCmd
|
|
457
|
-
.command('detach <id>')
|
|
458
|
-
.description('Detach session from an agent (โ offline)')
|
|
459
|
-
.action((id) => {
|
|
460
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
461
|
-
try {
|
|
462
|
-
const agent = am.detachSession(id);
|
|
463
|
-
console.log(`\n๐ Detached from agent: ${agent.icon} ${agent.name}`);
|
|
464
|
-
console.log(` Status: ${agent.status}\n`);
|
|
465
|
-
} catch (e) {
|
|
466
|
-
console.error(`\nโ ${e.message}\n`);
|
|
467
|
-
}
|
|
468
|
-
closeDb();
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
agentCmd
|
|
472
|
-
.command('assign <agent-id> <task-id>')
|
|
473
|
-
.description('Assign a task to a project agent')
|
|
474
|
-
.action((agentId, taskId) => {
|
|
475
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
476
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
477
|
-
try {
|
|
478
|
-
const agent = am.getAgent(agentId);
|
|
479
|
-
if (!agent) throw new Error(`Agent not found: ${agentId}`);
|
|
480
|
-
if (agent.status === 'offline') throw new Error(`Agent ${agentId} is offline. Attach first.`);
|
|
481
|
-
|
|
482
|
-
const task = tm.claimTask(taskId, agentId);
|
|
483
|
-
am.startWork(agentId, taskId);
|
|
484
|
-
|
|
485
|
-
console.log(`\n๐ฏ Task dispatched:`);
|
|
486
|
-
console.log(` Agent: ${agent.icon} ${agent.name} (${agentId})`);
|
|
487
|
-
console.log(` Task: ${task.id} โ ${task.title}`);
|
|
488
|
-
console.log(` Status: ${task.status}\n`);
|
|
489
|
-
} catch (e) {
|
|
490
|
-
console.error(`\nโ ${e.message}\n`);
|
|
491
|
-
}
|
|
492
|
-
closeDb();
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
agentCmd
|
|
496
|
-
.command('idle <id>')
|
|
497
|
-
.description('Mark an agent as idle')
|
|
498
|
-
.action((id) => {
|
|
499
|
-
const am = require(path.join(SYMPHONY_ROOT, 'core', 'agent-manager'));
|
|
500
|
-
try {
|
|
501
|
-
am.markIdle(id);
|
|
502
|
-
console.log(`\n๐ค Agent marked idle: ${id}\n`);
|
|
503
|
-
} catch (e) {
|
|
504
|
-
// Fallback to legacy orchestrator
|
|
505
|
-
try {
|
|
506
|
-
const orchestrator = require(path.join(SYMPHONY_ROOT, 'core', 'orchestrator'));
|
|
507
|
-
orchestrator.markIdle(id);
|
|
508
|
-
console.log(`\n๐ค Agent marked idle (legacy): ${id}\n`);
|
|
509
|
-
} catch (e2) {
|
|
510
|
-
console.error(`\nโ ${e2.message}\n`);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
closeDb();
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
// Legacy: keep 'register' as alias for backward compat
|
|
517
|
-
agentCmd
|
|
518
|
-
.command('register <id>')
|
|
519
|
-
.description('Register a legacy agent (use "create" for project-scoped)')
|
|
520
|
-
.option('-n, --name <name>', 'Agent display name')
|
|
521
|
-
.option('-s, --specialties <list>', 'Comma-separated specialties')
|
|
522
|
-
.option('-c, --color <hex>', 'Agent color (hex)', '#8888a0')
|
|
523
|
-
.action((id, opts) => {
|
|
524
|
-
const orchestrator = require(path.join(SYMPHONY_ROOT, 'core', 'orchestrator'));
|
|
525
|
-
try {
|
|
526
|
-
orchestrator.registerAgent(id, opts.name || id);
|
|
527
|
-
if (opts.specialties || opts.color !== '#8888a0') {
|
|
528
|
-
orchestrator.updateAgentProfile(id, {
|
|
529
|
-
specialties: opts.specialties ? opts.specialties.split(',').map(s => s.trim()) : undefined,
|
|
530
|
-
color: opts.color,
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
const agent = orchestrator.getAgent(id);
|
|
534
|
-
console.log(`\n๐ค Agent registered (legacy): ${agent.name} (${agent.id})`);
|
|
535
|
-
console.log(` ๐ก Use 'symphony agent create' for project-scoped agents\n`);
|
|
536
|
-
} catch (e) {
|
|
537
|
-
console.error(`\nโ ${e.message}\n`);
|
|
538
|
-
}
|
|
539
|
-
closeDb();
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
// โโโ Dispatch Command (shortcut) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
543
|
-
|
|
544
|
-
program
|
|
545
|
-
.command('dispatch <task-id>')
|
|
546
|
-
.description('๐ฏ Dispatch a task to the best available agent')
|
|
547
|
-
.option('-a, --agent <id>', 'Specific agent to dispatch to')
|
|
548
|
-
.action((taskId, opts) => {
|
|
549
|
-
const orchestrator = require(path.join(SYMPHONY_ROOT, 'core', 'orchestrator'));
|
|
550
|
-
try {
|
|
551
|
-
let agentId = opts.agent;
|
|
552
|
-
|
|
553
|
-
if (!agentId) {
|
|
554
|
-
// Auto-select: find idle agent or the one with fewest tasks
|
|
555
|
-
const agents = orchestrator.getAgents();
|
|
556
|
-
const idle = agents.find(a => a.status === 'idle');
|
|
557
|
-
if (idle) {
|
|
558
|
-
agentId = idle.id;
|
|
559
|
-
} else if (agents.length > 0) {
|
|
560
|
-
// Pick agent with least work
|
|
561
|
-
agentId = agents[0].id;
|
|
562
|
-
} else {
|
|
563
|
-
// No agents โ auto-register a default one
|
|
564
|
-
agentId = 'auto-agent';
|
|
565
|
-
orchestrator.registerAgent(agentId, 'Auto Agent');
|
|
566
|
-
console.log(' ๐ Auto-registered agent: auto-agent');
|
|
567
|
-
}
|
|
568
|
-
} else {
|
|
569
|
-
// Ensure agent exists
|
|
570
|
-
if (!orchestrator.getAgent(agentId)) {
|
|
571
|
-
orchestrator.registerAgent(agentId, agentId);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
const task = orchestrator.dispatchTask(agentId, taskId);
|
|
576
|
-
console.log(`\n๐ฏ Dispatched:`);
|
|
577
|
-
console.log(` Task: ${task.id} โ ${task.title}`);
|
|
578
|
-
console.log(` Agent: ${agentId}`);
|
|
579
|
-
console.log(` Status: ${task.status}\n`);
|
|
580
|
-
} catch (e) {
|
|
581
|
-
console.error(`\nโ ${e.message}\n`);
|
|
582
|
-
}
|
|
583
|
-
closeDb();
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
// โโโ Next Command (AI helper) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
587
|
-
|
|
588
|
-
program
|
|
589
|
-
.command('next')
|
|
590
|
-
.description('๐ Suggest the next task to work on')
|
|
591
|
-
.option('-n, --count <n>', 'Number of suggestions', '3')
|
|
592
|
-
.action((opts) => {
|
|
593
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
594
|
-
const ready = tm.listTasks({ status: 'ready', limit: parseInt(opts.count) });
|
|
595
|
-
|
|
596
|
-
if (ready.length === 0) {
|
|
597
|
-
console.log('\nโ
No tasks in queue โ all caught up!\n');
|
|
598
|
-
closeDb();
|
|
599
|
-
return;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
console.log(`\n๐ Next task suggestions (${ready.length}):\n`);
|
|
603
|
-
for (let i = 0; i < ready.length; i++) {
|
|
604
|
-
const t = ready[i];
|
|
605
|
-
console.log(` ${i + 1}. [P${t.priority}] ${t.title}`);
|
|
606
|
-
console.log(` ID: ${t.id}`);
|
|
607
|
-
if (t.description) console.log(` ${t.description.substring(0, 80)}`);
|
|
608
|
-
console.log('');
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
console.log(` ๐ก Start working: symphony task start ${ready[0].id}`);
|
|
612
|
-
console.log(` ๐ฏ Dispatch: symphony dispatch ${ready[0].id}\n`);
|
|
613
|
-
closeDb();
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
// โโโ Status Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
617
|
-
|
|
618
|
-
program
|
|
619
|
-
.command('status')
|
|
620
|
-
.description('Show system status')
|
|
621
|
-
.action(() => {
|
|
622
|
-
const orchestrator = require(path.join(SYMPHONY_ROOT, 'core', 'orchestrator'));
|
|
623
|
-
const status = orchestrator.getStatus();
|
|
624
|
-
|
|
625
|
-
console.log('\n๐ผ AWKit Symphony Status\n');
|
|
626
|
-
|
|
627
|
-
// Agents
|
|
628
|
-
console.log(`๐ค Agents (${status.agents.length}/${status.config.maxAgents}):`);
|
|
629
|
-
if (status.agents.length === 0) {
|
|
630
|
-
console.log(' No agents connected\n');
|
|
631
|
-
} else {
|
|
632
|
-
for (const a of status.agents) {
|
|
633
|
-
const taskInfo = a.currentTask ? ` โ ${a.currentTask}` : '';
|
|
634
|
-
console.log(` ${a.status === 'working' ? '๐ข' : '๐ค'} ${a.name} [${a.status}]${taskInfo}`);
|
|
635
|
-
}
|
|
636
|
-
console.log('');
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// Task stats
|
|
640
|
-
console.log('๐ Tasks:');
|
|
641
|
-
console.log(` โฌ Ready: ${status.stats.ready}`);
|
|
642
|
-
console.log(` ๐ต In Progress: ${status.stats.in_progress + status.stats.claimed}`);
|
|
643
|
-
console.log(` ๐ฃ Review: ${status.stats.review}`);
|
|
644
|
-
console.log(` โ
Done: ${status.stats.done}`);
|
|
645
|
-
console.log(` ๐ Total: ${status.stats.total}\n`);
|
|
646
|
-
|
|
647
|
-
// File locks
|
|
648
|
-
console.log(`๐ File Locks (${status.lockedFiles.length}):`);
|
|
649
|
-
if (status.lockedFiles.length === 0) {
|
|
650
|
-
console.log(' No files locked\n');
|
|
651
|
-
} else {
|
|
652
|
-
for (const l of status.lockedFiles) {
|
|
653
|
-
console.log(` ๐ ${l.file} โ ${l.agent} (${l.since})`);
|
|
654
|
-
}
|
|
655
|
-
console.log('');
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
closeDb();
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
// โโโ Preflight Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
662
|
-
|
|
663
|
-
program
|
|
664
|
-
.command('preflight')
|
|
665
|
-
.description('๐ฆ Gate check โ mandatory first action every session')
|
|
666
|
-
.option('-p, --project <id>', 'Project ID to scope preflight')
|
|
667
|
-
.option('--json', 'Output as JSON')
|
|
668
|
-
.action(async (opts) => {
|
|
669
|
-
const http = require('http');
|
|
670
|
-
|
|
671
|
-
// Try API first (server already running)
|
|
672
|
-
const url = `http://localhost:3100/api/preflight${opts.project ? `?project=${opts.project}` : ''}`;
|
|
673
|
-
|
|
674
|
-
try {
|
|
675
|
-
const data = await new Promise((resolve, reject) => {
|
|
676
|
-
const req = http.get(url, { timeout: 2000 }, (res) => {
|
|
677
|
-
let body = '';
|
|
678
|
-
res.on('data', (chunk) => body += chunk);
|
|
679
|
-
res.on('end', () => {
|
|
680
|
-
if (res.statusCode === 200) resolve(JSON.parse(body));
|
|
681
|
-
else reject(new Error(`HTTP ${res.statusCode}`));
|
|
682
|
-
});
|
|
683
|
-
});
|
|
684
|
-
req.on('error', reject);
|
|
685
|
-
req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
if (opts.json) {
|
|
689
|
-
console.log(JSON.stringify(data, null, 2));
|
|
690
|
-
} else {
|
|
691
|
-
printPreflight(data);
|
|
692
|
-
}
|
|
693
|
-
} catch (_) {
|
|
694
|
-
// Fallback: direct DB query (server not running)
|
|
695
|
-
console.log('โ ๏ธ Symphony server not running โ using direct DB\n');
|
|
696
|
-
try {
|
|
697
|
-
const tm = require(path.join(SYMPHONY_ROOT, 'core', 'task-manager'));
|
|
698
|
-
const inProgress = tm.listTasks({ status: 'in_progress', limit: 10 });
|
|
699
|
-
const claimed = tm.listTasks({ status: 'claimed', limit: 10 });
|
|
700
|
-
const ready = tm.listTasks({ status: 'ready', limit: 5 });
|
|
701
|
-
|
|
702
|
-
const fallbackData = {
|
|
703
|
-
gate_status: 'WARN',
|
|
704
|
-
gates: { server: 'OFFLINE', project: 'UNKNOWN', tasks: inProgress.length > 0 || claimed.length > 0 ? 'HAS_ACTIVE' : 'EMPTY', agents: 'UNKNOWN' },
|
|
705
|
-
server: { status: 'offline' },
|
|
706
|
-
active_project: null,
|
|
707
|
-
projects: [],
|
|
708
|
-
tasks: {
|
|
709
|
-
stats: { total: inProgress.length + claimed.length + ready.length },
|
|
710
|
-
in_progress: [...inProgress, ...claimed].map(t => ({ id: t.id, title: t.title, priority: t.priority })),
|
|
711
|
-
ready: ready.map(t => ({ id: t.id, title: t.title, priority: t.priority })),
|
|
712
|
-
},
|
|
713
|
-
agents: [],
|
|
714
|
-
};
|
|
715
|
-
|
|
716
|
-
if (opts.json) {
|
|
717
|
-
console.log(JSON.stringify(fallbackData, null, 2));
|
|
718
|
-
} else {
|
|
719
|
-
printPreflight(fallbackData);
|
|
720
|
-
}
|
|
721
|
-
} catch (dbErr) {
|
|
722
|
-
console.log('โ Cannot connect to Symphony server or database.\n');
|
|
723
|
-
console.log(' Start server: symphony start');
|
|
724
|
-
console.log(' Or install: cd ~/Dev/NodeJS/main-awf/symphony && npm link\n');
|
|
725
|
-
process.exit(1);
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
closeDb();
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
function printPreflight(data) {
|
|
732
|
-
const gateIcon = (g) => g === 'PASS' ? 'โ
' : g === 'WARN' || g === 'OFFLINE' || g === 'UNKNOWN' ? 'โ ๏ธ' : g === 'FAIL' ? 'โ' : '๐ต';
|
|
733
|
-
|
|
734
|
-
console.log('\n๐ฆ SYMPHONY PREFLIGHT');
|
|
735
|
-
console.log('โ'.repeat(50));
|
|
736
|
-
console.log(` Server: ${gateIcon(data.gates.server)} ${data.gates.server}`);
|
|
737
|
-
console.log(` Project: ${gateIcon(data.gates.project)} ${data.gates.project}${data.active_project ? ` โ ${data.active_project.icon || '๐'} ${data.active_project.name}` : ''}`);
|
|
738
|
-
console.log(` Tasks: ${gateIcon(data.gates.tasks)} ${data.gates.tasks}`);
|
|
739
|
-
if (data.gates.agents) {
|
|
740
|
-
console.log(` Agents: ${gateIcon(data.gates.agents)} ${data.gates.agents}`);
|
|
741
|
-
}
|
|
742
|
-
console.log(` Overall: ${data.gate_status === 'PASS' ? 'โ
PASS' : 'โ ๏ธ ' + data.gate_status}\n`);
|
|
743
|
-
|
|
744
|
-
// Projects
|
|
745
|
-
if (data.projects && data.projects.length > 0) {
|
|
746
|
-
console.log(`๐ Projects (${data.projects.length}):`);
|
|
747
|
-
for (const p of data.projects) {
|
|
748
|
-
console.log(` ${p.active ? 'โ' : 'โ'} ${p.icon || '๐'} ${p.name} [${p.id}]`);
|
|
749
|
-
}
|
|
750
|
-
console.log('');
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
// Agents
|
|
754
|
-
if (data.agents && data.agents.length > 0) {
|
|
755
|
-
const statusIcon = { offline: 'โซ', idle: '๐ค', working: '๐ข' };
|
|
756
|
-
console.log(`๐ค Agents (${data.agents.length}):`);
|
|
757
|
-
for (const a of data.agents) {
|
|
758
|
-
const icon = statusIcon[a.status] || 'โ';
|
|
759
|
-
const skills = a.skills && a.skills.length > 0 ? ` [${a.skills.join(', ')}]` : '';
|
|
760
|
-
const task = a.currentTask ? ` โ ${a.currentTask}` : '';
|
|
761
|
-
console.log(` ${icon} ${a.icon || '๐ค'} ${a.name} (${a.id})${skills}${task}`);
|
|
762
|
-
}
|
|
763
|
-
console.log('');
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
// In-progress tasks
|
|
767
|
-
if (data.tasks.in_progress && data.tasks.in_progress.length > 0) {
|
|
768
|
-
console.log(`๐ฟ In Progress (${data.tasks.in_progress.length}):`);
|
|
769
|
-
for (const t of data.tasks.in_progress) {
|
|
770
|
-
console.log(` ๐ต ${t.id} โ ${t.title} (P${t.priority})`);
|
|
771
|
-
}
|
|
772
|
-
console.log('');
|
|
773
|
-
} else {
|
|
774
|
-
console.log('๐ฟ In Progress: none\n');
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
// Ready tasks
|
|
778
|
-
if (data.tasks.ready && data.tasks.ready.length > 0) {
|
|
779
|
-
console.log(`๐ Ready (${data.tasks.ready.length}):`);
|
|
780
|
-
for (const t of data.tasks.ready) {
|
|
781
|
-
console.log(` โฌ ${t.id} โ ${t.title} (P${t.priority})`);
|
|
782
|
-
}
|
|
783
|
-
console.log('');
|
|
784
|
-
} else {
|
|
785
|
-
console.log('๐ Ready: none\n');
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
// โโโ Lock Commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
790
|
-
|
|
791
|
-
const lockCmd = program.command('lock').description('File lock management');
|
|
792
|
-
|
|
793
|
-
lockCmd
|
|
794
|
-
.command('list')
|
|
795
|
-
.description('List all file locks')
|
|
796
|
-
.action(() => {
|
|
797
|
-
const flm = require(path.join(SYMPHONY_ROOT, 'core', 'file-lock-manager'));
|
|
798
|
-
const locks = flm.getAllLocks();
|
|
799
|
-
|
|
800
|
-
if (locks.length === 0) {
|
|
801
|
-
console.log('\n๐ข No files locked\n');
|
|
802
|
-
closeDb();
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
console.log(`\n๐ Active File Locks (${locks.length}):\n`);
|
|
807
|
-
for (const l of locks) {
|
|
808
|
-
console.log(` ${l.file_path}`);
|
|
809
|
-
console.log(` โโ Agent: ${l.agent_id} | Task: ${l.task_id} | Since: ${l.acquired_at}`);
|
|
810
|
-
}
|
|
811
|
-
console.log('');
|
|
812
|
-
closeDb();
|
|
813
|
-
});
|
|
814
|
-
|
|
815
|
-
lockCmd
|
|
816
|
-
.command('release <file>')
|
|
817
|
-
.description('Force-release a file lock')
|
|
818
|
-
.action((file) => {
|
|
819
|
-
const flm = require(path.join(SYMPHONY_ROOT, 'core', 'file-lock-manager'));
|
|
820
|
-
const released = flm.forceRelease(file);
|
|
821
|
-
if (released) {
|
|
822
|
-
console.log(`\nโ
Lock released: ${file}\n`);
|
|
823
|
-
} else {
|
|
824
|
-
console.log(`\nโ ๏ธ No lock found for: ${file}\n`);
|
|
825
|
-
}
|
|
826
|
-
closeDb();
|
|
827
|
-
});
|
|
828
|
-
|
|
829
|
-
// โโโ Workspace Commands โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
830
|
-
|
|
831
|
-
const wsCmd = program.command('workspace').alias('ws').description('Workspace management');
|
|
832
|
-
|
|
833
|
-
wsCmd
|
|
834
|
-
.command('list')
|
|
835
|
-
.description('List active workspaces')
|
|
836
|
-
.action(() => {
|
|
837
|
-
const wm = require(path.join(SYMPHONY_ROOT, 'core', 'workspace-manager'));
|
|
838
|
-
const workspaces = wm.listWorkspaces();
|
|
839
|
-
|
|
840
|
-
if (workspaces.length === 0) {
|
|
841
|
-
console.log('\n๐ No active workspaces\n');
|
|
842
|
-
closeDb();
|
|
843
|
-
return;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
console.log(`\n๐ Active Workspaces (${workspaces.length}):\n`);
|
|
847
|
-
for (const ws of workspaces) {
|
|
848
|
-
console.log(` ${ws.branch}`);
|
|
849
|
-
console.log(` โโ Task: ${ws.task_id} | Type: ${ws.type} | Path: ${ws.path}`);
|
|
850
|
-
console.log(` Created: ${ws.created_at}`);
|
|
851
|
-
}
|
|
852
|
-
console.log('');
|
|
853
|
-
closeDb();
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
wsCmd
|
|
857
|
-
.command('create <task-id>')
|
|
858
|
-
.description('Create workspace for a task')
|
|
859
|
-
.option('-t, --type <type>', 'Workspace type (worktree|clone)', 'worktree')
|
|
860
|
-
.action((taskId, opts) => {
|
|
861
|
-
const wm = require(path.join(SYMPHONY_ROOT, 'core', 'workspace-manager'));
|
|
862
|
-
const repoPath = process.cwd();
|
|
863
|
-
|
|
864
|
-
try {
|
|
865
|
-
const ws = wm.createWorkspace(taskId, repoPath, { type: opts.type });
|
|
866
|
-
console.log(`\nโ
Workspace created:`);
|
|
867
|
-
console.log(` Branch: ${ws.branch}`);
|
|
868
|
-
console.log(` Path: ${ws.path}`);
|
|
869
|
-
console.log(` Type: ${ws.type}\n`);
|
|
870
|
-
} catch (e) {
|
|
871
|
-
console.error(`\nโ Failed to create workspace: ${e.message}\n`);
|
|
872
|
-
}
|
|
873
|
-
closeDb();
|
|
874
|
-
});
|
|
875
|
-
|
|
876
|
-
wsCmd
|
|
877
|
-
.command('merge <task-id>')
|
|
878
|
-
.description('Run auto-merge pipeline for a completed task')
|
|
879
|
-
.action((taskId) => {
|
|
880
|
-
const mp = require(path.join(SYMPHONY_ROOT, 'core', 'merge-pipeline'));
|
|
881
|
-
const repoPath = process.cwd();
|
|
882
|
-
|
|
883
|
-
console.log(`\n๐ Running merge pipeline for task ${taskId}...\n`);
|
|
884
|
-
const result = mp.autoMerge(taskId, repoPath);
|
|
885
|
-
|
|
886
|
-
if (result.status === 'merged') {
|
|
887
|
-
console.log(` โ
${result.message}\n`);
|
|
888
|
-
} else if (result.status === 'conflict') {
|
|
889
|
-
console.log(` โ ๏ธ ${result.message}`);
|
|
890
|
-
if (result.conflictingFiles && result.conflictingFiles.length > 0) {
|
|
891
|
-
console.log(' Conflicting files:');
|
|
892
|
-
for (const f of result.conflictingFiles) {
|
|
893
|
-
console.log(` - ${f}`);
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
console.log('');
|
|
897
|
-
} else {
|
|
898
|
-
console.error(` โ ${result.message}\n`);
|
|
899
|
-
}
|
|
900
|
-
closeDb();
|
|
901
|
-
});
|
|
902
|
-
|
|
903
|
-
wsCmd
|
|
904
|
-
.command('clean')
|
|
905
|
-
.description('Remove all completed/merged workspaces')
|
|
906
|
-
.action(() => {
|
|
907
|
-
const { getDb } = require(path.join(SYMPHONY_ROOT, 'core', 'db'));
|
|
908
|
-
const wm = require(path.join(SYMPHONY_ROOT, 'core', 'workspace-manager'));
|
|
909
|
-
const db = getDb();
|
|
910
|
-
|
|
911
|
-
const merged = db.prepare("SELECT * FROM workspaces WHERE status IN ('merged', 'cleaned')").all();
|
|
912
|
-
if (merged.length === 0) {
|
|
913
|
-
console.log('\n๐ข No workspaces to clean\n');
|
|
914
|
-
closeDb();
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
let cleaned = 0;
|
|
919
|
-
for (const ws of merged) {
|
|
920
|
-
if (ws.status !== 'cleaned') {
|
|
921
|
-
try {
|
|
922
|
-
wm.removeWorkspace(ws.task_id, process.cwd());
|
|
923
|
-
cleaned++;
|
|
924
|
-
} catch (_) { /* skip */ }
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
console.log(`\n๐งน Cleaned ${cleaned} workspace(s)\n`);
|
|
928
|
-
closeDb();
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
// โโโ Dashboard Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
932
|
-
|
|
933
|
-
program
|
|
934
|
-
.command('dashboard')
|
|
935
|
-
.description('Open dashboard in browser')
|
|
936
|
-
.action(() => {
|
|
937
|
-
const config = require(path.join(SYMPHONY_ROOT, 'symphony.config'));
|
|
938
|
-
const url = `http://localhost:${config.port}`;
|
|
939
|
-
console.log(`\n๐ Opening dashboard: ${url}\n`);
|
|
940
|
-
try {
|
|
941
|
-
require('child_process').execSync(`open "${url}"`, { stdio: 'ignore' });
|
|
942
|
-
} catch (_) {
|
|
943
|
-
console.log(` Open manually: ${url}`);
|
|
944
|
-
}
|
|
945
|
-
});
|
|
946
|
-
|
|
947
|
-
// โโโ MCP Serve Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
948
|
-
|
|
949
|
-
program
|
|
950
|
-
.command('mcp-serve')
|
|
951
|
-
.description('Start MCP server (stdio transport for IDE connections)')
|
|
952
|
-
.option('-n, --name <name>', 'Agent display name')
|
|
953
|
-
.action((opts) => {
|
|
954
|
-
// Set agent name env var before requiring the server
|
|
955
|
-
if (opts.name) {
|
|
956
|
-
process.env.SYMPHONY_AGENT_NAME = opts.name;
|
|
957
|
-
}
|
|
958
|
-
// The MCP server takes over stdio, so we require it directly
|
|
959
|
-
require(path.join(SYMPHONY_ROOT, 'mcp', 'server'));
|
|
960
|
-
});
|
|
961
|
-
|
|
962
|
-
// โโโ Build Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
963
|
-
|
|
964
|
-
program
|
|
965
|
-
.command('build')
|
|
966
|
-
.description('Build Symphony dashboard for production')
|
|
967
|
-
.action(() => {
|
|
968
|
-
console.log('\n๐ฆ Building Symphony dashboard...\n');
|
|
969
|
-
const { execSync } = require('child_process');
|
|
970
|
-
try {
|
|
971
|
-
execSync('npx next build', {
|
|
972
|
-
cwd: SYMPHONY_ROOT,
|
|
973
|
-
stdio: 'inherit',
|
|
974
|
-
env: { ...process.env, NODE_ENV: 'production' },
|
|
975
|
-
});
|
|
976
|
-
console.log('\nโ
Build complete!\n');
|
|
977
|
-
} catch (err) {
|
|
978
|
-
console.error('\nโ Build failed.\n');
|
|
979
|
-
process.exit(1);
|
|
980
|
-
}
|
|
981
|
-
});
|
|
982
|
-
|
|
983
|
-
// โโโ Start Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
984
|
-
|
|
985
|
-
program
|
|
986
|
-
.command('start')
|
|
987
|
-
.description('Start Symphony server (dashboard + API) in production mode')
|
|
988
|
-
.option('-p, --port <port>', 'Port number', '3100')
|
|
989
|
-
.action((opts) => {
|
|
990
|
-
const { execSync } = require('child_process');
|
|
991
|
-
const fs = require('fs');
|
|
992
|
-
|
|
993
|
-
// Auto-build if .next/ doesn't exist
|
|
994
|
-
if (!fs.existsSync(path.join(SYMPHONY_ROOT, '.next'))) {
|
|
995
|
-
console.log('\n๐ฆ First run โ building dashboard...\n');
|
|
996
|
-
try {
|
|
997
|
-
execSync('npx next build', {
|
|
998
|
-
cwd: SYMPHONY_ROOT,
|
|
999
|
-
stdio: 'inherit',
|
|
1000
|
-
env: { ...process.env, NODE_ENV: 'production' },
|
|
1001
|
-
});
|
|
1002
|
-
} catch (err) {
|
|
1003
|
-
console.error('\nโ Build failed. Try running: symphony build\n');
|
|
1004
|
-
process.exit(1);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
console.log(`\n๐ผ Starting AWKit Symphony on port ${opts.port}...\n`);
|
|
1009
|
-
try {
|
|
1010
|
-
execSync(`npx next start -p ${opts.port}`, {
|
|
1011
|
-
cwd: SYMPHONY_ROOT,
|
|
1012
|
-
stdio: 'inherit',
|
|
1013
|
-
});
|
|
1014
|
-
} catch (_) {
|
|
1015
|
-
// Interrupted (Ctrl+C)
|
|
1016
|
-
}
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
// โโโ Dev Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1020
|
-
|
|
1021
|
-
program
|
|
1022
|
-
.command('dev')
|
|
1023
|
-
.description('Start Symphony in development mode (hot reload)')
|
|
1024
|
-
.option('-p, --port <port>', 'Port number', '3100')
|
|
1025
|
-
.action((opts) => {
|
|
1026
|
-
console.log(`\n๐ง Starting AWKit Symphony (dev) on port ${opts.port}...\n`);
|
|
1027
|
-
const { execSync } = require('child_process');
|
|
1028
|
-
try {
|
|
1029
|
-
execSync(`npx next dev -p ${opts.port}`, {
|
|
1030
|
-
cwd: SYMPHONY_ROOT,
|
|
1031
|
-
stdio: 'inherit',
|
|
1032
|
-
});
|
|
1033
|
-
} catch (_) {
|
|
1034
|
-
// Interrupted (Ctrl+C)
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
|
|
1038
|
-
function closeDb() {
|
|
1039
|
-
try {
|
|
1040
|
-
require(path.join(SYMPHONY_ROOT, 'core', 'db')).close();
|
|
1041
|
-
} catch (_) { /* ignore */ }
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
// โโโ Global Error Handling โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
1045
|
-
|
|
1046
|
-
process.on('uncaughtException', (err) => {
|
|
1047
|
-
console.error(`\nโ Unexpected error: ${err.message}\n`);
|
|
1048
|
-
if (process.env.DEBUG) console.error(err.stack);
|
|
1049
|
-
closeDb();
|
|
1050
|
-
process.exit(1);
|
|
1051
|
-
});
|
|
1052
|
-
|
|
1053
|
-
process.on('unhandledRejection', (reason) => {
|
|
1054
|
-
console.error(`\nโ Unhandled rejection: ${reason}\n`);
|
|
1055
|
-
closeDb();
|
|
1056
|
-
process.exit(1);
|
|
1057
|
-
});
|
|
1058
|
-
|
|
1059
|
-
program.parse();
|
|
1060
|
-
|