@@ -1610,17 +1610,25 @@ func reopenTodos(cmd *cobra.Command, todoIDs []string) error {
16101610}
16111611
16121612func newTodosPositionCmd () * cobra.Command {
1613- var position int
1613+ var (
1614+ position int
1615+ list string
1616+ )
16141617
16151618 cmd := & cobra.Command {
16161619 Use : "position <id|url>" ,
16171620 Aliases : []string {"move" , "reorder" },
1618- Short : "Change todo position" ,
1619- Long : `Reorder a todo within its todolist. Position is 1-based (1 = top).
1621+ Short : "Change todo position or move between lists" ,
1622+ Long : `Reorder a todo within its todolist, or move it to a different list in the
1623+ same project. Position is 1-based (1 = top).
16201624
16211625You can pass either a todo ID or a Basecamp URL:
16221626 basecamp todos position 789 --to 1
1623- basecamp todos position https://3.basecamp.com/123/buckets/456/todos/789 --to 1` ,
1627+ basecamp todos position https://3.basecamp.com/123/buckets/456/todos/789 --to 1
1628+
1629+ Move to a different todolist in the same project:
1630+ basecamp todos position 789 --to 1 --list 321
1631+ basecamp todos position <todo-url> --to 1 --list <todolist-url>` ,
16241632 RunE : func (cmd * cobra.Command , args []string ) error {
16251633 if len (args ) == 0 {
16261634 return missingArg (cmd , "<id|url>" )
@@ -1639,21 +1647,44 @@ You can pass either a todo ID or a Basecamp URL:
16391647 return output .ErrUsage ("--to is required (1 = top)" )
16401648 }
16411649
1642- // Extract ID from URL if provided
1643- todoIDStr := extractID (args [0 ])
1650+ // Extract todo ID and project from URL if provided
1651+ todoIDStr , todoProjectID := extractWithProject (args [0 ])
16441652
16451653 todoID , err := strconv .ParseInt (todoIDStr , 10 , 64 )
16461654 if err != nil {
16471655 return output .ErrUsage ("Invalid todo ID" )
16481656 }
16491657
1650- err = app .Account ().Todos ().Reposition (cmd .Context (), todoID , position )
1658+ // Resolve destination todolist when --list is provided
1659+ var parentID * int64
1660+ if list != "" {
1661+ listIDStr , listProjectID := extractWithProject (list )
1662+
1663+ // Cross-project moves are not supported by the reposition endpoint
1664+ if todoProjectID != "" && listProjectID != "" && todoProjectID != listProjectID {
1665+ return output .ErrUsage ("Cannot move a todo to a list in a different project. " +
1666+ "Cross-project moves are not yet supported." )
1667+ }
1668+
1669+ listID , parseErr := strconv .ParseInt (listIDStr , 10 , 64 )
1670+ if parseErr != nil {
1671+ return output .ErrUsage ("Invalid todolist ID" )
1672+ }
1673+ parentID = & listID
1674+ }
1675+
1676+ err = app .Account ().Todos ().Reposition (cmd .Context (), todoID , position , parentID )
16511677 if err != nil {
16521678 return convertSDKError (err )
16531679 }
16541680
1681+ summary := fmt .Sprintf ("Moved todo #%d to position %d" , todoID , position )
1682+ if parentID != nil {
1683+ summary = fmt .Sprintf ("Moved todo #%d to list #%d at position %d" , todoID , * parentID , position )
1684+ }
1685+
16551686 return app .OK (map [string ]any {"repositioned" : true , "position" : position },
1656- output .WithSummary (fmt . Sprintf ( "Moved todo #%d to position %d" , todoID , position ) ),
1687+ output .WithSummary (summary ),
16571688 output .WithBreadcrumbs (
16581689 output.Breadcrumb {
16591690 Action : "show" ,
@@ -1667,6 +1698,7 @@ You can pass either a todo ID or a Basecamp URL:
16671698
16681699 cmd .Flags ().IntVar (& position , "to" , 0 , "Target position, 1-based (1 = top)" )
16691700 cmd .Flags ().IntVar (& position , "position" , 0 , "Target position (alias for --to)" )
1701+ cmd .Flags ().StringVarP (& list , "list" , "l" , "" , "Destination todolist ID or URL (move to a different list)" )
16701702
16711703 return cmd
16721704}
0 commit comments