@krodak/clickup-cli 1.21.0 → 1.22.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 +228 -96
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +28 -27
|
@@ -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.22.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Krzysztof Rodak"
|
|
7
7
|
},
|
package/dist/index.js
CHANGED
|
@@ -286,8 +286,10 @@ var ClickUpClient = class {
|
|
|
286
286
|
body: JSON.stringify({ name, multiple_assignees: true })
|
|
287
287
|
});
|
|
288
288
|
}
|
|
289
|
-
async getSpaces(teamId) {
|
|
290
|
-
const data = await this.request(
|
|
289
|
+
async getSpaces(teamId, archived = false) {
|
|
290
|
+
const data = await this.request(
|
|
291
|
+
`/team/${teamId}/space?archived=${archived ? "true" : "false"}`
|
|
292
|
+
);
|
|
291
293
|
return readCollectionField(data, "spaces", "spaces");
|
|
292
294
|
}
|
|
293
295
|
async getCustomTaskTypes(teamId) {
|
|
@@ -324,18 +326,22 @@ var ClickUpClient = class {
|
|
|
324
326
|
body: JSON.stringify({ name })
|
|
325
327
|
});
|
|
326
328
|
}
|
|
327
|
-
async getLists(spaceId) {
|
|
328
|
-
const data = await this.request(
|
|
329
|
+
async getLists(spaceId, archived = false) {
|
|
330
|
+
const data = await this.request(
|
|
331
|
+
`/space/${spaceId}/list?archived=${archived ? "true" : "false"}`
|
|
332
|
+
);
|
|
329
333
|
return readCollectionField(data, "lists", "space lists");
|
|
330
334
|
}
|
|
331
|
-
async getFolders(spaceId) {
|
|
335
|
+
async getFolders(spaceId, archived = false) {
|
|
332
336
|
const data = await this.request(
|
|
333
|
-
`/space/${spaceId}/folder?archived
|
|
337
|
+
`/space/${spaceId}/folder?archived=${archived ? "true" : "false"}`
|
|
334
338
|
);
|
|
335
339
|
return readCollectionField(data, "folders", "space folders");
|
|
336
340
|
}
|
|
337
|
-
async getFolderLists(folderId) {
|
|
338
|
-
const data = await this.request(
|
|
341
|
+
async getFolderLists(folderId, archived = false) {
|
|
342
|
+
const data = await this.request(
|
|
343
|
+
`/folder/${folderId}/list?archived=${archived ? "true" : "false"}`
|
|
344
|
+
);
|
|
339
345
|
return readCollectionField(data, "lists", "folder lists");
|
|
340
346
|
}
|
|
341
347
|
async getListViews(listId) {
|
|
@@ -1945,10 +1951,16 @@ function buildUpdatePayload(opts, timezone) {
|
|
|
1945
1951
|
}
|
|
1946
1952
|
if (opts.archive) payload.archived = true;
|
|
1947
1953
|
if (opts.unarchive) payload.archived = false;
|
|
1954
|
+
if (opts.type !== void 0) {
|
|
1955
|
+
const num = Number(opts.type);
|
|
1956
|
+
if (Number.isInteger(num) && num >= 0) {
|
|
1957
|
+
payload.custom_item_id = num;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1948
1960
|
return payload;
|
|
1949
1961
|
}
|
|
1950
1962
|
function hasUpdateFields(options) {
|
|
1951
|
-
return options.name !== void 0 || options.description !== void 0 || options.markdown_content !== void 0 || options.status !== void 0 || options.priority !== void 0 || options.due_date !== void 0 || options.start_date !== void 0 || options.time_estimate !== void 0 || options.assignees !== void 0 || options.parent !== void 0 || options.archived !== void 0;
|
|
1963
|
+
return options.name !== void 0 || options.description !== void 0 || options.markdown_content !== void 0 || options.status !== void 0 || options.priority !== void 0 || options.due_date !== void 0 || options.start_date !== void 0 || options.time_estimate !== void 0 || options.assignees !== void 0 || options.parent !== void 0 || options.archived !== void 0 || options.custom_item_id !== void 0;
|
|
1952
1964
|
}
|
|
1953
1965
|
async function resolveStatus(client, taskId, statusInput) {
|
|
1954
1966
|
const task = await client.getTask(taskId);
|
|
@@ -1964,16 +1976,29 @@ async function resolveStatus(client, taskId, statusInput) {
|
|
|
1964
1976
|
}
|
|
1965
1977
|
return matched;
|
|
1966
1978
|
}
|
|
1967
|
-
async function
|
|
1968
|
-
|
|
1979
|
+
async function resolveTaskType2(client, teamId, typeInput) {
|
|
1980
|
+
const types = await client.getCustomTaskTypes(teamId);
|
|
1981
|
+
const lower = typeInput.toLowerCase();
|
|
1982
|
+
const match = types.find((t) => t.name.toLowerCase() === lower);
|
|
1983
|
+
if (!match) {
|
|
1984
|
+
const available = types.map((t) => `${t.name} (${String(t.id)})`).join(", ");
|
|
1985
|
+
throw new Error(`No matching task type for "${typeInput}". Available: ${available}`);
|
|
1986
|
+
}
|
|
1987
|
+
return match.id;
|
|
1988
|
+
}
|
|
1989
|
+
async function updateTask(config, taskId, options, typeInput) {
|
|
1990
|
+
if (!hasUpdateFields(options) && typeInput === void 0)
|
|
1969
1991
|
throw new Error(
|
|
1970
|
-
"Provide at least one of: --name, --description, --status, --priority, --due-date, --start-date, --time-estimate, --assignee, --remove-assignee, --parent, --detach, --archive, --unarchive"
|
|
1992
|
+
"Provide at least one of: --name, --description, --status, --priority, --due-date, --start-date, --time-estimate, --assignee, --remove-assignee, --parent, --detach, --archive, --unarchive, --type"
|
|
1971
1993
|
);
|
|
1972
1994
|
const client = new ClickUpClient(config);
|
|
1973
1995
|
const resolved = { ...options };
|
|
1974
1996
|
if (resolved.status !== void 0) {
|
|
1975
1997
|
resolved.status = await resolveStatus(client, taskId, resolved.status);
|
|
1976
1998
|
}
|
|
1999
|
+
if (resolved.custom_item_id === void 0 && typeInput !== void 0) {
|
|
2000
|
+
resolved.custom_item_id = await resolveTaskType2(client, config.teamId, typeInput);
|
|
2001
|
+
}
|
|
1977
2002
|
const task = await client.updateTask(taskId, resolved);
|
|
1978
2003
|
return { id: task.id, name: task.name };
|
|
1979
2004
|
}
|
|
@@ -2502,12 +2527,14 @@ function printComments(comments, forceJson) {
|
|
|
2502
2527
|
async function fetchLists(config, spaceId, opts = {}) {
|
|
2503
2528
|
const client = new ClickUpClient(config);
|
|
2504
2529
|
const results = [];
|
|
2505
|
-
const folderlessLists = await client.getLists(spaceId);
|
|
2530
|
+
const folderlessLists = await client.getLists(spaceId, opts.archived);
|
|
2506
2531
|
for (const list of folderlessLists) {
|
|
2507
2532
|
results.push({ id: list.id, name: list.name, folder: "(none)" });
|
|
2508
2533
|
}
|
|
2509
|
-
const folders = await client.getFolders(spaceId);
|
|
2510
|
-
const folderListArrays = await Promise.all(
|
|
2534
|
+
const folders = await client.getFolders(spaceId, opts.archived);
|
|
2535
|
+
const folderListArrays = await Promise.all(
|
|
2536
|
+
folders.map((f) => client.getFolderLists(f.id, opts.archived))
|
|
2537
|
+
);
|
|
2511
2538
|
for (let i = 0; i < folders.length; i++) {
|
|
2512
2539
|
const folder = folders[i];
|
|
2513
2540
|
for (const list of folderListArrays[i]) {
|
|
@@ -2637,7 +2664,7 @@ async function printInbox(tasks, forceJson, config) {
|
|
|
2637
2664
|
// src/commands/spaces.ts
|
|
2638
2665
|
async function listSpaces(config, opts) {
|
|
2639
2666
|
const client = new ClickUpClient(config);
|
|
2640
|
-
let spaces = await client.getSpaces(config.teamId);
|
|
2667
|
+
let spaces = await client.getSpaces(config.teamId, opts.archived);
|
|
2641
2668
|
if (opts.name) {
|
|
2642
2669
|
const lower = opts.name.toLowerCase();
|
|
2643
2670
|
spaces = spaces.filter((s) => s.name.toLowerCase().includes(lower));
|
|
@@ -2949,6 +2976,9 @@ function configPath2() {
|
|
|
2949
2976
|
}
|
|
2950
2977
|
|
|
2951
2978
|
// src/commands/assign.ts
|
|
2979
|
+
function parseIdList(value) {
|
|
2980
|
+
return value.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
|
|
2981
|
+
}
|
|
2952
2982
|
async function assignTask(config, taskId, opts) {
|
|
2953
2983
|
if (!opts.to && !opts.remove) {
|
|
2954
2984
|
throw new Error("Provide at least one of: --to, --remove");
|
|
@@ -2957,10 +2987,14 @@ async function assignTask(config, taskId, opts) {
|
|
|
2957
2987
|
const add = [];
|
|
2958
2988
|
const rem = [];
|
|
2959
2989
|
if (opts.to) {
|
|
2960
|
-
|
|
2990
|
+
for (const value of parseIdList(opts.to)) {
|
|
2991
|
+
add.push(await resolveAssigneeId(client, value));
|
|
2992
|
+
}
|
|
2961
2993
|
}
|
|
2962
2994
|
if (opts.remove) {
|
|
2963
|
-
|
|
2995
|
+
for (const value of parseIdList(opts.remove)) {
|
|
2996
|
+
rem.push(await resolveAssigneeId(client, value));
|
|
2997
|
+
}
|
|
2964
2998
|
}
|
|
2965
2999
|
return client.updateTask(taskId, {
|
|
2966
3000
|
assignees: {
|
|
@@ -3164,6 +3198,7 @@ var commandMetadata = [
|
|
|
3164
3198
|
"--detach",
|
|
3165
3199
|
"--archive",
|
|
3166
3200
|
"--unarchive",
|
|
3201
|
+
"--type",
|
|
3167
3202
|
"--field",
|
|
3168
3203
|
"--json"
|
|
3169
3204
|
],
|
|
@@ -3294,7 +3329,7 @@ var commandMetadata = [
|
|
|
3294
3329
|
{
|
|
3295
3330
|
name: "lists",
|
|
3296
3331
|
description: "List all lists in a space (including lists inside folders)",
|
|
3297
|
-
flags: ["--name", "--json"],
|
|
3332
|
+
flags: ["--name", "--archived", "--json"],
|
|
3298
3333
|
quickReference: [
|
|
3299
3334
|
{ section: "read", usage: "lists <spaceId>", description: "List all lists in a space" }
|
|
3300
3335
|
]
|
|
@@ -3302,7 +3337,7 @@ var commandMetadata = [
|
|
|
3302
3337
|
{
|
|
3303
3338
|
name: "spaces",
|
|
3304
3339
|
description: "List spaces in your workspace",
|
|
3305
|
-
flags: ["--name", "--my", "--json"],
|
|
3340
|
+
flags: ["--name", "--my", "--archived", "--json"],
|
|
3306
3341
|
quickReference: [{ section: "read", usage: "spaces", description: "List spaces in workspace" }]
|
|
3307
3342
|
},
|
|
3308
3343
|
{
|
|
@@ -3627,7 +3662,7 @@ var commandMetadata = [
|
|
|
3627
3662
|
{
|
|
3628
3663
|
name: "folders",
|
|
3629
3664
|
description: "List folders in a space (with their lists)",
|
|
3630
|
-
flags: ["--name", "--json"],
|
|
3665
|
+
flags: ["--name", "--archived", "--json"],
|
|
3631
3666
|
quickReference: [
|
|
3632
3667
|
{ section: "read", usage: "folders <spaceId>", description: "List folders in a space" }
|
|
3633
3668
|
]
|
|
@@ -3733,6 +3768,16 @@ var commandMetadata = [
|
|
|
3733
3768
|
section: "write",
|
|
3734
3769
|
usage: "bulk tag <tagName> <taskIds...>",
|
|
3735
3770
|
description: "Bulk add/remove tag"
|
|
3771
|
+
},
|
|
3772
|
+
{
|
|
3773
|
+
section: "write",
|
|
3774
|
+
usage: "bulk priority <taskIds...>",
|
|
3775
|
+
description: "Bulk set priority on tasks"
|
|
3776
|
+
},
|
|
3777
|
+
{
|
|
3778
|
+
section: "write",
|
|
3779
|
+
usage: "bulk field <taskIds...>",
|
|
3780
|
+
description: "Bulk set a custom field value on tasks"
|
|
3736
3781
|
}
|
|
3737
3782
|
]
|
|
3738
3783
|
},
|
|
@@ -5219,7 +5264,11 @@ var SUPPORTED_TYPES = /* @__PURE__ */ new Set([
|
|
|
5219
5264
|
"checkbox",
|
|
5220
5265
|
"date",
|
|
5221
5266
|
"url",
|
|
5222
|
-
"email"
|
|
5267
|
+
"email",
|
|
5268
|
+
"emoji",
|
|
5269
|
+
"manual_progress",
|
|
5270
|
+
"tasks",
|
|
5271
|
+
"users"
|
|
5223
5272
|
]);
|
|
5224
5273
|
function findFieldByName(fields, name) {
|
|
5225
5274
|
const lower = name.toLowerCase();
|
|
@@ -5284,6 +5333,30 @@ function parseFieldValue(field, rawValue) {
|
|
|
5284
5333
|
throw new Error(`Value "${rawValue}" is not a valid date (use YYYY-MM-DD)`);
|
|
5285
5334
|
return ms;
|
|
5286
5335
|
}
|
|
5336
|
+
case "emoji": {
|
|
5337
|
+
const n = Number(rawValue);
|
|
5338
|
+
if (!Number.isFinite(n) || n < 0 || n > 5) {
|
|
5339
|
+
throw new Error("Rating value must be a number between 0 and 5");
|
|
5340
|
+
}
|
|
5341
|
+
return n;
|
|
5342
|
+
}
|
|
5343
|
+
case "manual_progress": {
|
|
5344
|
+
const n = Number(rawValue);
|
|
5345
|
+
if (!Number.isFinite(n) || n < 0 || n > 100) {
|
|
5346
|
+
throw new Error("Progress value must be a number between 0 and 100");
|
|
5347
|
+
}
|
|
5348
|
+
return { current: n };
|
|
5349
|
+
}
|
|
5350
|
+
case "tasks": {
|
|
5351
|
+
const ids = rawValue.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5352
|
+
if (ids.length === 0) throw new Error("Provide at least one task ID (comma-separated)");
|
|
5353
|
+
return { add: ids };
|
|
5354
|
+
}
|
|
5355
|
+
case "users": {
|
|
5356
|
+
const ids = rawValue.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5357
|
+
if (ids.length === 0) throw new Error("Provide at least one user ID (comma-separated)");
|
|
5358
|
+
return { add: ids.map(Number) };
|
|
5359
|
+
}
|
|
5287
5360
|
default:
|
|
5288
5361
|
return rawValue;
|
|
5289
5362
|
}
|
|
@@ -5668,9 +5741,9 @@ async function deleteDocPage(config, docId, pageId) {
|
|
|
5668
5741
|
|
|
5669
5742
|
// src/commands/folders.ts
|
|
5670
5743
|
import chalk12 from "chalk";
|
|
5671
|
-
async function listFolders(config, spaceId, nameFilter) {
|
|
5744
|
+
async function listFolders(config, spaceId, nameFilter, archived) {
|
|
5672
5745
|
const client = new ClickUpClient(config);
|
|
5673
|
-
const folders = await client.getFolders(spaceId);
|
|
5746
|
+
const folders = await client.getFolders(spaceId, archived);
|
|
5674
5747
|
let filtered = folders;
|
|
5675
5748
|
if (nameFilter) {
|
|
5676
5749
|
const lower = nameFilter.toLowerCase();
|
|
@@ -5932,66 +6005,98 @@ async function duplicateTask(config, taskId) {
|
|
|
5932
6005
|
}
|
|
5933
6006
|
|
|
5934
6007
|
// src/commands/bulk.ts
|
|
5935
|
-
|
|
5936
|
-
|
|
6008
|
+
var BULK_CONCURRENCY = 5;
|
|
6009
|
+
async function runInBatches(items, concurrency, fn) {
|
|
6010
|
+
const results = [];
|
|
6011
|
+
for (let i = 0; i < items.length; i += concurrency) {
|
|
6012
|
+
const batch = items.slice(i, i + concurrency);
|
|
6013
|
+
const batchResults = await Promise.allSettled(batch.map(fn));
|
|
6014
|
+
batchResults.forEach((res, idx) => {
|
|
6015
|
+
const item = batch[idx];
|
|
6016
|
+
if (res.status === "fulfilled") {
|
|
6017
|
+
results.push({ item, result: res.value });
|
|
6018
|
+
} else {
|
|
6019
|
+
results.push({
|
|
6020
|
+
item,
|
|
6021
|
+
error: res.reason instanceof Error ? res.reason : new Error(String(res.reason))
|
|
6022
|
+
});
|
|
6023
|
+
}
|
|
6024
|
+
});
|
|
6025
|
+
}
|
|
6026
|
+
return results;
|
|
6027
|
+
}
|
|
6028
|
+
function toBulkResult(outcomes) {
|
|
5937
6029
|
const failed = [];
|
|
5938
|
-
for (const
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
} catch (err) {
|
|
5942
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
5943
|
-
failed.push({ id, reason });
|
|
6030
|
+
for (const outcome of outcomes) {
|
|
6031
|
+
if (outcome.error) {
|
|
6032
|
+
failed.push({ id: outcome.item, reason: outcome.error.message });
|
|
5944
6033
|
}
|
|
5945
6034
|
}
|
|
5946
|
-
return { updated:
|
|
6035
|
+
return { updated: outcomes.length - failed.length, failed };
|
|
6036
|
+
}
|
|
6037
|
+
async function bulkUpdateStatus(config, taskIds, status) {
|
|
6038
|
+
const client = new ClickUpClient(config);
|
|
6039
|
+
const outcomes = await runInBatches(
|
|
6040
|
+
taskIds,
|
|
6041
|
+
BULK_CONCURRENCY,
|
|
6042
|
+
(id) => client.updateTask(id, { status })
|
|
6043
|
+
);
|
|
6044
|
+
return toBulkResult(outcomes);
|
|
5947
6045
|
}
|
|
5948
6046
|
async function bulkAssign(config, userIdOrMe, taskIds, action) {
|
|
5949
6047
|
const client = new ClickUpClient(config);
|
|
5950
6048
|
const numericId = await resolveAssigneeId(client, userIdOrMe);
|
|
5951
|
-
const
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
5959
|
-
failed.push({ id, reason });
|
|
5960
|
-
}
|
|
5961
|
-
}
|
|
5962
|
-
return { updated: taskIds.length - failed.length, failed };
|
|
6049
|
+
const payload = action === "add" ? { assignees: { add: [numericId] } } : { assignees: { rem: [numericId] } };
|
|
6050
|
+
const outcomes = await runInBatches(
|
|
6051
|
+
taskIds,
|
|
6052
|
+
BULK_CONCURRENCY,
|
|
6053
|
+
(id) => client.updateTask(id, payload)
|
|
6054
|
+
);
|
|
6055
|
+
return toBulkResult(outcomes);
|
|
5963
6056
|
}
|
|
5964
6057
|
async function bulkDueDate(config, date, taskIds) {
|
|
5965
6058
|
const client = new ClickUpClient(config);
|
|
5966
6059
|
const timezone = await client.getUserTimezone();
|
|
5967
6060
|
const payload = date === "none" || date === "clear" ? { due_date: null } : { due_date: parseDueDate(date, timezone), due_date_time: false };
|
|
5968
|
-
const
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
failed.push({ id, reason });
|
|
5975
|
-
}
|
|
5976
|
-
}
|
|
5977
|
-
return { updated: taskIds.length - failed.length, failed };
|
|
6061
|
+
const outcomes = await runInBatches(
|
|
6062
|
+
taskIds,
|
|
6063
|
+
BULK_CONCURRENCY,
|
|
6064
|
+
(id) => client.updateTask(id, payload)
|
|
6065
|
+
);
|
|
6066
|
+
return toBulkResult(outcomes);
|
|
5978
6067
|
}
|
|
5979
6068
|
async function bulkTag(config, tagName, taskIds, action) {
|
|
5980
6069
|
const client = new ClickUpClient(config);
|
|
5981
|
-
const
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
6070
|
+
const outcomes = await runInBatches(
|
|
6071
|
+
taskIds,
|
|
6072
|
+
BULK_CONCURRENCY,
|
|
6073
|
+
(id) => action === "add" ? client.addTagToTask(id, tagName) : client.removeTagFromTask(id, tagName)
|
|
6074
|
+
);
|
|
6075
|
+
return toBulkResult(outcomes);
|
|
6076
|
+
}
|
|
6077
|
+
async function bulkPriority(config, priorityValue, taskIds) {
|
|
6078
|
+
const priority = parsePriority(priorityValue);
|
|
6079
|
+
const client = new ClickUpClient(config);
|
|
6080
|
+
const outcomes = await runInBatches(
|
|
6081
|
+
taskIds,
|
|
6082
|
+
BULK_CONCURRENCY,
|
|
6083
|
+
(id) => client.updateTask(id, { priority })
|
|
6084
|
+
);
|
|
6085
|
+
return toBulkResult(outcomes);
|
|
6086
|
+
}
|
|
6087
|
+
async function bulkField(config, fieldName, rawValue, taskIds) {
|
|
6088
|
+
if (taskIds.length === 0) return { updated: 0, failed: [] };
|
|
6089
|
+
const client = new ClickUpClient(config);
|
|
6090
|
+
const firstTask = await client.getTask(taskIds[0]);
|
|
6091
|
+
const fields = firstTask.custom_fields ?? [];
|
|
6092
|
+
const field = findFieldByName(fields, fieldName);
|
|
6093
|
+
const parsed = parseFieldValue(field, rawValue);
|
|
6094
|
+
const outcomes = await runInBatches(
|
|
6095
|
+
taskIds,
|
|
6096
|
+
BULK_CONCURRENCY,
|
|
6097
|
+
(id) => client.setCustomFieldValue(id, field.id, parsed)
|
|
6098
|
+
);
|
|
6099
|
+
return toBulkResult(outcomes);
|
|
5995
6100
|
}
|
|
5996
6101
|
|
|
5997
6102
|
// src/commands/goals.ts
|
|
@@ -6596,7 +6701,7 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6596
6701
|
).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(
|
|
6597
6702
|
"--time-estimate <duration>",
|
|
6598
6703
|
'Time estimate (e.g. "2h", "30m", "1h30m", "0" or "none" to clear)'
|
|
6599
|
-
).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("--field <nameAndValue...>", 'Set custom field: --field "Name" value').option("--json", "Force JSON output even in terminal").action(
|
|
6704
|
+
).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(
|
|
6600
6705
|
wrapAction(
|
|
6601
6706
|
async (taskId, opts) => {
|
|
6602
6707
|
const config = loadConfig(getProfileName());
|
|
@@ -6612,14 +6717,15 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6612
6717
|
]);
|
|
6613
6718
|
const payload = buildUpdatePayload(opts, timezone);
|
|
6614
6719
|
const hasFields = (opts.field?.length ?? 0) > 0;
|
|
6615
|
-
|
|
6720
|
+
const hasTypeName = opts.type !== void 0;
|
|
6721
|
+
if (!hasFields && !hasTypeName && Object.keys(payload).length === 0) {
|
|
6616
6722
|
throw new Error(
|
|
6617
|
-
"Provide at least one of: --name, --description, --status, --priority, --due-date, --time-estimate, --assignee, --remove-assignee, --parent, --archive, --unarchive, --field"
|
|
6723
|
+
"Provide at least one of: --name, --description, --status, --priority, --due-date, --time-estimate, --assignee, --remove-assignee, --parent, --archive, --unarchive, --type, --field"
|
|
6618
6724
|
);
|
|
6619
6725
|
}
|
|
6620
6726
|
let result;
|
|
6621
|
-
if (Object.keys(payload).length > 0) {
|
|
6622
|
-
result = await updateTask(config, taskId, payload);
|
|
6727
|
+
if (Object.keys(payload).length > 0 || hasTypeName) {
|
|
6728
|
+
result = await updateTask(config, taskId, payload, opts.type);
|
|
6623
6729
|
}
|
|
6624
6730
|
if (hasFields) {
|
|
6625
6731
|
if ((opts.field?.length ?? 0) % 2 !== 0) {
|
|
@@ -6804,18 +6910,25 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6804
6910
|
printTimeInStatus(result, opts.json ?? false);
|
|
6805
6911
|
})
|
|
6806
6912
|
);
|
|
6807
|
-
program.command("lists <spaceId>").description("List all lists in a space (including lists inside folders)").option("--name <partial>", "Filter by name (case-insensitive contains)").option("--json", "Force JSON output even in terminal").action(
|
|
6808
|
-
wrapAction(
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6913
|
+
program.command("lists <spaceId>").description("List all lists in a space (including lists inside folders)").option("--name <partial>", "Filter by name (case-insensitive contains)").option("--archived", "Include only archived items (default: active items)").option("--json", "Force JSON output even in terminal").action(
|
|
6914
|
+
wrapAction(
|
|
6915
|
+
async (spaceId, opts) => {
|
|
6916
|
+
const config = loadConfig(getProfileName());
|
|
6917
|
+
const lists = await fetchLists(config, spaceId, {
|
|
6918
|
+
name: opts.name,
|
|
6919
|
+
archived: opts.archived
|
|
6920
|
+
});
|
|
6921
|
+
printLists(lists, opts.json ?? false);
|
|
6922
|
+
}
|
|
6923
|
+
)
|
|
6813
6924
|
);
|
|
6814
|
-
program.command("spaces").description("List spaces in your workspace").option("--name <partial>", "Filter spaces by name (case-insensitive contains)").option("--my", "Show only spaces where I have assigned tasks").option("--json", "Force JSON output even in terminal").action(
|
|
6815
|
-
wrapAction(
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6925
|
+
program.command("spaces").description("List spaces in your workspace").option("--name <partial>", "Filter spaces by name (case-insensitive contains)").option("--my", "Show only spaces where I have assigned tasks").option("--archived", "Include only archived items (default: active items)").option("--json", "Force JSON output even in terminal").action(
|
|
6926
|
+
wrapAction(
|
|
6927
|
+
async (opts) => {
|
|
6928
|
+
const config = loadConfig(getProfileName());
|
|
6929
|
+
await listSpaces(config, opts);
|
|
6930
|
+
}
|
|
6931
|
+
)
|
|
6819
6932
|
);
|
|
6820
6933
|
program.command("inbox").description("Recently updated tasks grouped by time period").option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").option("--days <n>", "Lookback period in days", "30").action(
|
|
6821
6934
|
wrapAction(async (opts) => {
|
|
@@ -7433,6 +7546,23 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
7433
7546
|
}
|
|
7434
7547
|
)
|
|
7435
7548
|
);
|
|
7549
|
+
bulkCmd.command("priority <taskIds...>").description("Bulk set priority on tasks (urgent/high/normal/low or 1-4)").requiredOption("--to <priority>", "Priority to set (urgent, high, normal, low, or 1-4)").option("--json", "Force JSON output even in terminal").action(
|
|
7550
|
+
wrapAction(async (taskIds, opts) => {
|
|
7551
|
+
const config = loadConfig(getProfileName());
|
|
7552
|
+
const result = await bulkPriority(config, opts.to, taskIds);
|
|
7553
|
+
outputBulkResult(result, opts.json ?? false, `priority ${opts.to}`);
|
|
7554
|
+
})
|
|
7555
|
+
);
|
|
7556
|
+
bulkCmd.command("field <taskIds...>").description("Bulk set the same custom field value on tasks").requiredOption("--set <nameAndValue...>", 'Set field: --set "Field Name" value').option("--json", "Force JSON output even in terminal").action(
|
|
7557
|
+
wrapAction(async (taskIds, opts) => {
|
|
7558
|
+
if (opts.set.length !== 2) {
|
|
7559
|
+
throw new Error("--set requires exactly two arguments: field name and value");
|
|
7560
|
+
}
|
|
7561
|
+
const config = loadConfig(getProfileName());
|
|
7562
|
+
const result = await bulkField(config, opts.set[0], opts.set[1], taskIds);
|
|
7563
|
+
outputBulkResult(result, opts.json ?? false, `field "${opts.set[0]}"`);
|
|
7564
|
+
})
|
|
7565
|
+
);
|
|
7436
7566
|
program.command("goals").description("List goals in your workspace").option("--json", "Force JSON output even in terminal").action(
|
|
7437
7567
|
wrapAction(async (opts) => {
|
|
7438
7568
|
const config = loadConfig(getProfileName());
|
|
@@ -7602,18 +7732,20 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
7602
7732
|
}
|
|
7603
7733
|
})
|
|
7604
7734
|
);
|
|
7605
|
-
program.command("folders <spaceId>").description("List folders in a space (with their lists)").option("--name <partial>", "Filter folders by partial name match").option("--json", "Force JSON output even in terminal").action(
|
|
7606
|
-
wrapAction(
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7735
|
+
program.command("folders <spaceId>").description("List folders in a space (with their lists)").option("--name <partial>", "Filter folders by partial name match").option("--archived", "Include only archived items (default: active items)").option("--json", "Force JSON output even in terminal").action(
|
|
7736
|
+
wrapAction(
|
|
7737
|
+
async (spaceId, opts) => {
|
|
7738
|
+
const config = loadConfig(getProfileName());
|
|
7739
|
+
const folders = await listFolders(config, spaceId, opts.name, opts.archived);
|
|
7740
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
7741
|
+
console.log(JSON.stringify(folders, null, 2));
|
|
7742
|
+
} else if (isTTY()) {
|
|
7743
|
+
console.log(formatFolders(folders));
|
|
7744
|
+
} else {
|
|
7745
|
+
console.log(formatFoldersMarkdown(folders));
|
|
7746
|
+
}
|
|
7615
7747
|
}
|
|
7616
|
-
|
|
7748
|
+
)
|
|
7617
7749
|
);
|
|
7618
7750
|
program.command("space-create <name>").description("Create a new space in your workspace").option("--json", "Force JSON output even in terminal").action(
|
|
7619
7751
|
wrapAction(async (name, opts) => {
|
package/package.json
CHANGED
|
@@ -152,7 +152,7 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
152
152
|
| `cup comment-delete <commentId>` or `cup comment-delete --task <taskId> --mine [--match text]` | Delete a comment by ID or delete one of your task comments |
|
|
153
153
|
| `cup replies <commentId>` | List threaded replies |
|
|
154
154
|
| `cup reply <commentId> -m text [--notify-all]` | Reply to a comment |
|
|
155
|
-
| `cup assign <id> [--to
|
|
155
|
+
| `cup assign <id> [--to ids\|me] [--remove ids\|me]` | Assign/unassign users (`--to`/`--remove` accept comma-separated IDs) |
|
|
156
156
|
| `cup depend <id> [--on taskId] [--blocks taskId] [--remove]` | Add/remove dependencies |
|
|
157
157
|
| `cup move <id> [--to listId\|sprint:current] [--remove listId]` | Add/remove task from lists (`--to` accepts `sprint:current`) |
|
|
158
158
|
| `cup field <id> [--set "Name" value] [--remove "Name"]` | Set/remove custom field values |
|
|
@@ -219,32 +219,32 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
219
219
|
|
|
220
220
|
## Flags & Conventions
|
|
221
221
|
|
|
222
|
-
| Topic | Detail
|
|
223
|
-
| ------------------------ |
|
|
224
|
-
| Task IDs | Native (`abc123def`) or custom (`PROJ-123`). Custom IDs auto-detected by `PREFIX-DIGITS` format
|
|
225
|
-
| `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr
|
|
226
|
-
| `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4)
|
|
227
|
-
| `--due-date` | `YYYY-MM-DD` format
|
|
228
|
-
| `--assignee` | User ID or `me`
|
|
229
|
-
| `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`)
|
|
230
|
-
| `--time-estimate` | Duration: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds
|
|
231
|
-
| `--type` | `task` (regular) or custom type name/ID (e.g. `initiative`, `Bug`)
|
|
232
|
-
| `--custom-item-id` | Custom task type ID for `cup create` (find with `cup task-types`)
|
|
233
|
-
| `--space` | Partial name match or exact ID
|
|
234
|
-
| `--name` | Partial match, case-insensitive
|
|
235
|
-
| `--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)
|
|
236
|
-
| `--include-closed` | Include closed/done tasks
|
|
237
|
-
| `--list` on create | Optional when `--parent` is given (auto-detected). Accepts `sprint:current` pseudo-ID
|
|
238
|
-
| `cup field --set` | Supports: text, number, checkbox (true/false), dropdown (option name), labels (comma-separated names), date (YYYY-MM-DD), url, email. Names resolved case-insensitively; errors list available fields/options |
|
|
239
|
-
| `cup field-create` | Use `--options "a,b,c"` for `drop_down` and `labels` types (required). Other types don't need `--options`
|
|
240
|
-
| `--field` filter | `--field "Name" value` on `tasks` and `search` requires `--list` to resolve field names to IDs
|
|
241
|
-
| `--due-before/after` | `YYYY-MM-DD` date filters for due date range
|
|
242
|
-
| `--created-before/after` | `YYYY-MM-DD` date filters for creation date range
|
|
243
|
-
| `cup sprint` | Auto-detects active sprint by folder name (sprint/iteration/cycle/scrum), parses multiple date formats. Override with `--folder <id>`, `cup config set sprintFolderId <id>`, or favorite a sprint folder
|
|
244
|
-
| `cup favorite` | Local-only favorites (not synced to ClickUp). Types: sprint-folder, space, list, folder, view, task. Favorited sprint-folders auto-used by sprint commands
|
|
245
|
-
| `cup link` | Both IDs must be the same type (both custom or both native)
|
|
246
|
-
| `cup delete` | DESTRUCTIVE. Requires `--confirm` in non-interactive mode. Cannot be undone
|
|
247
|
-
| Errors | stderr with exit code 1. Strict parsing - excess/unknown arguments rejected
|
|
222
|
+
| Topic | Detail |
|
|
223
|
+
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
224
|
+
| Task IDs | Native (`abc123def`) or custom (`PROJ-123`). Custom IDs auto-detected by `PREFIX-DIGITS` format |
|
|
225
|
+
| `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr |
|
|
226
|
+
| `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
|
|
227
|
+
| `--due-date` | `YYYY-MM-DD` format |
|
|
228
|
+
| `--assignee` | User ID or `me` |
|
|
229
|
+
| `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
|
|
230
|
+
| `--time-estimate` | Duration: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
|
|
231
|
+
| `--type` | `task` (regular) or custom type name/ID (e.g. `initiative`, `Bug`) |
|
|
232
|
+
| `--custom-item-id` | Custom task type ID for `cup create` (find with `cup task-types`) |
|
|
233
|
+
| `--space` | Partial name match or exact ID |
|
|
234
|
+
| `--name` | Partial match, case-insensitive |
|
|
235
|
+
| `--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) |
|
|
236
|
+
| `--include-closed` | Include closed/done tasks |
|
|
237
|
+
| `--list` on create | Optional when `--parent` is given (auto-detected). Accepts `sprint:current` pseudo-ID |
|
|
238
|
+
| `cup field --set` | Supports: text, number, checkbox (true/false), dropdown (option name), labels (comma-separated names), date (YYYY-MM-DD), url, email, emoji/rating (0-5), manual_progress (0-100), tasks/relationship (comma-separated task IDs), users/people (comma-separated user IDs). Names resolved case-insensitively; errors list available fields/options |
|
|
239
|
+
| `cup field-create` | Use `--options "a,b,c"` for `drop_down` and `labels` types (required). Other types don't need `--options` |
|
|
240
|
+
| `--field` filter | `--field "Name" value` on `tasks` and `search` requires `--list` to resolve field names to IDs |
|
|
241
|
+
| `--due-before/after` | `YYYY-MM-DD` date filters for due date range |
|
|
242
|
+
| `--created-before/after` | `YYYY-MM-DD` date filters for creation date range |
|
|
243
|
+
| `cup sprint` | Auto-detects active sprint by folder name (sprint/iteration/cycle/scrum), parses multiple date formats. Override with `--folder <id>`, `cup config set sprintFolderId <id>`, or favorite a sprint folder |
|
|
244
|
+
| `cup favorite` | Local-only favorites (not synced to ClickUp). Types: sprint-folder, space, list, folder, view, task. Favorited sprint-folders auto-used by sprint commands |
|
|
245
|
+
| `cup link` | Both IDs must be the same type (both custom or both native) |
|
|
246
|
+
| `cup delete` | DESTRUCTIVE. Requires `--confirm` in non-interactive mode. Cannot be undone |
|
|
247
|
+
| Errors | stderr with exit code 1. Strict parsing - excess/unknown arguments rejected |
|
|
248
248
|
|
|
249
249
|
## Agent Workflow Examples
|
|
250
250
|
|
|
@@ -289,6 +289,7 @@ cup create -n "Bug fix" -l sprint:current # create in active sprint
|
|
|
289
289
|
cup create -n "Q3 Roadmap" -l <listId> --custom-item-id 1
|
|
290
290
|
cup comment abc123def -m "Completed in PR #42"
|
|
291
291
|
cup assign abc123def --to me
|
|
292
|
+
cup assign abc123def --to 12345,67890 # assign multiple users at once
|
|
292
293
|
cup depend task3 --on task2 # task3 waits for task2
|
|
293
294
|
cup move task1 --to list2 --remove list1
|
|
294
295
|
cup move task1 --to sprint:current # move to active sprint
|