@krodak/clickup-cli 1.27.1 → 1.29.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clickup-cli",
3
3
  "description": "ClickUp CLI skills for managing tasks, sprints, comments, checklists, custom fields, tags, and time tracking via the cup command",
4
- "version": "1.27.1",
4
+ "version": "1.29.0",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/README.md CHANGED
@@ -185,8 +185,8 @@ Full CRUD for the core ClickUp workflow:
185
185
  | 🏃 **Sprints** | Auto-detect active sprint, `sprint:current` pseudo-ID for move/create, flexible date parsing, config override, favorite sprint folders |
186
186
  | ⭐ **Favorites** | Local favorites for quick access to sprint folders, spaces, lists, folders, views, tasks |
187
187
  | 👁️ **Views** | List, get, create, update, delete views on lists |
188
- | 🏢 **Workspace** | Spaces, folders, lists (read + create + rename + from template), members, task types, templates |
189
- | 📎 **Attachments** | Upload files to tasks, shown in detail views |
188
+ | 🏢 **Workspace** | Spaces, folders, lists (full CRUD + rename + from template), members, task types, templates, plan |
189
+ | 📎 **Attachments** | Upload files to tasks, list task attachments, shown in detail views |
190
190
 
191
191
  [Full API coverage details](docs/api-coverage.md) | [Command reference](docs/commands.md)
192
192
 
package/dist/index.js CHANGED
@@ -445,6 +445,28 @@ var ClickUpClient = class {
445
445
  async deleteTask(taskId) {
446
446
  await this.request(this.taskPath(taskId), { method: "DELETE" });
447
447
  }
448
+ async deleteList(listId) {
449
+ await this.request(`/list/${listId}`, { method: "DELETE" });
450
+ }
451
+ async deleteFolder(folderId) {
452
+ await this.request(`/folder/${folderId}`, { method: "DELETE" });
453
+ }
454
+ async deleteSpace(spaceId) {
455
+ await this.request(`/space/${spaceId}`, { method: "DELETE" });
456
+ }
457
+ async getTaskMembers(taskId) {
458
+ const data = await this.request(this.taskPath(taskId, "/member"));
459
+ return readCollectionField(data, "members", "task members");
460
+ }
461
+ async getWorkspacePlan() {
462
+ return this.request(`/team/${this.teamId}/plan`);
463
+ }
464
+ async getTaskAttachments(taskId) {
465
+ const data = await this.requestV3(
466
+ `/workspaces/${this.teamId}/tasks/${taskId}/attachments`
467
+ );
468
+ return expectArrayField(data, "data", "task attachments");
469
+ }
448
470
  async addTagToTask(taskId, tagName) {
449
471
  await this.request(this.taskPath(taskId, `/tag/${encodeURIComponent(tagName)}`), {
450
472
  method: "POST"
@@ -1993,27 +2015,54 @@ function parsePriority(value) {
1993
2015
  if (Number.isInteger(num) && num >= 1 && num <= 4) return num;
1994
2016
  throw new Error("Priority must be urgent, high, normal, low, or 1-4");
1995
2017
  }
2018
+ var DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
2019
+ var LOCAL_DATETIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/;
2020
+ var ISO_WITH_OFFSET_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})$/;
1996
2021
  function parseDueDate(value, timezone) {
1997
- if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
1998
- throw new Error("Date must be in YYYY-MM-DD format");
2022
+ if (DATE_ONLY_RE.test(value)) {
2023
+ const parts = value.split("-").map(Number);
2024
+ const y = parts[0];
2025
+ const m = parts[1];
2026
+ const d = parts[2];
2027
+ return { ms: wallClockToMs(y, m, d, 0, 0, 0, timezone, value), hasTime: false };
2028
+ }
2029
+ const localMatch = LOCAL_DATETIME_RE.exec(value);
2030
+ if (localMatch) {
2031
+ const y = Number(localMatch[1]);
2032
+ const m = Number(localMatch[2]);
2033
+ const d = Number(localMatch[3]);
2034
+ const hh = Number(localMatch[4]);
2035
+ const mm = Number(localMatch[5]);
2036
+ const ss = localMatch[6] !== void 0 ? Number(localMatch[6]) : 0;
2037
+ if (hh > 23 || mm > 59 || ss > 59) {
2038
+ throw new Error(
2039
+ "Date must be in YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 format (time component out of range)"
2040
+ );
2041
+ }
2042
+ return { ms: wallClockToMs(y, m, d, hh, mm, ss, timezone, value), hasTime: true };
1999
2043
  }
2000
- const parts = value.split("-").map(Number);
2001
- const y = parts[0];
2002
- const m = parts[1];
2003
- const d = parts[2];
2044
+ if (ISO_WITH_OFFSET_RE.test(value)) {
2045
+ const ms = Date.parse(value);
2046
+ if (!isNaN(ms)) return { ms, hasTime: true };
2047
+ }
2048
+ throw new Error(
2049
+ "Date must be in YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 format (e.g. 2025-03-15T14:30 or 2025-03-15T14:30:00+08:00)"
2050
+ );
2051
+ }
2052
+ function wallClockToMs(year, month, day, hour, minute, second, timezone, rawValue) {
2004
2053
  if (timezone) {
2005
2054
  try {
2006
- const ms2 = dateToTimezoneMs(y, m, d, timezone);
2055
+ const ms2 = wallClockToTimezoneMs(year, month, day, hour, minute, second, timezone);
2007
2056
  if (!isNaN(ms2)) return ms2;
2008
2057
  } catch {
2009
2058
  }
2010
2059
  }
2011
- const ms = Date.UTC(y, m - 1, d);
2012
- if (isNaN(ms)) throw new Error(`Invalid date: ${value}`);
2060
+ const ms = Date.UTC(year, month - 1, day, hour, minute, second);
2061
+ if (isNaN(ms)) throw new Error(`Invalid date: ${rawValue}`);
2013
2062
  return ms;
2014
2063
  }
2015
- function dateToTimezoneMs(year, month, day, timezone) {
2016
- const approxUtc = new Date(Date.UTC(year, month - 1, day));
2064
+ function wallClockToTimezoneMs(year, month, day, hour, minute, second, timezone) {
2065
+ const approxUtc = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
2017
2066
  const tzStr = approxUtc.toLocaleString("en-US", {
2018
2067
  timeZone: timezone,
2019
2068
  year: "numeric",
@@ -2074,13 +2123,15 @@ function buildUpdatePayload(opts, timezone) {
2074
2123
  if (opts.dueDate === "none" || opts.dueDate === "clear") {
2075
2124
  payload.due_date = null;
2076
2125
  } else {
2077
- payload.due_date = parseDueDate(opts.dueDate, timezone);
2078
- payload.due_date_time = false;
2126
+ const parsed = parseDueDate(opts.dueDate, timezone);
2127
+ payload.due_date = parsed.ms;
2128
+ payload.due_date_time = parsed.hasTime;
2079
2129
  }
2080
2130
  }
2081
2131
  if (opts.startDate !== void 0) {
2082
- payload.start_date = parseDueDate(opts.startDate, timezone);
2083
- payload.start_date_time = false;
2132
+ const parsed = parseDueDate(opts.startDate, timezone);
2133
+ payload.start_date = parsed.ms;
2134
+ payload.start_date_time = parsed.hasTime;
2084
2135
  }
2085
2136
  if (opts.assignee !== void 0 || opts.removeAssignee !== void 0) {
2086
2137
  payload.assignees = {};
@@ -2180,12 +2231,14 @@ async function createTask(config, options) {
2180
2231
  payload.priority = parsePriority(options.priority);
2181
2232
  }
2182
2233
  if (options.dueDate !== void 0) {
2183
- payload.due_date = parseDueDate(options.dueDate, timezone);
2184
- payload.due_date_time = false;
2234
+ const parsed = parseDueDate(options.dueDate, timezone);
2235
+ payload.due_date = parsed.ms;
2236
+ payload.due_date_time = parsed.hasTime;
2185
2237
  }
2186
2238
  if (options.startDate !== void 0) {
2187
- payload.start_date = parseDueDate(options.startDate, timezone);
2188
- payload.start_date_time = false;
2239
+ const parsed = parseDueDate(options.startDate, timezone);
2240
+ payload.start_date = parsed.ms;
2241
+ payload.start_date_time = parsed.hasTime;
2189
2242
  }
2190
2243
  if (options.assignee !== void 0) {
2191
2244
  payload.assignees = [parseAssigneeId(options.assignee)];
@@ -3857,6 +3910,60 @@ var commandMetadata = [
3857
3910
  flags: ["--confirm", "--json"],
3858
3911
  quickReference: [{ section: "write", usage: "delete <taskId>", description: "Delete a task" }]
3859
3912
  },
3913
+ {
3914
+ name: "list-delete",
3915
+ description: "Delete a list (requires confirmation)",
3916
+ flags: ["--confirm", "--json"],
3917
+ quickReference: [
3918
+ { section: "write", usage: "list-delete <listId>", description: "Delete a list" }
3919
+ ]
3920
+ },
3921
+ {
3922
+ name: "folder-delete",
3923
+ description: "Delete a folder (requires confirmation)",
3924
+ flags: ["--confirm", "--json"],
3925
+ quickReference: [
3926
+ { section: "write", usage: "folder-delete <folderId>", description: "Delete a folder" }
3927
+ ]
3928
+ },
3929
+ {
3930
+ name: "space-delete",
3931
+ description: "Delete a space (requires confirmation)",
3932
+ flags: ["--confirm", "--json"],
3933
+ quickReference: [
3934
+ { section: "write", usage: "space-delete <spaceId>", description: "Delete a space" }
3935
+ ]
3936
+ },
3937
+ {
3938
+ name: "attachments",
3939
+ description: "List attachments on a task",
3940
+ flags: ["--json"],
3941
+ quickReference: [
3942
+ {
3943
+ section: "read",
3944
+ usage: "attachments <taskId>",
3945
+ description: "List attachments on a task"
3946
+ }
3947
+ ]
3948
+ },
3949
+ {
3950
+ name: "task-members",
3951
+ description: "List members with access to a task",
3952
+ flags: ["--json"],
3953
+ quickReference: [
3954
+ {
3955
+ section: "read",
3956
+ usage: "task-members <taskId>",
3957
+ description: "List members with access to a task"
3958
+ }
3959
+ ]
3960
+ },
3961
+ {
3962
+ name: "plan",
3963
+ description: "Show workspace plan",
3964
+ flags: ["--json"],
3965
+ quickReference: [{ section: "read", usage: "plan", description: "Show workspace plan" }]
3966
+ },
3860
3967
  {
3861
3968
  name: "tag",
3862
3969
  description: "Add or remove tags from a task",
@@ -6119,6 +6226,144 @@ async function deleteTaskCommand(config, taskId, opts) {
6119
6226
  return { taskId, deleted: true };
6120
6227
  }
6121
6228
 
6229
+ // src/commands/list-delete.ts
6230
+ async function deleteListCommand(config, listId, opts) {
6231
+ const client = new ClickUpClient(config);
6232
+ if (!opts.confirm) {
6233
+ if (!isTTY()) {
6234
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
6235
+ }
6236
+ const list = await client.getListWithStatuses(listId);
6237
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
6238
+ const confirmed = await confirm3({
6239
+ message: `Delete list "${list.name}" (${listId})? This cannot be undone.`,
6240
+ default: false
6241
+ });
6242
+ if (!confirmed) {
6243
+ throw new Error("Cancelled");
6244
+ }
6245
+ }
6246
+ await client.deleteList(listId);
6247
+ return { listId, deleted: true };
6248
+ }
6249
+
6250
+ // src/commands/folder-delete.ts
6251
+ async function deleteFolderCommand(config, folderId, opts) {
6252
+ const client = new ClickUpClient(config);
6253
+ if (!opts.confirm) {
6254
+ if (!isTTY()) {
6255
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
6256
+ }
6257
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
6258
+ const confirmed = await confirm3({
6259
+ message: `Delete folder ${folderId}? This cannot be undone.`,
6260
+ default: false
6261
+ });
6262
+ if (!confirmed) {
6263
+ throw new Error("Cancelled");
6264
+ }
6265
+ }
6266
+ await client.deleteFolder(folderId);
6267
+ return { folderId, deleted: true };
6268
+ }
6269
+
6270
+ // src/commands/space-delete.ts
6271
+ async function deleteSpaceCommand(config, spaceId, opts) {
6272
+ const client = new ClickUpClient(config);
6273
+ if (!opts.confirm) {
6274
+ if (!isTTY()) {
6275
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
6276
+ }
6277
+ const space = await client.getSpaceWithStatuses(spaceId);
6278
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
6279
+ const confirmed = await confirm3({
6280
+ message: `Delete space "${space.name}" (${spaceId})? This cannot be undone.`,
6281
+ default: false
6282
+ });
6283
+ if (!confirmed) {
6284
+ throw new Error("Cancelled");
6285
+ }
6286
+ }
6287
+ await client.deleteSpace(spaceId);
6288
+ return { spaceId, deleted: true };
6289
+ }
6290
+
6291
+ // src/commands/attachments.ts
6292
+ function formatSize(size) {
6293
+ if (size < 1024) return `${size} B`;
6294
+ if (size < 1048576) return `${(size / 1024).toFixed(1)} KB`;
6295
+ return `${(size / 1048576).toFixed(1)} MB`;
6296
+ }
6297
+ var ATTACHMENT_COLUMNS = [
6298
+ { key: "title", label: "Title", maxWidth: 40 },
6299
+ { key: "extension", label: "Ext", maxWidth: 10 },
6300
+ { key: "size", label: "Size", maxWidth: 10 },
6301
+ { key: "date", label: "Date", maxWidth: 12 },
6302
+ { key: "url", label: "URL", maxWidth: 60 }
6303
+ ];
6304
+ function toRow(a) {
6305
+ return {
6306
+ title: a.title,
6307
+ extension: a.extension,
6308
+ size: formatSize(a.size),
6309
+ date: new Date(a.date_created).toLocaleDateString(),
6310
+ url: a.url
6311
+ };
6312
+ }
6313
+ async function listTaskAttachments(config, taskId) {
6314
+ const client = new ClickUpClient(config);
6315
+ return client.getTaskAttachments(taskId);
6316
+ }
6317
+ function formatAttachmentsTable(attachments) {
6318
+ if (attachments.length === 0) return "No attachments found";
6319
+ const rows = attachments.map(toRow);
6320
+ return formatTable(rows, ATTACHMENT_COLUMNS);
6321
+ }
6322
+ function formatAttachmentsMarkdown(attachments) {
6323
+ if (attachments.length === 0) return "No attachments found";
6324
+ return attachments.map((a) => `- **${a.title}** (${a.extension}, ${formatSize(a.size)}) \u2014 ${a.url}`).join("\n");
6325
+ }
6326
+
6327
+ // src/commands/task-members.ts
6328
+ var TASK_MEMBER_COLUMNS = [
6329
+ { key: "username", label: "Username", maxWidth: 25 },
6330
+ { key: "id", label: "ID", maxWidth: 15 },
6331
+ { key: "email", label: "Email", maxWidth: 40 }
6332
+ ];
6333
+ async function listTaskMembers(config, taskId) {
6334
+ const client = new ClickUpClient(config);
6335
+ return client.getTaskMembers(taskId);
6336
+ }
6337
+ function formatTaskMembers(members) {
6338
+ if (members.length === 0) return "No task members found";
6339
+ const rows = members.map((m) => ({
6340
+ username: m.username,
6341
+ id: String(m.id),
6342
+ email: m.email
6343
+ }));
6344
+ return formatTable(rows, TASK_MEMBER_COLUMNS);
6345
+ }
6346
+ function formatTaskMembersMarkdown(members) {
6347
+ if (members.length === 0) return "No task members found";
6348
+ return members.map((m) => `- **${m.username}** (${m.id}) - ${m.email}`).join("\n");
6349
+ }
6350
+
6351
+ // src/commands/plan.ts
6352
+ import chalk9 from "chalk";
6353
+ async function getWorkspacePlanCommand(config) {
6354
+ const client = new ClickUpClient(config);
6355
+ return client.getWorkspacePlan();
6356
+ }
6357
+ function formatPlan(plan) {
6358
+ return [
6359
+ `${chalk9.bold("Plan:")} ${plan.name}`,
6360
+ `${chalk9.bold("Plan ID:")} ${plan.plan_id}`
6361
+ ].join("\n");
6362
+ }
6363
+ function formatPlanMarkdown(plan) {
6364
+ return [`**Plan:** ${plan.name}`, `**Plan ID:** ${plan.plan_id}`].join("\n");
6365
+ }
6366
+
6122
6367
  // src/commands/archive.ts
6123
6368
  async function archiveTaskCommand(config, taskId, opts) {
6124
6369
  const client = new ClickUpClient(config);
@@ -6179,7 +6424,7 @@ async function manageTags(config, taskId, opts) {
6179
6424
  }
6180
6425
 
6181
6426
  // src/commands/checklist.ts
6182
- import chalk9 from "chalk";
6427
+ import chalk10 from "chalk";
6183
6428
  async function viewChecklists(config, taskId) {
6184
6429
  const client = new ClickUpClient(config);
6185
6430
  const task = await client.getTask(taskId);
@@ -6229,19 +6474,19 @@ function formatChecklists(checklists) {
6229
6474
  const lines = [];
6230
6475
  const renderItem = (item, depth) => {
6231
6476
  const indent = " ".repeat(depth + 1);
6232
- const check = item.resolved ? chalk9.green("[x]") : chalk9.dim("[ ]");
6233
- const name = item.resolved ? chalk9.dim(item.name) : item.name;
6234
- const assignee = item.assignee ? chalk9.dim(` @${item.assignee.username}`) : "";
6477
+ const check = item.resolved ? chalk10.green("[x]") : chalk10.dim("[ ]");
6478
+ const name = item.resolved ? chalk10.dim(item.name) : item.name;
6479
+ const assignee = item.assignee ? chalk10.dim(` @${item.assignee.username}`) : "";
6235
6480
  lines.push(`${indent}${check} ${name}${assignee}`);
6236
- lines.push(chalk9.dim(`${indent} item-id: ${item.id}`));
6481
+ lines.push(chalk10.dim(`${indent} item-id: ${item.id}`));
6237
6482
  for (const child of sortByOrder(item.children ?? [])) {
6238
6483
  renderItem(child, depth + 1);
6239
6484
  }
6240
6485
  };
6241
6486
  for (const cl of checklists) {
6242
6487
  const { total, resolved } = countItems(cl.items);
6243
- lines.push(chalk9.bold(`${cl.name} (${resolved}/${total})`));
6244
- lines.push(chalk9.dim(` ID: ${cl.id}`));
6488
+ lines.push(chalk10.bold(`${cl.name} (${resolved}/${total})`));
6489
+ lines.push(chalk10.dim(` ID: ${cl.id}`));
6245
6490
  for (const item of sortByOrder(cl.items)) renderItem(item, 0);
6246
6491
  }
6247
6492
  return lines.join("\n");
@@ -6307,7 +6552,7 @@ async function deleteCommentByTaskSelection(config, taskId, options) {
6307
6552
  }
6308
6553
 
6309
6554
  // src/commands/replies.ts
6310
- import chalk10 from "chalk";
6555
+ import chalk11 from "chalk";
6311
6556
  async function getReplies(config, commentId) {
6312
6557
  const client = new ClickUpClient(config);
6313
6558
  return client.getThreadedComments(commentId);
@@ -6323,7 +6568,7 @@ function formatReplies(replies) {
6323
6568
  return replies.map((r) => {
6324
6569
  const user = r.user?.username ?? "Unknown";
6325
6570
  const date = formatTimestamp(Number(r.date));
6326
- return `${chalk10.bold(user)} ${chalk10.dim(date)}
6571
+ return `${chalk11.bold(user)} ${chalk11.dim(date)}
6327
6572
  ${r.comment_text}`;
6328
6573
  }).join("\n\n");
6329
6574
  }
@@ -6386,7 +6631,7 @@ function formatDocsMarkdown(docs) {
6386
6631
  }
6387
6632
 
6388
6633
  // src/commands/doc.ts
6389
- import chalk11 from "chalk";
6634
+ import chalk12 from "chalk";
6390
6635
  async function getDocInfo(config, docId) {
6391
6636
  const client = new ClickUpClient(config);
6392
6637
  const [doc, pages] = await Promise.all([
@@ -6398,14 +6643,14 @@ async function getDocInfo(config, docId) {
6398
6643
  function formatDocInfo(doc, pages, indent = 0) {
6399
6644
  const lines = [];
6400
6645
  if (indent === 0) {
6401
- lines.push(`${chalk11.bold(doc.name)} ${chalk11.dim(doc.id)}`);
6646
+ lines.push(`${chalk12.bold(doc.name)} ${chalk12.dim(doc.id)}`);
6402
6647
  if (pages.length === 0) {
6403
6648
  lines.push(" (no pages)");
6404
6649
  }
6405
6650
  }
6406
6651
  for (const page of pages) {
6407
6652
  const prefix = " ".repeat(indent + 1);
6408
- lines.push(`${prefix}${page.name} ${chalk11.dim(page.id)}`);
6653
+ lines.push(`${prefix}${page.name} ${chalk12.dim(page.id)}`);
6409
6654
  if (page.pages && page.pages.length > 0) {
6410
6655
  lines.push(formatDocInfo(doc, page.pages, indent + 1));
6411
6656
  }
@@ -6484,7 +6729,7 @@ async function deleteDocPage(config, docId, pageId) {
6484
6729
  }
6485
6730
 
6486
6731
  // src/commands/folders.ts
6487
- import chalk12 from "chalk";
6732
+ import chalk13 from "chalk";
6488
6733
  async function listFolders(config, spaceId, nameFilter, archived) {
6489
6734
  const client = new ClickUpClient(config);
6490
6735
  const folders = await client.getFolders(spaceId, archived);
@@ -6503,9 +6748,9 @@ async function listFolders(config, spaceId, nameFilter, archived) {
6503
6748
  function formatFolders(folders) {
6504
6749
  if (folders.length === 0) return "No folders found";
6505
6750
  return folders.map((f) => {
6506
- const header = `${chalk12.bold(f.name)} ${chalk12.dim(f.id)}`;
6751
+ const header = `${chalk13.bold(f.name)} ${chalk13.dim(f.id)}`;
6507
6752
  if (f.lists.length === 0) return header;
6508
- const listLines = f.lists.map((l) => ` ${chalk12.dim(">")} ${l.name} ${chalk12.dim(l.id)}`);
6753
+ const listLines = f.lists.map((l) => ` ${chalk13.dim(">")} ${l.name} ${chalk13.dim(l.id)}`);
6509
6754
  return [header, ...listLines].join("\n");
6510
6755
  }).join("\n\n");
6511
6756
  }
@@ -6520,13 +6765,13 @@ function formatFoldersMarkdown(folders) {
6520
6765
  }
6521
6766
 
6522
6767
  // src/commands/time.ts
6523
- import chalk13 from "chalk";
6768
+ import chalk14 from "chalk";
6524
6769
  var TIME_COLUMNS = [
6525
6770
  { key: "task", label: "Task", maxWidth: 35 },
6526
6771
  { key: "duration", label: "Duration", maxWidth: 10 },
6527
6772
  { key: "date", label: "Date", maxWidth: 20 },
6528
6773
  { key: "description", label: "Description", maxWidth: 30 },
6529
- { key: "status", label: "", maxWidth: 10, format: (v) => v === "RUNNING" ? chalk13.green(v) : "" }
6774
+ { key: "status", label: "", maxWidth: 10, format: (v) => v === "RUNNING" ? chalk14.green(v) : "" }
6530
6775
  ];
6531
6776
  async function startTimer(config, taskId, description) {
6532
6777
  const client = new ClickUpClient(config);
@@ -6622,7 +6867,7 @@ function formatTimeEntriesMarkdown(entries) {
6622
6867
  }
6623
6868
 
6624
6869
  // src/commands/tags.ts
6625
- import chalk14 from "chalk";
6870
+ import chalk15 from "chalk";
6626
6871
  var TAG_COLUMNS = [
6627
6872
  { key: "name", label: "Name", maxWidth: 40 },
6628
6873
  { key: "fg", label: "FG", maxWidth: 10 },
@@ -6655,13 +6900,13 @@ function formatTags(tags) {
6655
6900
  if (tags.length === 0) return "No tags found";
6656
6901
  if (isTTY()) {
6657
6902
  const rows = tags.map((t) => ({
6658
- name: t.tag_bg ? chalk14.bgHex(t.tag_bg).hex(t.tag_fg || "#ffffff")(` ${t.name} `) : chalk14.bold(t.name),
6903
+ name: t.tag_bg ? chalk15.bgHex(t.tag_bg).hex(t.tag_fg || "#ffffff")(` ${t.name} `) : chalk15.bold(t.name),
6659
6904
  fg: t.tag_fg || "",
6660
6905
  bg: t.tag_bg || ""
6661
6906
  }));
6662
6907
  return formatTable(rows, TAG_COLUMNS);
6663
6908
  }
6664
- return tags.map((t) => chalk14.bold(t.name)).join(", ");
6909
+ return tags.map((t) => chalk15.bold(t.name)).join(", ");
6665
6910
  }
6666
6911
  function formatTagsMarkdown(tags) {
6667
6912
  if (tags.length === 0) return "No tags found";
@@ -6696,7 +6941,7 @@ function formatMembersMarkdown(members) {
6696
6941
  }
6697
6942
 
6698
6943
  // src/commands/fields.ts
6699
- import chalk15 from "chalk";
6944
+ import chalk16 from "chalk";
6700
6945
  var FIELD_COLUMNS = [
6701
6946
  { key: "id", label: "ID", maxWidth: 20 },
6702
6947
  { key: "name", label: "Name", maxWidth: 30 },
@@ -6705,7 +6950,7 @@ var FIELD_COLUMNS = [
6705
6950
  key: "required",
6706
6951
  label: "Required",
6707
6952
  maxWidth: 10,
6708
- format: (v) => v === "yes" ? chalk15.yellow(v) : chalk15.dim(v)
6953
+ format: (v) => v === "yes" ? chalk16.yellow(v) : chalk16.dim(v)
6709
6954
  },
6710
6955
  { key: "options", label: "Options", maxWidth: 40 }
6711
6956
  ];
@@ -6804,7 +7049,13 @@ async function bulkAssign(config, userIdOrMe, taskIds, action) {
6804
7049
  async function bulkDueDate(config, date, taskIds) {
6805
7050
  const client = new ClickUpClient(config);
6806
7051
  const timezone = await client.getUserTimezone();
6807
- const payload = date === "none" || date === "clear" ? { due_date: null } : { due_date: parseDueDate(date, timezone), due_date_time: false };
7052
+ let payload;
7053
+ if (date === "none" || date === "clear") {
7054
+ payload = { due_date: null };
7055
+ } else {
7056
+ const parsed = parseDueDate(date, timezone);
7057
+ payload = { due_date: parsed.ms, due_date_time: parsed.hasTime };
7058
+ }
6808
7059
  const outcomes = await runInBatches(
6809
7060
  taskIds,
6810
7061
  BULK_CONCURRENCY,
@@ -6857,13 +7108,13 @@ async function bulkMove(config, listId, taskIds) {
6857
7108
  }
6858
7109
 
6859
7110
  // src/commands/goals.ts
6860
- import chalk16 from "chalk";
7111
+ import chalk17 from "chalk";
6861
7112
  function colorProgress(value) {
6862
7113
  const num = parseInt(value, 10);
6863
7114
  if (isNaN(num)) return value;
6864
- if (num >= 75) return chalk16.green(value);
6865
- if (num >= 25) return chalk16.yellow(value);
6866
- return chalk16.red(value);
7115
+ if (num >= 75) return chalk17.green(value);
7116
+ if (num >= 25) return chalk17.yellow(value);
7117
+ return chalk17.red(value);
6867
7118
  }
6868
7119
  var GOAL_COLUMNS = [
6869
7120
  { key: "id", label: "ID", maxWidth: 15 },
@@ -6888,7 +7139,7 @@ async function createGoal(config, name, opts) {
6888
7139
  return client.createGoal(config.teamId, name, {
6889
7140
  description: opts?.description,
6890
7141
  color: opts?.color,
6891
- ...opts?.dueDate ? { dueDate: parseDueDate(opts.dueDate, timezone) } : {}
7142
+ ...opts?.dueDate ? { dueDate: parseDueDate(opts.dueDate, timezone).ms } : {}
6892
7143
  });
6893
7144
  }
6894
7145
  async function updateGoal(config, goalId, updates) {
@@ -6957,14 +7208,14 @@ function formatKeyResultsMarkdown(keyResults) {
6957
7208
  }
6958
7209
 
6959
7210
  // src/commands/task-types.ts
6960
- import chalk17 from "chalk";
7211
+ import chalk18 from "chalk";
6961
7212
  async function listTaskTypes(config) {
6962
7213
  const client = new ClickUpClient(config);
6963
7214
  return client.getCustomTaskTypes(config.teamId);
6964
7215
  }
6965
7216
  function formatTaskTypes(types) {
6966
7217
  if (types.length === 0) return "No custom task types";
6967
- return types.map((t) => `${chalk17.bold(t.name)} ${chalk17.dim(`(${t.id})`)}`).join("\n");
7218
+ return types.map((t) => `${chalk18.bold(t.name)} ${chalk18.dim(`(${t.id})`)}`).join("\n");
6968
7219
  }
6969
7220
  function formatTaskTypesMarkdown(types) {
6970
7221
  if (types.length === 0) return "No custom task types";
@@ -6972,14 +7223,14 @@ function formatTaskTypesMarkdown(types) {
6972
7223
  }
6973
7224
 
6974
7225
  // src/commands/templates.ts
6975
- import chalk18 from "chalk";
7226
+ import chalk19 from "chalk";
6976
7227
  async function listTemplates(config) {
6977
7228
  const client = new ClickUpClient(config);
6978
7229
  return client.getTaskTemplates(config.teamId);
6979
7230
  }
6980
7231
  function formatTemplates(templates) {
6981
7232
  if (templates.length === 0) return "No task templates";
6982
- return templates.map((t) => `${chalk18.bold(t.name)} ${chalk18.dim(`(${t.id})`)}`).join("\n");
7233
+ return templates.map((t) => `${chalk19.bold(t.name)} ${chalk19.dim(`(${t.id})`)}`).join("\n");
6983
7234
  }
6984
7235
  function formatTemplatesMarkdown(templates) {
6985
7236
  if (templates.length === 0) return "No task templates";
@@ -6987,14 +7238,14 @@ function formatTemplatesMarkdown(templates) {
6987
7238
  }
6988
7239
 
6989
7240
  // src/commands/list-templates.ts
6990
- import chalk19 from "chalk";
7241
+ import chalk20 from "chalk";
6991
7242
  async function listListTemplates(config) {
6992
7243
  const client = new ClickUpClient(config);
6993
7244
  return client.getListTemplates(config.teamId);
6994
7245
  }
6995
7246
  function formatListTemplates(templates) {
6996
7247
  if (templates.length === 0) return "No list templates";
6997
- return templates.map((t) => `${chalk19.bold(t.name)} ${chalk19.dim(`(${t.id})`)}`).join("\n");
7248
+ return templates.map((t) => `${chalk20.bold(t.name)} ${chalk20.dim(`(${t.id})`)}`).join("\n");
6998
7249
  }
6999
7250
  function formatListTemplatesMarkdown(templates) {
7000
7251
  if (templates.length === 0) return "No list templates";
@@ -7002,14 +7253,14 @@ function formatListTemplatesMarkdown(templates) {
7002
7253
  }
7003
7254
 
7004
7255
  // src/commands/folder-templates.ts
7005
- import chalk20 from "chalk";
7256
+ import chalk21 from "chalk";
7006
7257
  async function listFolderTemplates(config) {
7007
7258
  const client = new ClickUpClient(config);
7008
7259
  return client.getFolderTemplates(config.teamId);
7009
7260
  }
7010
7261
  function formatFolderTemplates(templates) {
7011
7262
  if (templates.length === 0) return "No folder templates";
7012
- return templates.map((t) => `${chalk20.bold(t.name)} ${chalk20.dim(`(${t.id})`)}`).join("\n");
7263
+ return templates.map((t) => `${chalk21.bold(t.name)} ${chalk21.dim(`(${t.id})`)}`).join("\n");
7013
7264
  }
7014
7265
  function formatFolderTemplatesMarkdown(templates) {
7015
7266
  if (templates.length === 0) return "No folder templates";
@@ -7032,7 +7283,7 @@ async function createListFromTemplate(config, name, opts) {
7032
7283
  }
7033
7284
 
7034
7285
  // src/commands/views.ts
7035
- import chalk21 from "chalk";
7286
+ import chalk22 from "chalk";
7036
7287
  async function listViews(config, id, container = "list") {
7037
7288
  const client = new ClickUpClient(config);
7038
7289
  if (container === "space") return client.getSpaceViews(id);
@@ -7043,7 +7294,7 @@ async function listViews(config, id, container = "list") {
7043
7294
  }
7044
7295
  function formatViews(views) {
7045
7296
  if (views.length === 0) return "No views";
7046
- return views.map((v) => `${chalk21.bold(v.name)} ${chalk21.dim(`(${v.id})`)} ${chalk21.dim(v.type)}`).join("\n");
7297
+ return views.map((v) => `${chalk22.bold(v.name)} ${chalk22.dim(`(${v.id})`)} ${chalk22.dim(v.type)}`).join("\n");
7047
7298
  }
7048
7299
  function formatViewsMarkdown(views) {
7049
7300
  if (views.length === 0) return "No views";
@@ -7051,23 +7302,23 @@ function formatViewsMarkdown(views) {
7051
7302
  }
7052
7303
 
7053
7304
  // src/commands/chat.ts
7054
- import chalk22 from "chalk";
7305
+ import chalk23 from "chalk";
7055
7306
  function channelName(c) {
7056
7307
  return c.name || "DM";
7057
7308
  }
7058
7309
  function colorChannelType(type) {
7059
- if (type === "CHANNEL") return chalk22.cyan(type);
7060
- if (type === "DM") return chalk22.dim(type);
7061
- if (type === "GROUP_DM") return chalk22.blue(type);
7310
+ if (type === "CHANNEL") return chalk23.cyan(type);
7311
+ if (type === "DM") return chalk23.dim(type);
7312
+ if (type === "GROUP_DM") return chalk23.blue(type);
7062
7313
  return type;
7063
7314
  }
7064
7315
  function colorVisibility(v) {
7065
- if (v === "PUBLIC") return chalk22.green(v);
7066
- return chalk22.dim(v);
7316
+ if (v === "PUBLIC") return chalk23.green(v);
7317
+ return chalk23.dim(v);
7067
7318
  }
7068
7319
  var CHANNEL_COLUMNS = [
7069
- { key: "name", label: "Name", maxWidth: 40, format: (v) => chalk22.bold(v) },
7070
- { key: "id", label: "ID", maxWidth: 20, format: (v) => chalk22.dim(v) },
7320
+ { key: "name", label: "Name", maxWidth: 40, format: (v) => chalk23.bold(v) },
7321
+ { key: "id", label: "ID", maxWidth: 20, format: (v) => chalk23.dim(v) },
7071
7322
  { key: "type", label: "Type", maxWidth: 12, format: (v) => colorChannelType(v) },
7072
7323
  { key: "visibility", label: "Visibility", maxWidth: 10, format: (v) => colorVisibility(v) },
7073
7324
  { key: "topic", label: "Topic", maxWidth: 40 }
@@ -7092,26 +7343,26 @@ function formatChannelsMarkdown(channels) {
7092
7343
  }
7093
7344
  function formatChannelDetail(channel) {
7094
7345
  const lines = [];
7095
- lines.push(chalk22.bold.underline(channelName(channel)));
7346
+ lines.push(chalk23.bold.underline(channelName(channel)));
7096
7347
  lines.push("");
7097
7348
  const fields = [
7098
- ["ID", chalk22.dim(channel.id)],
7349
+ ["ID", chalk23.dim(channel.id)],
7099
7350
  ["Type", colorChannelType(channel.type)],
7100
7351
  ["Visibility", colorVisibility(channel.visibility)]
7101
7352
  ];
7102
7353
  if (channel.topic) fields.push(["Topic", channel.topic]);
7103
7354
  if (channel.description) fields.push(["Description", channel.description]);
7104
- fields.push(["Archived", channel.archived ? chalk22.yellow("Yes") : "No"]);
7355
+ fields.push(["Archived", channel.archived ? chalk23.yellow("Yes") : "No"]);
7105
7356
  fields.push(["Created", formatDate(channel.created_at)]);
7106
7357
  const maxLabel = Math.max(...fields.map(([k]) => k.length));
7107
7358
  for (const [label, value] of fields) {
7108
- lines.push(` ${chalk22.bold(label.padEnd(maxLabel + 1))} ${value}`);
7359
+ lines.push(` ${chalk23.bold(label.padEnd(maxLabel + 1))} ${value}`);
7109
7360
  }
7110
7361
  return lines.join("\n");
7111
7362
  }
7112
7363
  var CHAT_MEMBER_COLUMNS = [
7113
- { key: "name", label: "Name", maxWidth: 30, format: (v) => chalk22.bold(v) },
7114
- { key: "id", label: "ID", maxWidth: 15, format: (v) => chalk22.dim(v) },
7364
+ { key: "name", label: "Name", maxWidth: 30, format: (v) => chalk23.bold(v) },
7365
+ { key: "id", label: "ID", maxWidth: 15, format: (v) => chalk23.dim(v) },
7115
7366
  { key: "email", label: "Email", maxWidth: 40 },
7116
7367
  { key: "type", label: "Type", maxWidth: 12 }
7117
7368
  ];
@@ -7134,22 +7385,22 @@ function formatChatMembersMarkdown(members) {
7134
7385
  }
7135
7386
 
7136
7387
  // src/commands/chat-message.ts
7137
- import chalk23 from "chalk";
7138
- var separator = chalk23.dim("-".repeat(60));
7388
+ import chalk24 from "chalk";
7389
+ var separator = chalk24.dim("-".repeat(60));
7139
7390
  function formatMessages(messages) {
7140
7391
  if (messages.length === 0) return "No messages";
7141
7392
  const lines = [];
7142
7393
  for (let i = 0; i < messages.length; i++) {
7143
7394
  const msg = messages[i];
7144
7395
  if (i > 0) lines.push(separator);
7145
- const meta = [chalk23.bold(`@${msg.user_id}`), chalk23.dim(formatTimestamp(msg.date))];
7396
+ const meta = [chalk24.bold(`@${msg.user_id}`), chalk24.dim(formatTimestamp(msg.date))];
7146
7397
  if (msg.replies_count) {
7147
- meta.push(chalk23.dim(`${msg.replies_count} replies`));
7398
+ meta.push(chalk24.dim(`${msg.replies_count} replies`));
7148
7399
  }
7149
- meta.push(chalk23.dim(`(${msg.id})`));
7400
+ meta.push(chalk24.dim(`(${msg.id})`));
7150
7401
  lines.push(meta.join(" "));
7151
7402
  if (msg.type === "post" && msg.post_data?.title) {
7152
- lines.push(chalk23.cyan.bold(msg.post_data.title));
7403
+ lines.push(chalk24.cyan.bold(msg.post_data.title));
7153
7404
  }
7154
7405
  lines.push(msg.content);
7155
7406
  }
@@ -7168,7 +7419,7 @@ ${msg.content}`;
7168
7419
  }
7169
7420
 
7170
7421
  // src/commands/chat-reaction.ts
7171
- import chalk24 from "chalk";
7422
+ import chalk25 from "chalk";
7172
7423
  var EMOJI_MAP = {
7173
7424
  thumbsup: "\u{1F44D}",
7174
7425
  thumbsdown: "\u{1F44E}",
@@ -7207,8 +7458,8 @@ function formatReactions(reactions) {
7207
7458
  const lines = [];
7208
7459
  for (const [emoji, users] of groups) {
7209
7460
  const icon = emojiChar(emoji);
7210
- const userList = users.map((u) => chalk24.bold(`@${u}`)).join(", ");
7211
- lines.push(`${icon} ${chalk24.dim(emoji)} ${chalk24.dim(`(${users.length})`)} \u2014 ${userList}`);
7461
+ const userList = users.map((u) => chalk25.bold(`@${u}`)).join(", ");
7462
+ lines.push(`${icon} ${chalk25.dim(emoji)} ${chalk25.dim(`(${users.length})`)} \u2014 ${userList}`);
7212
7463
  }
7213
7464
  return lines.join("\n");
7214
7465
  }
@@ -7223,20 +7474,20 @@ function formatReactionsMarkdown(reactions) {
7223
7474
  }
7224
7475
 
7225
7476
  // src/commands/view.ts
7226
- import chalk25 from "chalk";
7477
+ import chalk26 from "chalk";
7227
7478
  async function getView(config, viewId) {
7228
7479
  const client = new ClickUpClient(config);
7229
7480
  return client.getView(viewId);
7230
7481
  }
7231
7482
  function formatView(view) {
7232
7483
  const lines = [];
7233
- lines.push(chalk25.bold.underline(view.name));
7484
+ lines.push(chalk26.bold.underline(view.name));
7234
7485
  lines.push("");
7235
- lines.push(` ${chalk25.bold("ID")} ${view.id}`);
7236
- lines.push(` ${chalk25.bold("Type")} ${view.type}`);
7237
- if (view.visibility) lines.push(` ${chalk25.bold("Visibility")} ${view.visibility}`);
7238
- if (view.date_created) lines.push(` ${chalk25.bold("Created")} ${formatDate(view.date_created)}`);
7239
- if (view.protected !== void 0) lines.push(` ${chalk25.bold("Protected")} ${view.protected}`);
7486
+ lines.push(` ${chalk26.bold("ID")} ${view.id}`);
7487
+ lines.push(` ${chalk26.bold("Type")} ${view.type}`);
7488
+ if (view.visibility) lines.push(` ${chalk26.bold("Visibility")} ${view.visibility}`);
7489
+ if (view.date_created) lines.push(` ${chalk26.bold("Created")} ${formatDate(view.date_created)}`);
7490
+ if (view.protected !== void 0) lines.push(` ${chalk26.bold("Protected")} ${view.protected}`);
7240
7491
  return lines.join("\n");
7241
7492
  }
7242
7493
  function formatViewMarkdown(view) {
@@ -7654,7 +7905,13 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
7654
7905
  program.command("update <taskId>").description("Update a task").option("-n, --name <text>", "New task name").option("-d, --description <text>", "New description (markdown supported)").option(
7655
7906
  "-s, --status <status>",
7656
7907
  'New status (fuzzy matched, e.g. "prog" matches "in progress")'
7657
- ).option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", 'Due date (YYYY-MM-DD, or "none"/"clear" to remove)').option("--start-date <date>", "Start date (YYYY-MM-DD)").option(
7908
+ ).option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option(
7909
+ "--due-date <date>",
7910
+ 'Due date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset; or "none"/"clear" to remove)'
7911
+ ).option(
7912
+ "--start-date <date>",
7913
+ "Start date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset)"
7914
+ ).option(
7658
7915
  "--time-estimate <duration>",
7659
7916
  'Time estimate (e.g. "2h", "30m", "1h30m", "0" or "none" to clear)'
7660
7917
  ).option("--assignee <userId>", 'Add assignee by user ID or "me"').option("--remove-assignee <userId>", 'Remove assignee by user ID or "me"').option("--parent <taskId>", "Set parent task (makes this a subtask)").option("--detach", "Remove parent task (promote subtask to top-level)").option("--archive", "Archive the task").option("--unarchive", "Unarchive the task").option("--type <type>", "Change task type (name or custom_item_id)").option("--field <nameAndValue...>", 'Set custom field: --field "Name" value').option("--json", "Force JSON output even in terminal").action(
@@ -7704,7 +7961,13 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
7704
7961
  }
7705
7962
  )
7706
7963
  );
7707
- program.command("create").description("Create a new task").option("-l, --list <listId>", 'Target list ID or "sprint:current" for active sprint').requiredOption("-n, --name <name>", "Task name").option("-d, --description <text>", "Task description (markdown supported)").option("-p, --parent <taskId>", "Parent task ID (list auto-detected from parent)").option("-s, --status <status>", "Initial status").option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--start-date <date>", "Start date (YYYY-MM-DD)").option("--assignee <userId>", 'Assignee user ID or "me"').option("--tags <tags>", "Comma-separated tag names").option("--custom-item-id <id>", "Custom task type ID (use to create initiatives)").option("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--template <id>", "Create from a task template (find IDs with cup templates)").option("--field <nameAndValue...>", 'Set custom field: --field "Name" value (can repeat)').option("--json", "Force JSON output even in terminal").action(
7964
+ program.command("create").description("Create a new task").option("-l, --list <listId>", 'Target list ID or "sprint:current" for active sprint').requiredOption("-n, --name <name>", "Task name").option("-d, --description <text>", "Task description (markdown supported)").option("-p, --parent <taskId>", "Parent task ID (list auto-detected from parent)").option("-s, --status <status>", "Initial status").option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option(
7965
+ "--due-date <date>",
7966
+ "Due date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset)"
7967
+ ).option(
7968
+ "--start-date <date>",
7969
+ "Start date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset)"
7970
+ ).option("--assignee <userId>", 'Assignee user ID or "me"').option("--tags <tags>", "Comma-separated tag names").option("--custom-item-id <id>", "Custom task type ID (use to create initiatives)").option("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--template <id>", "Create from a task template (find IDs with cup templates)").option("--field <nameAndValue...>", 'Set custom field: --field "Name" value (can repeat)').option("--json", "Force JSON output even in terminal").action(
7708
7971
  wrapAction(async (opts) => {
7709
7972
  const config = loadConfig(getProfileName());
7710
7973
  if (opts.list === "sprint:current") {
@@ -8138,6 +8401,78 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
8138
8401
  }
8139
8402
  })
8140
8403
  );
8404
+ program.command("list-delete <listId>").description("Delete a list (requires confirmation)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8405
+ wrapAction(async (listId, opts) => {
8406
+ const config = loadConfig(getProfileName());
8407
+ const result = await deleteListCommand(config, listId, opts);
8408
+ if (shouldOutputJson(opts.json ?? false)) {
8409
+ console.log(JSON.stringify(result, null, 2));
8410
+ } else {
8411
+ console.log(`Deleted list ${result.listId}`);
8412
+ }
8413
+ })
8414
+ );
8415
+ program.command("folder-delete <folderId>").description("Delete a folder (requires confirmation)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8416
+ wrapAction(async (folderId, opts) => {
8417
+ const config = loadConfig(getProfileName());
8418
+ const result = await deleteFolderCommand(config, folderId, opts);
8419
+ if (shouldOutputJson(opts.json ?? false)) {
8420
+ console.log(JSON.stringify(result, null, 2));
8421
+ } else {
8422
+ console.log(`Deleted folder ${result.folderId}`);
8423
+ }
8424
+ })
8425
+ );
8426
+ program.command("space-delete <spaceId>").description("Delete a space (requires confirmation)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8427
+ wrapAction(async (spaceId, opts) => {
8428
+ const config = loadConfig(getProfileName());
8429
+ const result = await deleteSpaceCommand(config, spaceId, opts);
8430
+ if (shouldOutputJson(opts.json ?? false)) {
8431
+ console.log(JSON.stringify(result, null, 2));
8432
+ } else {
8433
+ console.log(`Deleted space ${result.spaceId}`);
8434
+ }
8435
+ })
8436
+ );
8437
+ program.command("attachments <taskId>").description("List attachments on a task").option("--json", "Force JSON output even in terminal").action(
8438
+ wrapAction(async (taskId, opts) => {
8439
+ const config = loadConfig(getProfileName());
8440
+ const attachments = await listTaskAttachments(config, taskId);
8441
+ if (shouldOutputJson(opts.json ?? false)) {
8442
+ console.log(JSON.stringify(attachments, null, 2));
8443
+ } else if (isTTY()) {
8444
+ console.log(formatAttachmentsTable(attachments));
8445
+ } else {
8446
+ console.log(formatAttachmentsMarkdown(attachments));
8447
+ }
8448
+ })
8449
+ );
8450
+ program.command("task-members <taskId>").description("List members with access to a task").option("--json", "Force JSON output even in terminal").action(
8451
+ wrapAction(async (taskId, opts) => {
8452
+ const config = loadConfig(getProfileName());
8453
+ const members = await listTaskMembers(config, taskId);
8454
+ if (shouldOutputJson(opts.json ?? false)) {
8455
+ console.log(JSON.stringify(members, null, 2));
8456
+ } else if (isTTY()) {
8457
+ console.log(formatTaskMembers(members));
8458
+ } else {
8459
+ console.log(formatTaskMembersMarkdown(members));
8460
+ }
8461
+ })
8462
+ );
8463
+ program.command("plan").description("Show workspace plan").option("--json", "Force JSON output even in terminal").action(
8464
+ wrapAction(async (opts) => {
8465
+ const config = loadConfig(getProfileName());
8466
+ const plan = await getWorkspacePlanCommand(config);
8467
+ if (shouldOutputJson(opts.json ?? false)) {
8468
+ console.log(JSON.stringify(plan, null, 2));
8469
+ } else if (isTTY()) {
8470
+ console.log(formatPlan(plan));
8471
+ } else {
8472
+ console.log(formatPlanMarkdown(plan));
8473
+ }
8474
+ })
8475
+ );
8141
8476
  program.command("archive <taskId>").description("Archive a task (or unarchive with --unarchive)").option("--unarchive", "Unarchive instead of archiving").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8142
8477
  wrapAction(
8143
8478
  async (taskId, opts) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "1.27.1",
3
+ "version": "1.29.0",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -3,11 +3,11 @@ name: clickup
3
3
  description: 'Use when managing ClickUp tasks, sprints, or comments via the `cup` CLI tool. Triggers: task queries, status updates, sprint tracking, creating subtasks, posting comments, threaded replies, standup summaries, searching tasks, checking overdue items, assigning tasks, listing spaces and lists, opening tasks in browser, checking auth or config, setting custom fields, deleting tasks, managing tags, managing checklists, editing comments, task links, time tracking, attachments, file uploads, listing members, listing fields, duplicating tasks, bulk operations, goals, key results, saved filters, favorites.'
4
4
  ---
5
5
 
6
- # ClickUp CLI (`cup`) - skill version 1.27.1
6
+ # ClickUp CLI (`cup`) - skill version 1.29.0
7
7
 
8
8
  Reference for AI agents using the `cup` CLI tool. Covers task management, sprint tracking, comments, time tracking, custom fields, goals, docs, and project workflows.
9
9
 
10
- > **Version check:** Run `cup --version`. If your installed version is older than 1.27.1, update with `npm install -g @krodak/clickup-cli` and refresh this skill with `cup skill`.
10
+ > **Version check:** Run `cup --version`. If your installed version is older than 1.29.0, update with `npm install -g @krodak/clickup-cli` and refresh this skill with `cup skill`.
11
11
 
12
12
  ## Install & Configure
13
13
 
@@ -127,6 +127,9 @@ All commands support `--help` for full flag details. All commands support `--jso
127
127
  | `cup time-in-status <id>` | Show how long a task has been in each status |
128
128
  | `cup members` | Workspace members (username, ID, email) |
129
129
  | `cup fields <listId>` | Custom fields on a list (type, required, options) |
130
+ | `cup attachments <taskId>` | List attachments on a task (name, size, URL) |
131
+ | `cup task-members <taskId>` | List members with access to a task |
132
+ | `cup plan` | Show workspace plan and usage |
130
133
  | `cup tags <spaceId>` | Tags available in a space |
131
134
  | `cup goals` | Workspace goals with progress |
132
135
  | `cup key-results <goalId>` | Key results for a goal |
@@ -169,6 +172,9 @@ All commands support `--help` for full flag details. All commands support `--jso
169
172
  | `cup link <taskId> <linksTo> [--remove]` | Link/unlink tasks |
170
173
  | `cup attach <taskId> <filePath>` | Upload file attachment |
171
174
  | `cup delete <id> [--confirm]` | Delete task (DESTRUCTIVE) |
175
+ | `cup list-delete <listId> [--confirm]` | Delete list (DESTRUCTIVE, requires --confirm in non-interactive) |
176
+ | `cup folder-delete <folderId> [--confirm]` | Delete folder (DESTRUCTIVE, requires --confirm in non-interactive) |
177
+ | `cup space-delete <spaceId> [--confirm]` | Delete space (DESTRUCTIVE, requires --confirm in non-interactive) |
172
178
  | `cup duplicate <taskId>` | Duplicate a task |
173
179
  | `cup bulk status <status> <taskIds...>` | Bulk update status |
174
180
  | `cup bulk assign <taskIds...> [--to userId\|me] [--remove userId\|me]` | Bulk assign/unassign user from tasks |
@@ -248,7 +254,7 @@ All commands support `--help` for full flag details. All commands support `--jso
248
254
  | Task IDs | Native (`abc123def`) or custom (`PROJ-123`). Custom IDs auto-detected by `PREFIX-DIGITS` format |
249
255
  | `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr |
250
256
  | `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
251
- | `--due-date` | `YYYY-MM-DD` format |
257
+ | `--due-date` | `YYYY-MM-DD` (date only), `YYYY-MM-DDTHH:MM` (with time), or full ISO 8601 with offset. Time-of-day formats set `due_date_time: true` in ClickUp |
252
258
  | `--assignee` | User ID or `me` |
253
259
  | `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
254
260
  | `--time-estimate` | Duration: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
@@ -282,7 +288,7 @@ When running in a terminal (not piped), task-listing commands (`cup tasks`, `cup
282
288
  - **Enter** to confirm and view details of selected tasks
283
289
  - After viewing details, prompted to open tasks in browser
284
290
 
285
- Other interactive prompts: sprint selection (when multiple match), workspace selection (`cup init`), agent selection (`cup skill`), destructive action confirmations (`cup delete`, `cup archive`, `cup view-delete`).
291
+ Other interactive prompts: sprint selection (when multiple match), workspace selection (`cup init`), agent selection (`cup skill`), destructive action confirmations (`cup delete`, `cup list-delete`, `cup folder-delete`, `cup space-delete`, `cup archive`, `cup view-delete`).
286
292
 
287
293
  When piped or called with `--json`, all commands output non-interactive markdown or JSON. Agents should always use `--json` for structured data or pipe for markdown.
288
294
 
@@ -323,6 +329,8 @@ cup inbox --days 7 # recently updated
323
329
  ```bash
324
330
  cup update abc123def -s "done"
325
331
  cup update abc123def --priority high --due-date 2025-03-15
332
+ cup update abc123def --due-date 2025-03-15T14:30 # date + time (user's timezone)
333
+ cup update abc123def --due-date 2025-03-15T14:30:00Z # UTC
326
334
  cup create -n "Fix the thing" -p abc123def
327
335
  cup create -n "Fix bug" -l <listId> --priority urgent --tags "bug,frontend"
328
336
  cup create -n "Bug fix" -l sprint:current # create in active sprint
@@ -418,4 +426,4 @@ cup summary --hours 48 # wider window
418
426
 
419
427
  ## DELETE SAFETY
420
428
 
421
- IMPORTANT: Always confirm with the user before running `cup delete`. This is a destructive, irreversible operation. Even when using `--confirm` flag, verify the task ID is correct with the user first.
429
+ IMPORTANT: Always confirm with the user before running `cup delete`, `cup list-delete`, `cup folder-delete`, or `cup space-delete`. These are destructive, irreversible operations. Even when using `--confirm` flag, verify the ID is correct with the user first.