@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/dist/cli-handlers.js
CHANGED
|
@@ -11,6 +11,7 @@ import { PipelineRunner } from './bot/pipeline-runner.js';
|
|
|
11
11
|
import { DashboardServer } from './bot/dashboard.js';
|
|
12
12
|
import { openBrowser } from './bot/utils.js';
|
|
13
13
|
import { AuditStore } from './bot/audit-store.js';
|
|
14
|
+
import { handleWeaverTool } from './ai-chat-provider.js';
|
|
14
15
|
export function parseArgs(argv) {
|
|
15
16
|
const result = {
|
|
16
17
|
command: 'run',
|
|
@@ -33,7 +34,6 @@ export function parseArgs(argv) {
|
|
|
33
34
|
autoApprove: false,
|
|
34
35
|
genesisInit: false,
|
|
35
36
|
genesisWatch: false,
|
|
36
|
-
sessionContinuous: false,
|
|
37
37
|
};
|
|
38
38
|
const args = argv.slice(2);
|
|
39
39
|
let i = 0;
|
|
@@ -169,14 +169,50 @@ export function parseArgs(argv) {
|
|
|
169
169
|
}
|
|
170
170
|
else if (arg === 'bot') {
|
|
171
171
|
result.command = 'bot';
|
|
172
|
-
// Next non-flag arg is the task string
|
|
172
|
+
// Next non-flag arg is the task string or subcommand
|
|
173
173
|
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
174
174
|
i++;
|
|
175
|
-
result.
|
|
175
|
+
result.subcommand = args[i];
|
|
176
|
+
// For non-registry subcommands, treat as task string
|
|
177
|
+
if (!['list', 'register', 'validate'].includes(result.subcommand)) {
|
|
178
|
+
result.botTask = result.subcommand;
|
|
179
|
+
result.subcommand = undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else if (arg === 'swarm') {
|
|
184
|
+
result.command = 'swarm';
|
|
185
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
186
|
+
i++;
|
|
187
|
+
result.subcommand = args[i];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else if (arg === 'task') {
|
|
191
|
+
result.command = 'task';
|
|
192
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
193
|
+
i++;
|
|
194
|
+
result.subcommand = args[i];
|
|
195
|
+
// For create/get/cancel/retry: next positional is the title/id
|
|
196
|
+
if (['create', 'get', 'cancel', 'retry'].includes(result.subcommand) && i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
197
|
+
i++;
|
|
198
|
+
result.botTask = args[i];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (arg === 'profile') {
|
|
203
|
+
result.command = 'profile';
|
|
204
|
+
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
205
|
+
i++;
|
|
206
|
+
result.subcommand = args[i];
|
|
207
|
+
// For create/delete: next positional is the name/id
|
|
208
|
+
if (['create', 'delete'].includes(result.subcommand) && i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
209
|
+
i++;
|
|
210
|
+
result.botTask = args[i];
|
|
211
|
+
}
|
|
176
212
|
}
|
|
177
213
|
}
|
|
178
|
-
else if (arg === '
|
|
179
|
-
result.command = '
|
|
214
|
+
else if (arg === 'reset') {
|
|
215
|
+
result.command = 'reset';
|
|
180
216
|
}
|
|
181
217
|
else if (arg === 'assistant') {
|
|
182
218
|
result.command = 'assistant';
|
|
@@ -192,28 +228,10 @@ export function parseArgs(argv) {
|
|
|
192
228
|
}
|
|
193
229
|
else if (arg === 'steer') {
|
|
194
230
|
result.command = 'steer';
|
|
195
|
-
// Next arg is the subcommand
|
|
231
|
+
// Next arg is the subcommand (pause/resume/cancel)
|
|
196
232
|
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
197
233
|
i++;
|
|
198
|
-
result.
|
|
199
|
-
// Next arg after redirect/queue is payload
|
|
200
|
-
if ((args[i] === 'redirect' || args[i] === 'queue') && i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
201
|
-
i++;
|
|
202
|
-
result.botFile = args[i];
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
else if (arg === 'queue') {
|
|
207
|
-
result.command = 'queue';
|
|
208
|
-
// Next arg is action (add/list/clear/remove)
|
|
209
|
-
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
210
|
-
i++;
|
|
211
|
-
result.botTask = args[i];
|
|
212
|
-
// Next arg is task/id
|
|
213
|
-
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
|
|
214
|
-
i++;
|
|
215
|
-
result.botFile = args[i];
|
|
216
|
-
}
|
|
234
|
+
result.subcommand = args[i];
|
|
217
235
|
}
|
|
218
236
|
}
|
|
219
237
|
else if (arg === '--file' && i + 1 < args.length) {
|
|
@@ -279,20 +297,47 @@ export function parseArgs(argv) {
|
|
|
279
297
|
i++;
|
|
280
298
|
result.assistantDelete = args[i];
|
|
281
299
|
}
|
|
282
|
-
else if (arg === '--
|
|
283
|
-
|
|
300
|
+
else if (arg === '--max-concurrent' && i + 1 < args.length) {
|
|
301
|
+
i++;
|
|
302
|
+
result.maxConcurrent = parseInt(args[i], 10) || undefined;
|
|
303
|
+
}
|
|
304
|
+
else if (arg === '--budget-tokens' && i + 1 < args.length) {
|
|
305
|
+
i++;
|
|
306
|
+
result.budgetTokens = parseInt(args[i], 10) || undefined;
|
|
307
|
+
}
|
|
308
|
+
else if (arg === '--budget-cost' && i + 1 < args.length) {
|
|
309
|
+
i++;
|
|
310
|
+
result.budgetCost = parseFloat(args[i]) || undefined;
|
|
284
311
|
}
|
|
285
|
-
else if (arg === '--
|
|
312
|
+
else if (arg === '--profile' && i + 1 < args.length) {
|
|
286
313
|
i++;
|
|
287
|
-
result.
|
|
314
|
+
result.profileId = args[i];
|
|
288
315
|
}
|
|
289
|
-
else if (arg === '--
|
|
316
|
+
else if (arg === '--cost-strategy' && i + 1 < args.length) {
|
|
290
317
|
i++;
|
|
291
|
-
result.
|
|
318
|
+
result.costStrategy = args[i];
|
|
292
319
|
}
|
|
293
|
-
else if (arg === '--
|
|
320
|
+
else if (arg === '--bot' && i + 1 < args.length) {
|
|
294
321
|
i++;
|
|
295
|
-
result.
|
|
322
|
+
result.botId = args[i];
|
|
323
|
+
}
|
|
324
|
+
else if (arg === '--confirm') {
|
|
325
|
+
result.confirmFlag = true;
|
|
326
|
+
}
|
|
327
|
+
else if (arg === '--description' && i + 1 < args.length) {
|
|
328
|
+
i++;
|
|
329
|
+
result.taskDescription = args[i];
|
|
330
|
+
}
|
|
331
|
+
else if (arg === '--complexity' && i + 1 < args.length) {
|
|
332
|
+
i++;
|
|
333
|
+
result.complexity = args[i];
|
|
334
|
+
}
|
|
335
|
+
else if (arg === '--auto-retry') {
|
|
336
|
+
result.autoRetry = true;
|
|
337
|
+
}
|
|
338
|
+
else if (arg === '--status' && i + 1 < args.length) {
|
|
339
|
+
i++;
|
|
340
|
+
result.historyOutcome = args[i];
|
|
296
341
|
}
|
|
297
342
|
else if (arg === '--project-dir' && i + 1 < args.length) {
|
|
298
343
|
i++;
|
|
@@ -1260,286 +1305,213 @@ export async function handleBot(opts) {
|
|
|
1260
1305
|
process.exit(1);
|
|
1261
1306
|
}
|
|
1262
1307
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
// Recover orphaned in-progress tasks back to pending
|
|
1295
|
-
const inProgressTasks = await recoveryStore.list({ status: 'in-progress' });
|
|
1296
|
-
let recovered = 0;
|
|
1297
|
-
for (const t of inProgressTasks) {
|
|
1298
|
-
await recoveryStore.update(t.id, { status: 'pending' });
|
|
1299
|
-
recovered++;
|
|
1300
|
-
}
|
|
1301
|
-
if (recovered > 0)
|
|
1302
|
-
renderer.info(`Recovered ${recovered} orphaned task(s)`);
|
|
1303
|
-
}
|
|
1304
|
-
// Clean stale cache files
|
|
1305
|
-
try {
|
|
1306
|
-
const { execSync: execSyncClean } = await import('node:child_process');
|
|
1307
|
-
const staleOutput = execSyncClean(`find "${projectDir}" -name "fw-exec-*" -type f`, { encoding: 'utf-8', timeout: 5000 }).trim();
|
|
1308
|
-
if (staleOutput) {
|
|
1309
|
-
const staleFiles = staleOutput.split('\n').filter(Boolean);
|
|
1310
|
-
for (const f of staleFiles) {
|
|
1311
|
-
try {
|
|
1312
|
-
fs.unlinkSync(f);
|
|
1313
|
-
}
|
|
1314
|
-
catch { }
|
|
1315
|
-
}
|
|
1308
|
+
// --- Swarm / Task / Profile / Reset handlers (thin wrappers around handleWeaverTool) ---
|
|
1309
|
+
function colorStatus(status) {
|
|
1310
|
+
switch (status) {
|
|
1311
|
+
case 'running': return '\x1b[32m' + status + '\x1b[0m';
|
|
1312
|
+
case 'paused': return '\x1b[33m' + status + '\x1b[0m';
|
|
1313
|
+
case 'stopped':
|
|
1314
|
+
case 'idle': return '\x1b[90m' + status + '\x1b[0m';
|
|
1315
|
+
default: return status;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
export async function handleSwarm(opts) {
|
|
1319
|
+
const projectDir = opts.file || process.cwd();
|
|
1320
|
+
const sub = opts.subcommand;
|
|
1321
|
+
if (!sub || sub === 'status') {
|
|
1322
|
+
const { result } = await handleWeaverTool('fw_weaver_swarm_status', {}, projectDir);
|
|
1323
|
+
const data = JSON.parse(result);
|
|
1324
|
+
console.log(`\x1b[1mSwarm: ${colorStatus(data.status)}\x1b[0m`);
|
|
1325
|
+
if (data.instances !== undefined)
|
|
1326
|
+
console.log(` Instances: ${data.instances}`);
|
|
1327
|
+
if (data.maxConcurrent !== undefined)
|
|
1328
|
+
console.log(` Max concurrent: ${data.maxConcurrent}`);
|
|
1329
|
+
if (data.taskCounts) {
|
|
1330
|
+
const tc = data.taskCounts;
|
|
1331
|
+
console.log(` Tasks: ${tc.pending ?? 0} pending, ${tc.running ?? 0} running, ${tc.done ?? 0} done, ${tc.failed ?? 0} failed`);
|
|
1332
|
+
}
|
|
1333
|
+
if (data.budget) {
|
|
1334
|
+
const b = data.budget;
|
|
1335
|
+
if (b.tokensUsed !== undefined)
|
|
1336
|
+
console.log(` Tokens: ${b.tokensUsed.toLocaleString()} / ${(b.tokensLimit ?? '∞').toLocaleString()}`);
|
|
1337
|
+
if (b.costUsed !== undefined)
|
|
1338
|
+
console.log(` Cost: $${b.costUsed.toFixed(4)} / $${(b.costLimit ?? '∞')}`);
|
|
1316
1339
|
}
|
|
1340
|
+
return;
|
|
1317
1341
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
const result = await runWorkflow(workflowPath, {
|
|
1328
|
-
params: { projectDir },
|
|
1329
|
-
verbose: opts.verbose,
|
|
1330
|
-
dryRun: opts.dryRun,
|
|
1331
|
-
config,
|
|
1332
|
-
});
|
|
1333
|
-
if (!opts.quiet) {
|
|
1334
|
-
const color = result.success ? '\x1b[32m' : '\x1b[31m';
|
|
1335
|
-
console.log(`${color}Session: ${result.outcome}\x1b[0m`);
|
|
1336
|
-
}
|
|
1337
|
-
process.exit(result.success ? 0 : 1);
|
|
1342
|
+
switch (sub) {
|
|
1343
|
+
case 'start': {
|
|
1344
|
+
await handleWeaverTool('fw_weaver_swarm_start', {
|
|
1345
|
+
maxConcurrent: opts.maxConcurrent,
|
|
1346
|
+
sessionBudgetTokens: opts.budgetTokens,
|
|
1347
|
+
sessionBudgetCost: opts.budgetCost,
|
|
1348
|
+
}, projectDir);
|
|
1349
|
+
console.log('\x1b[32m→ Swarm started\x1b[0m');
|
|
1350
|
+
break;
|
|
1338
1351
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
console.
|
|
1342
|
-
|
|
1352
|
+
case 'stop': {
|
|
1353
|
+
await handleWeaverTool('fw_weaver_swarm_stop', {}, projectDir);
|
|
1354
|
+
console.log('\x1b[33m→ Swarm stopped\x1b[0m');
|
|
1355
|
+
break;
|
|
1343
1356
|
}
|
|
1344
|
-
|
|
1357
|
+
case 'pause': {
|
|
1358
|
+
await handleWeaverTool('fw_weaver_swarm_pause', {}, projectDir);
|
|
1359
|
+
console.log('\x1b[33m→ Swarm paused\x1b[0m');
|
|
1360
|
+
break;
|
|
1361
|
+
}
|
|
1362
|
+
case 'config': {
|
|
1363
|
+
await handleWeaverTool('fw_weaver_swarm_config', {
|
|
1364
|
+
maxConcurrent: opts.maxConcurrent,
|
|
1365
|
+
workspaceBudgetTokens: opts.budgetTokens,
|
|
1366
|
+
workspaceBudgetCost: opts.budgetCost,
|
|
1367
|
+
autoRetry: opts.autoRetry,
|
|
1368
|
+
}, projectDir);
|
|
1369
|
+
console.log('\x1b[32m→ Config updated\x1b[0m');
|
|
1370
|
+
break;
|
|
1371
|
+
}
|
|
1372
|
+
default:
|
|
1373
|
+
console.error(`Unknown swarm command: ${sub}. Use: start, stop, pause, status, config`);
|
|
1345
1374
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
const
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
// Session stats
|
|
1357
|
-
let sessionCompleted = 0, sessionFailed = 0, sessionNoOp = 0;
|
|
1358
|
-
let sessionInputTokens = 0, sessionOutputTokens = 0, sessionCost = 0;
|
|
1359
|
-
process.on('SIGINT', () => { interrupted = true; });
|
|
1360
|
-
process.on('SIGTERM', () => { interrupted = true; });
|
|
1361
|
-
// Parallel task tracking
|
|
1362
|
-
const running = new Map();
|
|
1363
|
-
const filesInUse = new Set();
|
|
1364
|
-
const processTask = async (task) => {
|
|
1365
|
-
const taskPayload = { id: task.id, instruction: task.title, description: task.description, targets: task.context.files };
|
|
1366
|
-
try {
|
|
1367
|
-
const result = await runWorkflow(workflowPath, {
|
|
1368
|
-
params: { projectDir, taskJson: JSON.stringify(taskPayload) },
|
|
1369
|
-
verbose: opts.verbose,
|
|
1370
|
-
dryRun: opts.dryRun,
|
|
1371
|
-
config,
|
|
1372
|
-
});
|
|
1373
|
-
// Classify outcome: the summary contains "no changes" or "0 files" for no-ops
|
|
1374
|
-
const isNoOp = result.success && (result.summary.includes('no changes') ||
|
|
1375
|
-
result.summary.includes('0 file') ||
|
|
1376
|
-
result.summary.includes("doesn't exist") ||
|
|
1377
|
-
result.summary.includes('does not exist') ||
|
|
1378
|
-
result.summary.includes('nothing to') ||
|
|
1379
|
-
result.outcome === 'no-op');
|
|
1380
|
-
if (isNoOp) {
|
|
1381
|
-
await taskStore.update(task.id, { status: 'done' });
|
|
1382
|
-
sessionNoOp++;
|
|
1383
|
-
consecutiveNoOps++;
|
|
1384
|
-
consecutiveErrors = 0;
|
|
1385
|
-
}
|
|
1386
|
-
else if (result.success) {
|
|
1387
|
-
await taskStore.update(task.id, { status: 'done' });
|
|
1388
|
-
sessionCompleted++;
|
|
1389
|
-
consecutiveErrors = 0;
|
|
1390
|
-
consecutiveNoOps = 0;
|
|
1375
|
+
}
|
|
1376
|
+
export async function handleTask(opts) {
|
|
1377
|
+
const projectDir = opts.file || process.cwd();
|
|
1378
|
+
const sub = opts.subcommand;
|
|
1379
|
+
switch (sub) {
|
|
1380
|
+
case 'create': {
|
|
1381
|
+
const title = opts.botTask;
|
|
1382
|
+
if (!title) {
|
|
1383
|
+
console.error('Usage: weaver task create "title"');
|
|
1384
|
+
return;
|
|
1391
1385
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1386
|
+
const { result } = await handleWeaverTool('fw_weaver_task_create', {
|
|
1387
|
+
title,
|
|
1388
|
+
description: opts.taskDescription,
|
|
1389
|
+
priority: opts.historyOutcome, // reuse for priority if needed
|
|
1390
|
+
assignedProfile: opts.profileId,
|
|
1391
|
+
complexity: opts.complexity,
|
|
1392
|
+
}, projectDir);
|
|
1393
|
+
const data = JSON.parse(result);
|
|
1394
|
+
console.log(`\x1b[32m→ Task created: ${data.task?.id ?? data.id ?? 'unknown'}\x1b[0m`);
|
|
1395
|
+
break;
|
|
1396
|
+
}
|
|
1397
|
+
case 'list': {
|
|
1398
|
+
const { result } = await handleWeaverTool('fw_weaver_task_list', {
|
|
1399
|
+
status: opts.historyOutcome,
|
|
1400
|
+
limit: opts.historyLimit,
|
|
1401
|
+
}, projectDir);
|
|
1402
|
+
const tasks = JSON.parse(result);
|
|
1403
|
+
if (!Array.isArray(tasks) || tasks.length === 0) {
|
|
1404
|
+
console.log(' No tasks.');
|
|
1405
|
+
return;
|
|
1397
1406
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
sessionOutputTokens += result.cost.totalOutputTokens ?? 0;
|
|
1402
|
-
sessionCost += result.cost.totalCost ?? 0;
|
|
1407
|
+
for (const t of tasks) {
|
|
1408
|
+
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === 'failed' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
|
|
1409
|
+
console.log(` ${icon} ${(t.id ?? '').slice(0, 8)} ${t.title ?? ''} [${t.status ?? ''}] ${t.assignedProfile ? `→ ${t.assignedProfile}` : ''}`);
|
|
1403
1410
|
}
|
|
1411
|
+
break;
|
|
1404
1412
|
}
|
|
1405
|
-
|
|
1406
|
-
const
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
sessionFailed++;
|
|
1411
|
-
if (!isTransientError(err)) {
|
|
1412
|
-
consecutiveErrors++;
|
|
1413
|
+
case 'get': {
|
|
1414
|
+
const id = opts.botTask;
|
|
1415
|
+
if (!id) {
|
|
1416
|
+
console.error('Usage: weaver task get <id>');
|
|
1417
|
+
return;
|
|
1413
1418
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
for (const f of task.context.files)
|
|
1417
|
-
filesInUse.delete(f);
|
|
1418
|
-
running.delete(task.id);
|
|
1419
|
-
}
|
|
1420
|
-
};
|
|
1421
|
-
while (taskCount < maxTasks && !interrupted) {
|
|
1422
|
-
if (deadline && Date.now() >= deadline) {
|
|
1423
|
-
renderer.info('Deadline reached, stopping session.');
|
|
1419
|
+
const { result } = await handleWeaverTool('fw_weaver_task_get', { id }, projectDir);
|
|
1420
|
+
console.log(result);
|
|
1424
1421
|
break;
|
|
1425
1422
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1423
|
+
case 'clear': {
|
|
1424
|
+
const filter = opts.confirmFlag ? 'all' : 'completed';
|
|
1425
|
+
const { result } = await handleWeaverTool('fw_weaver_tasks_clear', { filter }, projectDir);
|
|
1426
|
+
const data = JSON.parse(result);
|
|
1427
|
+
console.log(`\x1b[32m→ Cleared ${data.cleared} tasks\x1b[0m`);
|
|
1428
1428
|
break;
|
|
1429
1429
|
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
}
|
|
1436
|
-
// Wait if at capacity
|
|
1437
|
-
if (running.size >= parallelism) {
|
|
1438
|
-
await Promise.race(running.values());
|
|
1439
|
-
continue;
|
|
1440
|
-
}
|
|
1441
|
-
const pendingTasks = await taskStore.list({ status: 'pending', limit: 1 });
|
|
1442
|
-
const task = pendingTasks[0] ?? null;
|
|
1443
|
-
if (!task) {
|
|
1444
|
-
if (running.size > 0) {
|
|
1445
|
-
// Tasks still running — wait for one to finish
|
|
1446
|
-
await Promise.race(running.values());
|
|
1447
|
-
continue;
|
|
1430
|
+
case 'cancel': {
|
|
1431
|
+
const id = opts.botTask;
|
|
1432
|
+
if (!id) {
|
|
1433
|
+
console.error('Usage: weaver task cancel <id>');
|
|
1434
|
+
return;
|
|
1448
1435
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1436
|
+
await handleWeaverTool('fw_weaver_task_cancel', { id }, projectDir);
|
|
1437
|
+
console.log(`\x1b[33m→ Task ${id} cancelled\x1b[0m`);
|
|
1438
|
+
break;
|
|
1452
1439
|
}
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
// Replace the broad task with per-file tasks
|
|
1459
|
-
await taskStore.update(task.id, { status: 'done' });
|
|
1460
|
-
for (const st of subtasks) {
|
|
1461
|
-
await taskStore.create({
|
|
1462
|
-
title: st.instruction,
|
|
1463
|
-
description: st.instruction,
|
|
1464
|
-
priority: st.priority ?? 0,
|
|
1465
|
-
createdBy: 'ai',
|
|
1466
|
-
});
|
|
1440
|
+
case 'retry': {
|
|
1441
|
+
const id = opts.botTask;
|
|
1442
|
+
if (!id) {
|
|
1443
|
+
console.error('Usage: weaver task retry <id>');
|
|
1444
|
+
return;
|
|
1467
1445
|
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1446
|
+
await handleWeaverTool('fw_weaver_task_retry', { id }, projectDir);
|
|
1447
|
+
console.log(`\x1b[32m→ Task ${id} retried\x1b[0m`);
|
|
1448
|
+
break;
|
|
1470
1449
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1450
|
+
default:
|
|
1451
|
+
// Default: list
|
|
1452
|
+
await handleTask({ ...opts, subcommand: 'list' });
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
export async function handleProfile(opts) {
|
|
1456
|
+
const projectDir = opts.file || process.cwd();
|
|
1457
|
+
const sub = opts.subcommand;
|
|
1458
|
+
switch (sub) {
|
|
1459
|
+
case 'create': {
|
|
1460
|
+
const name = opts.botTask;
|
|
1461
|
+
if (!name) {
|
|
1462
|
+
console.error('Usage: weaver profile create "name" --bot <botId>');
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
await handleWeaverTool('fw_weaver_profile_create', {
|
|
1466
|
+
name,
|
|
1467
|
+
botId: opts.botId,
|
|
1468
|
+
costStrategy: opts.costStrategy ?? 'balanced',
|
|
1469
|
+
instructions: opts.taskDescription,
|
|
1470
|
+
}, projectDir);
|
|
1471
|
+
console.log(`\x1b[32m→ Profile created\x1b[0m`);
|
|
1472
|
+
break;
|
|
1477
1473
|
}
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
const promise = processTask(task);
|
|
1488
|
-
running.set(task.id, promise);
|
|
1489
|
-
// In sequential mode (parallelism=1), await immediately
|
|
1490
|
-
if (parallelism <= 1) {
|
|
1491
|
-
await promise;
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
// Wait for all remaining parallel tasks
|
|
1495
|
-
if (running.size > 0) {
|
|
1496
|
-
await Promise.allSettled(running.values());
|
|
1497
|
-
}
|
|
1498
|
-
const elapsed = Date.now() - sessionStartTime;
|
|
1499
|
-
renderer.sessionEnd({
|
|
1500
|
-
tasks: taskCount,
|
|
1501
|
-
completed: sessionCompleted,
|
|
1502
|
-
failed: sessionFailed,
|
|
1503
|
-
totalInputTokens: sessionInputTokens,
|
|
1504
|
-
totalOutputTokens: sessionOutputTokens,
|
|
1505
|
-
totalCost: sessionCost,
|
|
1506
|
-
elapsed,
|
|
1507
|
-
});
|
|
1508
|
-
// Desktop notification on session end (cross-platform)
|
|
1509
|
-
if (taskCount > 0) {
|
|
1510
|
-
try {
|
|
1511
|
-
const { sendDesktopNotification } = await import('./bot/bot-manager.js');
|
|
1512
|
-
sendDesktopNotification('Weaver Session Complete', `${sessionCompleted} done, ${sessionFailed} failed, ${sessionNoOp} no-op`);
|
|
1474
|
+
case 'delete': {
|
|
1475
|
+
const id = opts.botTask;
|
|
1476
|
+
if (!id) {
|
|
1477
|
+
console.error('Usage: weaver profile delete <id>');
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
await handleWeaverTool('fw_weaver_profile_delete', { id }, projectDir);
|
|
1481
|
+
console.log(`\x1b[32m→ Profile deleted\x1b[0m`);
|
|
1482
|
+
break;
|
|
1513
1483
|
}
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
: null;
|
|
1522
|
-
if (webhookUrl) {
|
|
1523
|
-
fetch(webhookUrl, {
|
|
1524
|
-
method: 'POST',
|
|
1525
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1526
|
-
body: JSON.stringify({
|
|
1527
|
-
event: 'session.completed',
|
|
1528
|
-
timestamp: new Date().toISOString(),
|
|
1529
|
-
tasks: taskCount,
|
|
1530
|
-
completed: sessionCompleted,
|
|
1531
|
-
failed: sessionFailed,
|
|
1532
|
-
noOp: sessionNoOp,
|
|
1533
|
-
tokens: sessionInputTokens + sessionOutputTokens,
|
|
1534
|
-
cost: sessionCost,
|
|
1535
|
-
elapsed: Date.now() - sessionStartTime,
|
|
1536
|
-
}),
|
|
1537
|
-
}).catch(() => { }); // fire-and-forget
|
|
1484
|
+
default: {
|
|
1485
|
+
// Default: list
|
|
1486
|
+
const { result } = await handleWeaverTool('fw_weaver_profile_list', {}, projectDir);
|
|
1487
|
+
const profiles = JSON.parse(result);
|
|
1488
|
+
if (!Array.isArray(profiles) || profiles.length === 0) {
|
|
1489
|
+
console.log(' No profiles.');
|
|
1490
|
+
return;
|
|
1538
1491
|
}
|
|
1492
|
+
for (const p of profiles) {
|
|
1493
|
+
const caps = (p.capabilities || []).map((c) => c.name).join(', ');
|
|
1494
|
+
console.log(` ${p.name} (${p.id}) [${p.preferences?.costStrategy ?? 'balanced'}] → ${p.botId} | ${caps || 'no capabilities'}`);
|
|
1495
|
+
}
|
|
1496
|
+
break;
|
|
1539
1497
|
}
|
|
1540
|
-
catch { /* non-fatal */ }
|
|
1541
1498
|
}
|
|
1542
1499
|
}
|
|
1500
|
+
export async function handleReset(opts) {
|
|
1501
|
+
if (!opts.confirmFlag) {
|
|
1502
|
+
console.error('This will delete ALL tasks, profiles, history, and config.');
|
|
1503
|
+
console.error('Use --confirm to proceed: weaver reset --confirm');
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
const projectDir = opts.file || process.cwd();
|
|
1507
|
+
const { result } = await handleWeaverTool('fw_weaver_reset_all', {}, projectDir);
|
|
1508
|
+
const data = JSON.parse(result);
|
|
1509
|
+
console.log(`\x1b[32m→ Reset complete\x1b[0m`);
|
|
1510
|
+
console.log(` Tasks cleared: ${data.tasksCleared ?? 0}`);
|
|
1511
|
+
console.log(` Profiles cleared: ${data.profilesCleared ?? 0}`);
|
|
1512
|
+
console.log(` Bots registered: ${data.botsRegistered ?? 0}`);
|
|
1513
|
+
console.log(` Profiles created: ${data.profilesCreated ?? 0}`);
|
|
1514
|
+
}
|
|
1543
1515
|
export async function handleAssistant(opts) {
|
|
1544
1516
|
const projectDir = opts.file ?? process.cwd();
|
|
1545
1517
|
// Handle --list and --delete before creating provider
|
|
@@ -1673,143 +1645,48 @@ export async function handleAssistant(opts) {
|
|
|
1673
1645
|
});
|
|
1674
1646
|
}
|
|
1675
1647
|
export async function handleSteer(opts) {
|
|
1676
|
-
const
|
|
1677
|
-
const
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
console.error('[weaver] Usage: flow-weaver weaver steer <pause|resume|cancel|redirect|queue> [payload]');
|
|
1648
|
+
const projectDir = opts.file || process.cwd();
|
|
1649
|
+
const sub = opts.subcommand;
|
|
1650
|
+
if (!sub || !['pause', 'resume', 'cancel'].includes(sub)) {
|
|
1651
|
+
console.error('[weaver] Usage: flow-weaver weaver steer <pause|resume|cancel> [--bot <id>]');
|
|
1681
1652
|
process.exit(1);
|
|
1682
1653
|
}
|
|
1683
|
-
|
|
1684
|
-
command:
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
};
|
|
1688
|
-
await controller.write(command);
|
|
1689
|
-
console.log(`[weaver] Steering command sent: ${subcommand}${opts.botFile ? ' "' + opts.botFile + '"' : ''}`);
|
|
1690
|
-
}
|
|
1691
|
-
export async function handleQueue(opts) {
|
|
1692
|
-
const { TaskStore } = await import('./bot/task-store.js');
|
|
1693
|
-
const projectDir = process.env.WEAVER_PROJECT_DIR ?? process.cwd();
|
|
1694
|
-
const store = new TaskStore(projectDir);
|
|
1695
|
-
const action = opts.botTask;
|
|
1696
|
-
if (!action || !['add', 'list', 'clear', 'remove', 'retry'].includes(action)) {
|
|
1697
|
-
console.error('[weaver] Usage: flow-weaver weaver queue <add|list|clear|remove|retry> [task|id]');
|
|
1698
|
-
process.exit(1);
|
|
1699
|
-
}
|
|
1700
|
-
switch (action) {
|
|
1701
|
-
case 'add': {
|
|
1702
|
-
const instruction = opts.botFile;
|
|
1703
|
-
if (!instruction) {
|
|
1704
|
-
console.error('[weaver] Usage: flow-weaver weaver queue add "task instruction"');
|
|
1705
|
-
process.exit(1);
|
|
1706
|
-
}
|
|
1707
|
-
const task = await store.create({ title: instruction, description: instruction, priority: 0, createdBy: 'user' });
|
|
1708
|
-
console.log(`[weaver] Task added: ${task.id}`);
|
|
1709
|
-
break;
|
|
1710
|
-
}
|
|
1711
|
-
case 'list': {
|
|
1712
|
-
const tasks = await store.list();
|
|
1713
|
-
if (opts.historyJson) {
|
|
1714
|
-
console.log(JSON.stringify(tasks, null, 2));
|
|
1715
|
-
}
|
|
1716
|
-
else if (tasks.length === 0) {
|
|
1717
|
-
console.log('No tasks in queue.');
|
|
1718
|
-
}
|
|
1719
|
-
else {
|
|
1720
|
-
console.log('ID'.padEnd(10) + 'STATUS'.padEnd(14) + 'PRIORITY'.padEnd(10) + 'TITLE');
|
|
1721
|
-
for (const t of tasks) {
|
|
1722
|
-
console.log(t.id.padEnd(10) + t.status.padEnd(14) + String(t.priority).padEnd(10) + t.title.slice(0, 60));
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
break;
|
|
1726
|
-
}
|
|
1727
|
-
case 'clear': {
|
|
1728
|
-
const tasks = await store.list();
|
|
1729
|
-
let count = 0;
|
|
1730
|
-
for (const t of tasks) {
|
|
1731
|
-
await store.update(t.id, { status: 'cancelled' });
|
|
1732
|
-
count++;
|
|
1733
|
-
}
|
|
1734
|
-
console.log(`Cleared ${count} task(s).`);
|
|
1735
|
-
break;
|
|
1736
|
-
}
|
|
1737
|
-
case 'remove': {
|
|
1738
|
-
const id = opts.botFile;
|
|
1739
|
-
if (!id) {
|
|
1740
|
-
console.error('[weaver] Usage: flow-weaver weaver queue remove <id>');
|
|
1741
|
-
process.exit(1);
|
|
1742
|
-
}
|
|
1743
|
-
try {
|
|
1744
|
-
await store.update(id, { status: 'cancelled' });
|
|
1745
|
-
console.log(`Cancelled task ${id}.`);
|
|
1746
|
-
}
|
|
1747
|
-
catch {
|
|
1748
|
-
console.log(`No task found with id "${id}".`);
|
|
1749
|
-
}
|
|
1750
|
-
break;
|
|
1751
|
-
}
|
|
1752
|
-
case 'retry': {
|
|
1753
|
-
const id = opts.botFile;
|
|
1754
|
-
if (id) {
|
|
1755
|
-
try {
|
|
1756
|
-
await store.update(id, { status: 'pending' });
|
|
1757
|
-
console.log(`Task ${id} reset to pending.`);
|
|
1758
|
-
}
|
|
1759
|
-
catch {
|
|
1760
|
-
console.log(`No task found with id "${id}".`);
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
else {
|
|
1764
|
-
const failed = await store.list({ status: 'failed' });
|
|
1765
|
-
let count = 0;
|
|
1766
|
-
for (const t of failed) {
|
|
1767
|
-
await store.update(t.id, { status: 'pending' });
|
|
1768
|
-
count++;
|
|
1769
|
-
}
|
|
1770
|
-
console.log(`Reset ${count} failed task(s) to pending.`);
|
|
1771
|
-
}
|
|
1772
|
-
break;
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1654
|
+
await handleWeaverTool('fw_weaver_steer', {
|
|
1655
|
+
command: sub,
|
|
1656
|
+
botId: opts.botId,
|
|
1657
|
+
}, projectDir);
|
|
1658
|
+
console.log(`\x1b[33m→ Steer: ${sub}${opts.botId ? ` (bot: ${opts.botId})` : ''}\x1b[0m`);
|
|
1775
1659
|
}
|
|
1776
1660
|
export async function handleStatus(opts) {
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
const
|
|
1780
|
-
const
|
|
1781
|
-
|
|
1782
|
-
const
|
|
1783
|
-
const tasks =
|
|
1784
|
-
const pending = tasks.filter(t => t.status === 'pending').length;
|
|
1785
|
-
const running = tasks.filter(t => t.status === 'in-progress').length;
|
|
1786
|
-
const completed = tasks.filter(t => t.status === 'done').length;
|
|
1787
|
-
const failed = tasks.filter(t => t.status === 'failed').length;
|
|
1661
|
+
const projectDir = opts.file || process.cwd();
|
|
1662
|
+
// Get swarm status
|
|
1663
|
+
const { result: swarmResult } = await handleWeaverTool('fw_weaver_swarm_status', {}, projectDir);
|
|
1664
|
+
const swarm = JSON.parse(swarmResult);
|
|
1665
|
+
// Get task list
|
|
1666
|
+
const { result: taskResult } = await handleWeaverTool('fw_weaver_task_list', { limit: 10 }, projectDir);
|
|
1667
|
+
const tasks = JSON.parse(taskResult);
|
|
1788
1668
|
if (opts.historyJson) {
|
|
1789
|
-
console.log(JSON.stringify({
|
|
1790
|
-
queue: { pending, running, completed, failed, total: tasks.length },
|
|
1791
|
-
orphanedRuns: orphans.length,
|
|
1792
|
-
recentRuns: recentRuns.map(r => ({
|
|
1793
|
-
id: r.id, outcome: r.outcome, summary: r.summary,
|
|
1794
|
-
startedAt: r.startedAt, durationMs: r.durationMs,
|
|
1795
|
-
})),
|
|
1796
|
-
}, null, 2));
|
|
1669
|
+
console.log(JSON.stringify({ swarm, tasks }, null, 2));
|
|
1797
1670
|
return;
|
|
1798
1671
|
}
|
|
1799
|
-
console.log(
|
|
1800
|
-
console.log(`
|
|
1801
|
-
if (
|
|
1802
|
-
console.log(`
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1672
|
+
console.log(`\n\x1b[1mWeaver Status\x1b[0m\n`);
|
|
1673
|
+
console.log(` Swarm: ${colorStatus(swarm.status ?? 'unknown')}`);
|
|
1674
|
+
if (swarm.maxConcurrent !== undefined)
|
|
1675
|
+
console.log(` Max concurrent: ${swarm.maxConcurrent}`);
|
|
1676
|
+
if (Array.isArray(tasks) && tasks.length > 0) {
|
|
1677
|
+
const pending = tasks.filter((t) => t.status === 'pending').length;
|
|
1678
|
+
const running = tasks.filter((t) => t.status === 'in-progress').length;
|
|
1679
|
+
const done = tasks.filter((t) => t.status === 'done').length;
|
|
1680
|
+
const failed = tasks.filter((t) => t.status === 'failed').length;
|
|
1681
|
+
console.log(` Tasks: ${pending} pending, ${running} running, ${done} done, ${failed} failed`);
|
|
1682
|
+
console.log(`\n Recent tasks:`);
|
|
1683
|
+
for (const t of tasks.slice(0, 5)) {
|
|
1684
|
+
const icon = t.status === 'done' ? '\x1b[32m✓\x1b[0m' : t.status === 'failed' ? '\x1b[31m✗\x1b[0m' : t.status === 'in-progress' ? '\x1b[36m▸\x1b[0m' : '○';
|
|
1685
|
+
console.log(` ${icon} ${(t.id ?? '').slice(0, 8)} [${t.status}] ${(t.title ?? '').slice(0, 60)}`);
|
|
1809
1686
|
}
|
|
1810
1687
|
}
|
|
1811
1688
|
else {
|
|
1812
|
-
console.log('
|
|
1689
|
+
console.log(' Tasks: none');
|
|
1813
1690
|
}
|
|
1814
1691
|
console.log('');
|
|
1815
1692
|
}
|