@krodak/clickup-cli 1.16.1 → 1.17.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.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +252 -47
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +63 -55
|
@@ -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.
|
|
4
|
+
"version": "1.17.0",
|
|
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;
|
|
@@ -168,9 +176,22 @@ var ClickUpClient = class {
|
|
|
168
176
|
const me = await this.getMe();
|
|
169
177
|
baseParams.append("assignees[]", String(me.id));
|
|
170
178
|
}
|
|
179
|
+
if (filters.assignees) {
|
|
180
|
+
for (const id of filters.assignees) baseParams.append("assignees[]", String(id));
|
|
181
|
+
}
|
|
171
182
|
for (const s of filters.statuses ?? []) baseParams.append("statuses[]", s);
|
|
172
183
|
for (const id of filters.listIds ?? []) baseParams.append("list_ids[]", id);
|
|
173
184
|
for (const id of filters.spaceIds ?? []) baseParams.append("space_ids[]", id);
|
|
185
|
+
for (const tag of filters.tags ?? []) baseParams.append("tags[]", tag);
|
|
186
|
+
if (filters.dueDateGt) baseParams.set("due_date_gt", String(filters.dueDateGt));
|
|
187
|
+
if (filters.dueDateLt) baseParams.set("due_date_lt", String(filters.dueDateLt));
|
|
188
|
+
if (filters.dateCreatedGt) baseParams.set("date_created_gt", String(filters.dateCreatedGt));
|
|
189
|
+
if (filters.dateCreatedLt) baseParams.set("date_created_lt", String(filters.dateCreatedLt));
|
|
190
|
+
if (filters.dateUpdatedGt) baseParams.set("date_updated_gt", String(filters.dateUpdatedGt));
|
|
191
|
+
if (filters.dateUpdatedLt) baseParams.set("date_updated_lt", String(filters.dateUpdatedLt));
|
|
192
|
+
if (filters.customFields?.length) {
|
|
193
|
+
baseParams.set("custom_fields", JSON.stringify(filters.customFields));
|
|
194
|
+
}
|
|
174
195
|
return this.paginate((page) => {
|
|
175
196
|
const params = new URLSearchParams(baseParams);
|
|
176
197
|
params.set("page", String(page));
|
|
@@ -304,21 +325,21 @@ var ClickUpClient = class {
|
|
|
304
325
|
}
|
|
305
326
|
async getView(viewId) {
|
|
306
327
|
const data = await this.request(`/view/${viewId}`);
|
|
307
|
-
return data
|
|
328
|
+
return expectRecordField(data, "view", "view");
|
|
308
329
|
}
|
|
309
330
|
async createListView(listId, payload) {
|
|
310
331
|
const data = await this.request(`/list/${listId}/view`, {
|
|
311
332
|
method: "POST",
|
|
312
333
|
body: JSON.stringify(payload)
|
|
313
334
|
});
|
|
314
|
-
return data
|
|
335
|
+
return expectRecordField(data, "view", "view");
|
|
315
336
|
}
|
|
316
337
|
async updateView(viewId, payload) {
|
|
317
338
|
const data = await this.request(`/view/${viewId}`, {
|
|
318
339
|
method: "PUT",
|
|
319
340
|
body: JSON.stringify(payload)
|
|
320
341
|
});
|
|
321
|
-
return data
|
|
342
|
+
return expectRecordField(data, "view", "view");
|
|
322
343
|
}
|
|
323
344
|
async deleteView(viewId) {
|
|
324
345
|
await this.request(`/view/${viewId}`, { method: "DELETE" });
|
|
@@ -676,7 +697,7 @@ var ClickUpClient = class {
|
|
|
676
697
|
async createGoal(teamId, name, opts) {
|
|
677
698
|
const body = { name, multiple_owners: true };
|
|
678
699
|
if (opts?.description) body.description = opts.description;
|
|
679
|
-
if (opts?.dueDate) body.due_date =
|
|
700
|
+
if (opts?.dueDate != null) body.due_date = opts.dueDate;
|
|
680
701
|
if (opts?.color) body.color = opts.color;
|
|
681
702
|
const data = await this.request(`/team/${teamId}/goal`, {
|
|
682
703
|
method: "POST",
|
|
@@ -965,7 +986,7 @@ function loadConfig(profileName) {
|
|
|
965
986
|
...fileConfig.sprintFolderId ? { sprintFolderId: fileConfig.sprintFolderId } : {}
|
|
966
987
|
};
|
|
967
988
|
}
|
|
968
|
-
const multi =
|
|
989
|
+
const multi = loadMultiProfileConfig();
|
|
969
990
|
const resolvedProfile = profileName ?? process.env.CU_PROFILE?.trim() ?? multi.defaultProfile;
|
|
970
991
|
if (!resolvedProfile) {
|
|
971
992
|
throw new Error("No default profile set. Run: cup profile use <name>");
|
|
@@ -1158,9 +1179,9 @@ function formatDuration(ms) {
|
|
|
1158
1179
|
}
|
|
1159
1180
|
function formatDateISO(ms) {
|
|
1160
1181
|
const d = new Date(Number(ms));
|
|
1161
|
-
const year = d.
|
|
1162
|
-
const month = String(d.
|
|
1163
|
-
const day = String(d.
|
|
1182
|
+
const year = d.getUTCFullYear();
|
|
1183
|
+
const month = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
1184
|
+
const day = String(d.getUTCDate()).padStart(2, "0");
|
|
1164
1185
|
return `${year}-${month}-${day}`;
|
|
1165
1186
|
}
|
|
1166
1187
|
|
|
@@ -1725,14 +1746,40 @@ function parsePriority(value) {
|
|
|
1725
1746
|
if (Number.isInteger(num) && num >= 1 && num <= 4) return num;
|
|
1726
1747
|
throw new Error("Priority must be urgent, high, normal, low, or 1-4");
|
|
1727
1748
|
}
|
|
1728
|
-
function parseDueDate(value) {
|
|
1749
|
+
function parseDueDate(value, timezone) {
|
|
1729
1750
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
1730
1751
|
throw new Error("Date must be in YYYY-MM-DD format");
|
|
1731
1752
|
}
|
|
1732
|
-
const parts = value.split("-");
|
|
1733
|
-
const
|
|
1734
|
-
|
|
1735
|
-
|
|
1753
|
+
const parts = value.split("-").map(Number);
|
|
1754
|
+
const y = parts[0];
|
|
1755
|
+
const m = parts[1];
|
|
1756
|
+
const d = parts[2];
|
|
1757
|
+
if (timezone) {
|
|
1758
|
+
try {
|
|
1759
|
+
const ms2 = dateToTimezoneMs(y, m, d, timezone);
|
|
1760
|
+
if (!isNaN(ms2)) return ms2;
|
|
1761
|
+
} catch {
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
const ms = Date.UTC(y, m - 1, d);
|
|
1765
|
+
if (isNaN(ms)) throw new Error(`Invalid date: ${value}`);
|
|
1766
|
+
return ms;
|
|
1767
|
+
}
|
|
1768
|
+
function dateToTimezoneMs(year, month, day, timezone) {
|
|
1769
|
+
const approxUtc = new Date(Date.UTC(year, month - 1, day));
|
|
1770
|
+
const tzStr = approxUtc.toLocaleString("en-US", {
|
|
1771
|
+
timeZone: timezone,
|
|
1772
|
+
year: "numeric",
|
|
1773
|
+
month: "2-digit",
|
|
1774
|
+
day: "2-digit",
|
|
1775
|
+
hour: "2-digit",
|
|
1776
|
+
minute: "2-digit",
|
|
1777
|
+
second: "2-digit",
|
|
1778
|
+
hour12: false
|
|
1779
|
+
});
|
|
1780
|
+
const tzDate = /* @__PURE__ */ new Date(tzStr + " UTC");
|
|
1781
|
+
const offset = approxUtc.getTime() - tzDate.getTime();
|
|
1782
|
+
return approxUtc.getTime() + offset;
|
|
1736
1783
|
}
|
|
1737
1784
|
function parseAssigneeId(value) {
|
|
1738
1785
|
const id = Number(value);
|
|
@@ -1761,7 +1808,7 @@ function parseTimeEstimate(value) {
|
|
|
1761
1808
|
'Time estimate must be a duration (e.g. "2h", "30m", "1h30m"), milliseconds, or "0" to clear'
|
|
1762
1809
|
);
|
|
1763
1810
|
}
|
|
1764
|
-
function buildUpdatePayload(opts) {
|
|
1811
|
+
function buildUpdatePayload(opts, timezone) {
|
|
1765
1812
|
if (opts.archive && opts.unarchive) {
|
|
1766
1813
|
throw new Error("Cannot use --archive and --unarchive together");
|
|
1767
1814
|
}
|
|
@@ -1780,12 +1827,12 @@ function buildUpdatePayload(opts) {
|
|
|
1780
1827
|
if (opts.dueDate === "none" || opts.dueDate === "clear") {
|
|
1781
1828
|
payload.due_date = null;
|
|
1782
1829
|
} else {
|
|
1783
|
-
payload.due_date = parseDueDate(opts.dueDate);
|
|
1830
|
+
payload.due_date = parseDueDate(opts.dueDate, timezone);
|
|
1784
1831
|
payload.due_date_time = false;
|
|
1785
1832
|
}
|
|
1786
1833
|
}
|
|
1787
1834
|
if (opts.startDate !== void 0) {
|
|
1788
|
-
payload.start_date = parseDueDate(opts.startDate);
|
|
1835
|
+
payload.start_date = parseDueDate(opts.startDate, timezone);
|
|
1789
1836
|
payload.start_date_time = false;
|
|
1790
1837
|
}
|
|
1791
1838
|
if (opts.assignee !== void 0 || opts.removeAssignee !== void 0) {
|
|
@@ -1832,10 +1879,11 @@ async function updateTask(config, taskId, options) {
|
|
|
1832
1879
|
"Provide at least one of: --name, --description, --status, --priority, --due-date, --start-date, --time-estimate, --assignee, --remove-assignee, --parent, --detach, --archive, --unarchive"
|
|
1833
1880
|
);
|
|
1834
1881
|
const client = new ClickUpClient(config);
|
|
1835
|
-
|
|
1836
|
-
|
|
1882
|
+
const resolved = { ...options };
|
|
1883
|
+
if (resolved.status !== void 0) {
|
|
1884
|
+
resolved.status = await resolveStatus(client, taskId, resolved.status);
|
|
1837
1885
|
}
|
|
1838
|
-
const task = await client.updateTask(taskId,
|
|
1886
|
+
const task = await client.updateTask(taskId, resolved);
|
|
1839
1887
|
return { id: task.id, name: task.name };
|
|
1840
1888
|
}
|
|
1841
1889
|
|
|
@@ -1855,6 +1903,7 @@ async function createTask(config, options) {
|
|
|
1855
1903
|
const task2 = await client.createTaskFromTemplate(listId, options.template, options.name);
|
|
1856
1904
|
return { id: task2.id, name: task2.name, url: task2.url };
|
|
1857
1905
|
}
|
|
1906
|
+
const timezone = await client.getUserTimezone();
|
|
1858
1907
|
const payload = {
|
|
1859
1908
|
name: options.name,
|
|
1860
1909
|
...options.description !== void 0 ? { markdown_content: options.description } : {},
|
|
@@ -1865,11 +1914,11 @@ async function createTask(config, options) {
|
|
|
1865
1914
|
payload.priority = parsePriority(options.priority);
|
|
1866
1915
|
}
|
|
1867
1916
|
if (options.dueDate !== void 0) {
|
|
1868
|
-
payload.due_date = parseDueDate(options.dueDate);
|
|
1917
|
+
payload.due_date = parseDueDate(options.dueDate, timezone);
|
|
1869
1918
|
payload.due_date_time = false;
|
|
1870
1919
|
}
|
|
1871
1920
|
if (options.startDate !== void 0) {
|
|
1872
|
-
payload.start_date = parseDueDate(options.startDate);
|
|
1921
|
+
payload.start_date = parseDueDate(options.startDate, timezone);
|
|
1873
1922
|
payload.start_date_time = false;
|
|
1874
1923
|
}
|
|
1875
1924
|
if (options.assignee !== void 0) {
|
|
@@ -2819,6 +2868,13 @@ var commandMetadata = [
|
|
|
2819
2868
|
"--type",
|
|
2820
2869
|
"--all",
|
|
2821
2870
|
"--include-closed",
|
|
2871
|
+
"--assignee",
|
|
2872
|
+
"--tag",
|
|
2873
|
+
"--due-before",
|
|
2874
|
+
"--due-after",
|
|
2875
|
+
"--created-after",
|
|
2876
|
+
"--created-before",
|
|
2877
|
+
"--field",
|
|
2822
2878
|
"--json"
|
|
2823
2879
|
],
|
|
2824
2880
|
quickReference: [
|
|
@@ -3023,7 +3079,21 @@ var commandMetadata = [
|
|
|
3023
3079
|
{
|
|
3024
3080
|
name: "search",
|
|
3025
3081
|
description: "Search my tasks by name (use --all for all tasks)",
|
|
3026
|
-
flags: [
|
|
3082
|
+
flags: [
|
|
3083
|
+
"--status",
|
|
3084
|
+
"--list",
|
|
3085
|
+
"--space",
|
|
3086
|
+
"--all",
|
|
3087
|
+
"--include-closed",
|
|
3088
|
+
"--assignee",
|
|
3089
|
+
"--tag",
|
|
3090
|
+
"--due-before",
|
|
3091
|
+
"--due-after",
|
|
3092
|
+
"--created-after",
|
|
3093
|
+
"--created-before",
|
|
3094
|
+
"--field",
|
|
3095
|
+
"--json"
|
|
3096
|
+
],
|
|
3027
3097
|
quickReference: [
|
|
3028
3098
|
{ section: "read", usage: "search <query>", description: "Search my tasks by name" }
|
|
3029
3099
|
]
|
|
@@ -3783,7 +3853,15 @@ ${renderZshTopLevelCommands(name)}
|
|
|
3783
3853
|
'--space[Filter by space ID]:space_id:' \\
|
|
3784
3854
|
'--name[Filter by name]:query:' \\
|
|
3785
3855
|
'--type[Filter by task type]:type:' \\
|
|
3856
|
+
'--all[Include all tasks, not just mine]' \\
|
|
3786
3857
|
'--include-closed[Include done/closed tasks]' \\
|
|
3858
|
+
'--assignee[Filter by assignee]:user_id:' \\
|
|
3859
|
+
'--tag[Filter by tag name]:tag:' \\
|
|
3860
|
+
'--due-before[Tasks due before date]:date:' \\
|
|
3861
|
+
'--due-after[Tasks due after date]:date:' \\
|
|
3862
|
+
'--created-after[Tasks created after date]:date:' \\
|
|
3863
|
+
'--created-before[Tasks created before date]:date:' \\
|
|
3864
|
+
'--field[Filter by custom field]:field_name_and_value:' \\
|
|
3787
3865
|
'--json[Force JSON output]'
|
|
3788
3866
|
;;
|
|
3789
3867
|
task)
|
|
@@ -3897,8 +3975,17 @@ ${renderZshTopLevelCommands(name)}
|
|
|
3897
3975
|
_arguments \\
|
|
3898
3976
|
'1:query:' \\
|
|
3899
3977
|
'--status[Filter by status]:status:(open "in progress" "in review" done closed)' \\
|
|
3900
|
-
'--
|
|
3978
|
+
'--list[Filter by list ID]:list_id:' \\
|
|
3979
|
+
'--space[Filter by space ID]:space_id:' \\
|
|
3901
3980
|
'--all[Search all workspace tasks, not just mine]' \\
|
|
3981
|
+
'--include-closed[Include done/closed tasks in search]' \\
|
|
3982
|
+
'--assignee[Filter by assignee]:user_id:' \\
|
|
3983
|
+
'--tag[Filter by tag name]:tag:' \\
|
|
3984
|
+
'--due-before[Tasks due before date]:date:' \\
|
|
3985
|
+
'--due-after[Tasks due after date]:date:' \\
|
|
3986
|
+
'--created-after[Tasks created after date]:date:' \\
|
|
3987
|
+
'--created-before[Tasks created before date]:date:' \\
|
|
3988
|
+
'--field[Filter by custom field]:field_name_and_value:' \\
|
|
3902
3989
|
'--json[Force JSON output]'
|
|
3903
3990
|
;;
|
|
3904
3991
|
summary)
|
|
@@ -4587,7 +4674,19 @@ async function searchTasks(config, query, opts = {}) {
|
|
|
4587
4674
|
}
|
|
4588
4675
|
const client = new ClickUpClient(config);
|
|
4589
4676
|
const [allTasks, customTypes] = await Promise.all([
|
|
4590
|
-
client.getMyTasks(config.teamId, {
|
|
4677
|
+
client.getMyTasks(config.teamId, {
|
|
4678
|
+
all: opts.all,
|
|
4679
|
+
includeClosed: opts.includeClosed,
|
|
4680
|
+
listIds: opts.listIds,
|
|
4681
|
+
spaceIds: opts.spaceIds,
|
|
4682
|
+
assignees: opts.assignees,
|
|
4683
|
+
tags: opts.tags,
|
|
4684
|
+
dueDateGt: opts.dueDateGt,
|
|
4685
|
+
dueDateLt: opts.dueDateLt,
|
|
4686
|
+
dateCreatedGt: opts.dateCreatedGt,
|
|
4687
|
+
dateCreatedLt: opts.dateCreatedLt,
|
|
4688
|
+
customFields: opts.customFields
|
|
4689
|
+
}),
|
|
4591
4690
|
client.getCustomTaskTypes(config.teamId)
|
|
4592
4691
|
]);
|
|
4593
4692
|
const typeMap = buildTypeMap(customTypes);
|
|
@@ -5333,7 +5432,6 @@ function formatFieldsMarkdown(fields) {
|
|
|
5333
5432
|
}
|
|
5334
5433
|
|
|
5335
5434
|
// src/commands/duplicate.ts
|
|
5336
|
-
var PRIORITY_MAP2 = { urgent: 1, high: 2, normal: 3, low: 4 };
|
|
5337
5435
|
async function duplicateTask(config, taskId) {
|
|
5338
5436
|
const client = new ClickUpClient(config);
|
|
5339
5437
|
const task = await client.getTask(taskId);
|
|
@@ -5341,7 +5439,7 @@ async function duplicateTask(config, taskId) {
|
|
|
5341
5439
|
name: `${task.name} (copy)`,
|
|
5342
5440
|
description: task.description,
|
|
5343
5441
|
markdown_content: task.markdown_content,
|
|
5344
|
-
priority: task.priority ?
|
|
5442
|
+
priority: task.priority ? parsePriority(task.priority.priority.toLowerCase()) : void 0,
|
|
5345
5443
|
tags: task.tags?.map((t) => t.name),
|
|
5346
5444
|
time_estimate: task.time_estimate ?? void 0
|
|
5347
5445
|
});
|
|
@@ -5380,7 +5478,8 @@ async function bulkAssign(config, userIdOrMe, taskIds, action) {
|
|
|
5380
5478
|
}
|
|
5381
5479
|
async function bulkDueDate(config, date, taskIds) {
|
|
5382
5480
|
const client = new ClickUpClient(config);
|
|
5383
|
-
const
|
|
5481
|
+
const timezone = await client.getUserTimezone();
|
|
5482
|
+
const payload = date === "none" || date === "clear" ? { due_date: null } : { due_date: parseDueDate(date, timezone), due_date_time: false };
|
|
5384
5483
|
const failed = [];
|
|
5385
5484
|
for (const id of taskIds) {
|
|
5386
5485
|
try {
|
|
@@ -5438,7 +5537,12 @@ async function listGoals(config) {
|
|
|
5438
5537
|
}
|
|
5439
5538
|
async function createGoal(config, name, opts) {
|
|
5440
5539
|
const client = new ClickUpClient(config);
|
|
5441
|
-
|
|
5540
|
+
const timezone = await client.getUserTimezone();
|
|
5541
|
+
return client.createGoal(config.teamId, name, {
|
|
5542
|
+
description: opts?.description,
|
|
5543
|
+
color: opts?.color,
|
|
5544
|
+
...opts?.dueDate ? { dueDate: parseDueDate(opts.dueDate, timezone) } : {}
|
|
5545
|
+
});
|
|
5442
5546
|
}
|
|
5443
5547
|
async function updateGoal(config, goalId, updates) {
|
|
5444
5548
|
const client = new ClickUpClient(config);
|
|
@@ -5763,14 +5867,17 @@ function formatFiltersTable(filters) {
|
|
|
5763
5867
|
}));
|
|
5764
5868
|
return formatTable(rows, FILTER_COLUMNS);
|
|
5765
5869
|
}
|
|
5870
|
+
function escapeMarkdownCell(value) {
|
|
5871
|
+
return value.replace(/\|/g, "\\|");
|
|
5872
|
+
}
|
|
5766
5873
|
function formatFiltersMarkdown(filters) {
|
|
5767
5874
|
const entries = Object.entries(filters);
|
|
5768
5875
|
if (entries.length === 0) return "No filters saved";
|
|
5769
5876
|
const lines = ["| Name | Command | Description |", "| --- | --- | --- |"];
|
|
5770
5877
|
for (const [name, entry] of entries) {
|
|
5771
|
-
const command = entry.command.join(" ");
|
|
5772
|
-
const description = entry.description ?? "";
|
|
5773
|
-
lines.push(`| ${name} | ${command} | ${description} |`);
|
|
5878
|
+
const command = escapeMarkdownCell(entry.command.join(" "));
|
|
5879
|
+
const description = escapeMarkdownCell(entry.description ?? "");
|
|
5880
|
+
lines.push(`| ${escapeMarkdownCell(name)} | ${command} | ${description} |`);
|
|
5774
5881
|
}
|
|
5775
5882
|
return lines.join("\n");
|
|
5776
5883
|
}
|
|
@@ -5879,9 +5986,49 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
5879
5986
|
program.command("tasks").description("List tasks assigned to me (use --all for all tasks)").option("--status <status>", 'Filter by status (e.g. "in progress")').option("--list <listId>", "Filter by list ID").option("--space <spaceId>", "Filter by space ID").option("--name <partial>", "Filter by name (case-insensitive contains)").option(
|
|
5880
5987
|
"--type <type>",
|
|
5881
5988
|
'Filter by task type (e.g. "task", "initiative", or custom type name/ID)'
|
|
5882
|
-
).option("--all", "Include all tasks, not just mine").option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").action(
|
|
5989
|
+
).option("--all", "Include all tasks, not just mine").option("--include-closed", "Include done/closed tasks").option("--assignee <userId>", 'Filter by assignee (user ID or "me")').option("--tag <tag>", "Filter by tag name").option("--due-before <date>", "Tasks due before date (YYYY-MM-DD)").option("--due-after <date>", "Tasks due after date (YYYY-MM-DD)").option("--created-after <date>", "Tasks created after date (YYYY-MM-DD)").option("--created-before <date>", "Tasks created before date (YYYY-MM-DD)").option("--field <nameAndValue...>", 'Filter by custom field: --field "Name" value').option("--json", "Force JSON output even in terminal").action(
|
|
5883
5990
|
wrapAction(async (opts) => {
|
|
5884
5991
|
const config = loadConfig(getProfileName());
|
|
5992
|
+
let assigneeIds;
|
|
5993
|
+
if (opts.assignee) {
|
|
5994
|
+
if (opts.assignee === "me") {
|
|
5995
|
+
const client = new ClickUpClient(config);
|
|
5996
|
+
const me = await client.getMe();
|
|
5997
|
+
assigneeIds = [me.id];
|
|
5998
|
+
} else {
|
|
5999
|
+
assigneeIds = [Number(opts.assignee)];
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
const parseDateFilter = (d) => {
|
|
6003
|
+
const parts = d.split("-");
|
|
6004
|
+
return new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])).getTime();
|
|
6005
|
+
};
|
|
6006
|
+
let customFields;
|
|
6007
|
+
if (opts.field?.length) {
|
|
6008
|
+
if (opts.field.length % 2 !== 0) {
|
|
6009
|
+
throw new Error('--field requires pairs: --field "Name" value');
|
|
6010
|
+
}
|
|
6011
|
+
if (!opts.list) {
|
|
6012
|
+
throw new Error("--field filtering requires --list to resolve field names");
|
|
6013
|
+
}
|
|
6014
|
+
const client = new ClickUpClient(config);
|
|
6015
|
+
const fields = await client.getListCustomFields(opts.list);
|
|
6016
|
+
customFields = [];
|
|
6017
|
+
for (let i = 0; i < opts.field.length; i += 2) {
|
|
6018
|
+
const fieldName = opts.field[i];
|
|
6019
|
+
const fieldValue = opts.field[i + 1];
|
|
6020
|
+
const match = fields.find((f) => f.name.toLowerCase() === fieldName.toLowerCase());
|
|
6021
|
+
if (!match) {
|
|
6022
|
+
const available = fields.map((f) => f.name).join(", ");
|
|
6023
|
+
throw new Error(`Field "${fieldName}" not found. Available: ${available}`);
|
|
6024
|
+
}
|
|
6025
|
+
customFields.push({
|
|
6026
|
+
field_id: match.id,
|
|
6027
|
+
operator: "=",
|
|
6028
|
+
value: fieldValue
|
|
6029
|
+
});
|
|
6030
|
+
}
|
|
6031
|
+
}
|
|
5885
6032
|
const tasks = await fetchMyTasks(config, {
|
|
5886
6033
|
typeFilter: opts.type,
|
|
5887
6034
|
statuses: opts.status ? [opts.status] : void 0,
|
|
@@ -5889,6 +6036,13 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
5889
6036
|
spaceIds: opts.space ? [opts.space] : void 0,
|
|
5890
6037
|
name: opts.name,
|
|
5891
6038
|
all: opts.all,
|
|
6039
|
+
assignees: assigneeIds,
|
|
6040
|
+
tags: opts.tag ? [opts.tag] : void 0,
|
|
6041
|
+
dueDateLt: opts.dueBefore ? parseDateFilter(opts.dueBefore) : void 0,
|
|
6042
|
+
dueDateGt: opts.dueAfter ? parseDateFilter(opts.dueAfter) : void 0,
|
|
6043
|
+
dateCreatedGt: opts.createdAfter ? parseDateFilter(opts.createdAfter) : void 0,
|
|
6044
|
+
dateCreatedLt: opts.createdBefore ? parseDateFilter(opts.createdBefore) : void 0,
|
|
6045
|
+
customFields,
|
|
5892
6046
|
includeClosed: opts.includeClosed
|
|
5893
6047
|
});
|
|
5894
6048
|
await printTasks(tasks, opts.json ?? false, config);
|
|
@@ -5911,15 +6065,17 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
5911
6065
|
wrapAction(
|
|
5912
6066
|
async (taskId, opts) => {
|
|
5913
6067
|
const config = loadConfig(getProfileName());
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
opts.removeAssignee
|
|
5921
|
-
|
|
5922
|
-
|
|
6068
|
+
const client = new ClickUpClient(config);
|
|
6069
|
+
const [timezone] = await Promise.all([
|
|
6070
|
+
client.getUserTimezone(),
|
|
6071
|
+
opts.assignee === "me" ? resolveAssigneeId(client, "me").then((id) => {
|
|
6072
|
+
opts.assignee = String(id);
|
|
6073
|
+
}) : Promise.resolve(),
|
|
6074
|
+
opts.removeAssignee === "me" ? resolveAssigneeId(client, "me").then((id) => {
|
|
6075
|
+
opts.removeAssignee = String(id);
|
|
6076
|
+
}) : Promise.resolve()
|
|
6077
|
+
]);
|
|
6078
|
+
const payload = buildUpdatePayload(opts, timezone);
|
|
5923
6079
|
const hasFields = (opts.field?.length ?? 0) > 0;
|
|
5924
6080
|
if (!hasFields && Object.keys(payload).length === 0) {
|
|
5925
6081
|
throw new Error(
|
|
@@ -5938,8 +6094,8 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
5938
6094
|
await setCustomField(config, taskId, { set: [opts.field[i], opts.field[i + 1]] });
|
|
5939
6095
|
}
|
|
5940
6096
|
if (!result) {
|
|
5941
|
-
const
|
|
5942
|
-
const task = await
|
|
6097
|
+
const client2 = new ClickUpClient(config);
|
|
6098
|
+
const task = await client2.getTask(taskId);
|
|
5943
6099
|
result = { id: task.id, name: task.name };
|
|
5944
6100
|
}
|
|
5945
6101
|
}
|
|
@@ -6113,14 +6269,63 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6113
6269
|
await openTask(config, query, opts);
|
|
6114
6270
|
})
|
|
6115
6271
|
);
|
|
6116
|
-
program.command("search <query>").description("Search my tasks by name (use --all for all tasks)").option("--status <status>", "Filter by status").option("--all", "Search all tasks, not just mine").option("--include-closed", "Include done/closed tasks in search").option("--json", "Force JSON output even in terminal").action(
|
|
6272
|
+
program.command("search <query>").description("Search my tasks by name (use --all for all tasks)").option("--status <status>", "Filter by status").option("--list <listId>", "Filter by list ID").option("--space <spaceId>", "Filter by space ID").option("--all", "Search all tasks, not just mine").option("--include-closed", "Include done/closed tasks in search").option("--assignee <userId>", 'Filter by assignee (user ID or "me")').option("--tag <tag>", "Filter by tag name").option("--due-before <date>", "Tasks due before date (YYYY-MM-DD)").option("--due-after <date>", "Tasks due after date (YYYY-MM-DD)").option("--created-after <date>", "Tasks created after date (YYYY-MM-DD)").option("--created-before <date>", "Tasks created before date (YYYY-MM-DD)").option("--field <nameAndValue...>", 'Filter by custom field: --field "Name" value').option("--json", "Force JSON output even in terminal").action(
|
|
6117
6273
|
wrapAction(
|
|
6118
6274
|
async (query, opts) => {
|
|
6119
6275
|
const config = loadConfig(getProfileName());
|
|
6276
|
+
let assigneeIds;
|
|
6277
|
+
if (opts.assignee) {
|
|
6278
|
+
if (opts.assignee === "me") {
|
|
6279
|
+
const client = new ClickUpClient(config);
|
|
6280
|
+
const me = await client.getMe();
|
|
6281
|
+
assigneeIds = [me.id];
|
|
6282
|
+
} else {
|
|
6283
|
+
assigneeIds = [Number(opts.assignee)];
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
const parseDateFilter = (d) => {
|
|
6287
|
+
const parts = d.split("-");
|
|
6288
|
+
return new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])).getTime();
|
|
6289
|
+
};
|
|
6290
|
+
let customFields;
|
|
6291
|
+
if (opts.field?.length) {
|
|
6292
|
+
if (opts.field.length % 2 !== 0) {
|
|
6293
|
+
throw new Error('--field requires pairs: --field "Name" value');
|
|
6294
|
+
}
|
|
6295
|
+
if (!opts.list) {
|
|
6296
|
+
throw new Error("--field filtering requires --list to resolve field names");
|
|
6297
|
+
}
|
|
6298
|
+
const client = new ClickUpClient(config);
|
|
6299
|
+
const fields = await client.getListCustomFields(opts.list);
|
|
6300
|
+
customFields = [];
|
|
6301
|
+
for (let i = 0; i < opts.field.length; i += 2) {
|
|
6302
|
+
const fieldName = opts.field[i];
|
|
6303
|
+
const fieldValue = opts.field[i + 1];
|
|
6304
|
+
const match = fields.find((f) => f.name.toLowerCase() === fieldName.toLowerCase());
|
|
6305
|
+
if (!match) {
|
|
6306
|
+
const available = fields.map((f) => f.name).join(", ");
|
|
6307
|
+
throw new Error(`Field "${fieldName}" not found. Available: ${available}`);
|
|
6308
|
+
}
|
|
6309
|
+
customFields.push({
|
|
6310
|
+
field_id: match.id,
|
|
6311
|
+
operator: "=",
|
|
6312
|
+
value: fieldValue
|
|
6313
|
+
});
|
|
6314
|
+
}
|
|
6315
|
+
}
|
|
6120
6316
|
const tasks = await searchTasks(config, query, {
|
|
6121
6317
|
status: opts.status,
|
|
6122
6318
|
all: opts.all,
|
|
6123
|
-
includeClosed: opts.includeClosed
|
|
6319
|
+
includeClosed: opts.includeClosed,
|
|
6320
|
+
listIds: opts.list ? [opts.list] : void 0,
|
|
6321
|
+
spaceIds: opts.space ? [opts.space] : void 0,
|
|
6322
|
+
assignees: assigneeIds,
|
|
6323
|
+
tags: opts.tag ? [opts.tag] : void 0,
|
|
6324
|
+
dueDateLt: opts.dueBefore ? parseDateFilter(opts.dueBefore) : void 0,
|
|
6325
|
+
dueDateGt: opts.dueAfter ? parseDateFilter(opts.dueAfter) : void 0,
|
|
6326
|
+
dateCreatedGt: opts.createdAfter ? parseDateFilter(opts.createdAfter) : void 0,
|
|
6327
|
+
dateCreatedLt: opts.createdBefore ? parseDateFilter(opts.createdBefore) : void 0,
|
|
6328
|
+
customFields
|
|
6124
6329
|
});
|
|
6125
6330
|
await printTasks(tasks, opts.json ?? false, config);
|
|
6126
6331
|
}
|
package/package.json
CHANGED
|
@@ -48,39 +48,39 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
48
48
|
|
|
49
49
|
### Read
|
|
50
50
|
|
|
51
|
-
| Command
|
|
52
|
-
|
|
|
53
|
-
| `cup tasks [--status s] [--name q] [--type t] [--list id] [--space id] [--all] [--include-closed]` | My tasks (filter by status, name, type, list, space). `--all` for all tasks in workspace |
|
|
54
|
-
| `cup assigned [--status s] [--include-closed]`
|
|
55
|
-
| `cup sprint [--status s] [--space nameOrId] [--folder id] [--include-closed]`
|
|
56
|
-
| `cup sprints [--space nameOrId]`
|
|
57
|
-
| `cup search <query> [--status s] [--all] [--include-closed]`
|
|
58
|
-
| `cup task <id>`
|
|
59
|
-
| `cup subtasks <id> [--status s] [--name q] [--include-closed]`
|
|
60
|
-
| `cup comments <id>`
|
|
61
|
-
| `cup activity <id>`
|
|
62
|
-
| `cup inbox [--days n] [--include-closed]`
|
|
63
|
-
| `cup summary [--hours n]`
|
|
64
|
-
| `cup overdue [--include-closed]`
|
|
65
|
-
| `cup spaces [--name partial] [--my]`
|
|
66
|
-
| `cup lists <spaceId> [--name partial]`
|
|
67
|
-
| `cup folders <spaceId> [--name partial]`
|
|
68
|
-
| `cup members`
|
|
69
|
-
| `cup fields <listId>`
|
|
70
|
-
| `cup tags <spaceId>`
|
|
71
|
-
| `cup goals`
|
|
72
|
-
| `cup key-results <goalId>`
|
|
73
|
-
| `cup docs [query]`
|
|
74
|
-
| `cup doc <docId> [pageId]`
|
|
75
|
-
| `cup doc-pages <docId>`
|
|
76
|
-
| `cup task-types`
|
|
77
|
-
| `cup templates`
|
|
78
|
-
| `cup list-templates`
|
|
79
|
-
| `cup folder-templates`
|
|
80
|
-
| `cup views <listId>`
|
|
81
|
-
| `cup view <viewId>`
|
|
82
|
-
| `cup open <query>`
|
|
83
|
-
| `cup auth`
|
|
51
|
+
| Command | What it returns |
|
|
52
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| `cup tasks [--status s] [--name q] [--type t] [--list id] [--space id] [--all] [--include-closed] [--assignee id\|me] [--tag t] [--due-before d] [--due-after d] [--created-after d] [--created-before d] [--field "Name" val]` | My tasks (filter by status, name, type, list, space, assignee, tag, dates, custom fields). `--all` for all tasks in workspace |
|
|
54
|
+
| `cup assigned [--status s] [--include-closed]` | All my tasks grouped by status |
|
|
55
|
+
| `cup sprint [--status s] [--space nameOrId] [--folder id] [--include-closed]` | Tasks in active sprint (auto-detected) |
|
|
56
|
+
| `cup sprints [--space nameOrId]` | List all sprints (marks active with \*) |
|
|
57
|
+
| `cup search <query> [--status s] [--list id] [--space id] [--all] [--include-closed] [--assignee id\|me] [--tag t] [--due-before d] [--due-after d] [--created-after d] [--created-before d] [--field "Name" val]` | Search my tasks by name. `--all` for all tasks |
|
|
58
|
+
| `cup task <id>` | Single task details (custom fields, checklists, attachments, deps, links) |
|
|
59
|
+
| `cup subtasks <id> [--status s] [--name q] [--include-closed]` | Subtasks of a task |
|
|
60
|
+
| `cup comments <id>` | Comments on a task |
|
|
61
|
+
| `cup activity <id>` | Task details + comment history combined |
|
|
62
|
+
| `cup inbox [--days n] [--include-closed]` | Tasks updated in last n days (default 30) |
|
|
63
|
+
| `cup summary [--hours n]` | Standup: completed, in-progress, overdue |
|
|
64
|
+
| `cup overdue [--include-closed]` | Tasks past due date (most overdue first) |
|
|
65
|
+
| `cup spaces [--name partial] [--my]` | List/filter workspace spaces |
|
|
66
|
+
| `cup lists <spaceId> [--name partial]` | Lists in a space (including folder lists) |
|
|
67
|
+
| `cup folders <spaceId> [--name partial]` | Folders in a space (with their lists) |
|
|
68
|
+
| `cup members` | Workspace members (username, ID, email) |
|
|
69
|
+
| `cup fields <listId>` | Custom fields on a list (type, required, options) |
|
|
70
|
+
| `cup tags <spaceId>` | Tags available in a space |
|
|
71
|
+
| `cup goals` | Workspace goals with progress |
|
|
72
|
+
| `cup key-results <goalId>` | Key results for a goal |
|
|
73
|
+
| `cup docs [query]` | Workspace docs (optionally filter by name) |
|
|
74
|
+
| `cup doc <docId> [pageId]` | Doc metadata + page tree, or a specific page |
|
|
75
|
+
| `cup doc-pages <docId>` | All pages in a doc with content |
|
|
76
|
+
| `cup task-types` | Custom task types (for `--custom-item-id`) |
|
|
77
|
+
| `cup templates` | Task templates (for `--template`) |
|
|
78
|
+
| `cup list-templates` | List templates (for `list-from-template`) |
|
|
79
|
+
| `cup folder-templates` | Folder templates |
|
|
80
|
+
| `cup views <listId>` | List views on a list |
|
|
81
|
+
| `cup view <viewId>` | Get view details |
|
|
82
|
+
| `cup open <query>` | Open task in browser by ID or name |
|
|
83
|
+
| `cup auth` | Check authentication status |
|
|
84
84
|
|
|
85
85
|
### Write
|
|
86
86
|
|
|
@@ -157,28 +157,31 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
157
157
|
|
|
158
158
|
## Flags & Conventions
|
|
159
159
|
|
|
160
|
-
| Topic
|
|
161
|
-
|
|
|
162
|
-
| Task IDs
|
|
163
|
-
| `--status`
|
|
164
|
-
| `--priority`
|
|
165
|
-
| `--due-date`
|
|
166
|
-
| `--assignee`
|
|
167
|
-
| `--tags`
|
|
168
|
-
| `--time-estimate`
|
|
169
|
-
| `--type`
|
|
170
|
-
| `--custom-item-id`
|
|
171
|
-
| `--space`
|
|
172
|
-
| `--name`
|
|
173
|
-
| `--all`
|
|
174
|
-
| `--include-closed`
|
|
175
|
-
| `--list` on create
|
|
176
|
-
| `cup field --set`
|
|
177
|
-
| `cup field-create`
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
|
|
|
160
|
+
| Topic | Detail |
|
|
161
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
162
|
+
| Task IDs | Native (`abc123def`) or custom (`PROJ-123`). Custom IDs auto-detected by `PREFIX-DIGITS` format |
|
|
163
|
+
| `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr |
|
|
164
|
+
| `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
|
|
165
|
+
| `--due-date` | `YYYY-MM-DD` format |
|
|
166
|
+
| `--assignee` | User ID or `me` |
|
|
167
|
+
| `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
|
|
168
|
+
| `--time-estimate` | Duration: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
|
|
169
|
+
| `--type` | `task` (regular) or custom type name/ID (e.g. `initiative`, `Bug`) |
|
|
170
|
+
| `--custom-item-id` | Custom task type ID for `cup create` (find with `cup task-types`) |
|
|
171
|
+
| `--space` | Partial name match or exact ID |
|
|
172
|
+
| `--name` | Partial match, case-insensitive |
|
|
173
|
+
| `--all` | Show all tasks in workspace, not just assigned to me. Available on `tasks`, `search`, `overdue`. Default: my tasks only (smaller output for agent context windows) |
|
|
174
|
+
| `--include-closed` | Include closed/done tasks |
|
|
175
|
+
| `--list` on create | Optional when `--parent` is given (auto-detected) |
|
|
176
|
+
| `cup field --set` | Supports: text, number, checkbox (true/false), dropdown (option name), date (YYYY-MM-DD), url, email. Names resolved case-insensitively; errors list available fields/options |
|
|
177
|
+
| `cup field-create` | Use `--options "a,b,c"` for `drop_down` and `labels` types (required). Other types don't need `--options` |
|
|
178
|
+
| `--field` filter | `--field "Name" value` on `tasks` and `search` requires `--list` to resolve field names to IDs |
|
|
179
|
+
| `--due-before/after` | `YYYY-MM-DD` date filters for due date range |
|
|
180
|
+
| `--created-before/after` | `YYYY-MM-DD` date filters for creation date range |
|
|
181
|
+
| `cup sprint` | Auto-detects active sprint by folder name (sprint/iteration/cycle/scrum), parses multiple date formats. Override with `--folder <id>` or `cup config set sprintFolderId <id>` |
|
|
182
|
+
| `cup link` | Both IDs must be the same type (both custom or both native) |
|
|
183
|
+
| `cup delete` | DESTRUCTIVE. Requires `--confirm` in non-interactive mode. Cannot be undone |
|
|
184
|
+
| Errors | stderr with exit code 1. Strict parsing - excess/unknown arguments rejected |
|
|
182
185
|
|
|
183
186
|
## Agent Workflow Examples
|
|
184
187
|
|
|
@@ -199,8 +202,13 @@ cup tasks --status "in progress" # by status
|
|
|
199
202
|
cup tasks --name "login" # by partial name
|
|
200
203
|
cup tasks --type initiative # initiatives only
|
|
201
204
|
cup tasks --list 12345 --all # all tasks in list, not just mine
|
|
205
|
+
cup tasks --tag "bug" --status "to do" # by tag and status
|
|
206
|
+
cup tasks --list 123 --field "Sprint" "Week 1" # by custom field
|
|
207
|
+
cup tasks --due-before 2026-04-01 # due before a date
|
|
208
|
+
cup tasks --assignee me --created-after 2026-03-01 # my tasks created recently
|
|
202
209
|
cup search "payment flow" # multi-word search
|
|
203
210
|
cup search auth --status "prog" # fuzzy status match
|
|
211
|
+
cup search "auth" --list 123 --space 456 # search within list and space
|
|
204
212
|
cup sprint # current sprint
|
|
205
213
|
cup assigned # all my tasks by status
|
|
206
214
|
cup overdue # past due date
|