@krodak/clickup-cli 1.4.0 → 1.5.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.
@@ -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.0",
4
+ "version": "1.5.0",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/README.md CHANGED
@@ -134,20 +134,20 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
134
134
 
135
135
  ### Tasks
136
136
 
137
- | Feature | Command | Status |
138
- | -------------------- | -------------------- | ------------------ |
139
- | List my tasks | `cup tasks` | :white_check_mark: |
140
- | Get task details | `cup task <id>` | :white_check_mark: |
141
- | Create task | `cup create` | :white_check_mark: |
142
- | Update task | `cup update <id>` | :white_check_mark: |
143
- | Delete task | `cup delete <id>` | :white_check_mark: |
144
- | Search tasks | `cup search <query>` | :white_check_mark: |
145
- | Open in browser | `cup open <query>` | :white_check_mark: |
146
- | List subtasks | `cup subtasks <id>` | :white_check_mark: |
147
- | Assign / unassign | `cup assign <id>` | :white_check_mark: |
148
- | Duplicate task | `cup duplicate <id>` | :white_check_mark: |
149
- | Create from template | | :no_entry_sign: |
150
- | Bulk operations | `cup bulk status` | :white_check_mark: |
137
+ | Feature | Command | Status |
138
+ | -------------------- | ----------------------- | ------------------ |
139
+ | List my tasks | `cup tasks` | :white_check_mark: |
140
+ | Get task details | `cup task <id>` | :white_check_mark: |
141
+ | Create task | `cup create` | :white_check_mark: |
142
+ | Update task | `cup update <id>` | :white_check_mark: |
143
+ | Delete task | `cup delete <id>` | :white_check_mark: |
144
+ | Search tasks | `cup search <query>` | :white_check_mark: |
145
+ | Open in browser | `cup open <query>` | :white_check_mark: |
146
+ | List subtasks | `cup subtasks <id>` | :white_check_mark: |
147
+ | Assign / unassign | `cup assign <id>` | :white_check_mark: |
148
+ | Duplicate task | `cup duplicate <id>` | :white_check_mark: |
149
+ | Create from template | `cup create --template` | :white_check_mark: |
150
+ | Bulk operations | `cup bulk status` | :white_check_mark: |
151
151
 
152
152
  ### Dependencies & Relations
153
153
 
@@ -212,6 +212,7 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
212
212
  | Add/remove tag on task | `cup tag <id>` | :white_check_mark: |
213
213
  | List space tags | `cup tags <spaceId>` | :white_check_mark: |
214
214
  | Create space tag | `cup tag-create <spaceId> <name>` | :white_check_mark: |
215
+ | Update space tag | `cup tag-update <spaceId> <name>` | :white_check_mark: |
215
216
  | Delete space tag | `cup tag-delete <spaceId> <name>` | :white_check_mark: |
216
217
 
217
218
  ### Time Tracking
@@ -228,13 +229,15 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
228
229
 
229
230
  ### Workspace
230
231
 
231
- | Feature | Command | Status |
232
- | ------------ | ----------------------- | ------------------ |
233
- | List spaces | `cup spaces` | :white_check_mark: |
234
- | List lists | `cup lists <spaceId>` | :white_check_mark: |
235
- | Check auth | `cup auth` | :white_check_mark: |
236
- | List folders | `cup folders <spaceId>` | :white_check_mark: |
237
- | List members | `cup members` | :white_check_mark: |
232
+ | Feature | Command | Status |
233
+ | -------------- | ----------------------- | ------------------ |
234
+ | List spaces | `cup spaces` | :white_check_mark: |
235
+ | List lists | `cup lists <spaceId>` | :white_check_mark: |
236
+ | Check auth | `cup auth` | :white_check_mark: |
237
+ | List folders | `cup folders <spaceId>` | :white_check_mark: |
238
+ | List members | `cup members` | :white_check_mark: |
239
+ | Task types | `cup task-types` | :white_check_mark: |
240
+ | Task templates | `cup templates` | :white_check_mark: |
238
241
 
239
242
  ### Goals & Key Results
240
243
 
@@ -243,9 +246,11 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
243
246
  | List goals | `cup goals` | :white_check_mark: |
244
247
  | Create goal | `cup goal-create <name>` | :white_check_mark: |
245
248
  | Update goal | `cup goal-update <goalId>` | :white_check_mark: |
249
+ | Delete goal | `cup goal-delete <goalId>` | :white_check_mark: |
246
250
  | List key results | `cup key-results <goalId>` | :white_check_mark: |
247
251
  | Create key result | `cup key-result-create <goalId> <n>` | :white_check_mark: |
248
252
  | Update key result | `cup key-result-update <krId>` | :white_check_mark: |
253
+ | Delete key result | `cup key-result-delete <krId>` | :white_check_mark: |
249
254
 
250
255
  ### Docs
251
256
 
@@ -257,6 +262,8 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
257
262
  | Create doc | `cup doc-create <title>` | :white_check_mark: |
258
263
  | Create page | `cup doc-page-create <docId> <name>` | :white_check_mark: |
259
264
  | Edit page | `cup doc-page-edit <docId> <pageId>` | :white_check_mark: |
265
+ | Delete doc | `cup doc-delete <docId>` | :white_check_mark: |
266
+ | Delete page | `cup doc-page-delete <docId> <pId>` | :white_check_mark: |
260
267
 
261
268
  ### Attachments
262
269
 
@@ -267,16 +274,19 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
267
274
 
268
275
  ### :no_entry_sign: Won't add
269
276
 
270
- | Feature | Why |
271
- | --------------------- | --------------------------------------------------------------------------- |
272
- | Create from template | No public API for task templates. |
273
- | Webhooks | Server-side. A CLI can't listen for events. |
274
- | OAuth flow | `cup init` already handles auth with API tokens. |
275
- | Guest/ACL | Enterprise admin. Not what you reach for in a terminal. |
276
- | Chat/DM | Use the ClickUp app. Chat doesn't belong in a CLI. |
277
- | Audit logs | Enterprise admin. |
278
- | User/group management | Too destructive for a CLI - removing someone from a workspace is permanent. |
279
- | View CRUD | Views are visual layouts. Configure them in the UI. |
277
+ | Feature | Why |
278
+ | ----------------------- | --------------------------------------------------------------------------- |
279
+ | Webhooks | Server-side. A CLI can't listen for events. |
280
+ | OAuth flow | `cup init` already handles auth with API tokens. |
281
+ | Guest/ACL | Enterprise admin. Not what you reach for in a terminal. |
282
+ | Chat/DM | Use the ClickUp app. Chat doesn't belong in a CLI. |
283
+ | Audit logs | Enterprise admin. |
284
+ | User/group management | Too destructive for a CLI - removing someone from a workspace is permanent. |
285
+ | View CRUD | Views are visual layouts. Configure them in the UI. |
286
+ | List/Folder/Space CRUD | Structural changes. Set these up in the UI. |
287
+ | View/List/Chat comments | API only supports task-level comments. |
288
+ | User Groups | Enterprise admin feature. |
289
+ | Shared Hierarchy | Enterprise admin feature. |
280
290
 
281
291
  ### API Limitations
282
292
 
package/dist/index.js CHANGED
@@ -34,8 +34,8 @@ var ClickUpClient = class {
34
34
  }
35
35
  return "";
36
36
  }
37
- async request(path, options = {}) {
38
- const res = await fetch(`${BASE_URL}${path}`, {
37
+ async _fetch(baseUrl, path, options = {}) {
38
+ const res = await fetch(`${baseUrl}${path}`, {
39
39
  ...options,
40
40
  signal: AbortSignal.timeout(3e4),
41
41
  headers: {
@@ -57,28 +57,11 @@ var ClickUpClient = class {
57
57
  }
58
58
  return data;
59
59
  }
60
+ async request(path, options = {}) {
61
+ return this._fetch(BASE_URL, path, options);
62
+ }
60
63
  async requestV3(path, options = {}) {
61
- const res = await fetch(`${BASE_URL_V3}${path}`, {
62
- ...options,
63
- signal: AbortSignal.timeout(3e4),
64
- headers: {
65
- Authorization: this.apiToken,
66
- ...options.body ? { "Content-Type": "application/json" } : {},
67
- ...options.headers
68
- }
69
- });
70
- let data;
71
- try {
72
- data = await res.json();
73
- } catch {
74
- throw new Error(`ClickUp API error ${res.status}: response was not valid JSON`);
75
- }
76
- if (!res.ok) {
77
- const raw = data.err ?? data.error ?? data.ECODE ?? res.statusText;
78
- const msg = typeof raw === "string" ? raw : JSON.stringify(raw);
79
- throw new Error(`ClickUp API error ${res.status}: ${msg}`);
80
- }
81
- return data;
64
+ return this._fetch(BASE_URL_V3, path, options);
82
65
  }
83
66
  async getMe() {
84
67
  if (this.meCache) return this.meCache;
@@ -534,6 +517,50 @@ var ClickUpClient = class {
534
517
  });
535
518
  return data.key_result;
536
519
  }
520
+ async deleteGoal(goalId) {
521
+ await this.request(`/goal/${goalId}`, { method: "DELETE" });
522
+ }
523
+ async deleteKeyResult(keyResultId) {
524
+ await this.request(`/key_result/${keyResultId}`, { method: "DELETE" });
525
+ }
526
+ async deleteDoc(workspaceId, docId) {
527
+ await this.requestV3(`/workspaces/${workspaceId}/docs/${docId}`, {
528
+ method: "DELETE"
529
+ });
530
+ }
531
+ async deleteDocPage(workspaceId, docId, pageId) {
532
+ await this.requestV3(
533
+ `/workspaces/${workspaceId}/docs/${docId}/pages/${pageId}`,
534
+ { method: "DELETE" }
535
+ );
536
+ }
537
+ async updateSpaceTag(spaceId, tagName, updates) {
538
+ await this.request(
539
+ `/space/${spaceId}/tag/${encodeURIComponent(tagName)}`,
540
+ {
541
+ method: "PUT",
542
+ body: JSON.stringify({
543
+ tag: {
544
+ name: updates.name,
545
+ tag_fg: updates.tag_fg ?? "#000000",
546
+ tag_bg: updates.tag_bg ?? "#04A9F4"
547
+ }
548
+ })
549
+ }
550
+ );
551
+ }
552
+ async getTaskTemplates(teamId) {
553
+ const data = await this.request(
554
+ `/team/${teamId}/taskTemplate?page=0`
555
+ );
556
+ return data.templates ?? [];
557
+ }
558
+ async createTaskFromTemplate(listId, templateId, name) {
559
+ return this.request(`/list/${listId}/taskTemplate/${templateId}`, {
560
+ method: "POST",
561
+ body: JSON.stringify({ name })
562
+ });
563
+ }
537
564
  };
538
565
 
539
566
  // src/config.ts
@@ -1268,6 +1295,10 @@ async function createTask(config, options) {
1268
1295
  if (!listId) {
1269
1296
  throw new Error("Provide --list or --parent (list is auto-detected from parent task)");
1270
1297
  }
1298
+ if (options.template) {
1299
+ const task2 = await client.createTaskFromTemplate(listId, options.template, options.name);
1300
+ return { id: task2.id, name: task2.name, url: task2.url };
1301
+ }
1271
1302
  const payload = {
1272
1303
  name: options.name,
1273
1304
  ...options.description !== void 0 ? { markdown_content: options.description } : {},
@@ -2182,7 +2213,7 @@ function bashCompletion(name) {
2182
2213
  cword=$COMP_CWORD
2183
2214
  fi
2184
2215
 
2185
- local commands="init auth tasks task update create sprint sprints subtasks comment comment-edit comment-delete comments replies reply activity lists spaces inbox assigned open search summary overdue assign depend link attach move field delete tag tags tag-create tag-delete checklist time docs doc doc-create doc-pages doc-page-create doc-page-edit folders members fields duplicate bulk goals goal-create goal-update key-results key-result-create key-result-update config completion"
2216
+ local commands="init auth tasks task update create sprint sprints subtasks comment comment-edit comment-delete comments replies reply activity lists spaces inbox assigned open search summary overdue assign depend link attach move field delete tag tags tag-create tag-delete tag-update checklist time docs doc doc-create doc-pages doc-page-create doc-page-edit doc-delete doc-page-delete folders members fields duplicate bulk goals goal-create goal-update goal-delete key-results key-result-create key-result-update key-result-delete task-types templates config completion"
2186
2217
 
2187
2218
  if [[ $cword -eq 1 ]]; then
2188
2219
  COMPREPLY=($(compgen -W "$commands --help --version" -- "$cur"))
@@ -2213,7 +2244,7 @@ function bashCompletion(name) {
2213
2244
  COMPREPLY=($(compgen -W "-n --name -d --description -s --status --priority --due-date --time-estimate --assignee --parent --json" -- "$cur"))
2214
2245
  ;;
2215
2246
  create)
2216
- COMPREPLY=($(compgen -W "-l --list -n --name -d --description -p --parent -s --status --priority --due-date --assignee --tags --custom-item-id --time-estimate --json" -- "$cur"))
2247
+ COMPREPLY=($(compgen -W "-l --list -n --name -d --description -p --parent -s --status --priority --due-date --assignee --tags --custom-item-id --time-estimate --template --json" -- "$cur"))
2217
2248
  ;;
2218
2249
  sprint)
2219
2250
  COMPREPLY=($(compgen -W "--status --space --folder --include-closed --json" -- "$cur"))
@@ -2285,7 +2316,7 @@ function bashCompletion(name) {
2285
2316
  ;;
2286
2317
  time)
2287
2318
  if [[ $cword -eq 2 ]]; then
2288
- COMPREPLY=($(compgen -W "start stop status log list" -- "$cur"))
2319
+ COMPREPLY=($(compgen -W "start stop status log list update delete" -- "$cur"))
2289
2320
  fi
2290
2321
  ;;
2291
2322
  comment-edit)
@@ -2347,6 +2378,9 @@ function bashCompletion(name) {
2347
2378
  goal-update)
2348
2379
  COMPREPLY=($(compgen -W "-n --name -d --description --color --json" -- "$cur"))
2349
2380
  ;;
2381
+ goal-delete)
2382
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2383
+ ;;
2350
2384
  key-results)
2351
2385
  COMPREPLY=($(compgen -W "--json" -- "$cur"))
2352
2386
  ;;
@@ -2356,6 +2390,9 @@ function bashCompletion(name) {
2356
2390
  key-result-update)
2357
2391
  COMPREPLY=($(compgen -W "--progress --note --json" -- "$cur"))
2358
2392
  ;;
2393
+ key-result-delete)
2394
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2395
+ ;;
2359
2396
  folders)
2360
2397
  COMPREPLY=($(compgen -W "--name --json" -- "$cur"))
2361
2398
  ;;
@@ -2368,6 +2405,21 @@ function bashCompletion(name) {
2368
2405
  doc-page-edit)
2369
2406
  COMPREPLY=($(compgen -W "--name -c --content --json" -- "$cur"))
2370
2407
  ;;
2408
+ doc-delete)
2409
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2410
+ ;;
2411
+ doc-page-delete)
2412
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2413
+ ;;
2414
+ tag-update)
2415
+ COMPREPLY=($(compgen -W "--name --fg --bg --json" -- "$cur"))
2416
+ ;;
2417
+ task-types)
2418
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2419
+ ;;
2420
+ templates)
2421
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2422
+ ;;
2371
2423
  config)
2372
2424
  if [[ $cword -eq 2 ]]; then
2373
2425
  COMPREPLY=($(compgen -W "get set path" -- "$cur"))
@@ -2444,9 +2496,16 @@ _${name}() {
2444
2496
  'goals:List goals in your workspace'
2445
2497
  'goal-create:Create a goal'
2446
2498
  'goal-update:Update a goal'
2499
+ 'goal-delete:Delete a goal'
2447
2500
  'key-results:List key results for a goal'
2448
2501
  'key-result-create:Create a key result on a goal'
2449
2502
  'key-result-update:Update a key result'
2503
+ 'key-result-delete:Delete a key result'
2504
+ 'doc-delete:Delete a doc'
2505
+ 'doc-page-delete:Delete a doc page'
2506
+ 'tag-update:Update a tag in a space'
2507
+ 'task-types:List custom task types'
2508
+ 'templates:List task templates'
2450
2509
  'folders:List folders in a space'
2451
2510
  'config:Manage CLI configuration'
2452
2511
  'completion:Output shell completion script'
@@ -2505,6 +2564,7 @@ _${name}() {
2505
2564
  '--tags[Comma-separated tag names]:tags:' \\
2506
2565
  '--custom-item-id[Custom task type ID]:id:' \\
2507
2566
  '--time-estimate[Time estimate]:duration:' \\
2567
+ '--template[Create from a task template]:template_id:' \\
2508
2568
  '--json[Force JSON output]'
2509
2569
  ;;
2510
2570
  sprint)
@@ -2693,6 +2753,8 @@ _${name}() {
2693
2753
  'status:Show the currently running timer'
2694
2754
  'log:Log a manual time entry'
2695
2755
  'list:List recent time entries'
2756
+ 'update:Update a time entry'
2757
+ 'delete:Delete a time entry'
2696
2758
  )
2697
2759
  _arguments -C \\
2698
2760
  '1:time command:->time_cmd' \\
@@ -2728,6 +2790,18 @@ _${name}() {
2728
2790
  '--task[Filter by task ID]:task_id:' \\
2729
2791
  '--json[Force JSON output]'
2730
2792
  ;;
2793
+ update)
2794
+ _arguments \\
2795
+ '1:time_entry_id:' \\
2796
+ '(-d --description)'{-d,--description}'[New description]:text:' \\
2797
+ '--duration[New duration]:duration:' \\
2798
+ '--json[Force JSON output]'
2799
+ ;;
2800
+ delete)
2801
+ _arguments \\
2802
+ '1:time_entry_id:' \\
2803
+ '--json[Force JSON output]'
2804
+ ;;
2731
2805
  esac
2732
2806
  ;;
2733
2807
  esac
@@ -2879,6 +2953,44 @@ _${name}() {
2879
2953
  '--note[Progress note]:text:' \\
2880
2954
  '--json[Force JSON output]'
2881
2955
  ;;
2956
+ key-result-delete)
2957
+ _arguments \\
2958
+ '1:key_result_id:' \\
2959
+ '--json[Force JSON output]'
2960
+ ;;
2961
+ goal-delete)
2962
+ _arguments \\
2963
+ '1:goal_id:' \\
2964
+ '--json[Force JSON output]'
2965
+ ;;
2966
+ doc-delete)
2967
+ _arguments \\
2968
+ '1:doc_id:' \\
2969
+ '--json[Force JSON output]'
2970
+ ;;
2971
+ doc-page-delete)
2972
+ _arguments \\
2973
+ '1:doc_id:' \\
2974
+ '2:page_id:' \\
2975
+ '--json[Force JSON output]'
2976
+ ;;
2977
+ tag-update)
2978
+ _arguments \\
2979
+ '1:space_id:' \\
2980
+ '2:tag_name:' \\
2981
+ '--name[New tag name]:text:' \\
2982
+ '--fg[New foreground color]:color:' \\
2983
+ '--bg[New background color]:color:' \\
2984
+ '--json[Force JSON output]'
2985
+ ;;
2986
+ task-types)
2987
+ _arguments \\
2988
+ '--json[Force JSON output]'
2989
+ ;;
2990
+ templates)
2991
+ _arguments \\
2992
+ '--json[Force JSON output]'
2993
+ ;;
2882
2994
  folders)
2883
2995
  _arguments \\
2884
2996
  '1:space_id:' \\
@@ -3000,6 +3112,13 @@ complete -c ${name} -n __fish_use_subcommand -a goal-update -d 'Update a goal'
3000
3112
  complete -c ${name} -n __fish_use_subcommand -a key-results -d 'List key results for a goal'
3001
3113
  complete -c ${name} -n __fish_use_subcommand -a key-result-create -d 'Create a key result on a goal'
3002
3114
  complete -c ${name} -n __fish_use_subcommand -a key-result-update -d 'Update a key result'
3115
+ complete -c ${name} -n __fish_use_subcommand -a goal-delete -d 'Delete a goal'
3116
+ complete -c ${name} -n __fish_use_subcommand -a key-result-delete -d 'Delete a key result'
3117
+ complete -c ${name} -n __fish_use_subcommand -a doc-delete -d 'Delete a doc'
3118
+ complete -c ${name} -n __fish_use_subcommand -a doc-page-delete -d 'Delete a doc page'
3119
+ complete -c ${name} -n __fish_use_subcommand -a tag-update -d 'Update a tag in a space'
3120
+ complete -c ${name} -n __fish_use_subcommand -a task-types -d 'List custom task types'
3121
+ complete -c ${name} -n __fish_use_subcommand -a templates -d 'List task templates'
3003
3122
  complete -c ${name} -n __fish_use_subcommand -a folders -d 'List folders in a space'
3004
3123
  complete -c ${name} -n __fish_use_subcommand -a config -d 'Manage CLI configuration'
3005
3124
  complete -c ${name} -n __fish_use_subcommand -a completion -d 'Output shell completion script'
@@ -3037,6 +3156,7 @@ complete -c ${name} -n '__fish_seen_subcommand_from create' -l assignee -d 'Assi
3037
3156
  complete -c ${name} -n '__fish_seen_subcommand_from create' -l tags -d 'Comma-separated tag names'
3038
3157
  complete -c ${name} -n '__fish_seen_subcommand_from create' -l custom-item-id -d 'Custom task type ID'
3039
3158
  complete -c ${name} -n '__fish_seen_subcommand_from create' -l time-estimate -d 'Time estimate'
3159
+ complete -c ${name} -n '__fish_seen_subcommand_from create' -l template -d 'Create from a task template'
3040
3160
  complete -c ${name} -n '__fish_seen_subcommand_from create' -l json -d 'Force JSON output'
3041
3161
 
3042
3162
  complete -c ${name} -n '__fish_seen_subcommand_from sprint' -l status -d 'Filter by status'
@@ -3124,16 +3244,20 @@ complete -c ${name} -n '__fish_seen_subcommand_from edit-item' -l resolved -d 'M
3124
3244
  complete -c ${name} -n '__fish_seen_subcommand_from edit-item' -l unresolved -d 'Mark item as unresolved'
3125
3245
  complete -c ${name} -n '__fish_seen_subcommand_from edit-item' -l assignee -d 'Assign user by ID'
3126
3246
 
3127
- complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list' -a start -d 'Start tracking time on a task'
3128
- complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list' -a stop -d 'Stop the running timer'
3129
- complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list' -a status -d 'Show the currently running timer'
3130
- complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list' -a log -d 'Log a manual time entry'
3131
- complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list' -a list -d 'List recent time entries'
3132
- complete -c ${name} -n '__fish_seen_subcommand_from start stop status log list; and __fish_seen_subcommand_from time' -l json -d 'Force JSON output'
3247
+ complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list update delete' -a start -d 'Start tracking time on a task'
3248
+ complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list update delete' -a stop -d 'Stop the running timer'
3249
+ complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list update delete' -a status -d 'Show the currently running timer'
3250
+ complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list update delete' -a log -d 'Log a manual time entry'
3251
+ complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list update delete' -a list -d 'List recent time entries'
3252
+ complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list update delete' -a update -d 'Update a time entry'
3253
+ complete -c ${name} -n '__fish_seen_subcommand_from time; and not __fish_seen_subcommand_from start stop status log list update delete' -a delete -d 'Delete a time entry'
3254
+ complete -c ${name} -n '__fish_seen_subcommand_from start stop status log list update delete; and __fish_seen_subcommand_from time' -l json -d 'Force JSON output'
3133
3255
  complete -c ${name} -n '__fish_seen_subcommand_from start; and __fish_seen_subcommand_from time' -s d -l description -d 'Description'
3134
3256
  complete -c ${name} -n '__fish_seen_subcommand_from log; and __fish_seen_subcommand_from time' -s d -l description -d 'Description'
3135
3257
  complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l days -d 'Number of days to look back'
3136
3258
  complete -c ${name} -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l task -d 'Filter by task ID'
3259
+ complete -c ${name} -n '__fish_seen_subcommand_from update; and __fish_seen_subcommand_from time' -s d -l description -d 'New description'
3260
+ complete -c ${name} -n '__fish_seen_subcommand_from update; and __fish_seen_subcommand_from time' -l duration -d 'New duration'
3137
3261
 
3138
3262
  complete -c ${name} -n '__fish_seen_subcommand_from comment-delete' -l json -d 'Force JSON output'
3139
3263
 
@@ -3198,6 +3322,23 @@ complete -c ${name} -n '__fish_seen_subcommand_from key-result-update' -l progre
3198
3322
  complete -c ${name} -n '__fish_seen_subcommand_from key-result-update' -l note -d 'Progress note'
3199
3323
  complete -c ${name} -n '__fish_seen_subcommand_from key-result-update' -l json -d 'Force JSON output'
3200
3324
 
3325
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-delete' -l json -d 'Force JSON output'
3326
+
3327
+ complete -c ${name} -n '__fish_seen_subcommand_from key-result-delete' -l json -d 'Force JSON output'
3328
+
3329
+ complete -c ${name} -n '__fish_seen_subcommand_from doc-delete' -l json -d 'Force JSON output'
3330
+
3331
+ complete -c ${name} -n '__fish_seen_subcommand_from doc-page-delete' -l json -d 'Force JSON output'
3332
+
3333
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-update' -l name -d 'New tag name'
3334
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-update' -l fg -d 'New foreground color'
3335
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-update' -l bg -d 'New background color'
3336
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-update' -l json -d 'Force JSON output'
3337
+
3338
+ complete -c ${name} -n '__fish_seen_subcommand_from task-types' -l json -d 'Force JSON output'
3339
+
3340
+ complete -c ${name} -n '__fish_seen_subcommand_from templates' -l json -d 'Force JSON output'
3341
+
3201
3342
  complete -c ${name} -n '__fish_seen_subcommand_from folders' -l name -d 'Filter by folder name'
3202
3343
  complete -c ${name} -n '__fish_seen_subcommand_from folders' -l json -d 'Force JSON output'
3203
3344
 
@@ -3699,6 +3840,14 @@ async function editDocPage(config, docId, pageId, updates) {
3699
3840
  const client = new ClickUpClient(config);
3700
3841
  return client.editDocPage(config.teamId, docId, pageId, updates);
3701
3842
  }
3843
+ async function deleteDoc(config, docId) {
3844
+ const client = new ClickUpClient(config);
3845
+ await client.deleteDoc(config.teamId, docId);
3846
+ }
3847
+ async function deleteDocPage(config, docId, pageId) {
3848
+ const client = new ClickUpClient(config);
3849
+ await client.deleteDocPage(config.teamId, docId, pageId);
3850
+ }
3702
3851
 
3703
3852
  // src/commands/folders.ts
3704
3853
  import chalk9 from "chalk";
@@ -3826,6 +3975,14 @@ async function deleteSpaceTag(config, spaceId, tagName) {
3826
3975
  const client = new ClickUpClient(config);
3827
3976
  await client.deleteSpaceTag(spaceId, tagName);
3828
3977
  }
3978
+ async function updateSpaceTag(config, spaceId, tagName, updates) {
3979
+ const client = new ClickUpClient(config);
3980
+ await client.updateSpaceTag(spaceId, tagName, {
3981
+ name: updates.name,
3982
+ tag_fg: updates.fg,
3983
+ tag_bg: updates.bg
3984
+ });
3985
+ }
3829
3986
  function formatTags(tags) {
3830
3987
  if (tags.length === 0) return "No tags found";
3831
3988
  return tags.map((t) => chalk11.bold(t.name)).join(", ");
@@ -3874,6 +4031,7 @@ function formatFieldsMarkdown(fields) {
3874
4031
  }
3875
4032
 
3876
4033
  // src/commands/duplicate.ts
4034
+ var PRIORITY_MAP2 = { urgent: 1, high: 2, normal: 3, low: 4 };
3877
4035
  async function duplicateTask(config, taskId) {
3878
4036
  const client = new ClickUpClient(config);
3879
4037
  const task = await client.getTask(taskId);
@@ -3881,7 +4039,7 @@ async function duplicateTask(config, taskId) {
3881
4039
  name: `${task.name} (copy)`,
3882
4040
  description: task.description,
3883
4041
  markdown_content: task.markdown_content,
3884
- priority: task.priority ? Number(task.priority.priority) : void 0,
4042
+ priority: task.priority ? PRIORITY_MAP2[task.priority.priority.toLowerCase()] : void 0,
3885
4043
  tags: task.tags?.map((t) => t.name),
3886
4044
  time_estimate: task.time_estimate ?? void 0
3887
4045
  });
@@ -3895,8 +4053,9 @@ async function bulkUpdateStatus(config, taskIds, status) {
3895
4053
  for (const id of taskIds) {
3896
4054
  try {
3897
4055
  await client.updateTask(id, { status });
3898
- } catch {
3899
- failed.push(id);
4056
+ } catch (err) {
4057
+ const reason = err instanceof Error ? err.message : String(err);
4058
+ failed.push({ id, reason });
3900
4059
  }
3901
4060
  }
3902
4061
  return { updated: taskIds.length - failed.length, failed };
@@ -3916,6 +4075,14 @@ async function updateGoal(config, goalId, updates) {
3916
4075
  const client = new ClickUpClient(config);
3917
4076
  return client.updateGoal(goalId, updates);
3918
4077
  }
4078
+ async function deleteGoal(config, goalId) {
4079
+ const client = new ClickUpClient(config);
4080
+ await client.deleteGoal(goalId);
4081
+ }
4082
+ async function deleteKeyResult(config, keyResultId) {
4083
+ const client = new ClickUpClient(config);
4084
+ await client.deleteKeyResult(keyResultId);
4085
+ }
3919
4086
  async function listKeyResults(config, goalId) {
3920
4087
  const client = new ClickUpClient(config);
3921
4088
  return client.getKeyResults(goalId);
@@ -3962,6 +4129,36 @@ function formatKeyResultsMarkdown(keyResults) {
3962
4129
  }).join("\n");
3963
4130
  }
3964
4131
 
4132
+ // src/commands/task-types.ts
4133
+ import chalk15 from "chalk";
4134
+ async function listTaskTypes(config) {
4135
+ const client = new ClickUpClient(config);
4136
+ return client.getCustomTaskTypes(config.teamId);
4137
+ }
4138
+ function formatTaskTypes(types) {
4139
+ if (types.length === 0) return "No custom task types";
4140
+ return types.map((t) => `${chalk15.bold(t.name)} ${chalk15.dim(`(${t.id})`)}`).join("\n");
4141
+ }
4142
+ function formatTaskTypesMarkdown(types) {
4143
+ if (types.length === 0) return "No custom task types";
4144
+ return types.map((t) => `- **${t.name}** (${t.id})`).join("\n");
4145
+ }
4146
+
4147
+ // src/commands/templates.ts
4148
+ import chalk16 from "chalk";
4149
+ async function listTemplates(config) {
4150
+ const client = new ClickUpClient(config);
4151
+ return client.getTaskTemplates(config.teamId);
4152
+ }
4153
+ function formatTemplates(templates) {
4154
+ if (templates.length === 0) return "No task templates";
4155
+ return templates.map((t) => `${chalk16.bold(t.name)} ${chalk16.dim(`(${t.id})`)}`).join("\n");
4156
+ }
4157
+ function formatTemplatesMarkdown(templates) {
4158
+ if (templates.length === 0) return "No task templates";
4159
+ return templates.map((t) => `- **${t.name}** (${t.id})`).join("\n");
4160
+ }
4161
+
3965
4162
  // src/index.ts
3966
4163
  var require2 = createRequire(import.meta.url);
3967
4164
  var { version } = require2("../package.json");
@@ -4040,7 +4237,7 @@ program.command("update <taskId>").description("Update a task").option("-n, --na
4040
4237
  }
4041
4238
  })
4042
4239
  );
4043
- program.command("create").description("Create a new task").option("-l, --list <listId>", "Target list ID (auto-detected from --parent if omitted)").requiredOption("-n, --name <name>", "Task name").option("-d, --description <text>", "Task description (markdown supported)").option("-p, --parent <taskId>", "Parent task ID (list auto-detected from parent)").option("-s, --status <status>", "Initial status").option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--assignee <userId>", 'Assignee user ID or "me"').option("--tags <tags>", "Comma-separated tag names").option("--custom-item-id <id>", "Custom task type ID (use to create initiatives)").option("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--json", "Force JSON output even in terminal").action(
4240
+ program.command("create").description("Create a new task").option("-l, --list <listId>", "Target list ID (auto-detected from --parent if omitted)").requiredOption("-n, --name <name>", "Task name").option("-d, --description <text>", "Task description (markdown supported)").option("-p, --parent <taskId>", "Parent task ID (list auto-detected from parent)").option("-s, --status <status>", "Initial status").option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--assignee <userId>", 'Assignee user ID or "me"').option("--tags <tags>", "Comma-separated tag names").option("--custom-item-id <id>", "Custom task type ID (use to create initiatives)").option("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--template <id>", "Create from a task template").option("--json", "Force JSON output even in terminal").action(
4044
4241
  wrapAction(async (opts) => {
4045
4242
  const config = loadConfig();
4046
4243
  if (opts.assignee === "me") {
@@ -4617,7 +4814,9 @@ bulkCmd.command("status <status> <taskIds...>").description("Update status of mu
4617
4814
  } else {
4618
4815
  console.log(`Updated ${result.updated} tasks to "${status}"`);
4619
4816
  if (result.failed.length > 0) {
4620
- console.log(`Failed: ${result.failed.join(", ")}`);
4817
+ for (const f of result.failed) {
4818
+ console.log(` Failed ${f.id}: ${f.reason}`);
4819
+ }
4621
4820
  }
4622
4821
  }
4623
4822
  })
@@ -4668,6 +4867,17 @@ program.command("goal-update <goalId>").description("Update a goal").option("-n,
4668
4867
  }
4669
4868
  )
4670
4869
  );
4870
+ program.command("goal-delete <goalId>").description("Delete a goal").option("--json", "Force JSON output even in terminal").action(
4871
+ wrapAction(async (goalId, opts) => {
4872
+ const config = loadConfig();
4873
+ await deleteGoal(config, goalId);
4874
+ if (shouldOutputJson(opts.json ?? false)) {
4875
+ console.log(JSON.stringify({ success: true, goalId }, null, 2));
4876
+ } else {
4877
+ console.log(`Deleted goal ${goalId}`);
4878
+ }
4879
+ })
4880
+ );
4671
4881
  program.command("key-results <goalId>").description("List key results for a goal").option("--json", "Force JSON output even in terminal").action(
4672
4882
  wrapAction(async (goalId, opts) => {
4673
4883
  const config = loadConfig();
@@ -4718,6 +4928,17 @@ program.command("key-result-update <keyResultId>").description("Update a key res
4718
4928
  }
4719
4929
  )
4720
4930
  );
4931
+ program.command("key-result-delete <keyResultId>").description("Delete a key result").option("--json", "Force JSON output even in terminal").action(
4932
+ wrapAction(async (keyResultId, opts) => {
4933
+ const config = loadConfig();
4934
+ await deleteKeyResult(config, keyResultId);
4935
+ if (shouldOutputJson(opts.json ?? false)) {
4936
+ console.log(JSON.stringify({ success: true, keyResultId }, null, 2));
4937
+ } else {
4938
+ console.log(`Deleted key result ${keyResultId}`);
4939
+ }
4940
+ })
4941
+ );
4721
4942
  program.command("docs [query]").description("List workspace docs (optionally filter by name)").option("--json", "Force JSON output even in terminal").action(
4722
4943
  wrapAction(async (query, opts) => {
4723
4944
  const config = loadConfig();
@@ -4821,6 +5042,77 @@ program.command("doc-page-edit <docId> <pageId>").description("Edit a doc page")
4821
5042
  }
4822
5043
  )
4823
5044
  );
5045
+ program.command("doc-delete <docId>").description("Delete a doc").option("--json", "Force JSON output even in terminal").action(
5046
+ wrapAction(async (docId, opts) => {
5047
+ const config = loadConfig();
5048
+ await deleteDoc(config, docId);
5049
+ if (shouldOutputJson(opts.json ?? false)) {
5050
+ console.log(JSON.stringify({ success: true, docId }, null, 2));
5051
+ } else {
5052
+ console.log(`Deleted doc ${docId}`);
5053
+ }
5054
+ })
5055
+ );
5056
+ program.command("doc-page-delete <docId> <pageId>").description("Delete a doc page").option("--json", "Force JSON output even in terminal").action(
5057
+ wrapAction(async (docId, pageId, opts) => {
5058
+ const config = loadConfig();
5059
+ await deleteDocPage(config, docId, pageId);
5060
+ if (shouldOutputJson(opts.json ?? false)) {
5061
+ console.log(JSON.stringify({ success: true, docId, pageId }, null, 2));
5062
+ } else {
5063
+ console.log(`Deleted page ${pageId} from doc ${docId}`);
5064
+ }
5065
+ })
5066
+ );
5067
+ program.command("tag-update <spaceId> <tagName>").description("Update a tag in a space").requiredOption("--name <newName>", "New tag name").option("--fg <color>", "New foreground color (hex)").option("--bg <color>", "New background color (hex)").option("--json", "Force JSON output even in terminal").action(
5068
+ wrapAction(
5069
+ async (spaceId, tagName, opts) => {
5070
+ const config = loadConfig();
5071
+ await updateSpaceTag(config, spaceId, tagName, {
5072
+ name: opts.name,
5073
+ fg: opts.fg,
5074
+ bg: opts.bg
5075
+ });
5076
+ if (shouldOutputJson(opts.json ?? false)) {
5077
+ console.log(
5078
+ JSON.stringify(
5079
+ { success: true, spaceId, oldName: tagName, newName: opts.name },
5080
+ null,
5081
+ 2
5082
+ )
5083
+ );
5084
+ } else {
5085
+ console.log(`Renamed tag "${tagName}" to "${opts.name}" in space ${spaceId}`);
5086
+ }
5087
+ }
5088
+ )
5089
+ );
5090
+ program.command("task-types").description("List custom task types in your workspace").option("--json", "Force JSON output even in terminal").action(
5091
+ wrapAction(async (opts) => {
5092
+ const config = loadConfig();
5093
+ const types = await listTaskTypes(config);
5094
+ if (shouldOutputJson(opts.json ?? false)) {
5095
+ console.log(JSON.stringify(types, null, 2));
5096
+ } else if (isTTY()) {
5097
+ console.log(formatTaskTypes(types));
5098
+ } else {
5099
+ console.log(formatTaskTypesMarkdown(types));
5100
+ }
5101
+ })
5102
+ );
5103
+ program.command("templates").description("List task templates in your workspace").option("--json", "Force JSON output even in terminal").action(
5104
+ wrapAction(async (opts) => {
5105
+ const config = loadConfig();
5106
+ const templates = await listTemplates(config);
5107
+ if (shouldOutputJson(opts.json ?? false)) {
5108
+ console.log(JSON.stringify(templates, null, 2));
5109
+ } else if (isTTY()) {
5110
+ console.log(formatTemplates(templates));
5111
+ } else {
5112
+ console.log(formatTemplatesMarkdown(templates));
5113
+ }
5114
+ })
5115
+ );
4824
5116
  var configCmd = program.command("config").description("Manage CLI configuration");
4825
5117
  configCmd.command("get <key>").description("Print a config value").action(
4826
5118
  wrapAction(async (key) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -61,6 +61,8 @@ All commands support `--help` for full flag details.
61
61
  | `cup docs [query] [--json]` | List workspace docs (optionally filter by name) |
62
62
  | `cup doc <docId> [pageId] [--json]` | View doc metadata + page tree, or a specific page |
63
63
  | `cup doc-pages <docId> [--json]` | All pages in a doc with content |
64
+ | `cup task-types [--json]` | List custom task types in your workspace |
65
+ | `cup templates [--json]` | List task templates in your workspace |
64
66
 
65
67
  ### Write
66
68
 
@@ -105,6 +107,12 @@ All commands support `--help` for full flag details.
105
107
  | `cup goal-update <goalId> [-n name] [-d desc] [--color hex] [--json]` | Update a goal |
106
108
  | `cup key-result-create <goalId> <name> [--type t] [--target n] [--json]` | Create a key result on a goal |
107
109
  | `cup key-result-update <keyResultId> [--progress n] [--note text] [--json]` | Update a key result |
110
+ | `cup goal-delete <goalId> [--json]` | Delete a goal |
111
+ | `cup key-result-delete <keyResultId> [--json]` | Delete a key result |
112
+ | `cup doc-delete <docId> [--json]` | Delete a doc |
113
+ | `cup doc-page-delete <docId> <pageId> [--json]` | Delete a doc page |
114
+ | `cup tag-update <spaceId> <tagName> --name <newName> [--fg c] [--bg c] [--json]` | Update a tag in a space |
115
+ | `cup create -n name -l listId --template <templateId> [--json]` | Create task from a template |
108
116
  | `cup config get <key>` / `cup config set <key> <value>` / `cup config path` | Manage CLI config (keys: apiToken, teamId, sprintFolderId) |
109
117
  | `cup completion <shell>` | Shell completions (bash/zsh/fish) |
110
118
 
@@ -168,6 +176,14 @@ All commands support `--help` for full flag details.
168
176
  | `cup doc-create` | Create a new doc with optional initial content |
169
177
  | `cup doc-page-create` | Create a page in a doc, optionally nested under a parent page |
170
178
  | `cup doc-page-edit` | Edit a doc page name or content |
179
+ | `cup doc-delete` | Delete a doc |
180
+ | `cup doc-page-delete` | Delete a doc page |
181
+ | `cup goal-delete` | Delete a goal |
182
+ | `cup key-result-delete` | Delete a key result |
183
+ | `cup tag-update` | Rename a tag in a space, optionally update colors |
184
+ | `cup task-types` | List custom task types (useful for discovering `--custom-item-id` values) |
185
+ | `cup templates` | List task templates (useful for `cup create --template`) |
186
+ | `--template` | On `cup create`, create task from a template instead of from scratch |
171
187
  | `cup task` | Shows custom fields, checklists, attachments, dependencies, and linked tasks in detail view |
172
188
  | `cup lists` | Discovers list IDs needed for `--list` and `cup create -l` |
173
189
  | Errors | stderr with exit code 1 |
@@ -276,9 +292,27 @@ cup config set sprintFolderId <id> # pin sprint detection to a folder
276
292
  cup duplicate abc123 # duplicate a task
277
293
  cup bulk status "done" t1 t2 t3 # mark multiple tasks done
278
294
  cup tag-create <spaceId> "urgent" # create a space tag
295
+ cup tag-update <spaceId> "urgent" --name "critical" # rename a tag
279
296
  cup tag-delete <spaceId> "old-tag" # delete a space tag
280
297
  ```
281
298
 
299
+ ### Templates and task types
300
+
301
+ ```bash
302
+ cup task-types # list custom task types
303
+ cup templates # list task templates
304
+ cup create -n "Bug" -l <listId> --template <templateId> # create from template
305
+ ```
306
+
307
+ ### Delete operations
308
+
309
+ ```bash
310
+ cup goal-delete <goalId> # delete a goal
311
+ cup key-result-delete <krId> # delete a key result
312
+ cup doc-delete <docId> # delete a doc
313
+ cup doc-page-delete <docId> <pageId> # delete a doc page
314
+ ```
315
+
282
316
  ### Goals and key results
283
317
 
284
318
  ```bash