@krodak/clickup-cli 1.27.0 → 1.28.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.0",
4
+ "version": "1.28.0",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/dist/index.js CHANGED
@@ -1841,12 +1841,12 @@ async function groupedTaskPicker(groups) {
1841
1841
  }
1842
1842
  async function showDetailsAndOpen(tasks, fetchTask) {
1843
1843
  if (tasks.length === 0) return;
1844
- const separator = chalk2.dim("\u2500".repeat(60));
1844
+ const separator2 = chalk2.dim("\u2500".repeat(60));
1845
1845
  for (let i = 0; i < tasks.length; i++) {
1846
1846
  const task = tasks[i];
1847
1847
  if (i > 0) {
1848
1848
  console.log("");
1849
- console.log(separator);
1849
+ console.log(separator2);
1850
1850
  }
1851
1851
  console.log("");
1852
1852
  if (fetchTask) {
@@ -1993,27 +1993,54 @@ function parsePriority(value) {
1993
1993
  if (Number.isInteger(num) && num >= 1 && num <= 4) return num;
1994
1994
  throw new Error("Priority must be urgent, high, normal, low, or 1-4");
1995
1995
  }
1996
+ var DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
1997
+ var LOCAL_DATETIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/;
1998
+ 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
1999
  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");
2000
+ if (DATE_ONLY_RE.test(value)) {
2001
+ const parts = value.split("-").map(Number);
2002
+ const y = parts[0];
2003
+ const m = parts[1];
2004
+ const d = parts[2];
2005
+ return { ms: wallClockToMs(y, m, d, 0, 0, 0, timezone, value), hasTime: false };
2006
+ }
2007
+ const localMatch = LOCAL_DATETIME_RE.exec(value);
2008
+ if (localMatch) {
2009
+ const y = Number(localMatch[1]);
2010
+ const m = Number(localMatch[2]);
2011
+ const d = Number(localMatch[3]);
2012
+ const hh = Number(localMatch[4]);
2013
+ const mm = Number(localMatch[5]);
2014
+ const ss = localMatch[6] !== void 0 ? Number(localMatch[6]) : 0;
2015
+ if (hh > 23 || mm > 59 || ss > 59) {
2016
+ throw new Error(
2017
+ "Date must be in YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 format (time component out of range)"
2018
+ );
2019
+ }
2020
+ return { ms: wallClockToMs(y, m, d, hh, mm, ss, timezone, value), hasTime: true };
2021
+ }
2022
+ if (ISO_WITH_OFFSET_RE.test(value)) {
2023
+ const ms = Date.parse(value);
2024
+ if (!isNaN(ms)) return { ms, hasTime: true };
1999
2025
  }
2000
- const parts = value.split("-").map(Number);
2001
- const y = parts[0];
2002
- const m = parts[1];
2003
- const d = parts[2];
2026
+ throw new Error(
2027
+ "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)"
2028
+ );
2029
+ }
2030
+ function wallClockToMs(year, month, day, hour, minute, second, timezone, rawValue) {
2004
2031
  if (timezone) {
2005
2032
  try {
2006
- const ms2 = dateToTimezoneMs(y, m, d, timezone);
2033
+ const ms2 = wallClockToTimezoneMs(year, month, day, hour, minute, second, timezone);
2007
2034
  if (!isNaN(ms2)) return ms2;
2008
2035
  } catch {
2009
2036
  }
2010
2037
  }
2011
- const ms = Date.UTC(y, m - 1, d);
2012
- if (isNaN(ms)) throw new Error(`Invalid date: ${value}`);
2038
+ const ms = Date.UTC(year, month - 1, day, hour, minute, second);
2039
+ if (isNaN(ms)) throw new Error(`Invalid date: ${rawValue}`);
2013
2040
  return ms;
2014
2041
  }
2015
- function dateToTimezoneMs(year, month, day, timezone) {
2016
- const approxUtc = new Date(Date.UTC(year, month - 1, day));
2042
+ function wallClockToTimezoneMs(year, month, day, hour, minute, second, timezone) {
2043
+ const approxUtc = new Date(Date.UTC(year, month - 1, day, hour, minute, second));
2017
2044
  const tzStr = approxUtc.toLocaleString("en-US", {
2018
2045
  timeZone: timezone,
2019
2046
  year: "numeric",
@@ -2074,13 +2101,15 @@ function buildUpdatePayload(opts, timezone) {
2074
2101
  if (opts.dueDate === "none" || opts.dueDate === "clear") {
2075
2102
  payload.due_date = null;
2076
2103
  } else {
2077
- payload.due_date = parseDueDate(opts.dueDate, timezone);
2078
- payload.due_date_time = false;
2104
+ const parsed = parseDueDate(opts.dueDate, timezone);
2105
+ payload.due_date = parsed.ms;
2106
+ payload.due_date_time = parsed.hasTime;
2079
2107
  }
2080
2108
  }
2081
2109
  if (opts.startDate !== void 0) {
2082
- payload.start_date = parseDueDate(opts.startDate, timezone);
2083
- payload.start_date_time = false;
2110
+ const parsed = parseDueDate(opts.startDate, timezone);
2111
+ payload.start_date = parsed.ms;
2112
+ payload.start_date_time = parsed.hasTime;
2084
2113
  }
2085
2114
  if (opts.assignee !== void 0 || opts.removeAssignee !== void 0) {
2086
2115
  payload.assignees = {};
@@ -2180,12 +2209,14 @@ async function createTask(config, options) {
2180
2209
  payload.priority = parsePriority(options.priority);
2181
2210
  }
2182
2211
  if (options.dueDate !== void 0) {
2183
- payload.due_date = parseDueDate(options.dueDate, timezone);
2184
- payload.due_date_time = false;
2212
+ const parsed = parseDueDate(options.dueDate, timezone);
2213
+ payload.due_date = parsed.ms;
2214
+ payload.due_date_time = parsed.hasTime;
2185
2215
  }
2186
2216
  if (options.startDate !== void 0) {
2187
- payload.start_date = parseDueDate(options.startDate, timezone);
2188
- payload.start_date_time = false;
2217
+ const parsed = parseDueDate(options.startDate, timezone);
2218
+ payload.start_date = parsed.ms;
2219
+ payload.start_date_time = parsed.hasTime;
2189
2220
  }
2190
2221
  if (options.assignee !== void 0) {
2191
2222
  payload.assignees = [parseAssigneeId(options.assignee)];
@@ -2876,10 +2907,10 @@ function printComments(comments, forceJson) {
2876
2907
  console.log("No comments found.");
2877
2908
  return;
2878
2909
  }
2879
- const separator = chalk4.dim("-".repeat(60));
2910
+ const separator2 = chalk4.dim("-".repeat(60));
2880
2911
  for (let i = 0; i < comments.length; i++) {
2881
2912
  const c = comments[i];
2882
- if (i > 0) console.log(separator);
2913
+ if (i > 0) console.log(separator2);
2883
2914
  console.log(`${chalk4.bold(c.user)} ${chalk4.dim(formatTimestamp(c.date))}`);
2884
2915
  console.log(c.text);
2885
2916
  if (i < comments.length - 1) console.log("");
@@ -6804,7 +6835,13 @@ async function bulkAssign(config, userIdOrMe, taskIds, action) {
6804
6835
  async function bulkDueDate(config, date, taskIds) {
6805
6836
  const client = new ClickUpClient(config);
6806
6837
  const timezone = await client.getUserTimezone();
6807
- const payload = date === "none" || date === "clear" ? { due_date: null } : { due_date: parseDueDate(date, timezone), due_date_time: false };
6838
+ let payload;
6839
+ if (date === "none" || date === "clear") {
6840
+ payload = { due_date: null };
6841
+ } else {
6842
+ const parsed = parseDueDate(date, timezone);
6843
+ payload = { due_date: parsed.ms, due_date_time: parsed.hasTime };
6844
+ }
6808
6845
  const outcomes = await runInBatches(
6809
6846
  taskIds,
6810
6847
  BULK_CONCURRENCY,
@@ -6888,7 +6925,7 @@ async function createGoal(config, name, opts) {
6888
6925
  return client.createGoal(config.teamId, name, {
6889
6926
  description: opts?.description,
6890
6927
  color: opts?.color,
6891
- ...opts?.dueDate ? { dueDate: parseDueDate(opts.dueDate, timezone) } : {}
6928
+ ...opts?.dueDate ? { dueDate: parseDueDate(opts.dueDate, timezone).ms } : {}
6892
6929
  });
6893
6930
  }
6894
6931
  async function updateGoal(config, goalId, updates) {
@@ -7052,18 +7089,31 @@ function formatViewsMarkdown(views) {
7052
7089
 
7053
7090
  // src/commands/chat.ts
7054
7091
  import chalk22 from "chalk";
7092
+ function channelName(c) {
7093
+ return c.name || "DM";
7094
+ }
7095
+ function colorChannelType(type) {
7096
+ if (type === "CHANNEL") return chalk22.cyan(type);
7097
+ if (type === "DM") return chalk22.dim(type);
7098
+ if (type === "GROUP_DM") return chalk22.blue(type);
7099
+ return type;
7100
+ }
7101
+ function colorVisibility(v) {
7102
+ if (v === "PUBLIC") return chalk22.green(v);
7103
+ return chalk22.dim(v);
7104
+ }
7055
7105
  var CHANNEL_COLUMNS = [
7056
- { key: "id", label: "ID", maxWidth: 20 },
7057
- { key: "name", label: "Name", maxWidth: 40 },
7058
- { key: "type", label: "Type", maxWidth: 12 },
7059
- { key: "visibility", label: "Visibility", maxWidth: 10 },
7106
+ { key: "name", label: "Name", maxWidth: 40, format: (v) => chalk22.bold(v) },
7107
+ { key: "id", label: "ID", maxWidth: 20, format: (v) => chalk22.dim(v) },
7108
+ { key: "type", label: "Type", maxWidth: 12, format: (v) => colorChannelType(v) },
7109
+ { key: "visibility", label: "Visibility", maxWidth: 10, format: (v) => colorVisibility(v) },
7060
7110
  { key: "topic", label: "Topic", maxWidth: 40 }
7061
7111
  ];
7062
7112
  function formatChannelsTable(channels) {
7063
7113
  if (channels.length === 0) return "No channels found";
7064
7114
  const rows = channels.map((c) => ({
7065
7115
  id: c.id,
7066
- name: c.name || "(unnamed)",
7116
+ name: channelName(c),
7067
7117
  type: c.type,
7068
7118
  visibility: c.visibility,
7069
7119
  topic: c.topic ?? ""
@@ -7073,25 +7123,32 @@ function formatChannelsTable(channels) {
7073
7123
  function formatChannelsMarkdown(channels) {
7074
7124
  if (channels.length === 0) return "No channels found";
7075
7125
  return channels.map((c) => {
7076
- const name = c.name || "(unnamed)";
7126
+ const name = channelName(c);
7077
7127
  return `- **${name}** (${c.id}) \u2014 ${c.type}${c.topic ? `, ${c.topic}` : ""}`;
7078
7128
  }).join("\n");
7079
7129
  }
7080
7130
  function formatChannelDetail(channel) {
7081
7131
  const lines = [];
7082
- lines.push(chalk22.bold(channel.name || "(unnamed)"));
7083
- lines.push(chalk22.dim(`ID: ${channel.id}`));
7084
- lines.push(`Type: ${channel.type}`);
7085
- lines.push(`Visibility: ${channel.visibility}`);
7086
- if (channel.topic) lines.push(`Topic: ${channel.topic}`);
7087
- if (channel.description) lines.push(`Description: ${channel.description}`);
7088
- lines.push(`Archived: ${channel.archived}`);
7089
- lines.push(`Created: ${channel.created_at}`);
7132
+ lines.push(chalk22.bold.underline(channelName(channel)));
7133
+ lines.push("");
7134
+ const fields = [
7135
+ ["ID", chalk22.dim(channel.id)],
7136
+ ["Type", colorChannelType(channel.type)],
7137
+ ["Visibility", colorVisibility(channel.visibility)]
7138
+ ];
7139
+ if (channel.topic) fields.push(["Topic", channel.topic]);
7140
+ if (channel.description) fields.push(["Description", channel.description]);
7141
+ fields.push(["Archived", channel.archived ? chalk22.yellow("Yes") : "No"]);
7142
+ fields.push(["Created", formatDate(channel.created_at)]);
7143
+ const maxLabel = Math.max(...fields.map(([k]) => k.length));
7144
+ for (const [label, value] of fields) {
7145
+ lines.push(` ${chalk22.bold(label.padEnd(maxLabel + 1))} ${value}`);
7146
+ }
7090
7147
  return lines.join("\n");
7091
7148
  }
7092
7149
  var CHAT_MEMBER_COLUMNS = [
7093
- { key: "name", label: "Name", maxWidth: 30 },
7094
- { key: "id", label: "ID", maxWidth: 15 },
7150
+ { key: "name", label: "Name", maxWidth: 30, format: (v) => chalk22.bold(v) },
7151
+ { key: "id", label: "ID", maxWidth: 15, format: (v) => chalk22.dim(v) },
7095
7152
  { key: "email", label: "Email", maxWidth: 40 },
7096
7153
  { key: "type", label: "Type", maxWidth: 12 }
7097
7154
  ];
@@ -7115,23 +7172,25 @@ function formatChatMembersMarkdown(members) {
7115
7172
 
7116
7173
  // src/commands/chat-message.ts
7117
7174
  import chalk23 from "chalk";
7175
+ var separator = chalk23.dim("-".repeat(60));
7118
7176
  function formatMessages(messages) {
7119
7177
  if (messages.length === 0) return "No messages";
7120
7178
  const lines = [];
7121
- for (const msg of messages) {
7122
- const date = new Date(msg.date).toLocaleString();
7123
- const header = [chalk23.bold(`@${msg.user_id}`), chalk23.dim(date), chalk23.dim(`(${msg.id})`)];
7124
- if (msg.type === "post" && msg.post_data?.title) {
7125
- header.push(chalk23.cyan(`[${msg.post_data.title}]`));
7126
- }
7179
+ for (let i = 0; i < messages.length; i++) {
7180
+ const msg = messages[i];
7181
+ if (i > 0) lines.push(separator);
7182
+ const meta = [chalk23.bold(`@${msg.user_id}`), chalk23.dim(formatTimestamp(msg.date))];
7127
7183
  if (msg.replies_count) {
7128
- header.push(chalk23.dim(`${msg.replies_count} replies`));
7184
+ meta.push(chalk23.dim(`${msg.replies_count} replies`));
7129
7185
  }
7130
- lines.push(header.join(" "));
7131
- lines.push(` ${msg.content}`);
7132
- lines.push("");
7186
+ meta.push(chalk23.dim(`(${msg.id})`));
7187
+ lines.push(meta.join(" "));
7188
+ if (msg.type === "post" && msg.post_data?.title) {
7189
+ lines.push(chalk23.cyan.bold(msg.post_data.title));
7190
+ }
7191
+ lines.push(msg.content);
7133
7192
  }
7134
- return lines.join("\n").trimEnd();
7193
+ return lines.join("\n");
7135
7194
  }
7136
7195
  function formatMessagesMarkdown(messages) {
7137
7196
  if (messages.length === 0) return "No messages";
@@ -7147,6 +7206,29 @@ ${msg.content}`;
7147
7206
 
7148
7207
  // src/commands/chat-reaction.ts
7149
7208
  import chalk24 from "chalk";
7209
+ var EMOJI_MAP = {
7210
+ thumbsup: "\u{1F44D}",
7211
+ thumbsdown: "\u{1F44E}",
7212
+ heart: "\u2764\uFE0F",
7213
+ fire: "\u{1F525}",
7214
+ eyes: "\u{1F440}",
7215
+ rocket: "\u{1F680}",
7216
+ tada: "\u{1F389}",
7217
+ check: "\u2705",
7218
+ x: "\u274C",
7219
+ warning: "\u26A0\uFE0F",
7220
+ laugh: "\u{1F602}",
7221
+ smile: "\u{1F60A}",
7222
+ thinking: "\u{1F914}",
7223
+ clap: "\u{1F44F}",
7224
+ pray: "\u{1F64F}",
7225
+ 100: "\u{1F4AF}",
7226
+ star: "\u2B50",
7227
+ wave: "\u{1F44B}"
7228
+ };
7229
+ function emojiChar(name) {
7230
+ return EMOJI_MAP[name] ?? `:${name}:`;
7231
+ }
7150
7232
  function groupByEmoji(reactions) {
7151
7233
  const groups = /* @__PURE__ */ new Map();
7152
7234
  for (const r of reactions) {
@@ -7161,7 +7243,9 @@ function formatReactions(reactions) {
7161
7243
  const groups = groupByEmoji(reactions);
7162
7244
  const lines = [];
7163
7245
  for (const [emoji, users] of groups) {
7164
- lines.push(`${chalk24.bold(`:${emoji}:`)} ${users.join(", ")}`);
7246
+ const icon = emojiChar(emoji);
7247
+ const userList = users.map((u) => chalk24.bold(`@${u}`)).join(", ");
7248
+ lines.push(`${icon} ${chalk24.dim(emoji)} ${chalk24.dim(`(${users.length})`)} \u2014 ${userList}`);
7165
7249
  }
7166
7250
  return lines.join("\n");
7167
7251
  }
@@ -7607,7 +7691,13 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
7607
7691
  program.command("update <taskId>").description("Update a task").option("-n, --name <text>", "New task name").option("-d, --description <text>", "New description (markdown supported)").option(
7608
7692
  "-s, --status <status>",
7609
7693
  'New status (fuzzy matched, e.g. "prog" matches "in progress")'
7610
- ).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(
7694
+ ).option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option(
7695
+ "--due-date <date>",
7696
+ 'Due date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset; or "none"/"clear" to remove)'
7697
+ ).option(
7698
+ "--start-date <date>",
7699
+ "Start date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset)"
7700
+ ).option(
7611
7701
  "--time-estimate <duration>",
7612
7702
  'Time estimate (e.g. "2h", "30m", "1h30m", "0" or "none" to clear)'
7613
7703
  ).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(
@@ -7657,7 +7747,13 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
7657
7747
  }
7658
7748
  )
7659
7749
  );
7660
- 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(
7750
+ 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(
7751
+ "--due-date <date>",
7752
+ "Due date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset)"
7753
+ ).option(
7754
+ "--start-date <date>",
7755
+ "Start date (YYYY-MM-DD, YYYY-MM-DDTHH:MM[:SS], or full ISO 8601 with offset)"
7756
+ ).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(
7661
7757
  wrapAction(async (opts) => {
7662
7758
  const config = loadConfig(getProfileName());
7663
7759
  if (opts.list === "sprint:current") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "1.27.0",
3
+ "version": "1.28.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.0
6
+ # ClickUp CLI (`cup`) - skill version 1.28.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.0, 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.28.0, update with `npm install -g @krodak/clickup-cli` and refresh this skill with `cup skill`.
11
11
 
12
12
  ## Install & Configure
13
13
 
@@ -248,7 +248,7 @@ All commands support `--help` for full flag details. All commands support `--jso
248
248
  | Task IDs | Native (`abc123def`) or custom (`PROJ-123`). Custom IDs auto-detected by `PREFIX-DIGITS` format |
249
249
  | `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr |
250
250
  | `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
251
- | `--due-date` | `YYYY-MM-DD` format |
251
+ | `--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
252
  | `--assignee` | User ID or `me` |
253
253
  | `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
254
254
  | `--time-estimate` | Duration: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
@@ -323,6 +323,8 @@ cup inbox --days 7 # recently updated
323
323
  ```bash
324
324
  cup update abc123def -s "done"
325
325
  cup update abc123def --priority high --due-date 2025-03-15
326
+ cup update abc123def --due-date 2025-03-15T14:30 # date + time (user's timezone)
327
+ cup update abc123def --due-date 2025-03-15T14:30:00Z # UTC
326
328
  cup create -n "Fix the thing" -p abc123def
327
329
  cup create -n "Fix bug" -l <listId> --priority urgent --tags "bug,frontend"
328
330
  cup create -n "Bug fix" -l sprint:current # create in active sprint