@krodak/clickup-cli 1.15.0 → 1.16.1
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 +481 -19
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +79 -61
|
@@ -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.16.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Krzysztof Rodak"
|
|
7
7
|
},
|
package/dist/index.js
CHANGED
|
@@ -258,6 +258,12 @@ var ClickUpClient = class {
|
|
|
258
258
|
body: JSON.stringify({ name })
|
|
259
259
|
});
|
|
260
260
|
}
|
|
261
|
+
async updateList(listId, payload) {
|
|
262
|
+
return this.request(`/list/${listId}`, {
|
|
263
|
+
method: "PUT",
|
|
264
|
+
body: JSON.stringify(payload)
|
|
265
|
+
});
|
|
266
|
+
}
|
|
261
267
|
async createFolder(spaceId, name) {
|
|
262
268
|
return this.request(`/space/${spaceId}/folder`, {
|
|
263
269
|
method: "POST",
|
|
@@ -892,6 +898,7 @@ function migrateToMultiProfile(parsed, filePath) {
|
|
|
892
898
|
if (typeof value.teamId === "string" && value.teamId.trim()) p.teamId = value.teamId.trim();
|
|
893
899
|
if (typeof value.sprintFolderId === "string" && value.sprintFolderId.trim())
|
|
894
900
|
p.sprintFolderId = value.sprintFolderId.trim();
|
|
901
|
+
if (isRecord2(value.filters)) p.filters = value.filters;
|
|
895
902
|
profiles[name] = p;
|
|
896
903
|
}
|
|
897
904
|
}
|
|
@@ -1079,6 +1086,33 @@ function getConfigPath() {
|
|
|
1079
1086
|
migrateFromLegacy();
|
|
1080
1087
|
return configPath();
|
|
1081
1088
|
}
|
|
1089
|
+
function getFilters(profileName) {
|
|
1090
|
+
const multi = loadMultiProfileConfig();
|
|
1091
|
+
const name = profileName ?? process.env.CU_PROFILE?.trim() ?? multi.defaultProfile;
|
|
1092
|
+
const profile = name ? multi.profiles[name] ?? {} : {};
|
|
1093
|
+
return profile.filters ?? {};
|
|
1094
|
+
}
|
|
1095
|
+
function saveFilter(name, entry, profileName) {
|
|
1096
|
+
const multi = loadMultiProfileConfig();
|
|
1097
|
+
const pName = profileName ?? process.env.CU_PROFILE?.trim() ?? multi.defaultProfile ?? "default";
|
|
1098
|
+
const profile = multi.profiles[pName] ?? {};
|
|
1099
|
+
const filters = { ...profile.filters ?? {}, [name]: entry };
|
|
1100
|
+
multi.profiles[pName] = { ...profile, filters };
|
|
1101
|
+
if (!multi.defaultProfile) multi.defaultProfile = pName;
|
|
1102
|
+
saveMultiProfileConfig(multi);
|
|
1103
|
+
}
|
|
1104
|
+
function deleteFilter(name, profileName) {
|
|
1105
|
+
const multi = loadMultiProfileConfig();
|
|
1106
|
+
const pName = profileName ?? process.env.CU_PROFILE?.trim() ?? multi.defaultProfile ?? "default";
|
|
1107
|
+
const profile = multi.profiles[pName] ?? {};
|
|
1108
|
+
const filters = { ...profile.filters ?? {} };
|
|
1109
|
+
if (!(name in filters)) {
|
|
1110
|
+
throw new Error(`Filter "${name}" not found.`);
|
|
1111
|
+
}
|
|
1112
|
+
delete filters[name];
|
|
1113
|
+
multi.profiles[pName] = { ...profile, filters };
|
|
1114
|
+
saveMultiProfileConfig(multi);
|
|
1115
|
+
}
|
|
1082
1116
|
function writeConfig(config, profileName) {
|
|
1083
1117
|
const multi = loadMultiProfileConfig();
|
|
1084
1118
|
const name = profileName || multi.defaultProfile || "default";
|
|
@@ -1754,8 +1788,14 @@ function buildUpdatePayload(opts) {
|
|
|
1754
1788
|
payload.start_date = parseDueDate(opts.startDate);
|
|
1755
1789
|
payload.start_date_time = false;
|
|
1756
1790
|
}
|
|
1757
|
-
if (opts.assignee !== void 0) {
|
|
1758
|
-
payload.assignees = {
|
|
1791
|
+
if (opts.assignee !== void 0 || opts.removeAssignee !== void 0) {
|
|
1792
|
+
payload.assignees = {};
|
|
1793
|
+
if (opts.assignee !== void 0) {
|
|
1794
|
+
payload.assignees.add = [parseAssigneeId(opts.assignee)];
|
|
1795
|
+
}
|
|
1796
|
+
if (opts.removeAssignee !== void 0) {
|
|
1797
|
+
payload.assignees.rem = [parseAssigneeId(opts.removeAssignee)];
|
|
1798
|
+
}
|
|
1759
1799
|
}
|
|
1760
1800
|
if (opts.timeEstimate !== void 0) {
|
|
1761
1801
|
payload.time_estimate = parseTimeEstimate(opts.timeEstimate);
|
|
@@ -1789,7 +1829,7 @@ async function resolveStatus(client, taskId, statusInput) {
|
|
|
1789
1829
|
async function updateTask(config, taskId, options) {
|
|
1790
1830
|
if (!hasUpdateFields(options))
|
|
1791
1831
|
throw new Error(
|
|
1792
|
-
"Provide at least one of: --name, --description, --status, --priority, --due-date, --start-date, --time-estimate, --assignee, --parent, --detach, --archive, --unarchive"
|
|
1832
|
+
"Provide at least one of: --name, --description, --status, --priority, --due-date, --start-date, --time-estimate, --assignee, --remove-assignee, --parent, --detach, --archive, --unarchive"
|
|
1793
1833
|
);
|
|
1794
1834
|
const client = new ClickUpClient(config);
|
|
1795
1835
|
if (options.status !== void 0) {
|
|
@@ -2630,7 +2670,11 @@ async function fetchOverdueTasks(config, opts = {}) {
|
|
|
2630
2670
|
}
|
|
2631
2671
|
|
|
2632
2672
|
// src/commands/config.ts
|
|
2633
|
-
var VALID_KEYS = /* @__PURE__ */ new Set([
|
|
2673
|
+
var VALID_KEYS = /* @__PURE__ */ new Set([
|
|
2674
|
+
"apiToken",
|
|
2675
|
+
"teamId",
|
|
2676
|
+
"sprintFolderId"
|
|
2677
|
+
]);
|
|
2634
2678
|
function readStoredString(value) {
|
|
2635
2679
|
if (typeof value !== "string") return void 0;
|
|
2636
2680
|
const trimmed = value.trim();
|
|
@@ -2802,6 +2846,7 @@ var commandMetadata = [
|
|
|
2802
2846
|
"--start-date",
|
|
2803
2847
|
"--time-estimate",
|
|
2804
2848
|
"--assignee",
|
|
2849
|
+
"--remove-assignee",
|
|
2805
2850
|
"--parent",
|
|
2806
2851
|
"--detach",
|
|
2807
2852
|
"--archive",
|
|
@@ -3151,7 +3196,11 @@ var commandMetadata = [
|
|
|
3151
3196
|
usage: "time log <taskId> <duration>",
|
|
3152
3197
|
description: "Log a manual time entry"
|
|
3153
3198
|
},
|
|
3154
|
-
{
|
|
3199
|
+
{
|
|
3200
|
+
section: "write",
|
|
3201
|
+
usage: "time list",
|
|
3202
|
+
description: "List my recent time entries (--all for team)"
|
|
3203
|
+
},
|
|
3155
3204
|
{ section: "write", usage: "time update <timeEntryId>", description: "Update a time entry" },
|
|
3156
3205
|
{ section: "write", usage: "time delete <timeEntryId>", description: "Delete a time entry" }
|
|
3157
3206
|
]
|
|
@@ -3255,7 +3304,7 @@ var commandMetadata = [
|
|
|
3255
3304
|
{
|
|
3256
3305
|
name: "list-create",
|
|
3257
3306
|
description: "Create a new list in a space",
|
|
3258
|
-
flags: ["--folder", "--json"],
|
|
3307
|
+
flags: ["--folder", "--copy-statuses-from", "--json"],
|
|
3259
3308
|
quickReference: [
|
|
3260
3309
|
{
|
|
3261
3310
|
section: "write",
|
|
@@ -3318,6 +3367,21 @@ var commandMetadata = [
|
|
|
3318
3367
|
section: "write",
|
|
3319
3368
|
usage: "bulk status <status> <taskIds...>",
|
|
3320
3369
|
description: "Bulk update task status"
|
|
3370
|
+
},
|
|
3371
|
+
{
|
|
3372
|
+
section: "write",
|
|
3373
|
+
usage: "bulk assign <taskIds...>",
|
|
3374
|
+
description: "Bulk assign user to tasks"
|
|
3375
|
+
},
|
|
3376
|
+
{
|
|
3377
|
+
section: "write",
|
|
3378
|
+
usage: "bulk due-date <date> <taskIds...>",
|
|
3379
|
+
description: "Bulk set due date"
|
|
3380
|
+
},
|
|
3381
|
+
{
|
|
3382
|
+
section: "write",
|
|
3383
|
+
usage: "bulk tag <tagName> <taskIds...>",
|
|
3384
|
+
description: "Bulk add/remove tag"
|
|
3321
3385
|
}
|
|
3322
3386
|
]
|
|
3323
3387
|
},
|
|
@@ -3489,6 +3553,19 @@ var commandMetadata = [
|
|
|
3489
3553
|
{ section: "write", usage: "view-delete <viewId>", description: "Delete a view" }
|
|
3490
3554
|
]
|
|
3491
3555
|
},
|
|
3556
|
+
{
|
|
3557
|
+
name: "filter",
|
|
3558
|
+
description: "Manage saved command shortcuts",
|
|
3559
|
+
quickReference: [
|
|
3560
|
+
{
|
|
3561
|
+
section: "setup",
|
|
3562
|
+
usage: "filter save <name> [args...]",
|
|
3563
|
+
description: "Save a command shortcut"
|
|
3564
|
+
},
|
|
3565
|
+
{ section: "read", usage: "filter list", description: "List saved shortcuts" },
|
|
3566
|
+
{ section: "read", usage: "filter run <name>", description: "Run a saved shortcut" }
|
|
3567
|
+
]
|
|
3568
|
+
},
|
|
3492
3569
|
{
|
|
3493
3570
|
name: "profile",
|
|
3494
3571
|
description: "Manage profiles",
|
|
@@ -3556,6 +3633,7 @@ var bashSpecialCaseCommands = /* @__PURE__ */ new Set([
|
|
|
3556
3633
|
"checklist",
|
|
3557
3634
|
"time",
|
|
3558
3635
|
"bulk",
|
|
3636
|
+
"filter",
|
|
3559
3637
|
"config",
|
|
3560
3638
|
"profile",
|
|
3561
3639
|
"completion"
|
|
@@ -3649,7 +3727,7 @@ ${renderBashCommandCases()}
|
|
|
3649
3727
|
;;
|
|
3650
3728
|
bulk)
|
|
3651
3729
|
if [[ $cword -eq 2 ]]; then
|
|
3652
|
-
COMPREPLY=($(compgen -W "status" -- "$cur"))
|
|
3730
|
+
COMPREPLY=($(compgen -W "status assign due-date tag" -- "$cur"))
|
|
3653
3731
|
fi
|
|
3654
3732
|
;;
|
|
3655
3733
|
profile)
|
|
@@ -3724,6 +3802,7 @@ ${renderZshTopLevelCommands(name)}
|
|
|
3724
3802
|
'--start-date[Start date]:date:' \\
|
|
3725
3803
|
'--time-estimate[Time estimate]:duration:' \\
|
|
3726
3804
|
'--assignee[Add assignee]:user_id:' \\
|
|
3805
|
+
'--remove-assignee[Remove assignee]:user_id:' \\
|
|
3727
3806
|
'--parent[Set parent task]:task_id:' \\
|
|
3728
3807
|
'--detach[Remove parent task]' \\
|
|
3729
3808
|
'--archive[Archive the task]' \\
|
|
@@ -3970,6 +4049,10 @@ ${renderZshTopLevelCommands(name)}
|
|
|
3970
4049
|
_arguments \\
|
|
3971
4050
|
'--days[Number of days to look back]:days:' \\
|
|
3972
4051
|
'--task[Filter by task ID]:task_id:' \\
|
|
4052
|
+
'--space[Filter by space ID]:space_id:' \\
|
|
4053
|
+
'--list[Filter by list ID]:list_id:' \\
|
|
4054
|
+
'--assignee[Filter by assignee user ID]:user_id:' \\
|
|
4055
|
+
'--all[Show all team entries]' \\
|
|
3973
4056
|
'--json[Force JSON output]'
|
|
3974
4057
|
;;
|
|
3975
4058
|
update)
|
|
@@ -4087,6 +4170,9 @@ ${renderZshTopLevelCommands(name)}
|
|
|
4087
4170
|
local -a bulk_cmds
|
|
4088
4171
|
bulk_cmds=(
|
|
4089
4172
|
'status:Update status of multiple tasks'
|
|
4173
|
+
'assign:Bulk assign or unassign a user from tasks'
|
|
4174
|
+
'due-date:Bulk set due date on tasks'
|
|
4175
|
+
'tag:Bulk add or remove a tag on tasks'
|
|
4090
4176
|
)
|
|
4091
4177
|
_arguments -C \\
|
|
4092
4178
|
'1:bulk command:->bulk_cmd' \\
|
|
@@ -4100,6 +4186,15 @@ ${renderZshTopLevelCommands(name)}
|
|
|
4100
4186
|
status)
|
|
4101
4187
|
_arguments '1:status:' '*:task_ids:' '--json[Force JSON output]'
|
|
4102
4188
|
;;
|
|
4189
|
+
assign)
|
|
4190
|
+
_arguments '*:task_ids:' '--to[Add user (ID or me)]:userId:' '--remove[Remove user (ID or me)]:userId:' '--json[Force JSON output]'
|
|
4191
|
+
;;
|
|
4192
|
+
due-date)
|
|
4193
|
+
_arguments '1:date:' '*:task_ids:' '--json[Force JSON output]'
|
|
4194
|
+
;;
|
|
4195
|
+
tag)
|
|
4196
|
+
_arguments '1:tagName:' '*:task_ids:' '--add[Add tag]' '--remove[Remove tag]' '--json[Force JSON output]'
|
|
4197
|
+
;;
|
|
4103
4198
|
esac
|
|
4104
4199
|
;;
|
|
4105
4200
|
esac
|
|
@@ -4244,6 +4339,7 @@ ${renderZshTopLevelCommands(name)}
|
|
|
4244
4339
|
'1:space_id:' \\
|
|
4245
4340
|
'2:name:' \\
|
|
4246
4341
|
'--folder[Create inside a folder]:folder_id:' \\
|
|
4342
|
+
'--copy-statuses-from[Copy statuses from list or space]:id:' \\
|
|
4247
4343
|
'--json[Force JSON output]'
|
|
4248
4344
|
;;
|
|
4249
4345
|
folder-create)
|
|
@@ -4274,6 +4370,50 @@ ${renderZshTopLevelCommands(name)}
|
|
|
4274
4370
|
'(-c --content)'{-c,--content}'[New page content]:text:' \\
|
|
4275
4371
|
'--json[Force JSON output]'
|
|
4276
4372
|
;;
|
|
4373
|
+
filter)
|
|
4374
|
+
local -a filter_cmds
|
|
4375
|
+
filter_cmds=(
|
|
4376
|
+
'save:Save a command shortcut'
|
|
4377
|
+
'run:Run a saved shortcut'
|
|
4378
|
+
'list:List saved shortcuts'
|
|
4379
|
+
'delete:Delete a saved shortcut'
|
|
4380
|
+
'show:Show details of a saved shortcut'
|
|
4381
|
+
)
|
|
4382
|
+
_arguments -C \\
|
|
4383
|
+
'1:filter command:->filter_cmd' \\
|
|
4384
|
+
'*::filter_arg:->filter_args'
|
|
4385
|
+
case $state in
|
|
4386
|
+
filter_cmd)
|
|
4387
|
+
_describe 'filter command' filter_cmds
|
|
4388
|
+
;;
|
|
4389
|
+
filter_args)
|
|
4390
|
+
case $words[1] in
|
|
4391
|
+
save)
|
|
4392
|
+
_arguments \\
|
|
4393
|
+
'1:name:' \\
|
|
4394
|
+
'*:command:' \\
|
|
4395
|
+
'(-d --description)'{-d,--description}'[Filter description]:text:' \\
|
|
4396
|
+
'--json[Force JSON output]'
|
|
4397
|
+
;;
|
|
4398
|
+
run)
|
|
4399
|
+
_arguments '1:name:'
|
|
4400
|
+
;;
|
|
4401
|
+
list)
|
|
4402
|
+
_arguments '--json[Force JSON output]'
|
|
4403
|
+
;;
|
|
4404
|
+
delete)
|
|
4405
|
+
_arguments '1:name:' '--json[Force JSON output]'
|
|
4406
|
+
;;
|
|
4407
|
+
show)
|
|
4408
|
+
_arguments '1:name:' '--json[Force JSON output]'
|
|
4409
|
+
;;
|
|
4410
|
+
*)
|
|
4411
|
+
_arguments '1:subcommand:(save run list delete show)'
|
|
4412
|
+
;;
|
|
4413
|
+
esac
|
|
4414
|
+
;;
|
|
4415
|
+
esac
|
|
4416
|
+
;;
|
|
4277
4417
|
profile)
|
|
4278
4418
|
local -a profile_cmds
|
|
4279
4419
|
profile_cmds=(
|
|
@@ -4373,13 +4513,24 @@ complete -c ${name} -n '__fish_seen_subcommand_from start; and __fish_seen_subco
|
|
|
4373
4513
|
complete -c ${name} -n '__fish_seen_subcommand_from log; and __fish_seen_subcommand_from time' -s d -l description -d 'Description'
|
|
4374
4514
|
complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l days -d 'Number of days to look back'
|
|
4375
4515
|
complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l task -d 'Filter by task ID'
|
|
4516
|
+
complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l space -d 'Filter by space ID'
|
|
4517
|
+
complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l list -d 'Filter by list ID'
|
|
4518
|
+
complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l assignee -d 'Filter by assignee user ID'
|
|
4519
|
+
complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l all -d 'Show all team entries'
|
|
4376
4520
|
complete -c ${name} -n '__fish_seen_subcommand_from update; and __fish_seen_subcommand_from time' -s d -l description -d 'New description'
|
|
4377
4521
|
complete -c ${name} -n '__fish_seen_subcommand_from update; and __fish_seen_subcommand_from time' -l duration -d 'New duration'
|
|
4378
4522
|
|
|
4379
4523
|
complete -c ${name} -n '__fish_seen_subcommand_from attach' -F
|
|
4380
4524
|
|
|
4381
|
-
complete -c ${name} -n '__fish_seen_subcommand_from bulk; and not __fish_seen_subcommand_from status' -a status -d 'Update status of multiple tasks'
|
|
4382
|
-
complete -c ${name} -n '__fish_seen_subcommand_from
|
|
4525
|
+
complete -c ${name} -n '__fish_seen_subcommand_from bulk; and not __fish_seen_subcommand_from status assign due-date tag' -a status -d 'Update status of multiple tasks'
|
|
4526
|
+
complete -c ${name} -n '__fish_seen_subcommand_from bulk; and not __fish_seen_subcommand_from status assign due-date tag' -a assign -d 'Bulk assign or unassign a user from tasks'
|
|
4527
|
+
complete -c ${name} -n '__fish_seen_subcommand_from bulk; and not __fish_seen_subcommand_from status assign due-date tag' -a due-date -d 'Bulk set due date on tasks'
|
|
4528
|
+
complete -c ${name} -n '__fish_seen_subcommand_from bulk; and not __fish_seen_subcommand_from status assign due-date tag' -a tag -d 'Bulk add or remove a tag on tasks'
|
|
4529
|
+
complete -c ${name} -n '__fish_seen_subcommand_from status assign due-date tag; and __fish_seen_subcommand_from bulk' -l json -d 'Force JSON output'
|
|
4530
|
+
complete -c ${name} -n '__fish_seen_subcommand_from assign; and __fish_seen_subcommand_from bulk' -l to -d 'Add user (ID or me)'
|
|
4531
|
+
complete -c ${name} -n '__fish_seen_subcommand_from assign; and __fish_seen_subcommand_from bulk' -l remove -d 'Remove user (ID or me)'
|
|
4532
|
+
complete -c ${name} -n '__fish_seen_subcommand_from tag; and __fish_seen_subcommand_from bulk' -l add -d 'Add tag'
|
|
4533
|
+
complete -c ${name} -n '__fish_seen_subcommand_from tag; and __fish_seen_subcommand_from bulk' -l remove -d 'Remove tag'
|
|
4383
4534
|
|
|
4384
4535
|
complete -c ${name} -n '__fish_seen_subcommand_from profile; and not __fish_seen_subcommand_from list add remove use' -a list -d 'List all profiles'
|
|
4385
4536
|
complete -c ${name} -n '__fish_seen_subcommand_from profile; and not __fish_seen_subcommand_from list add remove use' -a add -d 'Add a new profile'
|
|
@@ -4393,6 +4544,14 @@ complete -c ${name} -n '__fish_seen_subcommand_from config; and not __fish_seen_
|
|
|
4393
4544
|
complete -c ${name} -n '__fish_seen_subcommand_from get set' -a 'apiToken teamId sprintFolderId' -d 'Config key'
|
|
4394
4545
|
|
|
4395
4546
|
complete -c ${name} -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish' -d 'Shell type'
|
|
4547
|
+
|
|
4548
|
+
complete -c ${name} -n '__fish_seen_subcommand_from filter; and not __fish_seen_subcommand_from save run list delete show' -a save -d 'Save a command shortcut'
|
|
4549
|
+
complete -c ${name} -n '__fish_seen_subcommand_from filter; and not __fish_seen_subcommand_from save run list delete show' -a run -d 'Run a saved shortcut'
|
|
4550
|
+
complete -c ${name} -n '__fish_seen_subcommand_from filter; and not __fish_seen_subcommand_from save run list delete show' -a list -d 'List saved shortcuts'
|
|
4551
|
+
complete -c ${name} -n '__fish_seen_subcommand_from filter; and not __fish_seen_subcommand_from save run list delete show' -a delete -d 'Delete a saved shortcut'
|
|
4552
|
+
complete -c ${name} -n '__fish_seen_subcommand_from filter; and not __fish_seen_subcommand_from save run list delete show' -a show -d 'Show details of a saved shortcut'
|
|
4553
|
+
complete -c ${name} -n '__fish_seen_subcommand_from save run list delete show; and __fish_seen_subcommand_from filter' -l json -d 'Force JSON output'
|
|
4554
|
+
complete -c ${name} -n '__fish_seen_subcommand_from save; and __fish_seen_subcommand_from filter' -s d -l description -d 'Filter description'
|
|
4396
4555
|
`;
|
|
4397
4556
|
}
|
|
4398
4557
|
function generateCompletion(shell, name = "cup") {
|
|
@@ -4990,13 +5149,18 @@ async function listTimeEntries(config, opts) {
|
|
|
4990
5149
|
const days = opts?.days ?? 7;
|
|
4991
5150
|
const endDate = Date.now();
|
|
4992
5151
|
const startDate = endDate - days * 24 * 60 * 60 * 1e3;
|
|
5152
|
+
let assigneeId = opts?.assigneeId;
|
|
5153
|
+
if (!opts?.all && !assigneeId) {
|
|
5154
|
+
const me = await client.getMe();
|
|
5155
|
+
assigneeId = String(me.id);
|
|
5156
|
+
}
|
|
4993
5157
|
return client.getTimeEntries(config.teamId, {
|
|
4994
5158
|
startDate,
|
|
4995
5159
|
endDate,
|
|
4996
5160
|
taskId: opts?.taskId,
|
|
4997
5161
|
spaceId: opts?.spaceId,
|
|
4998
5162
|
listId: opts?.listId,
|
|
4999
|
-
assigneeId
|
|
5163
|
+
assigneeId
|
|
5000
5164
|
});
|
|
5001
5165
|
}
|
|
5002
5166
|
async function updateTimeEntry(config, timeEntryId, opts) {
|
|
@@ -5198,6 +5362,53 @@ async function bulkUpdateStatus(config, taskIds, status) {
|
|
|
5198
5362
|
}
|
|
5199
5363
|
return { updated: taskIds.length - failed.length, failed };
|
|
5200
5364
|
}
|
|
5365
|
+
async function bulkAssign(config, userIdOrMe, taskIds, action) {
|
|
5366
|
+
const client = new ClickUpClient(config);
|
|
5367
|
+
const numericId = await resolveAssigneeId(client, userIdOrMe);
|
|
5368
|
+
const failed = [];
|
|
5369
|
+
for (const id of taskIds) {
|
|
5370
|
+
try {
|
|
5371
|
+
await client.updateTask(id, {
|
|
5372
|
+
assignees: action === "add" ? { add: [numericId] } : { rem: [numericId] }
|
|
5373
|
+
});
|
|
5374
|
+
} catch (err) {
|
|
5375
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5376
|
+
failed.push({ id, reason });
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
return { updated: taskIds.length - failed.length, failed };
|
|
5380
|
+
}
|
|
5381
|
+
async function bulkDueDate(config, date, taskIds) {
|
|
5382
|
+
const client = new ClickUpClient(config);
|
|
5383
|
+
const payload = date === "none" || date === "clear" ? { due_date: null } : { due_date: parseDueDate(date), due_date_time: false };
|
|
5384
|
+
const failed = [];
|
|
5385
|
+
for (const id of taskIds) {
|
|
5386
|
+
try {
|
|
5387
|
+
await client.updateTask(id, payload);
|
|
5388
|
+
} catch (err) {
|
|
5389
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5390
|
+
failed.push({ id, reason });
|
|
5391
|
+
}
|
|
5392
|
+
}
|
|
5393
|
+
return { updated: taskIds.length - failed.length, failed };
|
|
5394
|
+
}
|
|
5395
|
+
async function bulkTag(config, tagName, taskIds, action) {
|
|
5396
|
+
const client = new ClickUpClient(config);
|
|
5397
|
+
const failed = [];
|
|
5398
|
+
for (const id of taskIds) {
|
|
5399
|
+
try {
|
|
5400
|
+
if (action === "add") {
|
|
5401
|
+
await client.addTagToTask(id, tagName);
|
|
5402
|
+
} else {
|
|
5403
|
+
await client.removeTagFromTask(id, tagName);
|
|
5404
|
+
}
|
|
5405
|
+
} catch (err) {
|
|
5406
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5407
|
+
failed.push({ id, reason });
|
|
5408
|
+
}
|
|
5409
|
+
}
|
|
5410
|
+
return { updated: taskIds.length - failed.length, failed };
|
|
5411
|
+
}
|
|
5201
5412
|
|
|
5202
5413
|
// src/commands/goals.ts
|
|
5203
5414
|
import chalk14 from "chalk";
|
|
@@ -5499,6 +5710,130 @@ async function deleteViewCommand(config, viewId, opts) {
|
|
|
5499
5710
|
return { viewId, deleted: true };
|
|
5500
5711
|
}
|
|
5501
5712
|
|
|
5713
|
+
// src/commands/filter.ts
|
|
5714
|
+
import { spawnSync } from "child_process";
|
|
5715
|
+
var ALLOWED_FILTER_COMMANDS = /* @__PURE__ */ new Set([
|
|
5716
|
+
"tasks",
|
|
5717
|
+
"search",
|
|
5718
|
+
"sprint",
|
|
5719
|
+
"assigned",
|
|
5720
|
+
"overdue",
|
|
5721
|
+
"inbox",
|
|
5722
|
+
"summary",
|
|
5723
|
+
"views",
|
|
5724
|
+
"lists",
|
|
5725
|
+
"spaces",
|
|
5726
|
+
"folders",
|
|
5727
|
+
"members",
|
|
5728
|
+
"tags",
|
|
5729
|
+
"goals",
|
|
5730
|
+
"key-results",
|
|
5731
|
+
"task-types",
|
|
5732
|
+
"templates",
|
|
5733
|
+
"list-templates",
|
|
5734
|
+
"folder-templates",
|
|
5735
|
+
"docs"
|
|
5736
|
+
]);
|
|
5737
|
+
function isAllowedFilterCommand(tokens) {
|
|
5738
|
+
if (tokens.length === 0) return false;
|
|
5739
|
+
if (tokens[0] === "time" && tokens[1] === "list") return true;
|
|
5740
|
+
return ALLOWED_FILTER_COMMANDS.has(tokens[0]);
|
|
5741
|
+
}
|
|
5742
|
+
function runFilter(_name, entry) {
|
|
5743
|
+
const result = spawnSync(process.execPath, [process.argv[1], ...entry.command], {
|
|
5744
|
+
stdio: "inherit"
|
|
5745
|
+
});
|
|
5746
|
+
if (result.error) throw result.error;
|
|
5747
|
+
if (result.status !== null && result.status !== 0) {
|
|
5748
|
+
process.exitCode = result.status;
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
var FILTER_COLUMNS = [
|
|
5752
|
+
{ key: "name", label: "NAME", maxWidth: 30 },
|
|
5753
|
+
{ key: "command", label: "COMMAND", maxWidth: 60 },
|
|
5754
|
+
{ key: "description", label: "DESCRIPTION", maxWidth: 50 }
|
|
5755
|
+
];
|
|
5756
|
+
function formatFiltersTable(filters) {
|
|
5757
|
+
const entries = Object.entries(filters);
|
|
5758
|
+
if (entries.length === 0) return "No filters saved";
|
|
5759
|
+
const rows = entries.map(([name, entry]) => ({
|
|
5760
|
+
name,
|
|
5761
|
+
command: entry.command.join(" "),
|
|
5762
|
+
description: entry.description ?? ""
|
|
5763
|
+
}));
|
|
5764
|
+
return formatTable(rows, FILTER_COLUMNS);
|
|
5765
|
+
}
|
|
5766
|
+
function formatFiltersMarkdown(filters) {
|
|
5767
|
+
const entries = Object.entries(filters);
|
|
5768
|
+
if (entries.length === 0) return "No filters saved";
|
|
5769
|
+
const lines = ["| Name | Command | Description |", "| --- | --- | --- |"];
|
|
5770
|
+
for (const [name, entry] of entries) {
|
|
5771
|
+
const command = entry.command.join(" ");
|
|
5772
|
+
const description = entry.description ?? "";
|
|
5773
|
+
lines.push(`| ${name} | ${command} | ${description} |`);
|
|
5774
|
+
}
|
|
5775
|
+
return lines.join("\n");
|
|
5776
|
+
}
|
|
5777
|
+
function formatFilterDetail(name, entry) {
|
|
5778
|
+
if (isTTY()) {
|
|
5779
|
+
const lines2 = [`Name: ${name}`, `Command: ${entry.command.join(" ")}`];
|
|
5780
|
+
if (entry.description) lines2.push(`Description: ${entry.description}`);
|
|
5781
|
+
return lines2.join("\n");
|
|
5782
|
+
}
|
|
5783
|
+
const lines = [`**${name}**`, ``, `Command: \`${entry.command.join(" ")}\``];
|
|
5784
|
+
if (entry.description) lines.push(`Description: ${entry.description}`);
|
|
5785
|
+
return lines.join("\n");
|
|
5786
|
+
}
|
|
5787
|
+
|
|
5788
|
+
// src/commands/list-create.ts
|
|
5789
|
+
async function createListWithOptions(config, spaceId, name, opts) {
|
|
5790
|
+
const client = new ClickUpClient(config);
|
|
5791
|
+
let statuses;
|
|
5792
|
+
if (opts.copyStatusesFrom) {
|
|
5793
|
+
statuses = await copyStatusesFrom(client, opts.copyStatusesFrom);
|
|
5794
|
+
}
|
|
5795
|
+
const list = opts.folder ? await client.createFolderList(opts.folder, name) : await client.createList(spaceId, name);
|
|
5796
|
+
if (statuses) {
|
|
5797
|
+
try {
|
|
5798
|
+
await client.updateList(list.id, { statuses });
|
|
5799
|
+
} catch (err) {
|
|
5800
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5801
|
+
throw new Error(`List "${name}" (${list.id}) was created but status copy failed: ${reason}`, {
|
|
5802
|
+
cause: err
|
|
5803
|
+
});
|
|
5804
|
+
}
|
|
5805
|
+
}
|
|
5806
|
+
return {
|
|
5807
|
+
...list,
|
|
5808
|
+
...statuses ? { statusesCopied: statuses.length } : {}
|
|
5809
|
+
};
|
|
5810
|
+
}
|
|
5811
|
+
function isNotFound(err) {
|
|
5812
|
+
return err instanceof Error && /ClickUp API error 4(04|03)/.test(err.message);
|
|
5813
|
+
}
|
|
5814
|
+
async function copyStatusesFrom(client, sourceId) {
|
|
5815
|
+
try {
|
|
5816
|
+
const list = await client.getListWithStatuses(sourceId);
|
|
5817
|
+
return list.statuses.map((s) => ({ status: s.status, color: s.color, type: s.type ?? "custom" }));
|
|
5818
|
+
} catch (err) {
|
|
5819
|
+
if (!isNotFound(err)) throw err;
|
|
5820
|
+
try {
|
|
5821
|
+
const space = await client.getSpaceWithStatuses(sourceId);
|
|
5822
|
+
return space.statuses.map((s) => ({
|
|
5823
|
+
status: s.status,
|
|
5824
|
+
color: s.color,
|
|
5825
|
+
type: s.type ?? "custom"
|
|
5826
|
+
}));
|
|
5827
|
+
} catch (spaceErr) {
|
|
5828
|
+
if (!isNotFound(spaceErr)) throw spaceErr;
|
|
5829
|
+
throw new Error(
|
|
5830
|
+
`Could not find a list or space with ID "${sourceId}". Check the ID and try again.`,
|
|
5831
|
+
{ cause: spaceErr }
|
|
5832
|
+
);
|
|
5833
|
+
}
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
|
|
5502
5837
|
// src/index.ts
|
|
5503
5838
|
var require2 = createRequire(import.meta.url);
|
|
5504
5839
|
var { version } = require2("../package.json");
|
|
@@ -5572,7 +5907,7 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
5572
5907
|
}
|
|
5573
5908
|
})
|
|
5574
5909
|
);
|
|
5575
|
-
program.command("update <taskId>").description("Update a task").option("-n, --name <text>", "New task name").option("-d, --description <text>", "New description (markdown supported)").option("-s, --status <status>", 'New status (e.g. "in progress", "done")').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("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--assignee <userId>", 'Add 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(
|
|
5910
|
+
program.command("update <taskId>").description("Update a task").option("-n, --name <text>", "New task name").option("-d, --description <text>", "New description (markdown supported)").option("-s, --status <status>", 'New status (e.g. "in progress", "done")').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("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').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(
|
|
5576
5911
|
wrapAction(
|
|
5577
5912
|
async (taskId, opts) => {
|
|
5578
5913
|
const config = loadConfig(getProfileName());
|
|
@@ -5580,11 +5915,15 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
5580
5915
|
const client = new ClickUpClient(config);
|
|
5581
5916
|
opts.assignee = String(await resolveAssigneeId(client, "me"));
|
|
5582
5917
|
}
|
|
5918
|
+
if (opts.removeAssignee === "me") {
|
|
5919
|
+
const client = new ClickUpClient(config);
|
|
5920
|
+
opts.removeAssignee = String(await resolveAssigneeId(client, "me"));
|
|
5921
|
+
}
|
|
5583
5922
|
const payload = buildUpdatePayload(opts);
|
|
5584
5923
|
const hasFields = (opts.field?.length ?? 0) > 0;
|
|
5585
5924
|
if (!hasFields && Object.keys(payload).length === 0) {
|
|
5586
5925
|
throw new Error(
|
|
5587
|
-
"Provide at least one of: --name, --description, --status, --priority, --due-date, --time-estimate, --assignee, --parent, --archive, --unarchive, --field"
|
|
5926
|
+
"Provide at least one of: --name, --description, --status, --priority, --due-date, --time-estimate, --assignee, --remove-assignee, --parent, --archive, --unarchive, --field"
|
|
5588
5927
|
);
|
|
5589
5928
|
}
|
|
5590
5929
|
let result;
|
|
@@ -6080,7 +6419,7 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6080
6419
|
}
|
|
6081
6420
|
)
|
|
6082
6421
|
);
|
|
6083
|
-
timeCmd.command("list").description("List recent time entries (
|
|
6422
|
+
timeCmd.command("list").description("List my recent time entries (use --all for team entries)").option("--days <n>", "Number of days to look back", "7").option("--task <taskId>", "Filter by task ID").option("--space <spaceId>", "Filter by space ID").option("--list <listId>", "Filter by list ID").option("--assignee <userId>", "Filter by assignee user ID").option("--all", "Show all team entries (default: only mine)").option("--json", "Force JSON output even in terminal").action(
|
|
6084
6423
|
wrapAction(
|
|
6085
6424
|
async (opts) => {
|
|
6086
6425
|
const config = loadConfig(getProfileName());
|
|
@@ -6093,7 +6432,8 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6093
6432
|
taskId: opts.task,
|
|
6094
6433
|
spaceId: opts.space,
|
|
6095
6434
|
listId: opts.list,
|
|
6096
|
-
assigneeId: opts.assignee
|
|
6435
|
+
assigneeId: opts.assignee,
|
|
6436
|
+
all: opts.all
|
|
6097
6437
|
});
|
|
6098
6438
|
if (shouldOutputJson(opts.json ?? false)) {
|
|
6099
6439
|
console.log(JSON.stringify(entries, null, 2));
|
|
@@ -6254,6 +6594,18 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6254
6594
|
}
|
|
6255
6595
|
})
|
|
6256
6596
|
);
|
|
6597
|
+
function outputBulkResult(result, forceJson, operation) {
|
|
6598
|
+
if (shouldOutputJson(forceJson)) {
|
|
6599
|
+
console.log(JSON.stringify(result, null, 2));
|
|
6600
|
+
} else {
|
|
6601
|
+
console.log(
|
|
6602
|
+
`${operation}: ${result.updated} updated${result.failed.length > 0 ? `, ${result.failed.length} failed` : ""}`
|
|
6603
|
+
);
|
|
6604
|
+
for (const f of result.failed) {
|
|
6605
|
+
console.error(` ${f.id}: ${f.reason}`);
|
|
6606
|
+
}
|
|
6607
|
+
}
|
|
6608
|
+
}
|
|
6257
6609
|
const bulkCmd = program.command("bulk").description("Bulk task operations");
|
|
6258
6610
|
bulkCmd.command("status <status> <taskIds...>").description("Update status of multiple tasks").option("--json", "Force JSON output even in terminal").action(
|
|
6259
6611
|
wrapAction(async (status, taskIds, opts) => {
|
|
@@ -6271,6 +6623,37 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6271
6623
|
}
|
|
6272
6624
|
})
|
|
6273
6625
|
);
|
|
6626
|
+
bulkCmd.command("assign <taskIds...>").description("Bulk assign or unassign a user from tasks").option("--to <userId>", 'Add this user (user ID or "me")').option("--remove <userId>", 'Remove this user (user ID or "me")').option("--json", "Force JSON output even in terminal").action(
|
|
6627
|
+
wrapAction(
|
|
6628
|
+
async (taskIds, opts) => {
|
|
6629
|
+
if (!opts.to && !opts.remove)
|
|
6630
|
+
throw new Error("Provide --to <userId> or --remove <userId>");
|
|
6631
|
+
if (opts.to && opts.remove) throw new Error("Cannot use --to and --remove together");
|
|
6632
|
+
const config = loadConfig(getProfileName());
|
|
6633
|
+
const userId = opts.to ?? opts.remove;
|
|
6634
|
+
const action = opts.remove ? "remove" : "add";
|
|
6635
|
+
const result = await bulkAssign(config, userId, taskIds, action);
|
|
6636
|
+
outputBulkResult(result, opts.json ?? false, `assign ${action}`);
|
|
6637
|
+
}
|
|
6638
|
+
)
|
|
6639
|
+
);
|
|
6640
|
+
bulkCmd.command("due-date <date> <taskIds...>").description('Bulk set due date on tasks (use "none" to clear)').option("--json", "Force JSON output even in terminal").action(
|
|
6641
|
+
wrapAction(async (date, taskIds, opts) => {
|
|
6642
|
+
const config = loadConfig(getProfileName());
|
|
6643
|
+
const result = await bulkDueDate(config, date, taskIds);
|
|
6644
|
+
outputBulkResult(result, opts.json ?? false, "due-date");
|
|
6645
|
+
})
|
|
6646
|
+
);
|
|
6647
|
+
bulkCmd.command("tag <tagName> <taskIds...>").description("Bulk add or remove a tag on tasks (default: add)").option("--remove", "Remove tag instead of adding").option("--json", "Force JSON output even in terminal").action(
|
|
6648
|
+
wrapAction(
|
|
6649
|
+
async (tagName, taskIds, opts) => {
|
|
6650
|
+
const action = opts.remove ? "remove" : "add";
|
|
6651
|
+
const config = loadConfig(getProfileName());
|
|
6652
|
+
const result = await bulkTag(config, tagName, taskIds, action);
|
|
6653
|
+
outputBulkResult(result, opts.json ?? false, `tag ${action}`);
|
|
6654
|
+
}
|
|
6655
|
+
)
|
|
6656
|
+
);
|
|
6274
6657
|
program.command("goals").description("List goals in your workspace").option("--json", "Force JSON output even in terminal").action(
|
|
6275
6658
|
wrapAction(async (opts) => {
|
|
6276
6659
|
const config = loadConfig(getProfileName());
|
|
@@ -6466,17 +6849,24 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6466
6849
|
}
|
|
6467
6850
|
})
|
|
6468
6851
|
);
|
|
6469
|
-
program.command("list-create <spaceId> <name>").description("Create a new list in a space").option("--folder <folderId>", "Create the list inside a folder").option("--json", "Force JSON output even in terminal").action(
|
|
6852
|
+
program.command("list-create <spaceId> <name>").description("Create a new list in a space").option("--folder <folderId>", "Create the list inside a folder").option("--copy-statuses-from <id>", "Copy status set from this list or space ID").option("--json", "Force JSON output even in terminal").action(
|
|
6470
6853
|
wrapAction(
|
|
6471
6854
|
async (spaceId, name, opts) => {
|
|
6472
6855
|
if (!name.trim()) throw new Error("List name cannot be empty");
|
|
6473
6856
|
const config = loadConfig(getProfileName());
|
|
6474
|
-
const
|
|
6475
|
-
|
|
6857
|
+
const result = await createListWithOptions(config, spaceId, name, {
|
|
6858
|
+
folder: opts.folder,
|
|
6859
|
+
copyStatusesFrom: opts.copyStatusesFrom
|
|
6860
|
+
});
|
|
6476
6861
|
if (shouldOutputJson(opts.json ?? false)) {
|
|
6477
|
-
console.log(JSON.stringify(
|
|
6862
|
+
console.log(JSON.stringify(result, null, 2));
|
|
6478
6863
|
} else {
|
|
6479
|
-
console.log(`Created list "${
|
|
6864
|
+
console.log(`Created list "${result.name}" (${result.id})`);
|
|
6865
|
+
if (result.statusesCopied) {
|
|
6866
|
+
console.log(
|
|
6867
|
+
` Copied ${result.statusesCopied} statuses from ${opts.copyStatusesFrom}`
|
|
6868
|
+
);
|
|
6869
|
+
}
|
|
6480
6870
|
}
|
|
6481
6871
|
}
|
|
6482
6872
|
)
|
|
@@ -6724,6 +7114,78 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
6724
7114
|
}
|
|
6725
7115
|
})
|
|
6726
7116
|
);
|
|
7117
|
+
const filterCmd = program.command("filter").description("Manage saved command shortcuts");
|
|
7118
|
+
filterCmd.command("save <name> [args...]").description("Save a command shortcut").option("-d, --description <text>", "Description for this filter").option("--json", "Force JSON output even in terminal").action(
|
|
7119
|
+
wrapAction(
|
|
7120
|
+
async (name, args, opts) => {
|
|
7121
|
+
if (args.length === 0) {
|
|
7122
|
+
throw new Error(
|
|
7123
|
+
'Provide a command to save, e.g.: cup filter save my-sprint tasks --status "in progress"'
|
|
7124
|
+
);
|
|
7125
|
+
}
|
|
7126
|
+
if (!isAllowedFilterCommand(args)) {
|
|
7127
|
+
const allowed = [...ALLOWED_FILTER_COMMANDS, "time list"].join(", ");
|
|
7128
|
+
throw new Error(
|
|
7129
|
+
`Command "${args[0]}" is not allowed in saved filters. Allowed: ${allowed}`
|
|
7130
|
+
);
|
|
7131
|
+
}
|
|
7132
|
+
const entry = { command: args };
|
|
7133
|
+
if (opts.description) entry.description = opts.description;
|
|
7134
|
+
saveFilter(name, entry, getProfileName());
|
|
7135
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
7136
|
+
console.log(JSON.stringify({ name, ...entry }, null, 2));
|
|
7137
|
+
} else {
|
|
7138
|
+
console.log(`Saved filter "${name}": ${args.join(" ")}`);
|
|
7139
|
+
}
|
|
7140
|
+
}
|
|
7141
|
+
)
|
|
7142
|
+
);
|
|
7143
|
+
filterCmd.command("run <name>").description("Run a saved command shortcut").action(
|
|
7144
|
+
wrapAction(async (name) => {
|
|
7145
|
+
const filters = getFilters(getProfileName());
|
|
7146
|
+
const entry = filters[name];
|
|
7147
|
+
if (!entry) {
|
|
7148
|
+
throw new Error(`Filter "${name}" not found. Use: cup filter list`);
|
|
7149
|
+
}
|
|
7150
|
+
runFilter(name, entry);
|
|
7151
|
+
})
|
|
7152
|
+
);
|
|
7153
|
+
filterCmd.command("list").description("List all saved command shortcuts").option("--json", "Force JSON output even in terminal").action(
|
|
7154
|
+
wrapAction(async (opts) => {
|
|
7155
|
+
const filters = getFilters(getProfileName());
|
|
7156
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
7157
|
+
console.log(JSON.stringify(filters, null, 2));
|
|
7158
|
+
} else if (isTTY()) {
|
|
7159
|
+
console.log(formatFiltersTable(filters));
|
|
7160
|
+
} else {
|
|
7161
|
+
console.log(formatFiltersMarkdown(filters));
|
|
7162
|
+
}
|
|
7163
|
+
})
|
|
7164
|
+
);
|
|
7165
|
+
filterCmd.command("delete <name>").description("Delete a saved command shortcut").option("--json", "Force JSON output even in terminal").action(
|
|
7166
|
+
wrapAction(async (name, opts) => {
|
|
7167
|
+
deleteFilter(name, getProfileName());
|
|
7168
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
7169
|
+
console.log(JSON.stringify({ success: true, name }, null, 2));
|
|
7170
|
+
} else {
|
|
7171
|
+
console.log(`Deleted filter "${name}"`);
|
|
7172
|
+
}
|
|
7173
|
+
})
|
|
7174
|
+
);
|
|
7175
|
+
filterCmd.command("show <name>").description("Show details of a saved command shortcut").option("--json", "Force JSON output even in terminal").action(
|
|
7176
|
+
wrapAction(async (name, opts) => {
|
|
7177
|
+
const filters = getFilters(getProfileName());
|
|
7178
|
+
const entry = filters[name];
|
|
7179
|
+
if (!entry) {
|
|
7180
|
+
throw new Error(`Filter "${name}" not found. Use: cup filter list`);
|
|
7181
|
+
}
|
|
7182
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
7183
|
+
console.log(JSON.stringify({ name, ...entry }, null, 2));
|
|
7184
|
+
} else {
|
|
7185
|
+
console.log(formatFilterDetail(name, entry));
|
|
7186
|
+
}
|
|
7187
|
+
})
|
|
7188
|
+
);
|
|
6727
7189
|
const profileCmd = program.command("profile").description("Manage profiles");
|
|
6728
7190
|
profileCmd.command("list").description("List all profiles").option("--json", "Force JSON output even in terminal").action(
|
|
6729
7191
|
wrapAction(async (opts) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clickup
|
|
3
|
-
description: 'Use when managing ClickUp tasks, sprints, or comments via the `cup` CLI tool. Triggers: task queries, status updates, sprint tracking, creating subtasks, posting comments, threaded replies, standup summaries, searching tasks, checking overdue items, assigning tasks, listing spaces and lists, opening tasks in browser, checking auth or config, setting custom fields, deleting tasks, managing tags, managing checklists, editing comments, task links, time tracking, attachments, file uploads, listing members, listing fields, duplicating tasks, bulk operations, goals, key results.'
|
|
3
|
+
description: 'Use when managing ClickUp tasks, sprints, or comments via the `cup` CLI tool. Triggers: task queries, status updates, sprint tracking, creating subtasks, posting comments, threaded replies, standup summaries, searching tasks, checking overdue items, assigning tasks, listing spaces and lists, opening tasks in browser, checking auth or config, setting custom fields, deleting tasks, managing tags, managing checklists, editing comments, task links, time tracking, attachments, file uploads, listing members, listing fields, duplicating tasks, bulk operations, goals, key results, saved filters.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# ClickUp CLI (`cup`)
|
|
@@ -15,6 +15,21 @@ Multiple profiles supported - use `cup profile add <name>` to create, `cup profi
|
|
|
15
15
|
|
|
16
16
|
Environment variables `CU_API_TOKEN` and `CU_TEAM_ID` override config file when both are set. `CU_PROFILE` selects a profile (overridden by `-p` flag).
|
|
17
17
|
|
|
18
|
+
## Saved Filters (Quick Shortcuts)
|
|
19
|
+
|
|
20
|
+
At the start of each session, run `cup filter list` to discover saved shortcuts for this workspace.
|
|
21
|
+
|
|
22
|
+
| Command | What it does |
|
|
23
|
+
| -------------------------------------------------- | ---------------------------------------------------------------- |
|
|
24
|
+
| `cup filter list [--json]` | List all saved shortcuts with commands and descriptions |
|
|
25
|
+
| `cup filter run <name>` | Execute a saved shortcut (identical to running the full command) |
|
|
26
|
+
| `cup filter save <name> <cmd> [args...] [-d desc]` | Save a command shortcut |
|
|
27
|
+
| `cup filter delete <name>` | Remove a shortcut |
|
|
28
|
+
| `cup filter show <name> [--json]` | Show a single shortcut's details |
|
|
29
|
+
|
|
30
|
+
Example: `cup filter save sprint-tasks tasks --status "in progress" --list l1 -d "Current sprint tasks"`
|
|
31
|
+
Then: `cup filter run sprint-tasks` is equivalent to `cup tasks --status "in progress" --list l1`
|
|
32
|
+
|
|
18
33
|
## Output Modes
|
|
19
34
|
|
|
20
35
|
| Context | Default output | Override |
|
|
@@ -69,66 +84,69 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
69
84
|
|
|
70
85
|
### Write
|
|
71
86
|
|
|
72
|
-
| Command
|
|
73
|
-
|
|
|
74
|
-
| `cup create -n name [-l listId] [-p parentId] [-d desc] [-s status] [--priority p] [--due-date d] [--start-date d] [--time-estimate t] [--assignee id\|me] [--tags t] [--custom-item-id n] [--template id]`
|
|
75
|
-
| `cup update <id> [-n name] [-d desc] [-s status] [--priority p] [--due-date d\|none] [--start-date d] [--time-estimate t] [--assignee id\|me] [--parent id] [--detach] [--archive] [--unarchive] [--field "Name" val]` | Update task fields (including custom fields) |
|
|
76
|
-
| `cup comment <id> -m text [--notify-all]`
|
|
77
|
-
| `cup comment-edit <commentId> -m text [--resolved] [--unresolved]`
|
|
78
|
-
| `cup comment-delete <commentId>`
|
|
79
|
-
| `cup replies <commentId>`
|
|
80
|
-
| `cup reply <commentId> -m text [--notify-all]`
|
|
81
|
-
| `cup assign <id> [--to userId\|me] [--remove userId\|me]`
|
|
82
|
-
| `cup depend <id> [--on taskId] [--blocks taskId] [--remove]`
|
|
83
|
-
| `cup move <id> [--to listId] [--remove listId]`
|
|
84
|
-
| `cup field <id> [--set "Name" value] [--remove "Name"]`
|
|
85
|
-
| `cup field-create <name> -t <type> [-d desc] [--options "a,b,c"] [--required]`
|
|
86
|
-
| `cup tag <id> [--add tags] [--remove tags]`
|
|
87
|
-
| `cup link <taskId> <linksTo> [--remove]`
|
|
88
|
-
| `cup attach <taskId> <filePath>`
|
|
89
|
-
| `cup delete <id> [--confirm]`
|
|
90
|
-
| `cup duplicate <taskId>`
|
|
91
|
-
| `cup bulk status <status> <taskIds...>`
|
|
92
|
-
| `cup
|
|
93
|
-
| `cup
|
|
94
|
-
| `cup
|
|
95
|
-
| `cup checklist
|
|
96
|
-
| `cup checklist
|
|
97
|
-
| `cup checklist delete
|
|
98
|
-
| `cup
|
|
99
|
-
| `cup
|
|
100
|
-
| `cup
|
|
101
|
-
| `cup time
|
|
102
|
-
| `cup time
|
|
103
|
-
| `cup time
|
|
104
|
-
| `cup time
|
|
105
|
-
| `cup
|
|
106
|
-
| `cup
|
|
107
|
-
| `cup
|
|
108
|
-
| `cup
|
|
109
|
-
| `cup
|
|
110
|
-
| `cup
|
|
111
|
-
| `cup
|
|
112
|
-
| `cup
|
|
113
|
-
| `cup
|
|
114
|
-
| `cup doc-
|
|
115
|
-
| `cup doc-page-
|
|
116
|
-
| `cup
|
|
117
|
-
| `cup
|
|
118
|
-
| `cup
|
|
119
|
-
| `cup
|
|
120
|
-
| `cup
|
|
121
|
-
| `cup
|
|
122
|
-
| `cup
|
|
123
|
-
| `cup
|
|
124
|
-
| `cup
|
|
125
|
-
| `cup
|
|
126
|
-
| `cup
|
|
127
|
-
| `cup
|
|
128
|
-
| `cup
|
|
129
|
-
| `cup profile
|
|
130
|
-
| `cup
|
|
131
|
-
| `cup
|
|
87
|
+
| Command | What it does |
|
|
88
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
|
|
89
|
+
| `cup create -n name [-l listId] [-p parentId] [-d desc] [-s status] [--priority p] [--due-date d] [--start-date d] [--time-estimate t] [--assignee id\|me] [--tags t] [--custom-item-id n] [--template id]` | Create task |
|
|
90
|
+
| `cup update <id> [-n name] [-d desc] [-s status] [--priority p] [--due-date d\|none] [--start-date d] [--time-estimate t] [--assignee id\|me] [--remove-assignee id\|me] [--parent id] [--detach] [--archive] [--unarchive] [--field "Name" val]` | Update task fields (including custom fields) |
|
|
91
|
+
| `cup comment <id> -m text [--notify-all]` | Post comment on task |
|
|
92
|
+
| `cup comment-edit <commentId> -m text [--resolved] [--unresolved]` | Edit a comment |
|
|
93
|
+
| `cup comment-delete <commentId>` | Delete a comment |
|
|
94
|
+
| `cup replies <commentId>` | List threaded replies |
|
|
95
|
+
| `cup reply <commentId> -m text [--notify-all]` | Reply to a comment |
|
|
96
|
+
| `cup assign <id> [--to userId\|me] [--remove userId\|me]` | Assign/unassign users |
|
|
97
|
+
| `cup depend <id> [--on taskId] [--blocks taskId] [--remove]` | Add/remove dependencies |
|
|
98
|
+
| `cup move <id> [--to listId] [--remove listId]` | Add/remove task from lists |
|
|
99
|
+
| `cup field <id> [--set "Name" value] [--remove "Name"]` | Set/remove custom field values |
|
|
100
|
+
| `cup field-create <name> -t <type> [-d desc] [--options "a,b,c"] [--required]` | Create a custom field |
|
|
101
|
+
| `cup tag <id> [--add tags] [--remove tags]` | Add/remove tags on a task |
|
|
102
|
+
| `cup link <taskId> <linksTo> [--remove]` | Link/unlink tasks |
|
|
103
|
+
| `cup attach <taskId> <filePath>` | Upload file attachment |
|
|
104
|
+
| `cup delete <id> [--confirm]` | Delete task (DESTRUCTIVE) |
|
|
105
|
+
| `cup duplicate <taskId>` | Duplicate a task |
|
|
106
|
+
| `cup bulk status <status> <taskIds...>` | Bulk update status |
|
|
107
|
+
| `cup bulk assign <taskIds...> [--to userId\|me] [--remove userId\|me]` | Bulk assign/unassign user from tasks |
|
|
108
|
+
| `cup bulk due-date <date\|none\|clear> <taskIds...>` | Bulk set or clear due dates |
|
|
109
|
+
| `cup bulk tag <tagName> <taskIds...> [--add] [--remove]` | Bulk add/remove tag from tasks |
|
|
110
|
+
| `cup checklist view <id>` | View checklists on a task |
|
|
111
|
+
| `cup checklist create <id> <name>` | Create a checklist |
|
|
112
|
+
| `cup checklist delete <checklistId>` | Delete a checklist |
|
|
113
|
+
| `cup checklist add-item <checklistId> <name>` | Add item to checklist |
|
|
114
|
+
| `cup checklist edit-item <checklistId> <itemId> [--name n] [--resolved] [--unresolved] [--assignee id]` | Edit checklist item |
|
|
115
|
+
| `cup checklist delete-item <checklistId> <itemId>` | Delete checklist item |
|
|
116
|
+
| `cup time start <taskId> [-d desc]` | Start timer |
|
|
117
|
+
| `cup time stop` | Stop running timer |
|
|
118
|
+
| `cup time status` | Show running timer |
|
|
119
|
+
| `cup time log <taskId> <duration> [-d desc]` | Log manual entry (e.g. "2h", "30m") |
|
|
120
|
+
| `cup time list [--days n] [--task id] [--all]` | List my recent time entries (--all for team) |
|
|
121
|
+
| `cup time update <timeEntryId> [-d desc] [--duration dur]` | Update time entry |
|
|
122
|
+
| `cup time delete <timeEntryId>` | Delete time entry |
|
|
123
|
+
| `cup goal-create <name> [-d desc] [--color hex]` | Create a goal |
|
|
124
|
+
| `cup goal-update <goalId> [-n name] [-d desc] [--color hex]` | Update a goal |
|
|
125
|
+
| `cup goal-delete <goalId>` | Delete a goal |
|
|
126
|
+
| `cup key-result-create <goalId> <name> [--type t] [--target n]` | Create key result |
|
|
127
|
+
| `cup key-result-update <keyResultId> [--progress n] [--note text]` | Update key result |
|
|
128
|
+
| `cup key-result-delete <keyResultId>` | Delete key result |
|
|
129
|
+
| `cup doc-create <title> [-c content]` | Create a doc |
|
|
130
|
+
| `cup doc-page-create <docId> <name> [-c content] [--parent-page pageId]` | Create doc page |
|
|
131
|
+
| `cup doc-page-edit <docId> <pageId> [--name text] [-c content]` | Edit doc page |
|
|
132
|
+
| `cup doc-delete <docId>` | Delete a doc |
|
|
133
|
+
| `cup doc-page-delete <docId> <pageId>` | Delete doc page |
|
|
134
|
+
| `cup space-create <name>` | Create a space |
|
|
135
|
+
| `cup list-create <spaceId> <name> [--folder folderId] [--copy-statuses-from id]` | Create a list in a space or folder |
|
|
136
|
+
| `cup folder-create <spaceId> <name>` | Create a folder in a space |
|
|
137
|
+
| `cup tag-create <spaceId> <name> [--fg color] [--bg color]` | Create space tag |
|
|
138
|
+
| `cup tag-update <spaceId> <tagName> --name <newName> [--fg c] [--bg c]` | Update space tag |
|
|
139
|
+
| `cup tag-delete <spaceId> <name>` | Delete space tag |
|
|
140
|
+
| `cup list-from-template <name> --template <id> [--space id] [--folder id]` | Create list from template |
|
|
141
|
+
| `cup view-create <listId> <name> -t <type> [--group-by field]` | Create a view on a list |
|
|
142
|
+
| `cup view-update <viewId> [-n name] [--group-by field]` | Update a view |
|
|
143
|
+
| `cup view-delete <viewId> [--confirm]` | Delete a view (DESTRUCTIVE) |
|
|
144
|
+
| `cup profile list [--json]` | List all profiles |
|
|
145
|
+
| `cup profile add <name>` | Add a new profile (interactive) |
|
|
146
|
+
| `cup profile remove <name>` | Remove a profile |
|
|
147
|
+
| `cup profile use <name>` | Set the default profile |
|
|
148
|
+
| `cup config get <key>` / `set <key> <value>` / `path` | Manage config |
|
|
149
|
+
| `cup completion <shell>` | Shell completions (bash/zsh/fish) |
|
|
132
150
|
|
|
133
151
|
## Global Flags
|
|
134
152
|
|