@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.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +87 -41
- package/package.json +1 -1
|
@@ -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.
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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.
|
|
1162
|
-
const month = String(d.
|
|
1163
|
-
const day = String(d.
|
|
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
|
|
1734
|
-
|
|
1735
|
-
|
|
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
|
-
|
|
1836
|
-
|
|
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,
|
|
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 ?
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
opts.removeAssignee
|
|
5921
|
-
|
|
5922
|
-
|
|
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
|
|
5942
|
-
const task = await
|
|
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
|
}
|