feat(todos): move and copy to-dos, lists and groups to other projects

Merged
RO
2d ago
+7603-583

Why

The web can move or copy a to-do, a to-do list or a group to another project. The CLI couldn't. todos move only repositioned within a project, and it gave a bare validation error when pointed at another project's list. bc3 #14188, now deployed, lets API clients file to-dos and groups into another project's lists.

What changed

  • todos move <id>... --to-project <project> --to-list <list> moves to-dos to another project, and todos copy <id>... copies them, optionally with --include-comments. Both take several ids, as the web's bulk Move and Copy do. Loose to-dos work too.
  • todolists move|copy <id> --to-project … handles both kinds:
    • A top-level list goes into a project's To-dos (--to-todoset picks one).
    • A group goes into a to-do list (--to-list) and stays a group.
    • The same commands are on every alias group (todolist, todolistgroups, tlgroup, tlgroups, todolistgroup).
  • Passing another project's list with --list now refuses and names the --to-project command to use.
  • Every move and copy command refuses a container flag it inherits but would ignore, before anything is sent. todos copy 456 --todolist 901 used to copy into the to-do's own list and report success. One guard, guardFilingFlags, declares per command which container flags it reads; it replaces the separate refusals for todolistgroups copy --list, cards copy --card-table and files move --vault/--folder. A test walks the whole command tree and runs every move or copy command with every container flag it does not read.
  • Bulk reports each item and exits non-zero if any didn't finish. It only says to re-run items Basecamp refused, never a copy that was made or may have been.
  • Copy recovery for to-dos, lists, groups and cards points at one listing, recordings list <Type> --in … --status … --all. It finds the copy whatever its state. A copy keeps its original's dates, so the hint says to look for the original's title in the destination rather than at the top of the list.
  • todolists show <id> no longer needs a project. It reads the list by its account-wide id.

Scope

  • This is stacked on #975, the shared filing helper. Every other family (messages, Docs & Files, columns, schedule, check-ins, forwards, tools) comes in the next PR.
  • Existing same-project todos move --list, todolists move and group repositioning behave as before, except that a container flag they ignored is now refused (todos move --to 1 --todolist …, todolistgroups position --list …).

Verification

  • bin/ci passes.
  • Against a local bc3 with #14188, these all landed where expected when read back:
    • to-dos moved and copied across projects by name, and a bulk move;
    • loose to-dos moved and copied;
    • lists and groups moved and copied;
    • an old id plus its new id refused before anything was filed.
  • The recovery listing found a copied completed to-do and a copied archived group.
  • An independent GPT-6.1-sol review converged after 5 rounds. Each finding it raised has a test that fails without the fix.

Summary by cubic

Enables moving and copying to-dos, to-do lists, groups, messages, Docs & Files items, card table columns, schedule entries, check-in questions, email forwards, and dock tools to another project, matching the web's Move and Copy actions. Builds on the shared filing helper (#975) and bc3 #14188, now deployed, which lets API clients file recordings into another project's lists and tools.

New Features

  • todos move <id>... --to-project --to-list moves to-dos to another project; todos copy <id>... copies them with optional --include-comments. Both accept several ids and work for loose to-dos.
  • todolists move|copy <id> --to-project files a top-level list into a project's To-dos (--to-todoset picks one) and a group into a to-do list (--to-list), keeping it a group; all group aliases reach the same commands.
  • messages, files, cards column, schedule, checkins question, and forwards gain move and copy with --to-project and their own --to-<container> flags; several ids file one by one, and files copy --to-folder targets a nested folder.
  • tools move <id> --to-project moves a whole dock tool with everything in it; tools are not copied. tools move --position still reorders the dock.
  • filings show <id> reads a move or copy that is still running when the command's wait ends first.
  • Container flags a command would ignore are refused before anything is sent, with the command's own --to-<container> flags as the hint; a recording named twice by its old and new ids is refused before anything is filed.
  • Bulk operations report every item and fail the command if any didn't finish; only Basecamp-refused items are re-runnable, and a 401 keeps the remedy for the credential that failed.
  • Copy recovery lists recordings list <Type> --in … --status … --all unsorted and says to find the copy by the original's title, since a copy keeps its source's dates; where no listing shows the copy's status, the hint names the destination's app_url. Every command a filing prints carries the run's --profile and --account.
  • todolists show, files show, schedule show, cards column show, checkins question show, and forwards show no longer require a project; show commands follow up on the id their read returns.