trelloctl 0.2.3__tar.gz → 0.3.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: trelloctl
3
- Version: 0.2.3
3
+ Version: 0.3.0
4
4
  Summary: A command-line interface for Trello
5
5
  Author: Werner Robitza
6
6
  Author-email: Werner Robitza <werner.robitza@gmail.com>
@@ -170,10 +170,20 @@ Checklist management commands.
170
170
  | `checklist list <card_id>` | List all checklists and items on a card |
171
171
  | `checklist create <card_id> --name <name>` | Create a checklist on a card |
172
172
  | `checklist delete <checklist_id>` | Delete a checklist |
173
- | `checklist add-item <checklist_id> --name <name> [--checked]` | Add an item to a checklist |
173
+ | `checklist add-item <checklist_id> --name <name> [--checked] [--member <member>] [--due <date>] [--due-reminder <minutes>]` | Add an item to a checklist |
174
174
  | `checklist check <card_id> <item_id> [--uncheck]` | Mark a checklist item as complete or incomplete |
175
+ | `checklist assign <card_id> <item_id> <member>` | Assign a member to a checklist item |
176
+ | `checklist unassign <card_id> <item_id>` | Remove the assigned member from a checklist item |
177
+ | `checklist set-due <card_id> <item_id> <due> [--reminder <minutes>]` | Set or clear a checklist item's due date (`null` to clear) |
175
178
  | `checklist delete-item <checklist_id> <item_id>` | Delete a checklist item |
176
179
 
180
+ The `<member>` argument accepts a member ID, username, or full name (partial,
181
+ case-insensitive matching), the same way board, list, and card names are resolved.
182
+
183
+ Per-item members and due dates (`add-item --member`/`--due`, `assign`, `unassign`,
184
+ `set-due`) use Trello's advanced checklists, which require a paid Trello plan. On free
185
+ plans the API accepts the request without error but does not store the value.
186
+
177
187
  ### Multiple Profiles
178
188
 
179
189
  You can use multiple Trello accounts by specifying a profile:
@@ -157,10 +157,20 @@ Checklist management commands.
157
157
  | `checklist list <card_id>` | List all checklists and items on a card |
158
158
  | `checklist create <card_id> --name <name>` | Create a checklist on a card |
159
159
  | `checklist delete <checklist_id>` | Delete a checklist |
160
- | `checklist add-item <checklist_id> --name <name> [--checked]` | Add an item to a checklist |
160
+ | `checklist add-item <checklist_id> --name <name> [--checked] [--member <member>] [--due <date>] [--due-reminder <minutes>]` | Add an item to a checklist |
161
161
  | `checklist check <card_id> <item_id> [--uncheck]` | Mark a checklist item as complete or incomplete |
162
+ | `checklist assign <card_id> <item_id> <member>` | Assign a member to a checklist item |
163
+ | `checklist unassign <card_id> <item_id>` | Remove the assigned member from a checklist item |
164
+ | `checklist set-due <card_id> <item_id> <due> [--reminder <minutes>]` | Set or clear a checklist item's due date (`null` to clear) |
162
165
  | `checklist delete-item <checklist_id> <item_id>` | Delete a checklist item |
163
166
 
167
+ The `<member>` argument accepts a member ID, username, or full name (partial,
168
+ case-insensitive matching), the same way board, list, and card names are resolved.
169
+
170
+ Per-item members and due dates (`add-item --member`/`--due`, `assign`, `unassign`,
171
+ `set-due`) use Trello's advanced checklists, which require a paid Trello plan. On free
172
+ plans the API accepts the request without error but does not store the value.
173
+
164
174
  ### Multiple Profiles
165
175
 
166
176
  You can use multiple Trello accounts by specifying a profile:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "trelloctl"
3
- version = "0.2.3"
3
+ version = "0.3.0"
4
4
  description = "A command-line interface for Trello"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -178,6 +178,10 @@ class TrelloClient:
178
178
  """Get all checklists on a card."""
179
179
  return self.get(f"/cards/{card_id}/checklists")
180
180
 
181
+ def get_checklist(self, checklist_id: str) -> dict:
182
+ """Get a checklist by ID."""
183
+ return self.get(f"/checklists/{checklist_id}")
184
+
181
185
  def create_checklist(self, card_id: str, name: str) -> dict:
182
186
  """Create a checklist on a card."""
183
187
  return self.post(f"/cards/{card_id}/checklists", params={"name": name})
@@ -187,13 +191,30 @@ class TrelloClient:
187
191
  self.delete(f"/checklists/{checklist_id}")
188
192
 
189
193
  def add_checklist_item(
190
- self, checklist_id: str, name: str, checked: bool = False
194
+ self,
195
+ checklist_id: str,
196
+ name: str,
197
+ checked: bool = False,
198
+ id_member: str | None = None,
199
+ due: str | None = None,
200
+ due_reminder: int | None = None,
191
201
  ) -> dict:
192
- """Add an item to a checklist."""
193
- return self.post(
194
- f"/checklists/{checklist_id}/checkItems",
195
- params={"name": name, "checked": str(checked).lower()},
196
- )
202
+ """Add an item to a checklist.
203
+
204
+ If id_member or due is given, the item is assigned to that member or
205
+ given that due date. due_reminder is the number of minutes before the
206
+ due date to send a reminder. Per-item members and due dates require a
207
+ paid Trello plan (advanced checklists); on free plans the API accepts
208
+ the values but does not store them.
209
+ """
210
+ params: dict[str, str] = {"name": name, "checked": str(checked).lower()}
211
+ if id_member:
212
+ params["idMember"] = id_member
213
+ if due:
214
+ params["due"] = due
215
+ if due_reminder is not None:
216
+ params["dueReminder"] = str(due_reminder)
217
+ return self.post(f"/checklists/{checklist_id}/checkItems", params=params)
197
218
 
198
219
  def update_checklist_item(
199
220
  self, card_id: str, check_item_id: str, state: str
@@ -204,6 +225,42 @@ class TrelloClient:
204
225
  params={"state": state},
205
226
  )
206
227
 
228
+ def set_checklist_item_member(
229
+ self, card_id: str, check_item_id: str, id_member: str
230
+ ) -> dict:
231
+ """Assign a member to a checklist item.
232
+
233
+ Pass an empty string for id_member to remove the assigned member. A
234
+ checklist item holds at most one member. Requires a paid Trello plan
235
+ (advanced checklists); on free plans the value is silently dropped.
236
+ """
237
+ return self.put(
238
+ f"/cards/{card_id}/checkItem/{check_item_id}",
239
+ params={"idMember": id_member},
240
+ )
241
+
242
+ def set_checklist_item_due(
243
+ self,
244
+ card_id: str,
245
+ check_item_id: str,
246
+ due: str,
247
+ due_reminder: int | None = None,
248
+ ) -> dict:
249
+ """Set a checklist item's due date.
250
+
251
+ Pass an empty string for due to remove the due date. due_reminder is
252
+ the number of minutes before the due date to send a reminder (-1 to
253
+ clear it). Requires a paid Trello plan (advanced checklists); on free
254
+ plans the value is silently dropped.
255
+ """
256
+ params: dict[str, str] = {"due": due}
257
+ if due_reminder is not None:
258
+ params["dueReminder"] = str(due_reminder)
259
+ return self.put(
260
+ f"/cards/{card_id}/checkItem/{check_item_id}",
261
+ params=params,
262
+ )
263
+
207
264
  def delete_checklist_item(self, checklist_id: str, check_item_id: str) -> None:
208
265
  """Delete a checklist item."""
209
266
  self.delete(f"/checklists/{checklist_id}/checkItems/{check_item_id}")
@@ -0,0 +1,329 @@
1
+ """Checklist commands."""
2
+
3
+ from typing import Any
4
+
5
+ import click
6
+
7
+ from trelloctl.cli import Context, pass_context
8
+ from trelloctl.output import (
9
+ format_output,
10
+ print_error,
11
+ print_success,
12
+ print_warning,
13
+ )
14
+ from trelloctl.resolver import is_trello_id
15
+
16
+
17
+ @click.group()
18
+ def checklist() -> None:
19
+ """Checklist management commands."""
20
+ pass
21
+
22
+
23
+ @checklist.command("list")
24
+ @click.argument("card_id")
25
+ @pass_context
26
+ def list_checklists(ctx: Context, card_id: str) -> None:
27
+ """List all checklists and items on a card.
28
+
29
+ CARD_ID must be a Trello card ID.
30
+ """
31
+ client = ctx.ensure_client()
32
+
33
+ try:
34
+ checklists = client.get_card_checklists(card_id)
35
+ except Exception as e:
36
+ print_error(f"Failed to get checklists: {e}")
37
+ return
38
+
39
+ member_names = _resolve_item_members(client, card_id, checklists)
40
+
41
+ data = []
42
+ for cl in checklists:
43
+ items = cl.get("checkItems", [])
44
+ if not items:
45
+ data.append(
46
+ {
47
+ "checklist": cl["name"],
48
+ "checklist_id": cl["id"],
49
+ "item": "",
50
+ "item_id": "",
51
+ "state": "",
52
+ "member": "",
53
+ "due": "",
54
+ }
55
+ )
56
+ else:
57
+ for item in sorted(items, key=lambda i: i.get("pos", 0)):
58
+ state = "x" if item.get("state") == "complete" else " "
59
+ member_id = item.get("idMember") or ""
60
+ data.append(
61
+ {
62
+ "checklist": cl["name"],
63
+ "checklist_id": cl["id"],
64
+ "item": f"[{state}] {item['name']}",
65
+ "item_id": item["id"],
66
+ "state": item.get("state", ""),
67
+ "member": member_names.get(member_id, member_id),
68
+ "due": item.get("due") or "",
69
+ }
70
+ )
71
+
72
+ format_output(
73
+ data,
74
+ ctx.format,
75
+ columns=[
76
+ ("checklist", "Checklist"),
77
+ ("item", "Item"),
78
+ ("member", "Member"),
79
+ ("due", "Due"),
80
+ ("checklist_id", "Checklist ID"),
81
+ ("item_id", "Item ID"),
82
+ ],
83
+ title="Checklists",
84
+ template="{checklist}: {item}",
85
+ )
86
+
87
+
88
+ def _resolve_item_members(
89
+ client: Any, card_id: str, checklists: list[dict]
90
+ ) -> dict[str, str]:
91
+ """Build a map of member ID to display name for assigned checklist items.
92
+
93
+ Returns an empty map (so callers fall back to raw IDs) when no items are
94
+ assigned or the member lookup fails.
95
+ """
96
+ has_members = any(
97
+ item.get("idMember") for cl in checklists for item in cl.get("checkItems", [])
98
+ )
99
+ if not has_members:
100
+ return {}
101
+
102
+ try:
103
+ card = client.get_card(card_id)
104
+ members = client.get_board_members(card["idBoard"])
105
+ except Exception as e:
106
+ print_warning(f"Could not resolve member names, showing IDs instead: {e}")
107
+ return {}
108
+
109
+ return {m["id"]: m.get("fullName") or m.get("username") or m["id"] for m in members}
110
+
111
+
112
+ def _member_id_from_card(
113
+ ctx: Context, client: Any, card_id: str, member_ref: str
114
+ ) -> str:
115
+ """Resolve a member reference (ID, username, or name) via the card's board."""
116
+ if is_trello_id(member_ref):
117
+ return member_ref
118
+ card = client.get_card(card_id)
119
+ return ctx.resolver.resolve_member(card["idBoard"], member_ref)
120
+
121
+
122
+ def _member_id_from_checklist(
123
+ ctx: Context, client: Any, checklist_id: str, member_ref: str
124
+ ) -> str:
125
+ """Resolve a member reference (ID, username, or name) via the checklist's board."""
126
+ if is_trello_id(member_ref):
127
+ return member_ref
128
+ cl = client.get_checklist(checklist_id)
129
+ return ctx.resolver.resolve_member(cl["idBoard"], member_ref)
130
+
131
+
132
+ @checklist.command("create")
133
+ @click.argument("card_id")
134
+ @click.option("--name", "-n", required=True, help="Checklist name")
135
+ @pass_context
136
+ def create_checklist(ctx: Context, card_id: str, name: str) -> None:
137
+ """Create a checklist on a card.
138
+
139
+ CARD_ID must be a Trello card ID.
140
+ """
141
+ client = ctx.ensure_client()
142
+
143
+ try:
144
+ cl = client.create_checklist(card_id, name)
145
+ print_success(f"Created checklist: {cl['name']} ({cl['id']})")
146
+ except Exception as e:
147
+ print_error(f"Failed to create checklist: {e}")
148
+
149
+
150
+ @checklist.command("delete")
151
+ @click.argument("checklist_id")
152
+ @click.confirmation_option(prompt="Are you sure you want to delete this checklist?")
153
+ @pass_context
154
+ def delete_checklist(ctx: Context, checklist_id: str) -> None:
155
+ """Delete a checklist."""
156
+ client = ctx.ensure_client()
157
+
158
+ try:
159
+ client.delete_checklist(checklist_id)
160
+ print_success(f"Deleted checklist: {checklist_id}")
161
+ except Exception as e:
162
+ print_error(f"Failed to delete checklist: {e}")
163
+
164
+
165
+ @checklist.command("add-item")
166
+ @click.argument("checklist_id")
167
+ @click.option("--name", "-n", required=True, help="Item name")
168
+ @click.option("--checked", is_flag=True, help="Mark as checked")
169
+ @click.option(
170
+ "--member",
171
+ "-m",
172
+ help="Member ID, username, or name to assign (see 'board members')",
173
+ )
174
+ @click.option("--due", help="Due date (ISO format, e.g. 2026-07-01)")
175
+ @click.option(
176
+ "--due-reminder",
177
+ type=int,
178
+ help="Minutes before the due date to send a reminder",
179
+ )
180
+ @pass_context
181
+ def add_item(
182
+ ctx: Context,
183
+ checklist_id: str,
184
+ name: str,
185
+ checked: bool,
186
+ member: str | None,
187
+ due: str | None,
188
+ due_reminder: int | None,
189
+ ) -> None:
190
+ """Add an item to a checklist.
191
+
192
+ Use --member and --due to assign a member or due date to the item; both
193
+ require a paid Trello plan (advanced checklists).
194
+ """
195
+ client = ctx.ensure_client()
196
+
197
+ try:
198
+ member_id = (
199
+ _member_id_from_checklist(ctx, client, checklist_id, member)
200
+ if member
201
+ else None
202
+ )
203
+ item = client.add_checklist_item(
204
+ checklist_id,
205
+ name,
206
+ checked=checked,
207
+ id_member=member_id,
208
+ due=due,
209
+ due_reminder=due_reminder,
210
+ )
211
+ print_success(f"Added item: {item['name']} ({item['id']})")
212
+ except Exception as e:
213
+ print_error(f"Failed to add item: {e}")
214
+
215
+
216
+ @checklist.command("assign")
217
+ @click.argument("card_id")
218
+ @click.argument("item_id")
219
+ @click.argument("member")
220
+ @pass_context
221
+ def assign_item(ctx: Context, card_id: str, item_id: str, member: str) -> None:
222
+ """Assign a member to a checklist item.
223
+
224
+ CARD_ID is the card containing the item.
225
+ ITEM_ID is the checklist item to assign.
226
+ MEMBER is the member ID, username, or name (see 'board members').
227
+
228
+ Requires a paid Trello plan (advanced checklists).
229
+ """
230
+ client = ctx.ensure_client()
231
+
232
+ try:
233
+ member_id = _member_id_from_card(ctx, client, card_id, member)
234
+ client.set_checklist_item_member(card_id, item_id, member_id)
235
+ print_success(f"Assigned {member} to item {item_id}")
236
+ except Exception as e:
237
+ print_error(f"Failed to assign member: {e}")
238
+
239
+
240
+ @checklist.command("unassign")
241
+ @click.argument("card_id")
242
+ @click.argument("item_id")
243
+ @pass_context
244
+ def unassign_item(ctx: Context, card_id: str, item_id: str) -> None:
245
+ """Remove the assigned member from a checklist item.
246
+
247
+ CARD_ID is the card containing the item.
248
+ ITEM_ID is the checklist item to clear.
249
+ """
250
+ client = ctx.ensure_client()
251
+
252
+ try:
253
+ client.set_checklist_item_member(card_id, item_id, "")
254
+ print_success(f"Removed assigned member from item {item_id}")
255
+ except Exception as e:
256
+ print_error(f"Failed to unassign member: {e}")
257
+
258
+
259
+ @checklist.command("set-due")
260
+ @click.argument("card_id")
261
+ @click.argument("item_id")
262
+ @click.argument("due")
263
+ @click.option(
264
+ "--reminder",
265
+ type=int,
266
+ help="Minutes before the due date to send a reminder (-1 to clear)",
267
+ )
268
+ @pass_context
269
+ def set_item_due(
270
+ ctx: Context, card_id: str, item_id: str, due: str, reminder: int | None
271
+ ) -> None:
272
+ """Set or clear the due date on a checklist item.
273
+
274
+ CARD_ID is the card containing the item.
275
+ ITEM_ID is the checklist item to update.
276
+ DUE is an ISO date (e.g. 2026-07-01), or 'null' to remove the due date.
277
+
278
+ Requires a paid Trello plan (advanced checklists).
279
+ """
280
+ client = ctx.ensure_client()
281
+ due_value = "" if due.lower() == "null" else due
282
+
283
+ try:
284
+ client.set_checklist_item_due(
285
+ card_id, item_id, due_value, due_reminder=reminder
286
+ )
287
+ if due_value:
288
+ print_success(f"Set due date on item {item_id} to {due_value}")
289
+ else:
290
+ print_success(f"Removed due date from item {item_id}")
291
+ except Exception as e:
292
+ print_error(f"Failed to set due date: {e}")
293
+
294
+
295
+ @checklist.command("check")
296
+ @click.argument("card_id")
297
+ @click.argument("item_id")
298
+ @click.option("--uncheck", is_flag=True, help="Mark as incomplete instead")
299
+ @pass_context
300
+ def check_item(ctx: Context, card_id: str, item_id: str, uncheck: bool) -> None:
301
+ """Mark a checklist item as complete (or incomplete with --uncheck).
302
+
303
+ CARD_ID is the card containing the item.
304
+ ITEM_ID is the checklist item to update.
305
+ """
306
+ client = ctx.ensure_client()
307
+ state = "incomplete" if uncheck else "complete"
308
+
309
+ try:
310
+ client.update_checklist_item(card_id, item_id, state)
311
+ action = "Unchecked" if uncheck else "Checked"
312
+ print_success(f"{action} item: {item_id}")
313
+ except Exception as e:
314
+ print_error(f"Failed to update item: {e}")
315
+
316
+
317
+ @checklist.command("delete-item")
318
+ @click.argument("checklist_id")
319
+ @click.argument("item_id")
320
+ @pass_context
321
+ def delete_item(ctx: Context, checklist_id: str, item_id: str) -> None:
322
+ """Delete a checklist item."""
323
+ client = ctx.ensure_client()
324
+
325
+ try:
326
+ client.delete_checklist_item(checklist_id, item_id)
327
+ print_success(f"Deleted item: {item_id}")
328
+ except Exception as e:
329
+ print_error(f"Failed to delete item: {e}")
@@ -8,6 +8,11 @@ if TYPE_CHECKING:
8
8
  from trelloctl.client import TrelloClient
9
9
 
10
10
 
11
+ def is_trello_id(ref: str) -> bool:
12
+ """Return True if ref looks like a 24-character hex Trello ID."""
13
+ return len(ref) == 24 and all(c in "0123456789abcdef" for c in ref.lower())
14
+
15
+
11
16
  class Resolver:
12
17
  """Resolves names to Trello IDs."""
13
18
 
@@ -15,6 +20,7 @@ class Resolver:
15
20
  self.client = client
16
21
  self._boards_cache: list[dict] | None = None
17
22
  self._lists_cache: dict[str, list[dict]] = {}
23
+ self._members_cache: dict[str, list[dict]] = {}
18
24
 
19
25
  def _get_boards(self) -> list[dict]:
20
26
  """Get all boards (cached)."""
@@ -30,6 +36,12 @@ class Resolver:
30
36
  )
31
37
  return self._lists_cache[board_id]
32
38
 
39
+ def _get_members(self, board_id: str) -> list[dict]:
40
+ """Get all members for a board (cached)."""
41
+ if board_id not in self._members_cache:
42
+ self._members_cache[board_id] = self.client.get_board_members(board_id)
43
+ return self._members_cache[board_id]
44
+
33
45
  def resolve_board(self, board_ref: str) -> str:
34
46
  """Resolve a board reference (ID or name) to an ID.
35
47
 
@@ -43,9 +55,7 @@ class Resolver:
43
55
  ValueError: If board not found or multiple matches
44
56
  """
45
57
  # If it looks like an ID (24 hex chars), try it directly
46
- if len(board_ref) == 24 and all(
47
- c in "0123456789abcdef" for c in board_ref.lower()
48
- ):
58
+ if is_trello_id(board_ref):
49
59
  return board_ref
50
60
 
51
61
  boards = self._get_boards()
@@ -82,9 +92,7 @@ class Resolver:
82
92
  board_id = self.resolve_board(board_ref)
83
93
 
84
94
  # If it looks like an ID, try it directly
85
- if len(list_ref) == 24 and all(
86
- c in "0123456789abcdef" for c in list_ref.lower()
87
- ):
95
+ if is_trello_id(list_ref):
88
96
  return list_ref
89
97
 
90
98
  lists = self._get_lists(board_id)
@@ -124,9 +132,7 @@ class Resolver:
124
132
  list_id = self.resolve_list(board_ref, list_ref)
125
133
 
126
134
  # If it looks like an ID, try it directly
127
- if len(card_ref) == 24 and all(
128
- c in "0123456789abcdef" for c in card_ref.lower()
129
- ):
135
+ if is_trello_id(card_ref):
130
136
  return card_ref
131
137
 
132
138
  cards = self.client.get_list_cards(list_id)
@@ -146,3 +152,59 @@ class Resolver:
146
152
  raise ValueError(f"Multiple cards match '{card_ref}': {', '.join(names)}")
147
153
 
148
154
  raise ValueError(f"Card not found: '{card_ref}' in list '{list_ref}'")
155
+
156
+ def resolve_member(self, board_ref: str, member_ref: str) -> str:
157
+ """Resolve a member reference (ID, username, or full name) to an ID.
158
+
159
+ Args:
160
+ board_ref: Board ID or name the member belongs to
161
+ member_ref: Member ID, username, or full name (partial,
162
+ case-insensitive)
163
+
164
+ Returns:
165
+ Member ID
166
+
167
+ Raises:
168
+ ValueError: If member not found or multiple matches
169
+ """
170
+ # If it looks like an ID, try it directly
171
+ if is_trello_id(member_ref):
172
+ return member_ref
173
+
174
+ board_id = self.resolve_board(board_ref)
175
+ members = self._get_members(board_id)
176
+ member_ref_lower = member_ref.lower()
177
+
178
+ # Try exact username match first
179
+ username_matches = [
180
+ m for m in members if m.get("username", "").lower() == member_ref_lower
181
+ ]
182
+ if len(username_matches) == 1:
183
+ return username_matches[0]["id"]
184
+
185
+ # Then exact full name match
186
+ fullname_matches = [
187
+ m for m in members if m.get("fullName", "").lower() == member_ref_lower
188
+ ]
189
+ if len(fullname_matches) == 1:
190
+ return fullname_matches[0]["id"]
191
+
192
+ # Fall back to partial match on username or full name
193
+ partial_matches = [
194
+ m
195
+ for m in members
196
+ if member_ref_lower in m.get("username", "").lower()
197
+ or member_ref_lower in m.get("fullName", "").lower()
198
+ ]
199
+ if len(partial_matches) == 1:
200
+ return partial_matches[0]["id"]
201
+ if len(partial_matches) > 1:
202
+ names = [
203
+ m.get("username") or m.get("fullName") or m["id"]
204
+ for m in partial_matches
205
+ ]
206
+ raise ValueError(
207
+ f"Multiple members match '{member_ref}': {', '.join(names)}"
208
+ )
209
+
210
+ raise ValueError(f"Member not found: '{member_ref}' in board '{board_ref}'")
@@ -1,154 +0,0 @@
1
- """Checklist commands."""
2
-
3
- import click
4
-
5
- from trelloctl.cli import Context, pass_context
6
- from trelloctl.output import format_output, print_error, print_success
7
-
8
-
9
- @click.group()
10
- def checklist() -> None:
11
- """Checklist management commands."""
12
- pass
13
-
14
-
15
- @checklist.command("list")
16
- @click.argument("card_id")
17
- @pass_context
18
- def list_checklists(ctx: Context, card_id: str) -> None:
19
- """List all checklists and items on a card.
20
-
21
- CARD_ID must be a Trello card ID.
22
- """
23
- client = ctx.ensure_client()
24
-
25
- try:
26
- checklists = client.get_card_checklists(card_id)
27
- except Exception as e:
28
- print_error(f"Failed to get checklists: {e}")
29
- return
30
-
31
- data = []
32
- for cl in checklists:
33
- items = cl.get("checkItems", [])
34
- if not items:
35
- data.append(
36
- {
37
- "checklist": cl["name"],
38
- "checklist_id": cl["id"],
39
- "item": "",
40
- "item_id": "",
41
- "state": "",
42
- }
43
- )
44
- else:
45
- for item in sorted(items, key=lambda i: i.get("pos", 0)):
46
- state = "x" if item.get("state") == "complete" else " "
47
- data.append(
48
- {
49
- "checklist": cl["name"],
50
- "checklist_id": cl["id"],
51
- "item": f"[{state}] {item['name']}",
52
- "item_id": item["id"],
53
- "state": item.get("state", ""),
54
- }
55
- )
56
-
57
- format_output(
58
- data,
59
- ctx.format,
60
- columns=[
61
- ("checklist", "Checklist"),
62
- ("item", "Item"),
63
- ("checklist_id", "Checklist ID"),
64
- ("item_id", "Item ID"),
65
- ],
66
- title="Checklists",
67
- template="{checklist}: {item}",
68
- )
69
-
70
-
71
- @checklist.command("create")
72
- @click.argument("card_id")
73
- @click.option("--name", "-n", required=True, help="Checklist name")
74
- @pass_context
75
- def create_checklist(ctx: Context, card_id: str, name: str) -> None:
76
- """Create a checklist on a card.
77
-
78
- CARD_ID must be a Trello card ID.
79
- """
80
- client = ctx.ensure_client()
81
-
82
- try:
83
- cl = client.create_checklist(card_id, name)
84
- print_success(f"Created checklist: {cl['name']} ({cl['id']})")
85
- except Exception as e:
86
- print_error(f"Failed to create checklist: {e}")
87
-
88
-
89
- @checklist.command("delete")
90
- @click.argument("checklist_id")
91
- @click.confirmation_option(prompt="Are you sure you want to delete this checklist?")
92
- @pass_context
93
- def delete_checklist(ctx: Context, checklist_id: str) -> None:
94
- """Delete a checklist."""
95
- client = ctx.ensure_client()
96
-
97
- try:
98
- client.delete_checklist(checklist_id)
99
- print_success(f"Deleted checklist: {checklist_id}")
100
- except Exception as e:
101
- print_error(f"Failed to delete checklist: {e}")
102
-
103
-
104
- @checklist.command("add-item")
105
- @click.argument("checklist_id")
106
- @click.option("--name", "-n", required=True, help="Item name")
107
- @click.option("--checked", is_flag=True, help="Mark as checked")
108
- @pass_context
109
- def add_item(ctx: Context, checklist_id: str, name: str, checked: bool) -> None:
110
- """Add an item to a checklist."""
111
- client = ctx.ensure_client()
112
-
113
- try:
114
- item = client.add_checklist_item(checklist_id, name, checked=checked)
115
- print_success(f"Added item: {item['name']} ({item['id']})")
116
- except Exception as e:
117
- print_error(f"Failed to add item: {e}")
118
-
119
-
120
- @checklist.command("check")
121
- @click.argument("card_id")
122
- @click.argument("item_id")
123
- @click.option("--uncheck", is_flag=True, help="Mark as incomplete instead")
124
- @pass_context
125
- def check_item(ctx: Context, card_id: str, item_id: str, uncheck: bool) -> None:
126
- """Mark a checklist item as complete (or incomplete with --uncheck).
127
-
128
- CARD_ID is the card containing the item.
129
- ITEM_ID is the checklist item to update.
130
- """
131
- client = ctx.ensure_client()
132
- state = "incomplete" if uncheck else "complete"
133
-
134
- try:
135
- client.update_checklist_item(card_id, item_id, state)
136
- action = "Unchecked" if uncheck else "Checked"
137
- print_success(f"{action} item: {item_id}")
138
- except Exception as e:
139
- print_error(f"Failed to update item: {e}")
140
-
141
-
142
- @checklist.command("delete-item")
143
- @click.argument("checklist_id")
144
- @click.argument("item_id")
145
- @pass_context
146
- def delete_item(ctx: Context, checklist_id: str, item_id: str) -> None:
147
- """Delete a checklist item."""
148
- client = ctx.ensure_client()
149
-
150
- try:
151
- client.delete_checklist_item(checklist_id, item_id)
152
- print_success(f"Deleted item: {item_id}")
153
- except Exception as e:
154
- print_error(f"Failed to delete item: {e}")