@synergenius/flow-weaver-pack-weaver 0.9.80 → 0.9.82
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/dist/ai-chat-provider.d.ts.map +1 -1
- package/dist/ai-chat-provider.js +40 -1
- package/dist/ai-chat-provider.js.map +1 -1
- package/dist/cli-bridge.d.ts.map +1 -1
- package/dist/cli-bridge.js +5 -3
- package/dist/cli-bridge.js.map +1 -1
- package/dist/cli-handlers.d.ts +16 -7
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +293 -416
- package/dist/cli-handlers.js.map +1 -1
- package/dist/ui/swarm-dashboard.js +906 -323
- package/dist/ui/task-detail-view.js +27 -16
- package/dist/ui/task-editor.js +613 -0
- package/flowweaver.manifest.json +16 -5
- package/package.json +1 -1
- package/src/ai-chat-provider.ts +39 -1
- package/src/cli-bridge.ts +6 -3
- package/src/cli-handlers.ts +280 -433
- package/src/ui/swarm-dashboard.tsx +41 -12
- package/src/ui/task-detail-view.tsx +23 -15
- package/src/ui/task-editor.tsx +591 -0
- package/src/ui/task-create-form.tsx +0 -87
package/src/cli-handlers.ts
CHANGED
|
@@ -12,9 +12,11 @@ import { DashboardServer } from './bot/dashboard.js';
|
|
|
12
12
|
import { openBrowser } from './bot/utils.js';
|
|
13
13
|
import type { ExecutionEvent, WeaverConfig, RunRecord, RunOutcome, RunCostSummary, CostSummary, StageStatus, WorkflowResult, AuditEvent } from './bot/types.js';
|
|
14
14
|
import { AuditStore } from './bot/audit-store.js';
|
|
15
|
+
import { handleWeaverTool } from './ai-chat-provider.js';
|
|
15
16
|
|
|
16
17
|
export interface ParsedArgs {
|
|
17
|
-
command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | '
|
|
18
|
+
command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | 'swarm' | 'task' | 'profile' | 'reset' | 'steer' | 'status' | 'genesis' | 'audit' | 'init' | 'assistant' | 'examples' | 'doctor' | 'improve';
|
|
19
|
+
subcommand?: string;
|
|
18
20
|
file?: string;
|
|
19
21
|
verbose: boolean;
|
|
20
22
|
dryRun: boolean;
|
|
@@ -58,11 +60,17 @@ export interface ParsedArgs {
|
|
|
58
60
|
genesisWatch: boolean;
|
|
59
61
|
// eject
|
|
60
62
|
ejectWorkflow?: string;
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
// swarm / task / profile
|
|
64
|
+
maxConcurrent?: number;
|
|
65
|
+
budgetTokens?: number;
|
|
66
|
+
budgetCost?: number;
|
|
67
|
+
profileId?: string;
|
|
68
|
+
costStrategy?: string;
|
|
69
|
+
botId?: string;
|
|
70
|
+
confirmFlag?: boolean;
|
|
71
|
+
taskDescription?: string;
|
|
72
|
+
complexity?: string;
|
|
73
|
+
autoRetry?: boolean;
|
|
66
74
|
// assistant
|
|
67
75
|
assistantNew?: boolean;
|
|
68
76
|
assistantResume?: string;
|
|
@@ -102,7 +110,6 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
102
110
|
autoApprove: false,
|
|
103
111
|
genesisInit: false,
|
|
104
112
|
genesisWatch: false,
|
|
105
|
-
sessionContinuous: false,
|
|
106
113
|
};
|
|
107
114
|
|
|
108
115
|
const args = argv.slice(2);
|
|
@@ -207,13 +214,46 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
207
214
|
result.dashboard = true;
|
|
208
215
|
} else if (arg === 'bot') {
|
|
209
216
|
result.command = 'bot';
|
|
210
|
-
// Next non-flag arg is the task string
|
|
217
|
+
// Next non-flag arg is the task string or subcommand
|
|
211
218
|
if (i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
212
219
|
i++;
|
|
213
|
-
result.
|
|
220
|
+
result.subcommand = args[i];
|
|
221
|
+
// For non-registry subcommands, treat as task string
|
|
222
|
+
if (!['list', 'register', 'validate'].includes(result.subcommand!)) {
|
|
223
|
+
result.botTask = result.subcommand;
|
|
224
|
+
result.subcommand = undefined;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
} else if (arg === 'swarm') {
|
|
228
|
+
result.command = 'swarm';
|
|
229
|
+
if (i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
230
|
+
i++;
|
|
231
|
+
result.subcommand = args[i];
|
|
232
|
+
}
|
|
233
|
+
} else if (arg === 'task') {
|
|
234
|
+
result.command = 'task';
|
|
235
|
+
if (i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
236
|
+
i++;
|
|
237
|
+
result.subcommand = args[i];
|
|
238
|
+
// For create/get/cancel/retry: next positional is the title/id
|
|
239
|
+
if (['create', 'get', 'cancel', 'retry'].includes(result.subcommand!) && i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
240
|
+
i++;
|
|
241
|
+
result.botTask = args[i];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} else if (arg === 'profile') {
|
|
245
|
+
result.command = 'profile';
|
|
246
|
+
if (i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
247
|
+
i++;
|
|
248
|
+
result.subcommand = args[i];
|
|
249
|
+
// For create/delete: next positional is the name/id
|
|
250
|
+
if (['create', 'delete'].includes(result.subcommand!) && i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
251
|
+
i++;
|
|
252
|
+
result.botTask = args[i];
|
|
253
|
+
}
|
|
214
254
|
}
|
|
215
|
-
} else if (arg === '
|
|
216
|
-
result.command = '
|
|
255
|
+
} else if (arg === 'reset') {
|
|
256
|
+
result.command = 'reset';
|
|
217
257
|
} else if (arg === 'assistant') {
|
|
218
258
|
result.command = 'assistant';
|
|
219
259
|
} else if (arg === 'examples') {
|
|
@@ -224,27 +264,10 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
224
264
|
result.command = 'status';
|
|
225
265
|
} else if (arg === 'steer') {
|
|
226
266
|
result.command = 'steer';
|
|
227
|
-
// Next arg is the subcommand
|
|
267
|
+
// Next arg is the subcommand (pause/resume/cancel)
|
|
228
268
|
if (i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
229
269
|
i++;
|
|
230
|
-
result.
|
|
231
|
-
// Next arg after redirect/queue is payload
|
|
232
|
-
if ((args[i] === 'redirect' || args[i] === 'queue') && i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
233
|
-
i++;
|
|
234
|
-
result.botFile = args[i];
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
} else if (arg === 'queue') {
|
|
238
|
-
result.command = 'queue';
|
|
239
|
-
// Next arg is action (add/list/clear/remove)
|
|
240
|
-
if (i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
241
|
-
i++;
|
|
242
|
-
result.botTask = args[i];
|
|
243
|
-
// Next arg is task/id
|
|
244
|
-
if (i + 1 < args.length && !args[i + 1]!.startsWith('-')) {
|
|
245
|
-
i++;
|
|
246
|
-
result.botFile = args[i];
|
|
247
|
-
}
|
|
270
|
+
result.subcommand = args[i];
|
|
248
271
|
}
|
|
249
272
|
} else if (arg === '--file' && i + 1 < args.length) {
|
|
250
273
|
i++;
|
|
@@ -292,17 +315,37 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
292
315
|
} else if (arg === '--delete' && i + 1 < args.length) {
|
|
293
316
|
i++;
|
|
294
317
|
result.assistantDelete = args[i];
|
|
295
|
-
} else if (arg === '--
|
|
296
|
-
|
|
297
|
-
|
|
318
|
+
} else if (arg === '--max-concurrent' && i + 1 < args.length) {
|
|
319
|
+
i++;
|
|
320
|
+
result.maxConcurrent = parseInt(args[i]!, 10) || undefined;
|
|
321
|
+
} else if (arg === '--budget-tokens' && i + 1 < args.length) {
|
|
322
|
+
i++;
|
|
323
|
+
result.budgetTokens = parseInt(args[i]!, 10) || undefined;
|
|
324
|
+
} else if (arg === '--budget-cost' && i + 1 < args.length) {
|
|
325
|
+
i++;
|
|
326
|
+
result.budgetCost = parseFloat(args[i]!) || undefined;
|
|
327
|
+
} else if (arg === '--profile' && i + 1 < args.length) {
|
|
328
|
+
i++;
|
|
329
|
+
result.profileId = args[i];
|
|
330
|
+
} else if (arg === '--cost-strategy' && i + 1 < args.length) {
|
|
298
331
|
i++;
|
|
299
|
-
result.
|
|
300
|
-
} else if (arg === '--
|
|
332
|
+
result.costStrategy = args[i];
|
|
333
|
+
} else if (arg === '--bot' && i + 1 < args.length) {
|
|
301
334
|
i++;
|
|
302
|
-
result.
|
|
303
|
-
} else if (arg === '--
|
|
335
|
+
result.botId = args[i];
|
|
336
|
+
} else if (arg === '--confirm') {
|
|
337
|
+
result.confirmFlag = true;
|
|
338
|
+
} else if (arg === '--description' && i + 1 < args.length) {
|
|
304
339
|
i++;
|
|
305
|
-
result.
|
|
340
|
+
result.taskDescription = args[i];
|
|
341
|
+
} else if (arg === '--complexity' && i + 1 < args.length) {
|
|
342
|
+
i++;
|
|
343
|
+
result.complexity = args[i];
|
|
344
|
+
} else if (arg === '--auto-retry') {
|
|
345
|
+
result.autoRetry = true;
|
|
346
|
+
} else if (arg === '--status' && i + 1 < args.length) {
|
|
347
|
+
i++;
|
|
348
|
+
result.historyOutcome = args[i];
|
|
306
349
|
} else if (arg === '--project-dir' && i + 1 < args.length) {
|
|
307
350
|
i++;
|
|
308
351
|
result.file = args[i];
|
|
@@ -1342,300 +1385,198 @@ export async function handleBot(opts: ParsedArgs): Promise<void> {
|
|
|
1342
1385
|
}
|
|
1343
1386
|
}
|
|
1344
1387
|
|
|
1345
|
-
|
|
1346
|
-
const projectDir = opts.file ?? process.cwd();
|
|
1347
|
-
// Set project dir for per-project queue isolation
|
|
1348
|
-
process.env.WEAVER_PROJECT_DIR = projectDir;
|
|
1349
|
-
const config = await loadConfig(opts.configPath);
|
|
1350
|
-
const workflowPath = resolveWorkflowPath('agent', projectDir);
|
|
1351
|
-
|
|
1352
|
-
// Create terminal renderer for all session output
|
|
1353
|
-
const { TerminalRenderer } = await import('./bot/terminal-renderer.js');
|
|
1354
|
-
const renderer = new TerminalRenderer({ verbose: opts.verbose, quiet: opts.quiet });
|
|
1355
|
-
|
|
1356
|
-
// Parse --until HH:MM into a deadline timestamp
|
|
1357
|
-
let deadline: number | undefined;
|
|
1358
|
-
let deadlineStr: string | undefined;
|
|
1359
|
-
if (opts.sessionUntil) {
|
|
1360
|
-
const match = opts.sessionUntil.match(/^(\d{1,2}):(\d{2})$/);
|
|
1361
|
-
if (match) {
|
|
1362
|
-
const now = new Date();
|
|
1363
|
-
const target = new Date(now);
|
|
1364
|
-
target.setHours(parseInt(match[1]!, 10), parseInt(match[2]!, 10), 0, 0);
|
|
1365
|
-
if (target.getTime() <= now.getTime()) target.setDate(target.getDate() + 1);
|
|
1366
|
-
deadline = target.getTime();
|
|
1367
|
-
deadlineStr = target.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
const maxTasks = opts.sessionMaxTasks ?? Infinity;
|
|
1372
|
-
const continuous = opts.sessionContinuous || !!opts.sessionUntil || maxTasks < Infinity;
|
|
1373
|
-
const parallelism = opts.sessionParallel ?? 1;
|
|
1388
|
+
// --- Swarm / Task / Profile / Reset handlers (thin wrappers around handleWeaverTool) ---
|
|
1374
1389
|
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
let recovered = 0;
|
|
1382
|
-
for (const t of inProgressTasks) {
|
|
1383
|
-
await recoveryStore.update(t.id, { status: 'pending' });
|
|
1384
|
-
recovered++;
|
|
1385
|
-
}
|
|
1386
|
-
if (recovered > 0) renderer.info(`Recovered ${recovered} orphaned task(s)`);
|
|
1390
|
+
function colorStatus(status: string): string {
|
|
1391
|
+
switch (status) {
|
|
1392
|
+
case 'running': return '\x1b[32m' + status + '\x1b[0m';
|
|
1393
|
+
case 'paused': return '\x1b[33m' + status + '\x1b[0m';
|
|
1394
|
+
case 'stopped': case 'idle': return '\x1b[90m' + status + '\x1b[0m';
|
|
1395
|
+
default: return status;
|
|
1387
1396
|
}
|
|
1397
|
+
}
|
|
1388
1398
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1399
|
+
export async function handleSwarm(opts: ParsedArgs): Promise<void> {
|
|
1400
|
+
const projectDir = opts.file || process.cwd();
|
|
1401
|
+
const sub = opts.subcommand;
|
|
1402
|
+
|
|
1403
|
+
if (!sub || sub === 'status') {
|
|
1404
|
+
const { result } = await handleWeaverTool('fw_weaver_swarm_status', {}, projectDir);
|
|
1405
|
+
const data = JSON.parse(result);
|
|
1406
|
+
console.log(`\x1b[1mSwarm: ${colorStatus(data.status)}\x1b[0m`);
|
|
1407
|
+
if (data.instances !== undefined) console.log(` Instances: ${data.instances}`);
|
|
1408
|
+
if (data.maxConcurrent !== undefined) console.log(` Max concurrent: ${data.maxConcurrent}`);
|
|
1409
|
+
if (data.taskCounts) {
|
|
1410
|
+
const tc = data.taskCounts;
|
|
1411
|
+
console.log(` Tasks: ${tc.pending ?? 0} pending, ${tc.running ?? 0} running, ${tc.done ?? 0} done, ${tc.failed ?? 0} failed`);
|
|
1396
1412
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
const providerLabel = typeof providerType === 'object' ? providerType.name : String(providerType);
|
|
1402
|
-
const sessionStartTime = Date.now();
|
|
1403
|
-
renderer.sessionStart({ provider: providerLabel, parallel: parallelism, deadline: deadlineStr });
|
|
1404
|
-
|
|
1405
|
-
// Single-run mode (backwards compatible)
|
|
1406
|
-
if (!continuous) {
|
|
1407
|
-
try {
|
|
1408
|
-
const result = await runWorkflow(workflowPath, {
|
|
1409
|
-
params: { projectDir },
|
|
1410
|
-
verbose: opts.verbose,
|
|
1411
|
-
dryRun: opts.dryRun,
|
|
1412
|
-
config,
|
|
1413
|
-
});
|
|
1414
|
-
if (!opts.quiet) {
|
|
1415
|
-
const color = result.success ? '\x1b[32m' : '\x1b[31m';
|
|
1416
|
-
console.log(`${color}Session: ${result.outcome}\x1b[0m`);
|
|
1417
|
-
}
|
|
1418
|
-
process.exit(result.success ? 0 : 1);
|
|
1419
|
-
} catch (err: unknown) {
|
|
1420
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1421
|
-
console.error(`\x1b[31m[weaver] Fatal: ${msg}\x1b[0m`);
|
|
1422
|
-
process.exit(1);
|
|
1413
|
+
if (data.budget) {
|
|
1414
|
+
const b = data.budget;
|
|
1415
|
+
if (b.tokensUsed !== undefined) console.log(` Tokens: ${b.tokensUsed.toLocaleString()} / ${(b.tokensLimit ?? '∞').toLocaleString()}`);
|
|
1416
|
+
if (b.costUsed !== undefined) console.log(` Cost: $${b.costUsed.toFixed(4)} / $${(b.costLimit ?? '∞')}`);
|
|
1423
1417
|
}
|
|
1424
1418
|
return;
|
|
1425
1419
|
}
|
|
1426
1420
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
const MAX_CONSECUTIVE_NO_OPS = 5;
|
|
1437
|
-
|
|
1438
|
-
// Session stats
|
|
1439
|
-
let sessionCompleted = 0, sessionFailed = 0, sessionNoOp = 0;
|
|
1440
|
-
let sessionInputTokens = 0, sessionOutputTokens = 0, sessionCost = 0;
|
|
1441
|
-
|
|
1442
|
-
process.on('SIGINT', () => { interrupted = true; });
|
|
1443
|
-
process.on('SIGTERM', () => { interrupted = true; });
|
|
1444
|
-
|
|
1445
|
-
// Parallel task tracking
|
|
1446
|
-
const running = new Map<string, Promise<void>>();
|
|
1447
|
-
const filesInUse = new Set<string>();
|
|
1448
|
-
|
|
1449
|
-
const processTask = async (task: { id: string; title: string; description: string; context: { files: string[] } }) => {
|
|
1450
|
-
const taskPayload = { id: task.id, instruction: task.title, description: task.description, targets: task.context.files };
|
|
1451
|
-
try {
|
|
1452
|
-
const result = await runWorkflow(workflowPath, {
|
|
1453
|
-
params: { projectDir, taskJson: JSON.stringify(taskPayload) },
|
|
1454
|
-
verbose: opts.verbose,
|
|
1455
|
-
dryRun: opts.dryRun,
|
|
1456
|
-
config,
|
|
1457
|
-
});
|
|
1458
|
-
|
|
1459
|
-
// Classify outcome: the summary contains "no changes" or "0 files" for no-ops
|
|
1460
|
-
const isNoOp = result.success && (
|
|
1461
|
-
result.summary.includes('no changes') ||
|
|
1462
|
-
result.summary.includes('0 file') ||
|
|
1463
|
-
result.summary.includes("doesn't exist") ||
|
|
1464
|
-
result.summary.includes('does not exist') ||
|
|
1465
|
-
result.summary.includes('nothing to') ||
|
|
1466
|
-
result.outcome === 'no-op'
|
|
1467
|
-
);
|
|
1468
|
-
|
|
1469
|
-
if (isNoOp) {
|
|
1470
|
-
await taskStore.update(task.id, { status: 'done' });
|
|
1471
|
-
sessionNoOp++;
|
|
1472
|
-
consecutiveNoOps++;
|
|
1473
|
-
consecutiveErrors = 0;
|
|
1474
|
-
} else if (result.success) {
|
|
1475
|
-
await taskStore.update(task.id, { status: 'done' });
|
|
1476
|
-
sessionCompleted++;
|
|
1477
|
-
consecutiveErrors = 0;
|
|
1478
|
-
consecutiveNoOps = 0;
|
|
1479
|
-
} else {
|
|
1480
|
-
await taskStore.update(task.id, { status: 'failed' });
|
|
1481
|
-
sessionFailed++;
|
|
1482
|
-
consecutiveErrors++;
|
|
1483
|
-
consecutiveNoOps = 0;
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
// Track cost from workflow result
|
|
1487
|
-
if (result.cost) {
|
|
1488
|
-
sessionInputTokens += result.cost.totalInputTokens ?? 0;
|
|
1489
|
-
sessionOutputTokens += result.cost.totalOutputTokens ?? 0;
|
|
1490
|
-
sessionCost += result.cost.totalCost ?? 0;
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
} catch (err: unknown) {
|
|
1494
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1495
|
-
const guidance = getErrorGuidance(msg);
|
|
1496
|
-
renderer.error(`Task ${task.id.slice(0, 8)} error`, guidance ? `${msg}\n Hint: ${guidance}` : msg);
|
|
1497
|
-
await taskStore.update(task.id, { status: 'failed' });
|
|
1498
|
-
sessionFailed++;
|
|
1499
|
-
if (!isTransientError(err)) {
|
|
1500
|
-
consecutiveErrors++;
|
|
1501
|
-
}
|
|
1502
|
-
} finally {
|
|
1503
|
-
for (const f of task.context.files) filesInUse.delete(f);
|
|
1504
|
-
running.delete(task.id);
|
|
1421
|
+
switch (sub) {
|
|
1422
|
+
case 'start': {
|
|
1423
|
+
await handleWeaverTool('fw_weaver_swarm_start', {
|
|
1424
|
+
maxConcurrent: opts.maxConcurrent,
|
|
1425
|
+
sessionBudgetTokens: opts.budgetTokens,
|
|
1426
|
+
sessionBudgetCost: opts.budgetCost,
|
|
1427
|
+
}, projectDir);
|
|
1428
|
+
console.log('\x1b[32m→ Swarm started\x1b[0m');
|
|
1429
|
+
break;
|
|
1505
1430
|
}
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
if (deadline && Date.now() >= deadline) {
|
|
1510
|
-
renderer.info('Deadline reached, stopping session.');
|
|
1431
|
+
case 'stop': {
|
|
1432
|
+
await handleWeaverTool('fw_weaver_swarm_stop', {}, projectDir);
|
|
1433
|
+
console.log('\x1b[33m→ Swarm stopped\x1b[0m');
|
|
1511
1434
|
break;
|
|
1512
1435
|
}
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1436
|
+
case 'pause': {
|
|
1437
|
+
await handleWeaverTool('fw_weaver_swarm_pause', {}, projectDir);
|
|
1438
|
+
console.log('\x1b[33m→ Swarm paused\x1b[0m');
|
|
1516
1439
|
break;
|
|
1517
1440
|
}
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1441
|
+
case 'config': {
|
|
1442
|
+
await handleWeaverTool('fw_weaver_swarm_config', {
|
|
1443
|
+
maxConcurrent: opts.maxConcurrent,
|
|
1444
|
+
workspaceBudgetTokens: opts.budgetTokens,
|
|
1445
|
+
workspaceBudgetCost: opts.budgetCost,
|
|
1446
|
+
autoRetry: opts.autoRetry,
|
|
1447
|
+
}, projectDir);
|
|
1448
|
+
console.log('\x1b[32m→ Config updated\x1b[0m');
|
|
1449
|
+
break;
|
|
1524
1450
|
}
|
|
1451
|
+
default:
|
|
1452
|
+
console.error(`Unknown swarm command: ${sub}. Use: start, stop, pause, status, config`);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1525
1455
|
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1456
|
+
export async function handleTask(opts: ParsedArgs): Promise<void> {
|
|
1457
|
+
const projectDir = opts.file || process.cwd();
|
|
1458
|
+
const sub = opts.subcommand;
|
|
1459
|
+
|
|
1460
|
+
switch (sub) {
|
|
1461
|
+
case 'create': {
|
|
1462
|
+
const title = opts.botTask;
|
|
1463
|
+
if (!title) { console.error('Usage: weaver task create "title"'); return; }
|
|
1464
|
+
const { result } = await handleWeaverTool('fw_weaver_task_create', {
|
|
1465
|
+
title,
|
|
1466
|
+
description: opts.taskDescription,
|
|
1467
|
+
priority: opts.historyOutcome, // reuse for priority if needed
|
|
1468
|
+
assignedProfile: opts.profileId,
|
|
1469
|
+
complexity: opts.complexity,
|
|
1470
|
+
}, projectDir);
|
|
1471
|
+
const data = JSON.parse(result);
|
|
1472
|
+
console.log(`\x1b[32m→ Task created: ${data.task?.id ?? data.id ?? 'unknown'}\x1b[0m`);
|
|
1473
|
+
break;
|
|
1530
1474
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1475
|
+
case 'list': {
|
|
1476
|
+
const { result } = await handleWeaverTool('fw_weaver_task_list', {
|
|
1477
|
+
status: opts.historyOutcome,
|
|
1478
|
+
limit: opts.historyLimit,
|
|
1479
|
+
}, projectDir);
|
|
1480
|
+
const tasks = JSON.parse(result);
|
|
1481
|
+
if (!Array.isArray(tasks) || tasks.length === 0) {
|
|
1482
|
+
console.log(' No tasks.');
|
|
1483
|
+
return;
|
|
1539
1484
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
// Auto-decompose broad tasks into per-file tasks
|
|
1546
|
-
const { decomposeTask } = await import('./bot/task-decomposer.js');
|
|
1547
|
-
const decomposable = { id: task.id, instruction: task.title, targets: task.context.files, priority: task.priority };
|
|
1548
|
-
const { decomposed, tasks: subtasks } = decomposeTask(decomposable, projectDir);
|
|
1549
|
-
if (decomposed && subtasks.length > 1) {
|
|
1550
|
-
// Replace the broad task with per-file tasks
|
|
1551
|
-
await taskStore.update(task.id, { status: 'done' });
|
|
1552
|
-
for (const st of subtasks) {
|
|
1553
|
-
await taskStore.create({
|
|
1554
|
-
title: st.instruction,
|
|
1555
|
-
description: st.instruction,
|
|
1556
|
-
priority: st.priority ?? 0,
|
|
1557
|
-
createdBy: 'ai',
|
|
1558
|
-
});
|
|
1485
|
+
for (const t of tasks) {
|
|
1486
|
+
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === 'failed' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
|
|
1487
|
+
console.log(` ${icon} ${(t.id ?? '').slice(0, 8)} ${t.title ?? ''} [${t.status ?? ''}] ${t.assignedProfile ? `→ ${t.assignedProfile}` : ''}`);
|
|
1559
1488
|
}
|
|
1560
|
-
|
|
1561
|
-
continue;
|
|
1489
|
+
break;
|
|
1562
1490
|
}
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
continue;
|
|
1491
|
+
case 'get': {
|
|
1492
|
+
const id = opts.botTask;
|
|
1493
|
+
if (!id) { console.error('Usage: weaver task get <id>'); return; }
|
|
1494
|
+
const { result } = await handleWeaverTool('fw_weaver_task_get', { id }, projectDir);
|
|
1495
|
+
console.log(result);
|
|
1496
|
+
break;
|
|
1570
1497
|
}
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
// Reserve files
|
|
1578
|
-
for (const f of taskTargets) filesInUse.add(f);
|
|
1579
|
-
|
|
1580
|
-
// Launch task (parallel or sequential based on parallelism setting)
|
|
1581
|
-
const promise = processTask(task);
|
|
1582
|
-
running.set(task.id, promise);
|
|
1583
|
-
|
|
1584
|
-
// In sequential mode (parallelism=1), await immediately
|
|
1585
|
-
if (parallelism <= 1) {
|
|
1586
|
-
await promise;
|
|
1498
|
+
case 'clear': {
|
|
1499
|
+
const filter = opts.confirmFlag ? 'all' : 'completed';
|
|
1500
|
+
const { result } = await handleWeaverTool('fw_weaver_tasks_clear', { filter }, projectDir);
|
|
1501
|
+
const data = JSON.parse(result);
|
|
1502
|
+
console.log(`\x1b[32m→ Cleared ${data.cleared} tasks\x1b[0m`);
|
|
1503
|
+
break;
|
|
1587
1504
|
}
|
|
1505
|
+
case 'cancel': {
|
|
1506
|
+
const id = opts.botTask;
|
|
1507
|
+
if (!id) { console.error('Usage: weaver task cancel <id>'); return; }
|
|
1508
|
+
await handleWeaverTool('fw_weaver_task_cancel', { id }, projectDir);
|
|
1509
|
+
console.log(`\x1b[33m→ Task ${id} cancelled\x1b[0m`);
|
|
1510
|
+
break;
|
|
1511
|
+
}
|
|
1512
|
+
case 'retry': {
|
|
1513
|
+
const id = opts.botTask;
|
|
1514
|
+
if (!id) { console.error('Usage: weaver task retry <id>'); return; }
|
|
1515
|
+
await handleWeaverTool('fw_weaver_task_retry', { id }, projectDir);
|
|
1516
|
+
console.log(`\x1b[32m→ Task ${id} retried\x1b[0m`);
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
default:
|
|
1520
|
+
// Default: list
|
|
1521
|
+
await handleTask({ ...opts, subcommand: 'list' });
|
|
1588
1522
|
}
|
|
1523
|
+
}
|
|
1589
1524
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1525
|
+
export async function handleProfile(opts: ParsedArgs): Promise<void> {
|
|
1526
|
+
const projectDir = opts.file || process.cwd();
|
|
1527
|
+
const sub = opts.subcommand;
|
|
1528
|
+
|
|
1529
|
+
switch (sub) {
|
|
1530
|
+
case 'create': {
|
|
1531
|
+
const name = opts.botTask;
|
|
1532
|
+
if (!name) { console.error('Usage: weaver profile create "name" --bot <botId>'); return; }
|
|
1533
|
+
await handleWeaverTool('fw_weaver_profile_create', {
|
|
1534
|
+
name,
|
|
1535
|
+
botId: opts.botId,
|
|
1536
|
+
costStrategy: opts.costStrategy ?? 'balanced',
|
|
1537
|
+
instructions: opts.taskDescription,
|
|
1538
|
+
}, projectDir);
|
|
1539
|
+
console.log(`\x1b[32m→ Profile created\x1b[0m`);
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
case 'delete': {
|
|
1543
|
+
const id = opts.botTask;
|
|
1544
|
+
if (!id) { console.error('Usage: weaver profile delete <id>'); return; }
|
|
1545
|
+
await handleWeaverTool('fw_weaver_profile_delete', { id }, projectDir);
|
|
1546
|
+
console.log(`\x1b[32m→ Profile deleted\x1b[0m`);
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
default: {
|
|
1550
|
+
// Default: list
|
|
1551
|
+
const { result } = await handleWeaverTool('fw_weaver_profile_list', {}, projectDir);
|
|
1552
|
+
const profiles = JSON.parse(result);
|
|
1553
|
+
if (!Array.isArray(profiles) || profiles.length === 0) {
|
|
1554
|
+
console.log(' No profiles.');
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
for (const p of profiles) {
|
|
1558
|
+
const caps = (p.capabilities || []).map((c: { name: string }) => c.name).join(', ');
|
|
1559
|
+
console.log(` ${p.name} (${p.id}) [${p.preferences?.costStrategy ?? 'balanced'}] → ${p.botId} | ${caps || 'no capabilities'}`);
|
|
1560
|
+
}
|
|
1561
|
+
break;
|
|
1562
|
+
}
|
|
1612
1563
|
}
|
|
1564
|
+
}
|
|
1613
1565
|
|
|
1614
|
-
|
|
1615
|
-
if (
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
: null;
|
|
1620
|
-
if (webhookUrl) {
|
|
1621
|
-
fetch(webhookUrl, {
|
|
1622
|
-
method: 'POST',
|
|
1623
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1624
|
-
body: JSON.stringify({
|
|
1625
|
-
event: 'session.completed',
|
|
1626
|
-
timestamp: new Date().toISOString(),
|
|
1627
|
-
tasks: taskCount,
|
|
1628
|
-
completed: sessionCompleted,
|
|
1629
|
-
failed: sessionFailed,
|
|
1630
|
-
noOp: sessionNoOp,
|
|
1631
|
-
tokens: sessionInputTokens + sessionOutputTokens,
|
|
1632
|
-
cost: sessionCost,
|
|
1633
|
-
elapsed: Date.now() - sessionStartTime,
|
|
1634
|
-
}),
|
|
1635
|
-
}).catch(() => {}); // fire-and-forget
|
|
1636
|
-
}
|
|
1637
|
-
} catch { /* non-fatal */ }
|
|
1566
|
+
export async function handleReset(opts: ParsedArgs): Promise<void> {
|
|
1567
|
+
if (!opts.confirmFlag) {
|
|
1568
|
+
console.error('This will delete ALL tasks, profiles, history, and config.');
|
|
1569
|
+
console.error('Use --confirm to proceed: weaver reset --confirm');
|
|
1570
|
+
return;
|
|
1638
1571
|
}
|
|
1572
|
+
const projectDir = opts.file || process.cwd();
|
|
1573
|
+
const { result } = await handleWeaverTool('fw_weaver_reset_all', {}, projectDir);
|
|
1574
|
+
const data = JSON.parse(result);
|
|
1575
|
+
console.log(`\x1b[32m→ Reset complete\x1b[0m`);
|
|
1576
|
+
console.log(` Tasks cleared: ${data.tasksCleared ?? 0}`);
|
|
1577
|
+
console.log(` Profiles cleared: ${data.profilesCleared ?? 0}`);
|
|
1578
|
+
console.log(` Bots registered: ${data.botsRegistered ?? 0}`);
|
|
1579
|
+
console.log(` Profiles created: ${data.profilesCreated ?? 0}`);
|
|
1639
1580
|
}
|
|
1640
1581
|
|
|
1641
1582
|
export async function handleAssistant(opts: ParsedArgs): Promise<void> {
|
|
@@ -1776,148 +1717,54 @@ export async function handleAssistant(opts: ParsedArgs): Promise<void> {
|
|
|
1776
1717
|
}
|
|
1777
1718
|
|
|
1778
1719
|
export async function handleSteer(opts: ParsedArgs): Promise<void> {
|
|
1779
|
-
const
|
|
1780
|
-
const
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
if (!subcommand || !['pause', 'resume', 'cancel', 'redirect', 'queue'].includes(subcommand)) {
|
|
1784
|
-
console.error('[weaver] Usage: flow-weaver weaver steer <pause|resume|cancel|redirect|queue> [payload]');
|
|
1720
|
+
const projectDir = opts.file || process.cwd();
|
|
1721
|
+
const sub = opts.subcommand;
|
|
1722
|
+
if (!sub || !['pause', 'resume', 'cancel'].includes(sub)) {
|
|
1723
|
+
console.error('[weaver] Usage: flow-weaver weaver steer <pause|resume|cancel> [--bot <id>]');
|
|
1785
1724
|
process.exit(1);
|
|
1786
1725
|
}
|
|
1787
1726
|
|
|
1788
|
-
|
|
1789
|
-
command:
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
};
|
|
1793
|
-
|
|
1794
|
-
await controller.write(command);
|
|
1795
|
-
console.log(`[weaver] Steering command sent: ${subcommand}${opts.botFile ? ' "' + opts.botFile + '"' : ''}`);
|
|
1727
|
+
await handleWeaverTool('fw_weaver_steer', {
|
|
1728
|
+
command: sub,
|
|
1729
|
+
botId: opts.botId,
|
|
1730
|
+
}, projectDir);
|
|
1731
|
+
console.log(`\x1b[33m→ Steer: ${sub}${opts.botId ? ` (bot: ${opts.botId})` : ''}\x1b[0m`);
|
|
1796
1732
|
}
|
|
1797
1733
|
|
|
1798
|
-
export async function
|
|
1799
|
-
const
|
|
1800
|
-
const projectDir = process.env.WEAVER_PROJECT_DIR ?? process.cwd();
|
|
1801
|
-
const store = new TaskStore(projectDir);
|
|
1802
|
-
|
|
1803
|
-
const action = opts.botTask;
|
|
1804
|
-
if (!action || !['add', 'list', 'clear', 'remove', 'retry'].includes(action)) {
|
|
1805
|
-
console.error('[weaver] Usage: flow-weaver weaver queue <add|list|clear|remove|retry> [task|id]');
|
|
1806
|
-
process.exit(1);
|
|
1807
|
-
}
|
|
1734
|
+
export async function handleStatus(opts: ParsedArgs): Promise<void> {
|
|
1735
|
+
const projectDir = opts.file || process.cwd();
|
|
1808
1736
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
if (!instruction) {
|
|
1813
|
-
console.error('[weaver] Usage: flow-weaver weaver queue add "task instruction"');
|
|
1814
|
-
process.exit(1);
|
|
1815
|
-
}
|
|
1816
|
-
const task = await store.create({ title: instruction, description: instruction, priority: 0, createdBy: 'user' });
|
|
1817
|
-
console.log(`[weaver] Task added: ${task.id}`);
|
|
1818
|
-
break;
|
|
1819
|
-
}
|
|
1820
|
-
case 'list': {
|
|
1821
|
-
const tasks = await store.list();
|
|
1822
|
-
if (opts.historyJson) {
|
|
1823
|
-
console.log(JSON.stringify(tasks, null, 2));
|
|
1824
|
-
} else if (tasks.length === 0) {
|
|
1825
|
-
console.log('No tasks in queue.');
|
|
1826
|
-
} else {
|
|
1827
|
-
console.log('ID'.padEnd(10) + 'STATUS'.padEnd(14) + 'PRIORITY'.padEnd(10) + 'TITLE');
|
|
1828
|
-
for (const t of tasks) {
|
|
1829
|
-
console.log(t.id.padEnd(10) + t.status.padEnd(14) + String(t.priority).padEnd(10) + t.title.slice(0, 60));
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
break;
|
|
1833
|
-
}
|
|
1834
|
-
case 'clear': {
|
|
1835
|
-
const tasks = await store.list();
|
|
1836
|
-
let count = 0;
|
|
1837
|
-
for (const t of tasks) {
|
|
1838
|
-
await store.update(t.id, { status: 'cancelled' });
|
|
1839
|
-
count++;
|
|
1840
|
-
}
|
|
1841
|
-
console.log(`Cleared ${count} task(s).`);
|
|
1842
|
-
break;
|
|
1843
|
-
}
|
|
1844
|
-
case 'remove': {
|
|
1845
|
-
const id = opts.botFile;
|
|
1846
|
-
if (!id) {
|
|
1847
|
-
console.error('[weaver] Usage: flow-weaver weaver queue remove <id>');
|
|
1848
|
-
process.exit(1);
|
|
1849
|
-
}
|
|
1850
|
-
try {
|
|
1851
|
-
await store.update(id, { status: 'cancelled' });
|
|
1852
|
-
console.log(`Cancelled task ${id}.`);
|
|
1853
|
-
} catch {
|
|
1854
|
-
console.log(`No task found with id "${id}".`);
|
|
1855
|
-
}
|
|
1856
|
-
break;
|
|
1857
|
-
}
|
|
1858
|
-
case 'retry': {
|
|
1859
|
-
const id = opts.botFile;
|
|
1860
|
-
if (id) {
|
|
1861
|
-
try {
|
|
1862
|
-
await store.update(id, { status: 'pending' });
|
|
1863
|
-
console.log(`Task ${id} reset to pending.`);
|
|
1864
|
-
} catch {
|
|
1865
|
-
console.log(`No task found with id "${id}".`);
|
|
1866
|
-
}
|
|
1867
|
-
} else {
|
|
1868
|
-
const failed = await store.list({ status: 'failed' });
|
|
1869
|
-
let count = 0;
|
|
1870
|
-
for (const t of failed) {
|
|
1871
|
-
await store.update(t.id, { status: 'pending' });
|
|
1872
|
-
count++;
|
|
1873
|
-
}
|
|
1874
|
-
console.log(`Reset ${count} failed task(s) to pending.`);
|
|
1875
|
-
}
|
|
1876
|
-
break;
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1737
|
+
// Get swarm status
|
|
1738
|
+
const { result: swarmResult } = await handleWeaverTool('fw_weaver_swarm_status', {}, projectDir);
|
|
1739
|
+
const swarm = JSON.parse(swarmResult);
|
|
1880
1740
|
|
|
1881
|
-
|
|
1882
|
-
const
|
|
1883
|
-
const
|
|
1884
|
-
const projectDir = process.env.WEAVER_PROJECT_DIR ?? process.cwd();
|
|
1885
|
-
const taskStore = new TaskStore(projectDir);
|
|
1886
|
-
|
|
1887
|
-
const orphans = store.checkOrphans();
|
|
1888
|
-
const recentRuns = store.list({ limit: 5 });
|
|
1889
|
-
const tasks = await taskStore.list();
|
|
1890
|
-
const pending = tasks.filter(t => t.status === 'pending').length;
|
|
1891
|
-
const running = tasks.filter(t => t.status === 'in-progress').length;
|
|
1892
|
-
const completed = tasks.filter(t => t.status === 'done').length;
|
|
1893
|
-
const failed = tasks.filter(t => t.status === 'failed').length;
|
|
1741
|
+
// Get task list
|
|
1742
|
+
const { result: taskResult } = await handleWeaverTool('fw_weaver_task_list', { limit: 10 }, projectDir);
|
|
1743
|
+
const tasks = JSON.parse(taskResult);
|
|
1894
1744
|
|
|
1895
1745
|
if (opts.historyJson) {
|
|
1896
|
-
console.log(JSON.stringify({
|
|
1897
|
-
queue: { pending, running, completed, failed, total: tasks.length },
|
|
1898
|
-
orphanedRuns: orphans.length,
|
|
1899
|
-
recentRuns: recentRuns.map(r => ({
|
|
1900
|
-
id: r.id, outcome: r.outcome, summary: r.summary,
|
|
1901
|
-
startedAt: r.startedAt, durationMs: r.durationMs,
|
|
1902
|
-
})),
|
|
1903
|
-
}, null, 2));
|
|
1746
|
+
console.log(JSON.stringify({ swarm, tasks }, null, 2));
|
|
1904
1747
|
return;
|
|
1905
1748
|
}
|
|
1906
1749
|
|
|
1907
|
-
console.log(
|
|
1908
|
-
console.log(`
|
|
1909
|
-
if (
|
|
1910
|
-
|
|
1911
|
-
|
|
1750
|
+
console.log(`\n\x1b[1mWeaver Status\x1b[0m\n`);
|
|
1751
|
+
console.log(` Swarm: ${colorStatus(swarm.status ?? 'unknown')}`);
|
|
1752
|
+
if (swarm.maxConcurrent !== undefined) console.log(` Max concurrent: ${swarm.maxConcurrent}`);
|
|
1753
|
+
|
|
1754
|
+
if (Array.isArray(tasks) && tasks.length > 0) {
|
|
1755
|
+
const pending = tasks.filter((t: { status: string }) => t.status === 'pending').length;
|
|
1756
|
+
const running = tasks.filter((t: { status: string }) => t.status === 'in-progress').length;
|
|
1757
|
+
const done = tasks.filter((t: { status: string }) => t.status === 'done').length;
|
|
1758
|
+
const failed = tasks.filter((t: { status: string }) => t.status === 'failed').length;
|
|
1759
|
+
console.log(` Tasks: ${pending} pending, ${running} running, ${done} done, ${failed} failed`);
|
|
1912
1760
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
console.log(` ${icon} ${r.id.slice(0, 8)} ${r.outcome.padEnd(9)} ${formatDuration(r.durationMs).padEnd(8)} ${r.summary.slice(0, 60)}`);
|
|
1761
|
+
console.log(`\n Recent tasks:`);
|
|
1762
|
+
for (const t of tasks.slice(0, 5)) {
|
|
1763
|
+
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === 'failed' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
|
|
1764
|
+
console.log(` ${icon} ${(t.id ?? '').slice(0, 8)} [${t.status}] ${(t.title ?? '').slice(0, 60)}`);
|
|
1918
1765
|
}
|
|
1919
1766
|
} else {
|
|
1920
|
-
console.log('
|
|
1767
|
+
console.log(' Tasks: none');
|
|
1921
1768
|
}
|
|
1922
1769
|
console.log('');
|
|
1923
1770
|
}
|