@love-moon/conductor-cli 0.15.2 → 0.16.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.
@@ -16,6 +16,26 @@
16
16
  * schedule list <id>
17
17
  * schedule create <id> [<message>] (--delay 10m | --at ISO | --every 1h)
18
18
  * schedule delete <id> <schedule-id>
19
+ * schedule update <id> <schedule-id> [<message>] [--delay|--at|--every ...]
20
+ * stop <id> | interrupt <id> [--target-reply-to <msg-id>]
21
+ * restart <id> [--strategy inplace|new_task] [--backend] [--refresh-session]
22
+ * [--daemon-host <h>] [--first-message <text>]
23
+ * delete <id> --yes [--permanent] | archive <id> | unarchive <id> [--daemon-host <h>]
24
+ * rename <id> <title> | pin <id> | unpin <id> | labels <id> [<label-id>...] [--clear]
25
+ * move <id> [<project>] [--back] | share <id> | unshare <id> | shared <token|link>
26
+ * transcribe <audio.wav|mp3> [--language <tag>]
27
+ * persistent <id> [--enable|--disable] [--instructions ...] [--summary ...]
28
+ * round end <id> | round start <id> [<message>] [--backend] [--daemon-host] [--worktree inherit|new|none]
29
+ * cleanup-worktree <id> | terminal open|close|show <id>
30
+ * attachment download <id> <attachment-id> [-o FILE|DIR|-]
31
+ * resume --daemon-host <h> --backend <b> --session <id> [--session-file <p>] [--title] [--prompt]
32
+ *
33
+ * create also takes: --daemon-host, --agent <name>[:backend] (repeatable; first
34
+ * is the worker), --global-backend <host>:<backend>, --worktree,
35
+ * --remote-worktree <host>, --persistent
36
+ * send also takes: --attach FILE (repeatable)
37
+ * messages also takes: --follow [--interval <sec>] [--until-idle]
38
+ * list also takes (without --archived): --all-projects, --project-ids a,b
19
39
  *
20
40
  * Global flags supported on every write subcommand:
21
41
  * --json, --dry-run, --project, --config-file
@@ -42,6 +62,7 @@ import {
42
62
  reportError,
43
63
  resolveProject,
44
64
  } from "../src/entity-helpers.js";
65
+ import * as extra from "../src/task-commands.js";
45
66
 
46
67
  const isMainModule = (() => {
47
68
  const currentFile = fileURLToPath(import.meta.url);
@@ -281,8 +302,14 @@ async function handleList(argv, deps) {
281
302
  // Validated here rather than with yargs implies/conflicts: those count the
282
303
  // boolean defaults as "set", and the parser's fail() does not stop the handler.
283
304
  let argsProblem = null;
284
- if (!argv.archived && (argv.search !== undefined || argv.allProjects || argv.page !== undefined)) {
285
- argsProblem = "--search, --all-projects and --page require --archived";
305
+ if (!argv.archived && (argv.search !== undefined || argv.page !== undefined)) {
306
+ argsProblem = "--search and --page require --archived";
307
+ } else if (!argv.archived && (argv.allProjects || argv.projectIds) && (argv.project || argv.issue || argv.includeMoved)) {
308
+ argsProblem = "--all-projects / --project-ids cannot be combined with --project, --issue, or --include-moved";
309
+ } else if (argv.allProjects && argv.projectIds) {
310
+ argsProblem = "Pass either --all-projects or --project-ids, not both";
311
+ } else if (argv.archived && argv.projectIds) {
312
+ argsProblem = "--project-ids cannot be combined with --archived (use --all-projects)";
286
313
  } else if (argv.archived && (argv.issue || argv.status || argv.includeMoved || (argv.allProjects && argv.project))) {
287
314
  argsProblem = "--archived cannot be combined with --issue, --status, --include-moved, or --project with --all-projects";
288
315
  }
@@ -291,6 +318,7 @@ async function handleList(argv, deps) {
291
318
  err.code = "ARGS";
292
319
  throw err;
293
320
  }
321
+ if (!argv.archived && (argv.allProjects || argv.projectIds)) return extra.handleMultiProjectList(argv, deps);
294
322
  const apis = await buildApis(deps);
295
323
  if (argv.archived) return handleArchivedList(argv, deps, apis);
296
324
  const project = await resolveProject(apis, { env: deps.env, cwd: deps.cwd, project: argv.project });
@@ -335,6 +363,18 @@ async function handleCreate(argv, deps) {
335
363
  err.code = "ARGS";
336
364
  throw err;
337
365
  }
366
+ if (project.hidden) {
367
+ printPretty(
368
+ deps.stderr,
369
+ `Warning: project ${project.name || project.id} is hidden; the task will not show in the web task list until you unhide it.`,
370
+ );
371
+ }
372
+ if (extra.hasExtendedCreateOptions(argv)) {
373
+ // The SDK's createTask only forwards the basic fields; the extended options
374
+ // (daemon, agent group, global backend, worktree, persistent) go straight to
375
+ // the route with the same payload the web Create Task dialog sends.
376
+ return extra.handleExtendedCreate(argv, deps, project.id, title);
377
+ }
338
378
 
339
379
  const body = {
340
380
  projectId: project.id,
@@ -446,6 +486,9 @@ async function handleSend(argv, deps) {
446
486
  // `--metadata-json '{"actor":"system"}'` cannot spoof CLI audit fields
447
487
  // (review H1). The CLI's `actor: "cli"` always wins inside `audit`.
448
488
  const metadata = buildAuditMetadata(deps.env, extraMetadata || {});
489
+ if ([].concat(argv.attach || []).filter(Boolean).length > 0) {
490
+ return extra.handleSendWithAttachments(argv, deps, content, metadata);
491
+ }
449
492
  const body = {
450
493
  role: "user",
451
494
  content,
@@ -466,10 +509,10 @@ async function handleSend(argv, deps) {
466
509
  // SDK signature is `sendTaskMessage(taskId, content, options?)`. Earlier we
467
510
  // were passing the body object as the second arg, which caused the SDK's
468
511
  // `typeof content === 'string'` guard to throw at runtime (review B1).
469
- const result = await apis.tasks.sendTaskMessage(argv.id, content, {
512
+ const result = await extra.retryWhileFireOwnerMissing(() => apis.tasks.sendTaskMessage(argv.id, content, {
470
513
  role: body.role,
471
514
  metadata: body.metadata,
472
- });
515
+ }), deps);
473
516
  if (argv.json) {
474
517
  printJson(deps.stdout, result ?? { sent: true });
475
518
  return EXIT.OK;
@@ -527,6 +570,9 @@ async function handleMessages(argv, deps) {
527
570
  limit: argv.limit ? Number(argv.limit) : undefined,
528
571
  before: argv.before ? String(argv.before) : undefined,
529
572
  });
573
+ if (argv.follow) {
574
+ return extra.followMessages(argv, deps, apis, Array.isArray(list) ? list : []);
575
+ }
530
576
  if (argv.json) {
531
577
  printJson(deps.stdout, Array.isArray(list) ? list : []);
532
578
  return EXIT.OK;
@@ -623,6 +669,43 @@ async function handleScheduleDelete(argv, deps) {
623
669
  return EXIT.OK;
624
670
  }
625
671
 
672
+ async function handleScheduleUpdate(argv, deps) {
673
+ const hasMessage = (argv.message !== undefined && argv.message !== "") || argv.fromFile || argv.stdin;
674
+ const content = hasMessage
675
+ ? readMessageInput({
676
+ positional: argv.message,
677
+ fromFile: argv.fromFile,
678
+ useStdin: Boolean(argv.stdin),
679
+ stdin: deps.stdin,
680
+ })
681
+ : undefined;
682
+ const hasSchedule = argv.delay !== undefined || argv.at !== undefined || argv.every !== undefined;
683
+ const schedule = hasSchedule ? parseScheduleOptions(argv) : undefined;
684
+ return extra.handleScheduleUpdate(argv, deps, content, schedule);
685
+ }
686
+
687
+ function withMessageInput(cmd) {
688
+ return cmd
689
+ .option("stdin", { type: "boolean", default: false })
690
+ .option("from-file", { type: "string" });
691
+ }
692
+
693
+ function scheduleModeOptions(cmd) {
694
+ return cmd
695
+ .option("delay", { type: "string", describe: "Send once after a duration, e.g. 10m or 2h" })
696
+ .option("at", { type: "string", describe: "Send once at an ISO/local date-time string" })
697
+ .option("every", { type: "string", describe: "Repeat every duration, e.g. 30m or 1h" })
698
+ .option("if-idle", { type: "boolean", default: false, describe: "For repeats, only send when the AI is idle" })
699
+ .option("max-runs", { type: "number", describe: "For repeats, stop after N sends" })
700
+ .option("max-skips", { type: "number", describe: "For repeats, stop after N skips" })
701
+ .option("stop-at", { type: "string", describe: "For repeats, stop after this date-time" })
702
+ .option("keep-when-task-stopped", {
703
+ type: "boolean",
704
+ default: false,
705
+ describe: "For repeats, skip instead of completing when the task is not running",
706
+ });
707
+ }
708
+
626
709
  export async function main(argvInput = hideBin(process.argv), deps = {}) {
627
710
  const stdout = deps.stdout || process.stdout;
628
711
  const stderr = deps.stderr || process.stderr;
@@ -630,6 +713,9 @@ export async function main(argvInput = hideBin(process.argv), deps = {}) {
630
713
  const cwd = deps.cwd || process.cwd();
631
714
  const consoleErr = { error: (msg) => stderr.write(`${msg}\n`) };
632
715
  const handlerDeps = { ...deps, stdout, stderr, env, cwd };
716
+ const run = (handler) => async (argv) => {
717
+ exitCode = await handler(argv, { ...handlerDeps, configFile: argv.configFile });
718
+ };
633
719
 
634
720
  let exitCode = EXIT.OK;
635
721
  try {
@@ -654,7 +740,8 @@ export async function main(argvInput = hideBin(process.argv), deps = {}) {
654
740
  })
655
741
  .option("archived", { type: "boolean", describe: "List archived tasks instead of active ones" })
656
742
  .option("search", { type: "string", describe: "With --archived: match title or transcript text" })
657
- .option("all-projects", { type: "boolean", describe: "With --archived: search every project" })
743
+ .option("all-projects", { type: "boolean", describe: "List tasks across every project (with --archived: search every project)" })
744
+ .option("project-ids", { type: "string", describe: "Comma-separated project ids to list together (merged cross-daemon view)" })
658
745
  .option("page", { type: "number", describe: "With --archived: result page (10 per page)" }),
659
746
  async (argv) => {
660
747
  exitCode = await handleList(argv, { ...handlerDeps, configFile: argv.configFile });
@@ -680,7 +767,17 @@ export async function main(argvInput = hideBin(process.argv), deps = {}) {
680
767
  .option("parent-task-id", {
681
768
  type: "string",
682
769
  describe: "Display the new task in the same task-card group as this task",
683
- }),
770
+ })
771
+ .option("daemon-host", { type: "string", describe: "Run the AI on this daemon (defaults to the project's daemon)" })
772
+ .option("agent", {
773
+ type: "string",
774
+ array: true,
775
+ describe: "Multi-agent group member <name>[:backend]; repeat it. The first is the worker, the rest reviewers",
776
+ })
777
+ .option("global-backend", { type: "string", describe: "Run the AI on a global backend <host>:<backend> (RFC 0041)" })
778
+ .option("worktree", { type: "boolean", describe: "Run in a new git worktree on the project's daemon" })
779
+ .option("remote-worktree", { type: "string", describe: "Run in a new git worktree on this remote daemon" })
780
+ .option("persistent", { type: "boolean", describe: "Create a persistent task (RFC 0039)" }),
684
781
  async (argv) => {
685
782
  exitCode = await handleCreate(argv, { ...handlerDeps, configFile: argv.configFile });
686
783
  },
@@ -709,7 +806,8 @@ export async function main(argvInput = hideBin(process.argv), deps = {}) {
709
806
  .positional("message", { type: "string" })
710
807
  .option("stdin", { type: "boolean", default: false })
711
808
  .option("from-file", { type: "string" })
712
- .option("metadata-json", { type: "string", describe: "Extra JSON metadata to merge into the message" }),
809
+ .option("metadata-json", { type: "string", describe: "Extra JSON metadata to merge into the message" })
810
+ .option("attach", { type: "string", array: true, describe: "Attach a local file; repeat for several" }),
713
811
  async (argv) => {
714
812
  exitCode = await handleSend(argv, { ...handlerDeps, configFile: argv.configFile });
715
813
  },
@@ -730,15 +828,213 @@ export async function main(argvInput = hideBin(process.argv), deps = {}) {
730
828
  )
731
829
  .command(
732
830
  "messages <id>",
733
- "Pull a slice of task messages and exit (no --follow in this RFC)",
831
+ "Pull a slice of task messages (or --follow to stream new ones)",
734
832
  (cmd) => cmd
735
833
  .positional("id", { type: "string", demandOption: true })
736
834
  .option("limit", { type: "number" })
737
- .option("before", { type: "string", describe: "Cursor: message id to paginate before" }),
835
+ .option("before", { type: "string", describe: "Cursor: message id to paginate before" })
836
+ .option("follow", { alias: "f", type: "boolean", default: false, describe: "Keep polling and print new messages" })
837
+ .option("interval", { type: "number", default: 2, describe: "With --follow: poll interval in seconds" })
838
+ .option("until-idle", { type: "boolean", default: false, describe: "With --follow: stop once the task is no longer running" }),
738
839
  async (argv) => {
739
840
  exitCode = await handleMessages(argv, { ...handlerDeps, configFile: argv.configFile });
740
841
  },
741
842
  )
843
+ .command(
844
+ "stop <id>",
845
+ "Stop (kill) a running task",
846
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }),
847
+ run(extra.handleStop),
848
+ )
849
+ .command(
850
+ "interrupt <id>",
851
+ "Interrupt the current AI turn (the Stop button in the chat)",
852
+ (cmd) => cmd
853
+ .positional("id", { type: "string", demandOption: true })
854
+ .option("target-reply-to", { type: "string", describe: "Reply target of the turn to interrupt (defaults to the latest user message)" }),
855
+ run(extra.handleInterrupt),
856
+ )
857
+ .command(
858
+ "restart <id>",
859
+ "Restart a task's AI session (in place, or as a new task)",
860
+ (cmd) => cmd
861
+ .positional("id", { type: "string", demandOption: true })
862
+ .option("strategy", { choices: ["inplace", "new_task"], describe: "inplace resumes the session; new_task forks a successor" })
863
+ .option("backend", { type: "string", describe: "Switch to this AI backend" })
864
+ .option("refresh-session", { type: "boolean", default: false, describe: "Start a fresh AI session in place" })
865
+ .option("daemon-host", { type: "string", describe: "Run the restarted session on this daemon" })
866
+ .option("first-message", { type: "string", describe: "With --strategy new_task: first message instead of the transcript handoff" }),
867
+ run(extra.handleRestart),
868
+ )
869
+ .command(
870
+ "delete <id>",
871
+ "Delete a task (--permanent deletes an archived task for good)",
872
+ (cmd) => cmd
873
+ .positional("id", { type: "string", demandOption: true })
874
+ .option("permanent", { type: "boolean", default: false, describe: "Permanently delete an archived task" })
875
+ .option("yes", { alias: "y", type: "boolean", default: false, describe: "Confirm the deletion" }),
876
+ run(extra.handleDelete),
877
+ )
878
+ .command(
879
+ "archive <id>",
880
+ "Archive (pack) a task: stop it and keep its transcript searchable",
881
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }),
882
+ run(extra.handleArchive),
883
+ )
884
+ .command(
885
+ "unarchive <id>",
886
+ "Restore an archived task",
887
+ (cmd) => cmd
888
+ .positional("id", { type: "string", demandOption: true })
889
+ .option("daemon-host", { type: "string", describe: "Restore on this daemon (required when the original is offline)" })
890
+ .option("backend", { type: "string", describe: "Restore with this AI backend" }),
891
+ run(extra.handleUnarchive),
892
+ )
893
+ .command(
894
+ "rename <id> <title>",
895
+ "Rename a task",
896
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }).positional("title", { type: "string", demandOption: true }),
897
+ run(extra.handleRename),
898
+ )
899
+ .command(
900
+ "pin <id>",
901
+ "Pin a task to the top of the list",
902
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }),
903
+ run((argv, deps) => extra.handlePin(argv, deps, true)),
904
+ )
905
+ .command(
906
+ "unpin <id>",
907
+ "Unpin a task",
908
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }),
909
+ run((argv, deps) => extra.handlePin(argv, deps, false)),
910
+ )
911
+ .command(
912
+ "move <id> [targetProject]",
913
+ "Show a task under another project (display only; --back files it under its own project)",
914
+ (cmd) => cmd
915
+ .positional("id", { type: "string", demandOption: true })
916
+ .positional("targetProject", { type: "string", describe: "Target project id or name" })
917
+ .option("back", { type: "boolean", default: false }),
918
+ run(extra.handleMove),
919
+ )
920
+ .command(
921
+ "labels <id> [labelIds..]",
922
+ "Replace the labels attached to a task",
923
+ (cmd) => cmd
924
+ .positional("id", { type: "string", demandOption: true })
925
+ .positional("labelIds", { type: "string", array: true })
926
+ .option("clear", { type: "boolean", default: false, describe: "Remove all labels" }),
927
+ run(extra.handleLabels),
928
+ )
929
+ .command(
930
+ "share <id>",
931
+ "Create (or print) the read-only share link of a task",
932
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }),
933
+ run(extra.handleShare),
934
+ )
935
+ .command(
936
+ "unshare <id>",
937
+ "Revoke a task's share link",
938
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }),
939
+ run(extra.handleUnshare),
940
+ )
941
+ .command(
942
+ "shared <token>",
943
+ "Read a shared task (token or share link, as opened by the /share page)",
944
+ (cmd) => cmd.positional("token", { type: "string", demandOption: true, describe: "Share token or https://…/share/<token> link" }),
945
+ run(extra.handleShared),
946
+ )
947
+ .command(
948
+ "transcribe <file>",
949
+ "Speech to text for a .wav/.mp3 file (the web composer's voice input)",
950
+ (cmd) => cmd
951
+ .positional("file", { type: "string", demandOption: true })
952
+ .option("language", { type: "string", describe: "Language hint, e.g. zh or en" }),
953
+ run(extra.handleTranscribe),
954
+ )
955
+ .command(
956
+ "persistent <id>",
957
+ "Change persistent-task settings (RFC 0039)",
958
+ (cmd) => cmd
959
+ .positional("id", { type: "string", demandOption: true })
960
+ .option("enable", { type: "boolean" })
961
+ .option("disable", { type: "boolean" })
962
+ .option("auto-end-round", { type: "boolean", describe: "End the round after 1h without a reply (default on; --no-auto-end-round to turn off)" })
963
+ .option("instructions", { type: "string", describe: "Standing instructions carried into every round" })
964
+ .option("summary", { type: "string", describe: "Carried-over summary" }),
965
+ run(extra.handlePersistent),
966
+ )
967
+ .command(
968
+ "round",
969
+ "End the current round or start a new one on a persistent task",
970
+ (cmd) => cmd
971
+ .command(
972
+ "end <id>",
973
+ "End the current round",
974
+ (sub) => sub.positional("id", { type: "string", demandOption: true }),
975
+ run(extra.handleRoundEnd),
976
+ )
977
+ .command(
978
+ "start <id> [message]",
979
+ "Start a new round (fresh AI session) with a first message",
980
+ (sub) => withMessageInput(sub
981
+ .positional("id", { type: "string", demandOption: true })
982
+ .positional("message", { type: "string" }))
983
+ .option("backend", { type: "string" })
984
+ .option("daemon-host", { type: "string" })
985
+ .option("worktree", { choices: ["inherit", "new", "none"] })
986
+ .option("expected-round", { type: "number", describe: "Refuse if another client already moved past this round" }),
987
+ run((argv, deps) => extra.handleRoundStart(argv, deps, () => readMessageInput({
988
+ positional: argv.message,
989
+ fromFile: argv.fromFile,
990
+ useStdin: Boolean(argv.stdin),
991
+ stdin: deps.stdin,
992
+ }))),
993
+ )
994
+ .demandCommand(1),
995
+ )
996
+ .command(
997
+ "cleanup-worktree <id>",
998
+ "Remove a stopped task's isolated git worktree",
999
+ (cmd) => cmd.positional("id", { type: "string", demandOption: true }),
1000
+ run(extra.handleCleanupWorktree),
1001
+ )
1002
+ .command(
1003
+ "terminal",
1004
+ "Open, show, or close the PTY terminal attached to a task",
1005
+ (cmd) => cmd
1006
+ .command("open <id>", "Attach a PTY terminal to the task", (sub) => sub.positional("id", { type: "string", demandOption: true }), run((argv, deps) => extra.handleTerminal(argv, deps, "open")))
1007
+ .command("show <id>", "Show the attached terminal", (sub) => sub.positional("id", { type: "string", demandOption: true }), run((argv, deps) => extra.handleTerminal(argv, deps, "show")))
1008
+ .command("close <id>", "Close and delete the attached terminal", (sub) => sub.positional("id", { type: "string", demandOption: true }), run((argv, deps) => extra.handleTerminal(argv, deps, "close")))
1009
+ .demandCommand(1),
1010
+ )
1011
+ .command(
1012
+ "attachment",
1013
+ "Download a message attachment",
1014
+ (cmd) => cmd
1015
+ .command(
1016
+ "download <id> <attachmentId>",
1017
+ "Save an attachment (ids are in `task messages --json`)",
1018
+ (sub) => sub
1019
+ .positional("id", { type: "string", demandOption: true })
1020
+ .positional("attachmentId", { type: "string", demandOption: true })
1021
+ .option("output", { alias: "o", type: "string", describe: "File or directory to write to; - for stdout" }),
1022
+ run(extra.handleAttachmentDownload),
1023
+ )
1024
+ .demandCommand(1),
1025
+ )
1026
+ .command(
1027
+ "resume",
1028
+ "Resume an existing AI CLI session as a new app task (see `conductor daemon sessions <host>`)",
1029
+ (cmd) => cmd
1030
+ .option("daemon-host", { type: "string", demandOption: true })
1031
+ .option("backend", { type: "string", demandOption: true })
1032
+ .option("session", { type: "string", demandOption: true, describe: "Session id" })
1033
+ .option("session-file", { type: "string", describe: "Session file path on the daemon" })
1034
+ .option("title", { type: "string" })
1035
+ .option("prompt", { type: "string", describe: "First instruction after resuming" }),
1036
+ run(extra.handleResume),
1037
+ )
742
1038
  .command(
743
1039
  "schedule",
744
1040
  "Create, list, and delete scheduled messages for a task",
@@ -776,6 +1072,17 @@ export async function main(argvInput = hideBin(process.argv), deps = {}) {
776
1072
  exitCode = await handleScheduleCreate(argv, { ...handlerDeps, configFile: argv.configFile });
777
1073
  },
778
1074
  )
1075
+ .command(
1076
+ "update <id> <scheduleId> [message]",
1077
+ "Edit an active scheduled message's text and/or schedule",
1078
+ (sub) => scheduleModeOptions(withMessageInput(sub
1079
+ .positional("id", { type: "string", demandOption: true })
1080
+ .positional("scheduleId", { type: "string", demandOption: true })
1081
+ .positional("message", { type: "string" }))),
1082
+ async (argv) => {
1083
+ exitCode = await handleScheduleUpdate(argv, { ...handlerDeps, configFile: argv.configFile });
1084
+ },
1085
+ )
779
1086
  .command(
780
1087
  "delete <id> <scheduleId>",
781
1088
  "Delete an active scheduled message",
package/bin/conductor.js CHANGED
@@ -5,17 +5,20 @@
5
5
  *
6
6
  * Subcommands:
7
7
  * fire - Run AI coding agents with Conductor integration
8
- * daemon - Start long-running daemon for task orchestration
8
+ * daemon - Start the daemon, or query/control daemons (list/restart/upgrade/share/...)
9
9
  * config - Interactive configuration setup
10
10
  * update - Update the CLI to the latest version
11
11
  * diagnose - Diagnose a task in production/backend
12
12
  * send-file - Upload a local file into a task session
13
13
  * channel - Connect user-owned chat channel providers
14
14
  * serve-ai - Start an OpenAI-compatible local AI server
15
- * project - Manage Conductor projects (list/show/create/...)
16
- * issue - Manage issues (list/show/create/update/start/done)
17
- * task - Manage tasks (create/list/show/send/messages/schedule)
15
+ * project - Manage Conductor projects (list/create/update/delete/collab/labels/...)
16
+ * issue - Manage issues (list/show/create/update/start/done/delete)
17
+ * task - Manage tasks (create/send/stop/restart/archive/share/schedule/...)
18
18
  * remote - Run commands and copy files on another daemon's host
19
+ * search - Search message history across all tasks
20
+ * settings - Manage user preferences (global backends, catchphrases, daily report, ...)
21
+ * auth - Show the signed-in user and manage API tokens
19
22
  */
20
23
 
21
24
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -52,6 +55,9 @@ export function runConductorCli(args = argv, deps = {}) {
52
55
  "issue",
53
56
  "task",
54
57
  "remote",
58
+ "search",
59
+ "settings",
60
+ "auth",
55
61
  ];
56
62
 
57
63
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
@@ -127,17 +133,23 @@ Usage: conductor <subcommand> [options]
127
133
 
128
134
  Subcommands:
129
135
  fire Run AI coding agents with Conductor integration
130
- daemon Start long-running daemon (or: daemon list/tools/quota to query daemons)
136
+ daemon Start long-running daemon (or: daemon list/tools/quota/sessions/restart/
137
+ upgrade/accounts/commands/share to query and control daemons)
131
138
  config Interactive configuration setup
132
139
  update Update the CLI to the latest version
133
140
  diagnose Diagnose a task and print likely root cause
134
141
  send-file Upload a local file into a task session
135
142
  channel Connect user-owned chat channel providers
136
143
  serve-ai Start an OpenAI-compatible local AI server
137
- project Manage Conductor projects (list/show/create/...)
138
- issue Manage issues (list/show/create/update/start/done)
139
- task Manage tasks (create/list/show/send/messages/schedule)
144
+ project Manage Conductor projects (list/show/create/update/delete/reorder/
145
+ agents/collab/labels/...)
146
+ issue Manage issues (list/show/create/update/start/done/delete)
147
+ task Manage tasks (create/send/insert/interrupt/stop/restart/delete/
148
+ archive/unarchive/rename/pin/move/labels/share/round/resume/...)
140
149
  remote Run commands and copy files on another daemon's host (exec/cp/wait/mcp)
150
+ search Search message history across all tasks
151
+ settings Manage user preferences (global-backends/catchphrases/daily-report/reports/...)
152
+ auth Show the signed-in user and manage API tokens (whoami/tokens)
141
153
 
142
154
  Options:
143
155
  -h, --help Show this help message
@@ -163,8 +175,14 @@ Examples:
163
175
  conductor task create --title "Refactor module" --prompt "Extract the parser" --backend codex
164
176
  conductor task send <task-id> "please add a unit test"
165
177
  conductor task schedule create <task-id> "follow up" --delay 10m
178
+ conductor task create --title "Refactor" --agent feature-dev --agent code-reviewer --worktree
179
+ conductor task restart <task-id> --strategy new_task --backend codex
180
+ conductor daemon upgrade macmini --wait
166
181
  conductor remote exec --target ubuntu --workspace /home/duino/ws/holomotion ls .
167
182
  conductor remote cp ./build.tar.gz ubuntu:/srv/app/build.tar.gz
183
+ conductor search "flaky test" --limit 20
184
+ conductor settings catchphrases add "please add a unit test"
185
+ conductor auth tokens create --name ci
168
186
 
169
187
  For subcommand-specific help:
170
188
  conductor fire --help
@@ -179,6 +197,9 @@ For subcommand-specific help:
179
197
  conductor issue --help
180
198
  conductor task --help
181
199
  conductor remote --help
200
+ conductor search --help
201
+ conductor settings --help
202
+ conductor auth --help
182
203
 
183
204
  Version: ${pkgJson.version}
184
205
  `);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@love-moon/conductor-cli",
3
- "version": "0.15.2",
4
- "gitCommitId": "f795a61",
3
+ "version": "0.16.0",
4
+ "gitCommitId": "77239b9",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/lovemoon-ai/conductor.git"
@@ -24,8 +24,8 @@
24
24
  "test": "node --test test/*.test.js"
25
25
  },
26
26
  "dependencies": {
27
- "@love-moon/ai-sdk": "0.15.2",
28
- "@love-moon/conductor-sdk": "0.15.2",
27
+ "@love-moon/ai-sdk": "0.16.0",
28
+ "@love-moon/conductor-sdk": "0.16.0",
29
29
  "@github/copilot-sdk": "^0.3.0",
30
30
  "chrome-launcher": "^1.2.1",
31
31
  "chrome-remote-interface": "^0.33.0",
@@ -38,7 +38,7 @@
38
38
  },
39
39
  "optionalDependencies": {
40
40
  "@roamhq/wrtc": "^0.10.0",
41
- "@love-moon/chat-web": "0.15.2"
41
+ "@love-moon/chat-web": "0.16.0"
42
42
  },
43
43
  "pnpm": {
44
44
  "onlyBuiltDependencies": [