Skip to content

Commit d6d2eff

Browse files
committed
Add --list flag to todos position for cross-list moves
Support moving a todo to a different todolist within the same project via the reposition endpoint's parent_id field. Detects and rejects cross-project moves with guidance. Blocked on SDK exposing parentID on Reposition — will compile after rebasing onto main with the new SDK release. Closes #337
1 parent 2009404 commit d6d2eff

2 files changed

Lines changed: 41 additions & 8 deletions

File tree

internal/commands/todos.go

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,17 +1610,25 @@ func reopenTodos(cmd *cobra.Command, todoIDs []string) error {
16101610
}
16111611

16121612
func 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
16211625
You 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
}

skills/basecamp/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ basecamp unassign <id> [id...] --card --from <person> --in <project> # Remove ca
352352
basecamp assign <id> [id...] --step --to <person> --in <project> # Assign card step
353353
basecamp unassign <id> [id...] --step --from <person> --in <project> # Remove step assignee
354354
basecamp todos position <id> --to 1 # Move to top
355+
basecamp todos position <id> --to 1 --list <list> # Move to different list
355356
basecamp todos sweep --overdue --complete --comment "Done" --in <project>
356357
```
357358

0 commit comments

Comments
 (0)