chainlesschain 0.45.81 → 0.47.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 +10 -0
- package/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{Analytics-C1AnPdMx.js → Analytics-DgypYeUB.js} +2 -2
- package/src/assets/web-panel/assets/AppLayout-Bzf3mSZI.js +1 -0
- package/src/assets/web-panel/assets/AppLayout-DQyDwGut.css +1 -0
- package/src/assets/web-panel/assets/{Backup-D31iZX3l.js → Backup-Ba9UybpT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DiXJ3TuK.js → Chat-BwXskT21.js} +1 -1
- package/src/assets/web-panel/assets/Cowork-CXuhlHew.css +1 -0
- package/src/assets/web-panel/assets/Cowork-UmOe7qvE.js +7 -0
- package/src/assets/web-panel/assets/{Cron-DBt1ueXh.js → Cron-JHS-rc-4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-HPh9FcPt.js → Dashboard-B95cMCO7.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
- package/src/assets/web-panel/assets/{Git-hwQ1oZHj.js → Git-CSYO0_zk.js} +2 -2
- package/src/assets/web-panel/assets/{Logs-4D9p6PRM.js → Logs-Hxw_K0km.js} +2 -2
- package/src/assets/web-panel/assets/{McpTools-CyAUjbbs.js → McpTools-DIE75TrB.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-BMqOR7S-.js → Memory-C4KVnLlp.js} +2 -2
- package/src/assets/web-panel/assets/{Notes-Cmas8i4E.js → Notes-DuzrHMAk.js} +2 -2
- package/src/assets/web-panel/assets/{Organization-DnSa58Tl.js → Organization-DTq6uF82.js} +4 -4
- package/src/assets/web-panel/assets/{P2P-BxksIBWs.js → P2P-C0hjlhsR.js} +2 -2
- package/src/assets/web-panel/assets/{Permissions-Bq5Qn2s3.js → Permissions-Ec0NH-xC.js} +4 -4
- package/src/assets/web-panel/assets/{Projects-B7EM0uPg.js → Projects-U8D0asCS.js} +2 -2
- package/src/assets/web-panel/assets/{Providers-DAwgG5KV.js → Providers-BngtTLvJ.js} +2 -2
- package/src/assets/web-panel/assets/{RssFeed-HSZoRXvS.js → RssFeed-B9NbwCKM.js} +3 -3
- package/src/assets/web-panel/assets/{Security-Cz17qBny.js → Security-BL5Rkr1T.js} +3 -3
- package/src/assets/web-panel/assets/{Services-D2EsLq-v.js → Services-D4MJzLld.js} +2 -2
- package/src/assets/web-panel/assets/{Skills-C9v-f3vZ.js → Skills-CQTOMDwF.js} +1 -1
- package/src/assets/web-panel/assets/{Tasks-yMEcU0n7.js → Tasks-DepbJMnL.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-l7SvlKuB.js → Templates-C24PVZPu.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BHWhLWn9.js → Wallet-PQoSpN_P.js} +3 -3
- package/src/assets/web-panel/assets/{WebAuthn-kWhFYaUK.js → WebAuthn-BcuyQ4Lr.js} +4 -4
- package/src/assets/web-panel/assets/WorkflowEditor-C-SvXbHW.js +1 -0
- package/src/assets/web-panel/assets/WorkflowEditor-D5bX6woe.css +1 -0
- package/src/assets/web-panel/assets/{antd-D6h4fDFf.js → antd-DEjZPGMj.js} +82 -82
- package/src/assets/web-panel/assets/index-CwvzTTw_.js +2 -0
- package/src/assets/web-panel/assets/{markdown-BZsB-Dsv.js → markdown-CusdXFxb.js} +1 -1
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/cowork.js +867 -0
- package/src/gateways/ws/action-protocol.js +182 -2
- package/src/gateways/ws/message-dispatcher.js +5 -0
- package/src/gateways/ws/ws-server.js +21 -0
- package/src/lib/cowork-cron.js +474 -0
- package/src/lib/cowork-evomap-adapter.js +121 -0
- package/src/lib/cowork-learning.js +438 -0
- package/src/lib/cowork-mcp-tools.js +182 -0
- package/src/lib/cowork-observe-html.js +108 -0
- package/src/lib/cowork-observe.js +160 -0
- package/src/lib/cowork-share.js +322 -0
- package/src/lib/cowork-task-runner.js +317 -3
- package/src/lib/cowork-task-templates.js +101 -13
- package/src/lib/cowork-template-marketplace.js +205 -0
- package/src/lib/cowork-workflow.js +571 -0
- package/src/lib/provider-options.js +133 -0
- package/src/lib/skill-loader.js +65 -0
- package/src/lib/sub-agent-context.js +54 -2
- package/src/lib/sub-agent-profiles.js +164 -0
- package/src/lib/todo-manager.js +108 -0
- package/src/lib/turn-context.js +95 -0
- package/src/lib/web-fetch.js +224 -0
- package/src/lib/workflow-expr.js +318 -0
- package/src/repl/agent-repl.js +4 -0
- package/src/runtime/agent-core.js +135 -3
- package/src/runtime/coding-agent-contract-shared.cjs +131 -0
- package/src/runtime/coding-agent-policy.cjs +30 -0
- package/src/assets/web-panel/assets/AppLayout-YdvJBMHH.js +0 -1
- package/src/assets/web-panel/assets/AppLayout-cxfKLu-m.css +0 -1
- package/src/assets/web-panel/assets/Cowork-BnrHWwZw.js +0 -7
- package/src/assets/web-panel/assets/Cowork-CcSoS3eX.css +0 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
- package/src/assets/web-panel/assets/index-ByUk2Wmr.js +0 -2
package/src/commands/cowork.js
CHANGED
|
@@ -290,6 +290,861 @@ export function registerCoworkCommand(program) {
|
|
|
290
290
|
}
|
|
291
291
|
});
|
|
292
292
|
|
|
293
|
+
// cowork template — marketplace subcommands
|
|
294
|
+
const tpl = cowork
|
|
295
|
+
.command("template")
|
|
296
|
+
.description(
|
|
297
|
+
"Cowork template marketplace (search/install/publish via EvoMap)",
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
tpl
|
|
301
|
+
.command("search [query]")
|
|
302
|
+
.description("Search for Cowork templates on the EvoMap hub")
|
|
303
|
+
.option("--hub <url>", "EvoMap hub URL")
|
|
304
|
+
.option("--limit <n>", "Max results", "20")
|
|
305
|
+
.option("--json", "Output as JSON")
|
|
306
|
+
.action(async (query, options) => {
|
|
307
|
+
const { searchTemplatesInHub } =
|
|
308
|
+
await import("../lib/cowork-evomap-adapter.js");
|
|
309
|
+
try {
|
|
310
|
+
const results = await searchTemplatesInHub(query || "", {
|
|
311
|
+
hubUrl: options.hub,
|
|
312
|
+
limit: parseInt(options.limit, 10) || 20,
|
|
313
|
+
});
|
|
314
|
+
if (options.json) {
|
|
315
|
+
console.log(JSON.stringify(results, null, 2));
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (results.length === 0) {
|
|
319
|
+
logger.info("No templates found.");
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
logger.log(chalk.bold(`\n${results.length} template(s) found:\n`));
|
|
323
|
+
for (const g of results) {
|
|
324
|
+
logger.log(
|
|
325
|
+
` ${chalk.cyan(g.id)} ${chalk.gray("v" + (g.version || "?"))} by ${g.author || "unknown"}`,
|
|
326
|
+
);
|
|
327
|
+
if (g.description) {
|
|
328
|
+
logger.log(chalk.gray(` ${g.description.slice(0, 100)}`));
|
|
329
|
+
}
|
|
330
|
+
logger.log(
|
|
331
|
+
chalk.gray(
|
|
332
|
+
` downloads: ${g.downloads || 0} rating: ${g.rating || "N/A"}`,
|
|
333
|
+
),
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
logger.log("");
|
|
337
|
+
} catch (err) {
|
|
338
|
+
logger.error(`Search failed: ${err.message}`);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
tpl
|
|
344
|
+
.command("install <geneId>")
|
|
345
|
+
.description("Install a Cowork template from the EvoMap hub")
|
|
346
|
+
.option("--hub <url>", "EvoMap hub URL")
|
|
347
|
+
.option("--require-signed", "Reject genes without an Ed25519 signature")
|
|
348
|
+
.option(
|
|
349
|
+
"--trust <did>",
|
|
350
|
+
"Only accept genes signed by this DID (repeatable)",
|
|
351
|
+
(value, prev) => (prev ? [...prev, value] : [value]),
|
|
352
|
+
)
|
|
353
|
+
.action(async (geneId, options) => {
|
|
354
|
+
const { installTemplateFromHub } =
|
|
355
|
+
await import("../lib/cowork-evomap-adapter.js");
|
|
356
|
+
try {
|
|
357
|
+
const template = await installTemplateFromHub(process.cwd(), geneId, {
|
|
358
|
+
hubUrl: options.hub,
|
|
359
|
+
requireSigned: !!options.requireSigned,
|
|
360
|
+
trustedDids: options.trust || null,
|
|
361
|
+
});
|
|
362
|
+
logger.log(
|
|
363
|
+
chalk.green(
|
|
364
|
+
`✓ Installed template '${template.id}' (${template.name})`,
|
|
365
|
+
),
|
|
366
|
+
);
|
|
367
|
+
} catch (err) {
|
|
368
|
+
logger.error(`Install failed: ${err.message}`);
|
|
369
|
+
process.exit(1);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
tpl
|
|
374
|
+
.command("list")
|
|
375
|
+
.description("List locally installed Cowork templates")
|
|
376
|
+
.option("--json", "Output as JSON")
|
|
377
|
+
.action(async (options) => {
|
|
378
|
+
const { listUserTemplates } =
|
|
379
|
+
await import("../lib/cowork-template-marketplace.js");
|
|
380
|
+
const templates = listUserTemplates(process.cwd());
|
|
381
|
+
if (options.json) {
|
|
382
|
+
console.log(JSON.stringify(templates, null, 2));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (templates.length === 0) {
|
|
386
|
+
logger.info("No user templates installed.");
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
logger.log(chalk.bold(`\n${templates.length} installed template(s):\n`));
|
|
390
|
+
for (const t of templates) {
|
|
391
|
+
logger.log(
|
|
392
|
+
` ${chalk.cyan(t.id)} — ${t.name} ${chalk.gray("[" + t.category + "]")}`,
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
logger.log("");
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
tpl
|
|
399
|
+
.command("remove <id>")
|
|
400
|
+
.description("Remove an installed Cowork template")
|
|
401
|
+
.action(async (id) => {
|
|
402
|
+
const { removeUserTemplate } =
|
|
403
|
+
await import("../lib/cowork-template-marketplace.js");
|
|
404
|
+
if (removeUserTemplate(process.cwd(), id)) {
|
|
405
|
+
logger.log(chalk.green(`✓ Removed '${id}'`));
|
|
406
|
+
} else {
|
|
407
|
+
logger.error(`Template not installed: ${id}`);
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
tpl
|
|
413
|
+
.command("publish <templateId>")
|
|
414
|
+
.description("Publish a built-in or installed Cowork template to EvoMap")
|
|
415
|
+
.option("--hub <url>", "EvoMap hub URL")
|
|
416
|
+
.option("--api-key <key>", "EvoMap API key (or set EVOMAP_API_KEY)")
|
|
417
|
+
.option(
|
|
418
|
+
"--sign <did>",
|
|
419
|
+
"Sign the gene with this DID from the local identity store",
|
|
420
|
+
)
|
|
421
|
+
.action(async (templateId, options) => {
|
|
422
|
+
const [{ publishTemplateToHub }, { getTemplate }] = await Promise.all([
|
|
423
|
+
import("../lib/cowork-evomap-adapter.js"),
|
|
424
|
+
import("../lib/cowork-task-templates.js"),
|
|
425
|
+
]);
|
|
426
|
+
|
|
427
|
+
const template = getTemplate(templateId);
|
|
428
|
+
if (template.id === "free" && templateId !== "free") {
|
|
429
|
+
logger.error(`Unknown template: ${templateId}`);
|
|
430
|
+
process.exit(1);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
let signer;
|
|
434
|
+
if (options.sign) {
|
|
435
|
+
signer = await _resolveSigner(options.sign);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
const result = await publishTemplateToHub(template, {
|
|
440
|
+
hubUrl: options.hub,
|
|
441
|
+
apiKey: options.apiKey,
|
|
442
|
+
signer,
|
|
443
|
+
});
|
|
444
|
+
logger.log(
|
|
445
|
+
chalk.green(`✓ Published ${result?.id || template.id}`) +
|
|
446
|
+
(signer ? chalk.gray(` (signed by ${signer.did})`) : ""),
|
|
447
|
+
);
|
|
448
|
+
} catch (err) {
|
|
449
|
+
logger.error(`Publish failed: ${err.message}`);
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// cowork cron — schedule daily tasks
|
|
455
|
+
const cron = cowork
|
|
456
|
+
.command("cron")
|
|
457
|
+
.description("Schedule Cowork tasks on a cron expression");
|
|
458
|
+
|
|
459
|
+
cron
|
|
460
|
+
.command("list")
|
|
461
|
+
.description("List all scheduled Cowork tasks")
|
|
462
|
+
.option("--json", "Output as JSON")
|
|
463
|
+
.action(async (options) => {
|
|
464
|
+
const { loadSchedules } = await import("../lib/cowork-cron.js");
|
|
465
|
+
const schedules = loadSchedules(process.cwd());
|
|
466
|
+
if (options.json) {
|
|
467
|
+
console.log(JSON.stringify(schedules, null, 2));
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (schedules.length === 0) {
|
|
471
|
+
logger.log(chalk.gray("No scheduled tasks."));
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
logger.log(chalk.bold(`\n${schedules.length} scheduled task(s):\n`));
|
|
475
|
+
for (const s of schedules) {
|
|
476
|
+
const flag = s.enabled ? chalk.green("✓") : chalk.gray("✗");
|
|
477
|
+
logger.log(
|
|
478
|
+
` ${flag} ${chalk.cyan(s.id)} ${chalk.yellow(s.cron)} [${s.templateId || "free"}]`,
|
|
479
|
+
);
|
|
480
|
+
logger.log(chalk.gray(` ${s.userMessage.slice(0, 80)}`));
|
|
481
|
+
if (s.lastRunAt) {
|
|
482
|
+
logger.log(
|
|
483
|
+
chalk.gray(` last run: ${s.lastRunAt} (${s.lastStatus})`),
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
logger.log("");
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
cron
|
|
491
|
+
.command("add")
|
|
492
|
+
.description("Add a scheduled Cowork task")
|
|
493
|
+
.requiredOption(
|
|
494
|
+
"--cron <expr>",
|
|
495
|
+
"5-field cron expression (e.g. '0 9 * * 1-5')",
|
|
496
|
+
)
|
|
497
|
+
.requiredOption("--message <text>", "Task prompt / user message")
|
|
498
|
+
.option(
|
|
499
|
+
"--template <id>",
|
|
500
|
+
"Template id (e.g. doc-convert); omit for free mode",
|
|
501
|
+
)
|
|
502
|
+
.option("--files <list>", "Comma-separated absolute file paths", "")
|
|
503
|
+
.action(async (options) => {
|
|
504
|
+
const { addSchedule } = await import("../lib/cowork-cron.js");
|
|
505
|
+
try {
|
|
506
|
+
const entry = addSchedule(process.cwd(), {
|
|
507
|
+
cron: options.cron,
|
|
508
|
+
templateId: options.template || null,
|
|
509
|
+
userMessage: options.message,
|
|
510
|
+
files: options.files
|
|
511
|
+
? options.files
|
|
512
|
+
.split(",")
|
|
513
|
+
.map((f) => f.trim())
|
|
514
|
+
.filter(Boolean)
|
|
515
|
+
: [],
|
|
516
|
+
});
|
|
517
|
+
logger.log(chalk.green(`✓ Added schedule ${entry.id}`));
|
|
518
|
+
logger.log(chalk.gray(` cron: ${entry.cron}`));
|
|
519
|
+
logger.log(chalk.gray(` template: ${entry.templateId || "free"}`));
|
|
520
|
+
} catch (err) {
|
|
521
|
+
logger.error(err.message);
|
|
522
|
+
process.exit(1);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
cron
|
|
527
|
+
.command("remove <id>")
|
|
528
|
+
.description("Remove a scheduled task by id")
|
|
529
|
+
.action(async (id) => {
|
|
530
|
+
const { removeSchedule } = await import("../lib/cowork-cron.js");
|
|
531
|
+
if (removeSchedule(process.cwd(), id)) {
|
|
532
|
+
logger.log(chalk.green(`✓ Removed ${id}`));
|
|
533
|
+
} else {
|
|
534
|
+
logger.error(`Schedule not found: ${id}`);
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
cron
|
|
540
|
+
.command("enable <id>")
|
|
541
|
+
.description("Enable a scheduled task")
|
|
542
|
+
.action(async (id) => {
|
|
543
|
+
const { setScheduleEnabled } = await import("../lib/cowork-cron.js");
|
|
544
|
+
if (setScheduleEnabled(process.cwd(), id, true)) {
|
|
545
|
+
logger.log(chalk.green(`✓ Enabled ${id}`));
|
|
546
|
+
} else {
|
|
547
|
+
logger.error(`Schedule not found: ${id}`);
|
|
548
|
+
process.exit(1);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
cron
|
|
553
|
+
.command("disable <id>")
|
|
554
|
+
.description("Disable a scheduled task (keeps the record)")
|
|
555
|
+
.action(async (id) => {
|
|
556
|
+
const { setScheduleEnabled } = await import("../lib/cowork-cron.js");
|
|
557
|
+
if (setScheduleEnabled(process.cwd(), id, false)) {
|
|
558
|
+
logger.log(chalk.yellow(`✓ Disabled ${id}`));
|
|
559
|
+
} else {
|
|
560
|
+
logger.error(`Schedule not found: ${id}`);
|
|
561
|
+
process.exit(1);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
cron
|
|
566
|
+
.command("run")
|
|
567
|
+
.description("Start the cron scheduler in the foreground (Ctrl-C to stop)")
|
|
568
|
+
.option("--interval <ms>", "Tick interval in ms (default 60000)", "60000")
|
|
569
|
+
.action(async (options) => {
|
|
570
|
+
const [{ CoworkCronScheduler, _deps: cronDeps }, { runCoworkTask }] =
|
|
571
|
+
await Promise.all([
|
|
572
|
+
import("../lib/cowork-cron.js"),
|
|
573
|
+
import("../lib/cowork-task-runner.js"),
|
|
574
|
+
]);
|
|
575
|
+
// Inject the runner so the scheduler doesn't require a circular import
|
|
576
|
+
cronDeps.runTask = runCoworkTask;
|
|
577
|
+
|
|
578
|
+
const scheduler = new CoworkCronScheduler({
|
|
579
|
+
cwd: process.cwd(),
|
|
580
|
+
intervalMs: parseInt(options.interval, 10) || 60_000,
|
|
581
|
+
onEvent: (e) => {
|
|
582
|
+
const ts = new Date().toISOString();
|
|
583
|
+
logger.log(chalk.gray(`[${ts}] ${JSON.stringify(e)}`));
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
scheduler.start();
|
|
587
|
+
logger.log(
|
|
588
|
+
chalk.green("Cowork cron scheduler running. Press Ctrl-C to stop."),
|
|
589
|
+
);
|
|
590
|
+
process.on("SIGINT", () => {
|
|
591
|
+
scheduler.stop();
|
|
592
|
+
process.exit(0);
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// cowork share — export/import signed packets for P2P transfer
|
|
597
|
+
const share = cowork
|
|
598
|
+
.command("share")
|
|
599
|
+
.description(
|
|
600
|
+
"Export/import signed Cowork packets (templates or task results)",
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
async function _resolveSigner(didQuery) {
|
|
604
|
+
if (!didQuery) return null;
|
|
605
|
+
const { getConnection } = await import("../lib/db-connection.js");
|
|
606
|
+
const { getIdentity } = await import("../lib/did-manager.js");
|
|
607
|
+
const db = await getConnection();
|
|
608
|
+
const identity = getIdentity(db, didQuery);
|
|
609
|
+
if (!identity) {
|
|
610
|
+
throw new Error(`DID identity not found: ${didQuery}`);
|
|
611
|
+
}
|
|
612
|
+
return {
|
|
613
|
+
did: identity.did,
|
|
614
|
+
publicKey: identity.public_key,
|
|
615
|
+
privateKey: identity.secret_key,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
share
|
|
620
|
+
.command("export-template <id>")
|
|
621
|
+
.description("Export an installed template as a signed packet")
|
|
622
|
+
.requiredOption("--out <file>", "Output packet file (.json)")
|
|
623
|
+
.option("--author <name>", "Author name", "anonymous")
|
|
624
|
+
.option("--sign <did>", "Sign packet with a local DID identity")
|
|
625
|
+
.action(async (id, options) => {
|
|
626
|
+
const [{ listUserTemplates }, { exportTemplatePacket, writePacket }] =
|
|
627
|
+
await Promise.all([
|
|
628
|
+
import("../lib/cowork-template-marketplace.js"),
|
|
629
|
+
import("../lib/cowork-share.js"),
|
|
630
|
+
]);
|
|
631
|
+
const templates = listUserTemplates(process.cwd());
|
|
632
|
+
const tpl = templates.find((t) => t.id === id);
|
|
633
|
+
if (!tpl) {
|
|
634
|
+
logger.error(`Template not installed: ${id}`);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
let signer = null;
|
|
638
|
+
try {
|
|
639
|
+
signer = await _resolveSigner(options.sign);
|
|
640
|
+
} catch (err) {
|
|
641
|
+
logger.error(err.message);
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
const packet = exportTemplatePacket(tpl, {
|
|
645
|
+
author: options.author,
|
|
646
|
+
signer,
|
|
647
|
+
});
|
|
648
|
+
writePacket(options.out, packet);
|
|
649
|
+
logger.log(chalk.green(`✓ Wrote template packet to ${options.out}`));
|
|
650
|
+
if (signer) logger.log(chalk.gray(` signed by: ${signer.did}`));
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
share
|
|
654
|
+
.command("export-result <taskId>")
|
|
655
|
+
.description("Export a historical Cowork task result as a signed packet")
|
|
656
|
+
.requiredOption("--out <file>", "Output packet file (.json)")
|
|
657
|
+
.option("--author <name>", "Author name", "anonymous")
|
|
658
|
+
.option("--sign <did>", "Sign packet with a local DID identity")
|
|
659
|
+
.action(async (taskId, options) => {
|
|
660
|
+
const { findHistoryRecord, exportResultPacket, writePacket } =
|
|
661
|
+
await import("../lib/cowork-share.js");
|
|
662
|
+
const rec = findHistoryRecord(process.cwd(), taskId);
|
|
663
|
+
if (!rec) {
|
|
664
|
+
logger.error(`Task not found in history: ${taskId}`);
|
|
665
|
+
process.exit(1);
|
|
666
|
+
}
|
|
667
|
+
let signer = null;
|
|
668
|
+
try {
|
|
669
|
+
signer = await _resolveSigner(options.sign);
|
|
670
|
+
} catch (err) {
|
|
671
|
+
logger.error(err.message);
|
|
672
|
+
process.exit(1);
|
|
673
|
+
}
|
|
674
|
+
const packet = exportResultPacket(rec, {
|
|
675
|
+
author: options.author,
|
|
676
|
+
signer,
|
|
677
|
+
});
|
|
678
|
+
writePacket(options.out, packet);
|
|
679
|
+
logger.log(chalk.green(`✓ Wrote result packet to ${options.out}`));
|
|
680
|
+
if (signer) logger.log(chalk.gray(` signed by: ${signer.did}`));
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
share
|
|
684
|
+
.command("import <file>")
|
|
685
|
+
.description(
|
|
686
|
+
"Import a signed packet (auto-detects template vs result by kind)",
|
|
687
|
+
)
|
|
688
|
+
.option("--require-signed", "Reject unsigned packets")
|
|
689
|
+
.option(
|
|
690
|
+
"--trust <did>",
|
|
691
|
+
"Accept only packets signed by one of these DIDs (repeatable)",
|
|
692
|
+
(value, prev) => (prev ? [...prev, value] : [value]),
|
|
693
|
+
)
|
|
694
|
+
.action(async (file, options) => {
|
|
695
|
+
const { readPacket, importTemplatePacket, importResultPacket } =
|
|
696
|
+
await import("../lib/cowork-share.js");
|
|
697
|
+
try {
|
|
698
|
+
const packet = readPacket(file, {
|
|
699
|
+
requireSigned: !!options.requireSigned,
|
|
700
|
+
trustedDids: options.trust || null,
|
|
701
|
+
});
|
|
702
|
+
if (packet.kind === "template") {
|
|
703
|
+
const tpl = importTemplatePacket(process.cwd(), packet);
|
|
704
|
+
logger.log(chalk.green(`✓ Imported template '${tpl.id}'`));
|
|
705
|
+
} else if (packet.kind === "result") {
|
|
706
|
+
const { file: outPath, taskId } = importResultPacket(
|
|
707
|
+
process.cwd(),
|
|
708
|
+
packet,
|
|
709
|
+
);
|
|
710
|
+
logger.log(chalk.green(`✓ Imported result ${taskId} → ${outPath}`));
|
|
711
|
+
} else {
|
|
712
|
+
logger.error(`Unknown packet kind: ${packet.kind}`);
|
|
713
|
+
process.exit(1);
|
|
714
|
+
}
|
|
715
|
+
} catch (err) {
|
|
716
|
+
logger.error(err.message);
|
|
717
|
+
process.exit(1);
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
share
|
|
722
|
+
.command("verify <file>")
|
|
723
|
+
.description("Verify a packet's checksum without importing")
|
|
724
|
+
.action(async (file) => {
|
|
725
|
+
const { readPacket } = await import("../lib/cowork-share.js");
|
|
726
|
+
try {
|
|
727
|
+
const pkt = readPacket(file);
|
|
728
|
+
logger.log(chalk.green(`✓ Valid ${pkt.kind} packet`));
|
|
729
|
+
logger.log(chalk.gray(` author: ${pkt.meta.author}`));
|
|
730
|
+
logger.log(chalk.gray(` createdAt: ${pkt.meta.createdAt}`));
|
|
731
|
+
logger.log(chalk.gray(` checksum: ${pkt.checksum.slice(0, 16)}…`));
|
|
732
|
+
if (pkt.signature) {
|
|
733
|
+
logger.log(chalk.gray(` signed by: ${pkt.signature.did}`));
|
|
734
|
+
} else {
|
|
735
|
+
logger.log(chalk.gray(` signed: no`));
|
|
736
|
+
}
|
|
737
|
+
} catch (err) {
|
|
738
|
+
logger.error(err.message);
|
|
739
|
+
process.exit(1);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
// cowork workflow — chain multiple Cowork tasks into a DAG
|
|
744
|
+
const workflow = cowork
|
|
745
|
+
.command("workflow")
|
|
746
|
+
.description("Define and execute multi-step Cowork workflows (DAG)");
|
|
747
|
+
|
|
748
|
+
workflow
|
|
749
|
+
.command("list")
|
|
750
|
+
.description("List saved workflows")
|
|
751
|
+
.option("--json", "Output as JSON")
|
|
752
|
+
.action(async (options) => {
|
|
753
|
+
const { listWorkflows } = await import("../lib/cowork-workflow.js");
|
|
754
|
+
const wfs = listWorkflows(process.cwd());
|
|
755
|
+
if (options.json) {
|
|
756
|
+
console.log(JSON.stringify(wfs, null, 2));
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
if (wfs.length === 0) {
|
|
760
|
+
logger.log(chalk.gray("No workflows saved."));
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
logger.log(chalk.bold(`\n${wfs.length} workflow(s):\n`));
|
|
764
|
+
for (const wf of wfs) {
|
|
765
|
+
logger.log(
|
|
766
|
+
` ${chalk.cyan(wf.id)} ${wf.name} (${wf.steps.length} steps)`,
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
logger.log("");
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
workflow
|
|
773
|
+
.command("show <id>")
|
|
774
|
+
.description("Show a workflow definition")
|
|
775
|
+
.action(async (id) => {
|
|
776
|
+
const { getWorkflow } = await import("../lib/cowork-workflow.js");
|
|
777
|
+
const wf = getWorkflow(process.cwd(), id);
|
|
778
|
+
if (!wf) {
|
|
779
|
+
logger.error(`Workflow not found: ${id}`);
|
|
780
|
+
process.exit(1);
|
|
781
|
+
}
|
|
782
|
+
console.log(JSON.stringify(wf, null, 2));
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
workflow
|
|
786
|
+
.command("add <file>")
|
|
787
|
+
.description("Add a workflow from a JSON definition file")
|
|
788
|
+
.action(async (file) => {
|
|
789
|
+
const fs = await import("node:fs");
|
|
790
|
+
const { saveWorkflow } = await import("../lib/cowork-workflow.js");
|
|
791
|
+
try {
|
|
792
|
+
const body = fs.readFileSync(file, "utf-8");
|
|
793
|
+
const wf = JSON.parse(body);
|
|
794
|
+
saveWorkflow(process.cwd(), wf);
|
|
795
|
+
logger.log(chalk.green(`✓ Saved workflow '${wf.id}'`));
|
|
796
|
+
} catch (err) {
|
|
797
|
+
logger.error(err.message);
|
|
798
|
+
process.exit(1);
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
workflow
|
|
803
|
+
.command("remove <id>")
|
|
804
|
+
.description("Remove a saved workflow")
|
|
805
|
+
.action(async (id) => {
|
|
806
|
+
const { removeWorkflow } = await import("../lib/cowork-workflow.js");
|
|
807
|
+
if (removeWorkflow(process.cwd(), id)) {
|
|
808
|
+
logger.log(chalk.green(`✓ Removed '${id}'`));
|
|
809
|
+
} else {
|
|
810
|
+
logger.error(`Workflow not found: ${id}`);
|
|
811
|
+
process.exit(1);
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
workflow
|
|
816
|
+
.command("run <id>")
|
|
817
|
+
.description("Execute a saved workflow end-to-end")
|
|
818
|
+
.option("--continue-on-error", "Keep running after a step fails", false)
|
|
819
|
+
.option("--max-parallel <n>", "Max parallel steps per batch", "4")
|
|
820
|
+
.action(async (id, options) => {
|
|
821
|
+
const [
|
|
822
|
+
{ getWorkflow, executeWorkflow, _deps: wfDeps },
|
|
823
|
+
{ runCoworkTask },
|
|
824
|
+
] = await Promise.all([
|
|
825
|
+
import("../lib/cowork-workflow.js"),
|
|
826
|
+
import("../lib/cowork-task-runner.js"),
|
|
827
|
+
]);
|
|
828
|
+
const wf = getWorkflow(process.cwd(), id);
|
|
829
|
+
if (!wf) {
|
|
830
|
+
logger.error(`Workflow not found: ${id}`);
|
|
831
|
+
process.exit(1);
|
|
832
|
+
}
|
|
833
|
+
wfDeps.runTask = runCoworkTask;
|
|
834
|
+
|
|
835
|
+
logger.log(
|
|
836
|
+
chalk.bold(
|
|
837
|
+
`\nExecuting workflow '${wf.name}' (${wf.steps.length} steps)...`,
|
|
838
|
+
),
|
|
839
|
+
);
|
|
840
|
+
try {
|
|
841
|
+
const result = await executeWorkflow({
|
|
842
|
+
workflow: wf,
|
|
843
|
+
cwd: process.cwd(),
|
|
844
|
+
maxParallel: parseInt(options.maxParallel, 10) || 4,
|
|
845
|
+
continueOnError: !!options.continueOnError,
|
|
846
|
+
onStepStart: ({ stepId }) =>
|
|
847
|
+
logger.log(chalk.gray(` → step ${stepId} ...`)),
|
|
848
|
+
onStepComplete: (out) => {
|
|
849
|
+
const flag =
|
|
850
|
+
out.status === "completed"
|
|
851
|
+
? chalk.green("✓")
|
|
852
|
+
: out.status === "skipped"
|
|
853
|
+
? chalk.gray("—")
|
|
854
|
+
: chalk.red("✗");
|
|
855
|
+
logger.log(` ${flag} ${out.id} (${out.status})`);
|
|
856
|
+
},
|
|
857
|
+
});
|
|
858
|
+
const color =
|
|
859
|
+
result.status === "completed"
|
|
860
|
+
? chalk.green
|
|
861
|
+
: result.status === "partial"
|
|
862
|
+
? chalk.yellow
|
|
863
|
+
: chalk.red;
|
|
864
|
+
logger.log(color(`\nWorkflow ${result.status}.\n`));
|
|
865
|
+
} catch (err) {
|
|
866
|
+
logger.error(err.message);
|
|
867
|
+
process.exit(1);
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
// cowork observe — unified dashboard over tasks/workflows/schedules
|
|
872
|
+
const observe = cowork
|
|
873
|
+
.command("observe")
|
|
874
|
+
.description(
|
|
875
|
+
"Aggregate view over Cowork history, workflows, and schedules",
|
|
876
|
+
);
|
|
877
|
+
|
|
878
|
+
observe
|
|
879
|
+
.command("report", { isDefault: true })
|
|
880
|
+
.description("Print the aggregate report (default)")
|
|
881
|
+
.option("--days <n>", "Window size in days", "7")
|
|
882
|
+
.option("--json", "Output as JSON")
|
|
883
|
+
.action(async (options) => {
|
|
884
|
+
const { aggregate } = await import("../lib/cowork-observe.js");
|
|
885
|
+
const windowDays = parseInt(options.days, 10) || 7;
|
|
886
|
+
const data = aggregate(process.cwd(), { windowDays });
|
|
887
|
+
if (options.json) {
|
|
888
|
+
console.log(JSON.stringify(data, null, 2));
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
const pct = (x) => `${Math.round((x || 0) * 100)}%`;
|
|
892
|
+
logger.log(chalk.bold(`Cowork Observe — last ${data.window.days}d`));
|
|
893
|
+
logger.log(chalk.gray(` ${data.window.from} → ${data.window.to}`));
|
|
894
|
+
logger.log("");
|
|
895
|
+
logger.log(chalk.cyan("Tasks"));
|
|
896
|
+
logger.log(` total: ${data.tasks.total}`);
|
|
897
|
+
logger.log(` completed: ${data.tasks.completed}`);
|
|
898
|
+
logger.log(` failed: ${data.tasks.failed}`);
|
|
899
|
+
logger.log(` success rate: ${pct(data.tasks.successRate)}`);
|
|
900
|
+
logger.log(` avg tokens: ${data.tasks.avgTokens}`);
|
|
901
|
+
logger.log("");
|
|
902
|
+
logger.log(chalk.cyan(`Templates (${data.templates.length})`));
|
|
903
|
+
for (const t of data.templates.slice(0, 5)) {
|
|
904
|
+
logger.log(
|
|
905
|
+
` ${t.templateName || t.templateId} runs=${t.runs} success=${pct(t.successRate)}`,
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
logger.log("");
|
|
909
|
+
logger.log(chalk.cyan("Schedules"));
|
|
910
|
+
logger.log(` active: ${data.schedules.active}`);
|
|
911
|
+
for (const n of data.schedules.nextTriggers) {
|
|
912
|
+
logger.log(
|
|
913
|
+
chalk.gray(` next: ${n.at} ${n.cron} (${n.scheduleId || "-"})`),
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
if (data.failures.length > 0) {
|
|
917
|
+
logger.log("");
|
|
918
|
+
logger.log(chalk.yellow(`Failures (${data.failures.length})`));
|
|
919
|
+
for (const f of data.failures.slice(0, 3)) {
|
|
920
|
+
const top = f.commonSummaries?.[0]?.summary || "—";
|
|
921
|
+
logger.log(
|
|
922
|
+
` ${f.templateName || f.templateId} ×${f.failureCount} ${top.slice(0, 60)}`,
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
observe
|
|
929
|
+
.command("serve")
|
|
930
|
+
.description("Start a read-only HTTP dashboard")
|
|
931
|
+
.option("--port <n>", "HTTP port (0 for random)", "18820")
|
|
932
|
+
.option("--host <addr>", "Bind address", "127.0.0.1")
|
|
933
|
+
.option("--days <n>", "Window size in days", "7")
|
|
934
|
+
.action(async (options) => {
|
|
935
|
+
const http = await import("node:http");
|
|
936
|
+
const { aggregate } = await import("../lib/cowork-observe.js");
|
|
937
|
+
const { buildHtml } = await import("../lib/cowork-observe-html.js");
|
|
938
|
+
const windowDays = parseInt(options.days, 10) || 7;
|
|
939
|
+
|
|
940
|
+
const server = http.createServer((req, res) => {
|
|
941
|
+
const urlPath = (req.url || "/").split("?")[0];
|
|
942
|
+
try {
|
|
943
|
+
if (urlPath === "/" || urlPath === "/index.html") {
|
|
944
|
+
const data = aggregate(process.cwd(), { windowDays });
|
|
945
|
+
const html = buildHtml(data);
|
|
946
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
947
|
+
res.end(html);
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (urlPath === "/api/observe") {
|
|
951
|
+
const data = aggregate(process.cwd(), { windowDays });
|
|
952
|
+
res.writeHead(200, {
|
|
953
|
+
"content-type": "application/json; charset=utf-8",
|
|
954
|
+
});
|
|
955
|
+
res.end(JSON.stringify(data));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
959
|
+
res.end("Not found");
|
|
960
|
+
} catch (err) {
|
|
961
|
+
res.writeHead(500, { "content-type": "text/plain; charset=utf-8" });
|
|
962
|
+
res.end(`Error: ${err.message}`);
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
const port = parseInt(options.port, 10);
|
|
967
|
+
server.listen(Number.isFinite(port) ? port : 18820, options.host, () => {
|
|
968
|
+
const addr = server.address();
|
|
969
|
+
const actualPort = typeof addr === "object" && addr ? addr.port : port;
|
|
970
|
+
logger.log(
|
|
971
|
+
chalk.green(
|
|
972
|
+
`✓ Cowork Observe dashboard at http://${options.host}:${actualPort}/`,
|
|
973
|
+
),
|
|
974
|
+
);
|
|
975
|
+
logger.log(chalk.gray(" Press Ctrl+C to stop."));
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
process.on("SIGINT", () => {
|
|
979
|
+
server.close(() => process.exit(0));
|
|
980
|
+
});
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
// cowork learning — analyze historical runs
|
|
984
|
+
const learning = cowork
|
|
985
|
+
.command("learning")
|
|
986
|
+
.description("Analyze Cowork history for stats, recommendations, failures");
|
|
987
|
+
|
|
988
|
+
learning
|
|
989
|
+
.command("stats")
|
|
990
|
+
.description("Per-template aggregate stats across all runs")
|
|
991
|
+
.option("--json", "Output as JSON")
|
|
992
|
+
.action(async (options) => {
|
|
993
|
+
const { loadHistory, computeTemplateStats } =
|
|
994
|
+
await import("../lib/cowork-learning.js");
|
|
995
|
+
const stats = computeTemplateStats(loadHistory(process.cwd()));
|
|
996
|
+
if (options.json) {
|
|
997
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
if (stats.length === 0) {
|
|
1001
|
+
logger.log(chalk.gray("No history yet. Run some tasks first."));
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
logger.log(chalk.bold(`\nTemplate stats (${stats.length}):\n`));
|
|
1005
|
+
for (const s of stats) {
|
|
1006
|
+
const pct = Math.round(s.successRate * 100);
|
|
1007
|
+
logger.log(
|
|
1008
|
+
` ${chalk.cyan(s.templateId)} runs=${s.runs} ok=${s.successes} fail=${s.failures} ${pct}% avgTok=${s.avgTokens} avgIter=${s.avgIterations}`,
|
|
1009
|
+
);
|
|
1010
|
+
if (s.topTools.length) {
|
|
1011
|
+
logger.log(
|
|
1012
|
+
chalk.gray(
|
|
1013
|
+
` tools: ${s.topTools.map((t) => `${t.tool}(${t.count})`).join(", ")}`,
|
|
1014
|
+
),
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
logger.log("");
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
learning
|
|
1022
|
+
.command("recommend <message...>")
|
|
1023
|
+
.description("Recommend the best template for a new user message")
|
|
1024
|
+
.option("--min-runs <n>", "Only consider templates with ≥N past runs", "1")
|
|
1025
|
+
.option("--json", "Output as JSON")
|
|
1026
|
+
.action(async (messageParts, options) => {
|
|
1027
|
+
const { loadHistory, recommendTemplate } =
|
|
1028
|
+
await import("../lib/cowork-learning.js");
|
|
1029
|
+
const message = messageParts.join(" ");
|
|
1030
|
+
const rec = recommendTemplate(message, loadHistory(process.cwd()), {
|
|
1031
|
+
minRuns: parseInt(options.minRuns, 10) || 1,
|
|
1032
|
+
});
|
|
1033
|
+
if (options.json) {
|
|
1034
|
+
console.log(JSON.stringify(rec, null, 2));
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
if (!rec) {
|
|
1038
|
+
logger.log(
|
|
1039
|
+
chalk.gray("No recommendation — no overlapping history found."),
|
|
1040
|
+
);
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
logger.log(chalk.bold(`\nRecommended: ${chalk.cyan(rec.templateId)}`));
|
|
1044
|
+
logger.log(
|
|
1045
|
+
chalk.gray(` score: ${rec.score} confidence: ${rec.confidence}`),
|
|
1046
|
+
);
|
|
1047
|
+
for (const r of rec.reasons) logger.log(chalk.gray(` - ${r}`));
|
|
1048
|
+
logger.log("");
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
learning
|
|
1052
|
+
.command("failures")
|
|
1053
|
+
.description("Group failures by template with common summaries")
|
|
1054
|
+
.option("--limit <n>", "Max examples per template", "3")
|
|
1055
|
+
.option("--json", "Output as JSON")
|
|
1056
|
+
.action(async (options) => {
|
|
1057
|
+
const { loadHistory, summarizeFailures } =
|
|
1058
|
+
await import("../lib/cowork-learning.js");
|
|
1059
|
+
const out = summarizeFailures(loadHistory(process.cwd()), {
|
|
1060
|
+
limit: parseInt(options.limit, 10) || 3,
|
|
1061
|
+
});
|
|
1062
|
+
if (options.json) {
|
|
1063
|
+
console.log(JSON.stringify(out, null, 2));
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
if (out.length === 0) {
|
|
1067
|
+
logger.log(chalk.green("✓ No failures in history."));
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
logger.log(chalk.bold(`\nFailures by template:\n`));
|
|
1071
|
+
for (const g of out) {
|
|
1072
|
+
logger.log(` ${chalk.red(g.templateId)} failures=${g.failureCount}`);
|
|
1073
|
+
for (const cs of g.commonSummaries) {
|
|
1074
|
+
logger.log(chalk.gray(` × ${cs.count} ${cs.summary}`));
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
logger.log("");
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
learning
|
|
1081
|
+
.command("suggest")
|
|
1082
|
+
.description("Suggest systemPromptExtension patches from failure history")
|
|
1083
|
+
.option("--json", "Output as JSON")
|
|
1084
|
+
.action(async (options) => {
|
|
1085
|
+
const { loadHistory, suggestPromptPatch } =
|
|
1086
|
+
await import("../lib/cowork-learning.js");
|
|
1087
|
+
const patches = suggestPromptPatch(loadHistory(process.cwd()));
|
|
1088
|
+
if (options.json) {
|
|
1089
|
+
console.log(JSON.stringify(patches, null, 2));
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
if (patches.length === 0) {
|
|
1093
|
+
logger.log(
|
|
1094
|
+
chalk.gray(
|
|
1095
|
+
"No patch suggestions — not enough history (need ≥10 runs and ≥3 failures per template).",
|
|
1096
|
+
),
|
|
1097
|
+
);
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
logger.log(chalk.bold(`\nSuggested patches (${patches.length}):\n`));
|
|
1101
|
+
for (const p of patches) {
|
|
1102
|
+
const color =
|
|
1103
|
+
p.confidence === "high"
|
|
1104
|
+
? chalk.red
|
|
1105
|
+
: p.confidence === "medium"
|
|
1106
|
+
? chalk.yellow
|
|
1107
|
+
: chalk.gray;
|
|
1108
|
+
logger.log(
|
|
1109
|
+
` ${chalk.cyan(p.templateId)} ${color(p.confidence)} runs=${p.runs} failures=${p.failures} (${Math.round(p.failureRate * 100)}%)`,
|
|
1110
|
+
);
|
|
1111
|
+
logger.log(chalk.gray(` ${p.patch}`));
|
|
1112
|
+
}
|
|
1113
|
+
logger.log(
|
|
1114
|
+
chalk.dim(
|
|
1115
|
+
"\n Apply with: cc cowork learning apply <templateId> (writes to user-templates/)",
|
|
1116
|
+
),
|
|
1117
|
+
);
|
|
1118
|
+
logger.log("");
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
learning
|
|
1122
|
+
.command("apply <templateId>")
|
|
1123
|
+
.description("Apply a suggested patch to the user-templates layer")
|
|
1124
|
+
.option("--json", "Output as JSON")
|
|
1125
|
+
.action(async (templateId, options) => {
|
|
1126
|
+
const { loadHistory, suggestPromptPatch, applyPromptPatch } =
|
|
1127
|
+
await import("../lib/cowork-learning.js");
|
|
1128
|
+
const patches = suggestPromptPatch(loadHistory(process.cwd()));
|
|
1129
|
+
const match = patches.find((p) => p.templateId === templateId);
|
|
1130
|
+
if (!match) {
|
|
1131
|
+
logger.error(
|
|
1132
|
+
`No qualifying patch for template '${templateId}'. Run 'cowork learning suggest' to see available patches.`,
|
|
1133
|
+
);
|
|
1134
|
+
process.exit(1);
|
|
1135
|
+
}
|
|
1136
|
+
const result = applyPromptPatch(process.cwd(), match);
|
|
1137
|
+
if (options.json) {
|
|
1138
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
logger.log(
|
|
1142
|
+
chalk.green(
|
|
1143
|
+
`✓ Applied patch to ${chalk.cyan(result.templateId)} (${result.file}).`,
|
|
1144
|
+
),
|
|
1145
|
+
);
|
|
1146
|
+
});
|
|
1147
|
+
|
|
293
1148
|
// cowork status — show collaboration state
|
|
294
1149
|
cowork
|
|
295
1150
|
.command("status")
|
|
@@ -306,6 +1161,18 @@ export function registerCoworkCommand(program) {
|
|
|
306
1161
|
logger.log(
|
|
307
1162
|
` ${chalk.cyan("cowork analyze <path>")} Code analysis (style/knowledge-graph/decisions)`,
|
|
308
1163
|
);
|
|
1164
|
+
logger.log(
|
|
1165
|
+
` ${chalk.cyan("cowork cron list|add|remove|run")} Schedule recurring Cowork tasks`,
|
|
1166
|
+
);
|
|
1167
|
+
logger.log(
|
|
1168
|
+
` ${chalk.cyan("cowork learning stats|recommend|failures")} Analyze run history`,
|
|
1169
|
+
);
|
|
1170
|
+
logger.log(
|
|
1171
|
+
` ${chalk.cyan("cowork workflow list|show|add|remove|run")} DAG of chained Cowork tasks`,
|
|
1172
|
+
);
|
|
1173
|
+
logger.log(
|
|
1174
|
+
` ${chalk.cyan("cowork share export-template|export-result|import|verify")} Signed packet exchange`,
|
|
1175
|
+
);
|
|
309
1176
|
logger.log("");
|
|
310
1177
|
logger.log(
|
|
311
1178
|
chalk.gray(
|