@krodak/clickup-cli 1.16.1 → 1.16.2

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.16.1",
4
+ "version": "1.16.2",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/dist/index.js CHANGED
@@ -128,14 +128,22 @@ var ClickUpClient = class {
128
128
  }
129
129
  async getMe() {
130
130
  if (this.meCache) return this.meCache;
131
- const data = await this.request("/user");
131
+ const data = await this.request(
132
+ "/user"
133
+ );
132
134
  const user = expectRecordField(data, "user", "user");
135
+ const timezone = typeof user.timezone === "string" && user.timezone ? user.timezone : void 0;
133
136
  this.meCache = {
134
137
  id: expectNumericField(user, "id", "user"),
135
- username: expectStringField(user, "username", "user")
138
+ username: expectStringField(user, "username", "user"),
139
+ ...timezone ? { timezone } : {}
136
140
  };
137
141
  return this.meCache;
138
142
  }
143
+ async getUserTimezone() {
144
+ const me = await this.getMe();
145
+ return me.timezone;
146
+ }
139
147
  async paginate(buildPath) {
140
148
  const allTasks = [];
141
149
  let page = 0;
@@ -304,21 +312,21 @@ var ClickUpClient = class {
304
312
  }
305
313
  async getView(viewId) {
306
314
  const data = await this.request(`/view/${viewId}`);
307
- return data.view;
315
+ return expectRecordField(data, "view", "view");
308
316
  }
309
317
  async createListView(listId, payload) {
310
318
  const data = await this.request(`/list/${listId}/view`, {
311
319
  method: "POST",
312
320
  body: JSON.stringify(payload)
313
321
  });
314
- return data.view;
322
+ return expectRecordField(data, "view", "view");
315
323
  }
316
324
  async updateView(viewId, payload) {
317
325
  const data = await this.request(`/view/${viewId}`, {
318
326
  method: "PUT",
319
327
  body: JSON.stringify(payload)
320
328
  });
321
- return data.view;
329
+ return expectRecordField(data, "view", "view");
322
330
  }
323
331
  async deleteView(viewId) {
324
332
  await this.request(`/view/${viewId}`, { method: "DELETE" });
@@ -676,7 +684,7 @@ var ClickUpClient = class {
676
684
  async createGoal(teamId, name, opts) {
677
685
  const body = { name, multiple_owners: true };
678
686
  if (opts?.description) body.description = opts.description;
679
- if (opts?.dueDate) body.due_date = Number(opts.dueDate);
687
+ if (opts?.dueDate != null) body.due_date = opts.dueDate;
680
688
  if (opts?.color) body.color = opts.color;
681
689
  const data = await this.request(`/team/${teamId}/goal`, {
682
690
  method: "POST",
@@ -965,7 +973,7 @@ function loadConfig(profileName) {
965
973
  ...fileConfig.sprintFolderId ? { sprintFolderId: fileConfig.sprintFolderId } : {}
966
974
  };
967
975
  }
968
- const multi = migrateToMultiProfile(parsed, path);
976
+ const multi = loadMultiProfileConfig();
969
977
  const resolvedProfile = profileName ?? process.env.CU_PROFILE?.trim() ?? multi.defaultProfile;
970
978
  if (!resolvedProfile) {
971
979
  throw new Error("No default profile set. Run: cup profile use <name>");
@@ -1158,9 +1166,9 @@ function formatDuration(ms) {
1158
1166
  }
1159
1167
  function formatDateISO(ms) {
1160
1168
  const d = new Date(Number(ms));
1161
- const year = d.getFullYear();
1162
- const month = String(d.getMonth() + 1).padStart(2, "0");
1163
- const day = String(d.getDate()).padStart(2, "0");
1169
+ const year = d.getUTCFullYear();
1170
+ const month = String(d.getUTCMonth() + 1).padStart(2, "0");
1171
+ const day = String(d.getUTCDate()).padStart(2, "0");
1164
1172
  return `${year}-${month}-${day}`;
1165
1173
  }
1166
1174
 
@@ -1725,14 +1733,40 @@ function parsePriority(value) {
1725
1733
  if (Number.isInteger(num) && num >= 1 && num <= 4) return num;
1726
1734
  throw new Error("Priority must be urgent, high, normal, low, or 1-4");
1727
1735
  }
1728
- function parseDueDate(value) {
1736
+ function parseDueDate(value, timezone) {
1729
1737
  if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
1730
1738
  throw new Error("Date must be in YYYY-MM-DD format");
1731
1739
  }
1732
- const parts = value.split("-");
1733
- const date = new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
1734
- if (isNaN(date.getTime())) throw new Error(`Invalid date: ${value}`);
1735
- return date.getTime();
1740
+ const parts = value.split("-").map(Number);
1741
+ const y = parts[0];
1742
+ const m = parts[1];
1743
+ const d = parts[2];
1744
+ if (timezone) {
1745
+ try {
1746
+ const ms2 = dateToTimezoneMs(y, m, d, timezone);
1747
+ if (!isNaN(ms2)) return ms2;
1748
+ } catch {
1749
+ }
1750
+ }
1751
+ const ms = Date.UTC(y, m - 1, d);
1752
+ if (isNaN(ms)) throw new Error(`Invalid date: ${value}`);
1753
+ return ms;
1754
+ }
1755
+ function dateToTimezoneMs(year, month, day, timezone) {
1756
+ const approxUtc = new Date(Date.UTC(year, month - 1, day));
1757
+ const tzStr = approxUtc.toLocaleString("en-US", {
1758
+ timeZone: timezone,
1759
+ year: "numeric",
1760
+ month: "2-digit",
1761
+ day: "2-digit",
1762
+ hour: "2-digit",
1763
+ minute: "2-digit",
1764
+ second: "2-digit",
1765
+ hour12: false
1766
+ });
1767
+ const tzDate = /* @__PURE__ */ new Date(tzStr + " UTC");
1768
+ const offset = approxUtc.getTime() - tzDate.getTime();
1769
+ return approxUtc.getTime() + offset;
1736
1770
  }
1737
1771
  function parseAssigneeId(value) {
1738
1772
  const id = Number(value);
@@ -1761,7 +1795,7 @@ function parseTimeEstimate(value) {
1761
1795
  'Time estimate must be a duration (e.g. "2h", "30m", "1h30m"), milliseconds, or "0" to clear'
1762
1796
  );
1763
1797
  }
1764
- function buildUpdatePayload(opts) {
1798
+ function buildUpdatePayload(opts, timezone) {
1765
1799
  if (opts.archive && opts.unarchive) {
1766
1800
  throw new Error("Cannot use --archive and --unarchive together");
1767
1801
  }
@@ -1780,12 +1814,12 @@ function buildUpdatePayload(opts) {
1780
1814
  if (opts.dueDate === "none" || opts.dueDate === "clear") {
1781
1815
  payload.due_date = null;
1782
1816
  } else {
1783
- payload.due_date = parseDueDate(opts.dueDate);
1817
+ payload.due_date = parseDueDate(opts.dueDate, timezone);
1784
1818
  payload.due_date_time = false;
1785
1819
  }
1786
1820
  }
1787
1821
  if (opts.startDate !== void 0) {
1788
- payload.start_date = parseDueDate(opts.startDate);
1822
+ payload.start_date = parseDueDate(opts.startDate, timezone);
1789
1823
  payload.start_date_time = false;
1790
1824
  }
1791
1825
  if (opts.assignee !== void 0 || opts.removeAssignee !== void 0) {
@@ -1832,10 +1866,11 @@ async function updateTask(config, taskId, options) {
1832
1866
  "Provide at least one of: --name, --description, --status, --priority, --due-date, --start-date, --time-estimate, --assignee, --remove-assignee, --parent, --detach, --archive, --unarchive"
1833
1867
  );
1834
1868
  const client = new ClickUpClient(config);
1835
- if (options.status !== void 0) {
1836
- options.status = await resolveStatus(client, taskId, options.status);
1869
+ const resolved = { ...options };
1870
+ if (resolved.status !== void 0) {
1871
+ resolved.status = await resolveStatus(client, taskId, resolved.status);
1837
1872
  }
1838
- const task = await client.updateTask(taskId, options);
1873
+ const task = await client.updateTask(taskId, resolved);
1839
1874
  return { id: task.id, name: task.name };
1840
1875
  }
1841
1876
 
@@ -1855,6 +1890,7 @@ async function createTask(config, options) {
1855
1890
  const task2 = await client.createTaskFromTemplate(listId, options.template, options.name);
1856
1891
  return { id: task2.id, name: task2.name, url: task2.url };
1857
1892
  }
1893
+ const timezone = await client.getUserTimezone();
1858
1894
  const payload = {
1859
1895
  name: options.name,
1860
1896
  ...options.description !== void 0 ? { markdown_content: options.description } : {},
@@ -1865,11 +1901,11 @@ async function createTask(config, options) {
1865
1901
  payload.priority = parsePriority(options.priority);
1866
1902
  }
1867
1903
  if (options.dueDate !== void 0) {
1868
- payload.due_date = parseDueDate(options.dueDate);
1904
+ payload.due_date = parseDueDate(options.dueDate, timezone);
1869
1905
  payload.due_date_time = false;
1870
1906
  }
1871
1907
  if (options.startDate !== void 0) {
1872
- payload.start_date = parseDueDate(options.startDate);
1908
+ payload.start_date = parseDueDate(options.startDate, timezone);
1873
1909
  payload.start_date_time = false;
1874
1910
  }
1875
1911
  if (options.assignee !== void 0) {
@@ -5333,7 +5369,6 @@ function formatFieldsMarkdown(fields) {
5333
5369
  }
5334
5370
 
5335
5371
  // src/commands/duplicate.ts
5336
- var PRIORITY_MAP2 = { urgent: 1, high: 2, normal: 3, low: 4 };
5337
5372
  async function duplicateTask(config, taskId) {
5338
5373
  const client = new ClickUpClient(config);
5339
5374
  const task = await client.getTask(taskId);
@@ -5341,7 +5376,7 @@ async function duplicateTask(config, taskId) {
5341
5376
  name: `${task.name} (copy)`,
5342
5377
  description: task.description,
5343
5378
  markdown_content: task.markdown_content,
5344
- priority: task.priority ? PRIORITY_MAP2[task.priority.priority.toLowerCase()] : void 0,
5379
+ priority: task.priority ? parsePriority(task.priority.priority.toLowerCase()) : void 0,
5345
5380
  tags: task.tags?.map((t) => t.name),
5346
5381
  time_estimate: task.time_estimate ?? void 0
5347
5382
  });
@@ -5380,7 +5415,8 @@ async function bulkAssign(config, userIdOrMe, taskIds, action) {
5380
5415
  }
5381
5416
  async function bulkDueDate(config, date, taskIds) {
5382
5417
  const client = new ClickUpClient(config);
5383
- const payload = date === "none" || date === "clear" ? { due_date: null } : { due_date: parseDueDate(date), due_date_time: false };
5418
+ const timezone = await client.getUserTimezone();
5419
+ const payload = date === "none" || date === "clear" ? { due_date: null } : { due_date: parseDueDate(date, timezone), due_date_time: false };
5384
5420
  const failed = [];
5385
5421
  for (const id of taskIds) {
5386
5422
  try {
@@ -5438,7 +5474,12 @@ async function listGoals(config) {
5438
5474
  }
5439
5475
  async function createGoal(config, name, opts) {
5440
5476
  const client = new ClickUpClient(config);
5441
- return client.createGoal(config.teamId, name, opts);
5477
+ const timezone = await client.getUserTimezone();
5478
+ return client.createGoal(config.teamId, name, {
5479
+ description: opts?.description,
5480
+ color: opts?.color,
5481
+ ...opts?.dueDate ? { dueDate: parseDueDate(opts.dueDate, timezone) } : {}
5482
+ });
5442
5483
  }
5443
5484
  async function updateGoal(config, goalId, updates) {
5444
5485
  const client = new ClickUpClient(config);
@@ -5763,14 +5804,17 @@ function formatFiltersTable(filters) {
5763
5804
  }));
5764
5805
  return formatTable(rows, FILTER_COLUMNS);
5765
5806
  }
5807
+ function escapeMarkdownCell(value) {
5808
+ return value.replace(/\|/g, "\\|");
5809
+ }
5766
5810
  function formatFiltersMarkdown(filters) {
5767
5811
  const entries = Object.entries(filters);
5768
5812
  if (entries.length === 0) return "No filters saved";
5769
5813
  const lines = ["| Name | Command | Description |", "| --- | --- | --- |"];
5770
5814
  for (const [name, entry] of entries) {
5771
- const command = entry.command.join(" ");
5772
- const description = entry.description ?? "";
5773
- lines.push(`| ${name} | ${command} | ${description} |`);
5815
+ const command = escapeMarkdownCell(entry.command.join(" "));
5816
+ const description = escapeMarkdownCell(entry.description ?? "");
5817
+ lines.push(`| ${escapeMarkdownCell(name)} | ${command} | ${description} |`);
5774
5818
  }
5775
5819
  return lines.join("\n");
5776
5820
  }
@@ -5911,15 +5955,17 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
5911
5955
  wrapAction(
5912
5956
  async (taskId, opts) => {
5913
5957
  const config = loadConfig(getProfileName());
5914
- if (opts.assignee === "me") {
5915
- const client = new ClickUpClient(config);
5916
- opts.assignee = String(await resolveAssigneeId(client, "me"));
5917
- }
5918
- if (opts.removeAssignee === "me") {
5919
- const client = new ClickUpClient(config);
5920
- opts.removeAssignee = String(await resolveAssigneeId(client, "me"));
5921
- }
5922
- const payload = buildUpdatePayload(opts);
5958
+ const client = new ClickUpClient(config);
5959
+ const [timezone] = await Promise.all([
5960
+ client.getUserTimezone(),
5961
+ opts.assignee === "me" ? resolveAssigneeId(client, "me").then((id) => {
5962
+ opts.assignee = String(id);
5963
+ }) : Promise.resolve(),
5964
+ opts.removeAssignee === "me" ? resolveAssigneeId(client, "me").then((id) => {
5965
+ opts.removeAssignee = String(id);
5966
+ }) : Promise.resolve()
5967
+ ]);
5968
+ const payload = buildUpdatePayload(opts, timezone);
5923
5969
  const hasFields = (opts.field?.length ?? 0) > 0;
5924
5970
  if (!hasFields && Object.keys(payload).length === 0) {
5925
5971
  throw new Error(
@@ -5938,8 +5984,8 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
5938
5984
  await setCustomField(config, taskId, { set: [opts.field[i], opts.field[i + 1]] });
5939
5985
  }
5940
5986
  if (!result) {
5941
- const client = new ClickUpClient(config);
5942
- const task = await client.getTask(taskId);
5987
+ const client2 = new ClickUpClient(config);
5988
+ const task = await client2.getTask(taskId);
5943
5989
  result = { id: task.id, name: task.name };
5944
5990
  }
5945
5991
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "1.16.1",
3
+ "version": "1.16.2",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",