@krodak/clickup-cli 1.3.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.
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;
@@ -382,6 +365,25 @@ var ClickUpClient = class {
382
365
  const data = await this.request(`/space/${spaceId}/tag`);
383
366
  return data.tags ?? [];
384
367
  }
368
+ async createSpaceTag(spaceId, name, fg, bg) {
369
+ await this.request(`/space/${spaceId}/tag`, {
370
+ method: "POST",
371
+ body: JSON.stringify({
372
+ tag: { name, tag_fg: fg ?? "#000000", tag_bg: bg ?? "#04A9F4" }
373
+ })
374
+ });
375
+ }
376
+ async deleteSpaceTag(spaceId, tagName) {
377
+ await this.request(
378
+ `/space/${spaceId}/tag/${encodeURIComponent(tagName)}`,
379
+ { method: "DELETE" }
380
+ );
381
+ }
382
+ async getWorkspaceMembers(teamId) {
383
+ const data = await this.request("/team");
384
+ const team = data.teams?.find((t) => t.id === teamId);
385
+ return team?.members?.map((m) => m.user) ?? [];
386
+ }
385
387
  async deleteTimeEntry(teamId, timeEntryId) {
386
388
  await this.request(`/team/${teamId}/time_entries/${timeEntryId}`, {
387
389
  method: "DELETE"
@@ -469,6 +471,96 @@ var ClickUpClient = class {
469
471
  );
470
472
  return data.pages ?? [];
471
473
  }
474
+ async getGoals(teamId) {
475
+ const data = await this.request(`/team/${teamId}/goal`);
476
+ return data.goals ?? [];
477
+ }
478
+ async createGoal(teamId, name, opts) {
479
+ const body = { name, multiple_owners: true };
480
+ if (opts?.description) body.description = opts.description;
481
+ if (opts?.dueDate) body.due_date = Number(opts.dueDate);
482
+ if (opts?.color) body.color = opts.color;
483
+ const data = await this.request(`/team/${teamId}/goal`, {
484
+ method: "POST",
485
+ body: JSON.stringify(body)
486
+ });
487
+ return data.goal;
488
+ }
489
+ async updateGoal(goalId, updates) {
490
+ const data = await this.request(`/goal/${goalId}`, {
491
+ method: "PUT",
492
+ body: JSON.stringify(updates)
493
+ });
494
+ return data.goal;
495
+ }
496
+ async getKeyResults(goalId) {
497
+ const data = await this.request(`/goal/${goalId}`);
498
+ return data.goal?.key_results ?? [];
499
+ }
500
+ async createKeyResult(goalId, name, type, stepsEnd) {
501
+ const data = await this.request(`/goal/${goalId}/key_result`, {
502
+ method: "POST",
503
+ body: JSON.stringify({
504
+ name,
505
+ type,
506
+ steps_start: 0,
507
+ steps_end: stepsEnd,
508
+ unit: type === "number" ? "items" : "%"
509
+ })
510
+ });
511
+ return data.key_result;
512
+ }
513
+ async updateKeyResult(keyResultId, updates) {
514
+ const data = await this.request(`/key_result/${keyResultId}`, {
515
+ method: "PUT",
516
+ body: JSON.stringify(updates)
517
+ });
518
+ return data.key_result;
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
+ }
472
564
  };
473
565
 
474
566
  // src/config.ts
@@ -1203,6 +1295,10 @@ async function createTask(config, options) {
1203
1295
  if (!listId) {
1204
1296
  throw new Error("Provide --list or --parent (list is auto-detected from parent task)");
1205
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
+ }
1206
1302
  const payload = {
1207
1303
  name: options.name,
1208
1304
  ...options.description !== void 0 ? { markdown_content: options.description } : {},
@@ -2117,7 +2213,7 @@ function bashCompletion(name) {
2117
2213
  cword=$COMP_CWORD
2118
2214
  fi
2119
2215
 
2120
- 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 checklist time docs doc doc-create doc-pages doc-page-create doc-page-edit folders 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"
2121
2217
 
2122
2218
  if [[ $cword -eq 1 ]]; then
2123
2219
  COMPREPLY=($(compgen -W "$commands --help --version" -- "$cur"))
@@ -2148,7 +2244,7 @@ function bashCompletion(name) {
2148
2244
  COMPREPLY=($(compgen -W "-n --name -d --description -s --status --priority --due-date --time-estimate --assignee --parent --json" -- "$cur"))
2149
2245
  ;;
2150
2246
  create)
2151
- 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"))
2152
2248
  ;;
2153
2249
  sprint)
2154
2250
  COMPREPLY=($(compgen -W "--status --space --folder --include-closed --json" -- "$cur"))
@@ -2220,7 +2316,7 @@ function bashCompletion(name) {
2220
2316
  ;;
2221
2317
  time)
2222
2318
  if [[ $cword -eq 2 ]]; then
2223
- COMPREPLY=($(compgen -W "start stop status log list" -- "$cur"))
2319
+ COMPREPLY=($(compgen -W "start stop status log list update delete" -- "$cur"))
2224
2320
  fi
2225
2321
  ;;
2226
2322
  comment-edit)
@@ -2253,6 +2349,50 @@ function bashCompletion(name) {
2253
2349
  tags)
2254
2350
  COMPREPLY=($(compgen -W "--json" -- "$cur"))
2255
2351
  ;;
2352
+ tag-create)
2353
+ COMPREPLY=($(compgen -W "--fg --bg --json" -- "$cur"))
2354
+ ;;
2355
+ tag-delete)
2356
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2357
+ ;;
2358
+ members)
2359
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2360
+ ;;
2361
+ fields)
2362
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2363
+ ;;
2364
+ duplicate)
2365
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2366
+ ;;
2367
+ bulk)
2368
+ if [[ $cword -eq 2 ]]; then
2369
+ COMPREPLY=($(compgen -W "status" -- "$cur"))
2370
+ fi
2371
+ ;;
2372
+ goals)
2373
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2374
+ ;;
2375
+ goal-create)
2376
+ COMPREPLY=($(compgen -W "-d --description --color --json" -- "$cur"))
2377
+ ;;
2378
+ goal-update)
2379
+ COMPREPLY=($(compgen -W "-n --name -d --description --color --json" -- "$cur"))
2380
+ ;;
2381
+ goal-delete)
2382
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2383
+ ;;
2384
+ key-results)
2385
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2386
+ ;;
2387
+ key-result-create)
2388
+ COMPREPLY=($(compgen -W "--type --target --json" -- "$cur"))
2389
+ ;;
2390
+ key-result-update)
2391
+ COMPREPLY=($(compgen -W "--progress --note --json" -- "$cur"))
2392
+ ;;
2393
+ key-result-delete)
2394
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
2395
+ ;;
2256
2396
  folders)
2257
2397
  COMPREPLY=($(compgen -W "--name --json" -- "$cur"))
2258
2398
  ;;
@@ -2265,6 +2405,21 @@ function bashCompletion(name) {
2265
2405
  doc-page-edit)
2266
2406
  COMPREPLY=($(compgen -W "--name -c --content --json" -- "$cur"))
2267
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
+ ;;
2268
2423
  config)
2269
2424
  if [[ $cword -eq 2 ]]; then
2270
2425
  COMPREPLY=($(compgen -W "get set path" -- "$cur"))
@@ -2332,6 +2487,25 @@ _${name}() {
2332
2487
  'doc-page-create:Create a page in a doc'
2333
2488
  'doc-page-edit:Edit a doc page'
2334
2489
  'tags:List tags in a space'
2490
+ 'tag-create:Create a tag in a space'
2491
+ 'tag-delete:Delete a tag from a space'
2492
+ 'members:List workspace members'
2493
+ 'fields:List custom fields for a list'
2494
+ 'duplicate:Duplicate a task'
2495
+ 'bulk:Bulk task operations'
2496
+ 'goals:List goals in your workspace'
2497
+ 'goal-create:Create a goal'
2498
+ 'goal-update:Update a goal'
2499
+ 'goal-delete:Delete a goal'
2500
+ 'key-results:List key results for a goal'
2501
+ 'key-result-create:Create a key result on a goal'
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'
2335
2509
  'folders:List folders in a space'
2336
2510
  'config:Manage CLI configuration'
2337
2511
  'completion:Output shell completion script'
@@ -2390,6 +2564,7 @@ _${name}() {
2390
2564
  '--tags[Comma-separated tag names]:tags:' \\
2391
2565
  '--custom-item-id[Custom task type ID]:id:' \\
2392
2566
  '--time-estimate[Time estimate]:duration:' \\
2567
+ '--template[Create from a task template]:template_id:' \\
2393
2568
  '--json[Force JSON output]'
2394
2569
  ;;
2395
2570
  sprint)
@@ -2578,6 +2753,8 @@ _${name}() {
2578
2753
  'status:Show the currently running timer'
2579
2754
  'log:Log a manual time entry'
2580
2755
  'list:List recent time entries'
2756
+ 'update:Update a time entry'
2757
+ 'delete:Delete a time entry'
2581
2758
  )
2582
2759
  _arguments -C \\
2583
2760
  '1:time command:->time_cmd' \\
@@ -2613,6 +2790,18 @@ _${name}() {
2613
2790
  '--task[Filter by task ID]:task_id:' \\
2614
2791
  '--json[Force JSON output]'
2615
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
+ ;;
2616
2805
  esac
2617
2806
  ;;
2618
2807
  esac
@@ -2676,6 +2865,132 @@ _${name}() {
2676
2865
  '1:space_id:' \\
2677
2866
  '--json[Force JSON output]'
2678
2867
  ;;
2868
+ tag-create)
2869
+ _arguments \\
2870
+ '1:space_id:' \\
2871
+ '2:name:' \\
2872
+ '--fg[Foreground color]:color:' \\
2873
+ '--bg[Background color]:color:' \\
2874
+ '--json[Force JSON output]'
2875
+ ;;
2876
+ tag-delete)
2877
+ _arguments \\
2878
+ '1:space_id:' \\
2879
+ '2:name:' \\
2880
+ '--json[Force JSON output]'
2881
+ ;;
2882
+ members)
2883
+ _arguments \\
2884
+ '--json[Force JSON output]'
2885
+ ;;
2886
+ fields)
2887
+ _arguments \\
2888
+ '1:list_id:' \\
2889
+ '--json[Force JSON output]'
2890
+ ;;
2891
+ duplicate)
2892
+ _arguments \\
2893
+ '1:task_id:' \\
2894
+ '--json[Force JSON output]'
2895
+ ;;
2896
+ bulk)
2897
+ local -a bulk_cmds
2898
+ bulk_cmds=(
2899
+ 'status:Update status of multiple tasks'
2900
+ )
2901
+ _arguments -C \\
2902
+ '1:bulk command:->bulk_cmd' \\
2903
+ '*::bulk_arg:->bulk_args'
2904
+ case $state in
2905
+ bulk_cmd)
2906
+ _describe 'bulk command' bulk_cmds
2907
+ ;;
2908
+ bulk_args)
2909
+ case $words[1] in
2910
+ status)
2911
+ _arguments '1:status:' '*:task_ids:' '--json[Force JSON output]'
2912
+ ;;
2913
+ esac
2914
+ ;;
2915
+ esac
2916
+ ;;
2917
+ goals)
2918
+ _arguments \\
2919
+ '--json[Force JSON output]'
2920
+ ;;
2921
+ goal-create)
2922
+ _arguments \\
2923
+ '1:name:' \\
2924
+ '(-d --description)'{-d,--description}'[Goal description]:text:' \\
2925
+ '--color[Goal color]:color:' \\
2926
+ '--json[Force JSON output]'
2927
+ ;;
2928
+ goal-update)
2929
+ _arguments \\
2930
+ '1:goal_id:' \\
2931
+ '(-n --name)'{-n,--name}'[New goal name]:text:' \\
2932
+ '(-d --description)'{-d,--description}'[New description]:text:' \\
2933
+ '--color[New color]:color:' \\
2934
+ '--json[Force JSON output]'
2935
+ ;;
2936
+ key-results)
2937
+ _arguments \\
2938
+ '1:goal_id:' \\
2939
+ '--json[Force JSON output]'
2940
+ ;;
2941
+ key-result-create)
2942
+ _arguments \\
2943
+ '1:goal_id:' \\
2944
+ '2:name:' \\
2945
+ '--type[Key result type]:type:(number percentage)' \\
2946
+ '--target[Target value]:number:' \\
2947
+ '--json[Force JSON output]'
2948
+ ;;
2949
+ key-result-update)
2950
+ _arguments \\
2951
+ '1:key_result_id:' \\
2952
+ '--progress[Current progress]:number:' \\
2953
+ '--note[Progress note]:text:' \\
2954
+ '--json[Force JSON output]'
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
+ ;;
2679
2994
  folders)
2680
2995
  _arguments \\
2681
2996
  '1:space_id:' \\
@@ -2785,6 +3100,25 @@ complete -c ${name} -n __fish_use_subcommand -a doc-pages -d 'List all pages in
2785
3100
  complete -c ${name} -n __fish_use_subcommand -a doc-page-create -d 'Create a page in a doc'
2786
3101
  complete -c ${name} -n __fish_use_subcommand -a doc-page-edit -d 'Edit a doc page'
2787
3102
  complete -c ${name} -n __fish_use_subcommand -a tags -d 'List tags in a space'
3103
+ complete -c ${name} -n __fish_use_subcommand -a tag-create -d 'Create a tag in a space'
3104
+ complete -c ${name} -n __fish_use_subcommand -a tag-delete -d 'Delete a tag from a space'
3105
+ complete -c ${name} -n __fish_use_subcommand -a members -d 'List workspace members'
3106
+ complete -c ${name} -n __fish_use_subcommand -a fields -d 'List custom fields for a list'
3107
+ complete -c ${name} -n __fish_use_subcommand -a duplicate -d 'Duplicate a task'
3108
+ complete -c ${name} -n __fish_use_subcommand -a bulk -d 'Bulk task operations'
3109
+ complete -c ${name} -n __fish_use_subcommand -a goals -d 'List goals in your workspace'
3110
+ complete -c ${name} -n __fish_use_subcommand -a goal-create -d 'Create a goal'
3111
+ complete -c ${name} -n __fish_use_subcommand -a goal-update -d 'Update a goal'
3112
+ complete -c ${name} -n __fish_use_subcommand -a key-results -d 'List key results for a goal'
3113
+ complete -c ${name} -n __fish_use_subcommand -a key-result-create -d 'Create a key result on a goal'
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'
2788
3122
  complete -c ${name} -n __fish_use_subcommand -a folders -d 'List folders in a space'
2789
3123
  complete -c ${name} -n __fish_use_subcommand -a config -d 'Manage CLI configuration'
2790
3124
  complete -c ${name} -n __fish_use_subcommand -a completion -d 'Output shell completion script'
@@ -2822,6 +3156,7 @@ complete -c ${name} -n '__fish_seen_subcommand_from create' -l assignee -d 'Assi
2822
3156
  complete -c ${name} -n '__fish_seen_subcommand_from create' -l tags -d 'Comma-separated tag names'
2823
3157
  complete -c ${name} -n '__fish_seen_subcommand_from create' -l custom-item-id -d 'Custom task type ID'
2824
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'
2825
3160
  complete -c ${name} -n '__fish_seen_subcommand_from create' -l json -d 'Force JSON output'
2826
3161
 
2827
3162
  complete -c ${name} -n '__fish_seen_subcommand_from sprint' -l status -d 'Filter by status'
@@ -2909,16 +3244,20 @@ complete -c ${name} -n '__fish_seen_subcommand_from edit-item' -l resolved -d 'M
2909
3244
  complete -c ${name} -n '__fish_seen_subcommand_from edit-item' -l unresolved -d 'Mark item as unresolved'
2910
3245
  complete -c ${name} -n '__fish_seen_subcommand_from edit-item' -l assignee -d 'Assign user by ID'
2911
3246
 
2912
- 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'
2913
- 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'
2914
- 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'
2915
- 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'
2916
- 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'
2917
- 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'
2918
3255
  complete -c ${name} -n '__fish_seen_subcommand_from start; and __fish_seen_subcommand_from time' -s d -l description -d 'Description'
2919
3256
  complete -c ${name} -n '__fish_seen_subcommand_from log; and __fish_seen_subcommand_from time' -s d -l description -d 'Description'
2920
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'
2921
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'
2922
3261
 
2923
3262
  complete -c ${name} -n '__fish_seen_subcommand_from comment-delete' -l json -d 'Force JSON output'
2924
3263
 
@@ -2947,6 +3286,59 @@ complete -c ${name} -n '__fish_seen_subcommand_from doc-pages' -l json -d 'Force
2947
3286
 
2948
3287
  complete -c ${name} -n '__fish_seen_subcommand_from tags' -l json -d 'Force JSON output'
2949
3288
 
3289
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-create' -l fg -d 'Foreground color'
3290
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-create' -l bg -d 'Background color'
3291
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-create' -l json -d 'Force JSON output'
3292
+
3293
+ complete -c ${name} -n '__fish_seen_subcommand_from tag-delete' -l json -d 'Force JSON output'
3294
+
3295
+ complete -c ${name} -n '__fish_seen_subcommand_from members' -l json -d 'Force JSON output'
3296
+
3297
+ complete -c ${name} -n '__fish_seen_subcommand_from fields' -l json -d 'Force JSON output'
3298
+
3299
+ complete -c ${name} -n '__fish_seen_subcommand_from duplicate' -l json -d 'Force JSON output'
3300
+
3301
+ complete -c ${name} -n '__fish_seen_subcommand_from bulk; and not __fish_seen_subcommand_from status' -a status -d 'Update status of multiple tasks'
3302
+ complete -c ${name} -n '__fish_seen_subcommand_from status; and __fish_seen_subcommand_from bulk' -l json -d 'Force JSON output'
3303
+
3304
+ complete -c ${name} -n '__fish_seen_subcommand_from goals' -l json -d 'Force JSON output'
3305
+
3306
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-create' -s d -l description -d 'Goal description'
3307
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-create' -l color -d 'Goal color'
3308
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-create' -l json -d 'Force JSON output'
3309
+
3310
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-update' -s n -l name -d 'New goal name'
3311
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-update' -s d -l description -d 'New description'
3312
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-update' -l color -d 'New color'
3313
+ complete -c ${name} -n '__fish_seen_subcommand_from goal-update' -l json -d 'Force JSON output'
3314
+
3315
+ complete -c ${name} -n '__fish_seen_subcommand_from key-results' -l json -d 'Force JSON output'
3316
+
3317
+ complete -c ${name} -n '__fish_seen_subcommand_from key-result-create' -l type -d 'Key result type' -a 'number percentage'
3318
+ complete -c ${name} -n '__fish_seen_subcommand_from key-result-create' -l target -d 'Target value'
3319
+ complete -c ${name} -n '__fish_seen_subcommand_from key-result-create' -l json -d 'Force JSON output'
3320
+
3321
+ complete -c ${name} -n '__fish_seen_subcommand_from key-result-update' -l progress -d 'Current progress'
3322
+ complete -c ${name} -n '__fish_seen_subcommand_from key-result-update' -l note -d 'Progress note'
3323
+ complete -c ${name} -n '__fish_seen_subcommand_from key-result-update' -l json -d 'Force JSON output'
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
+
2950
3342
  complete -c ${name} -n '__fish_seen_subcommand_from folders' -l name -d 'Filter by folder name'
2951
3343
  complete -c ${name} -n '__fish_seen_subcommand_from folders' -l json -d 'Force JSON output'
2952
3344
 
@@ -3448,6 +3840,14 @@ async function editDocPage(config, docId, pageId, updates) {
3448
3840
  const client = new ClickUpClient(config);
3449
3841
  return client.editDocPage(config.teamId, docId, pageId, updates);
3450
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
+ }
3451
3851
 
3452
3852
  // src/commands/folders.ts
3453
3853
  import chalk9 from "chalk";
@@ -3567,6 +3967,22 @@ async function listSpaceTags(config, spaceId) {
3567
3967
  const client = new ClickUpClient(config);
3568
3968
  return client.getSpaceTags(spaceId);
3569
3969
  }
3970
+ async function createSpaceTag(config, spaceId, name, fg, bg) {
3971
+ const client = new ClickUpClient(config);
3972
+ await client.createSpaceTag(spaceId, name, fg, bg);
3973
+ }
3974
+ async function deleteSpaceTag(config, spaceId, tagName) {
3975
+ const client = new ClickUpClient(config);
3976
+ await client.deleteSpaceTag(spaceId, tagName);
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
+ }
3570
3986
  function formatTags(tags) {
3571
3987
  if (tags.length === 0) return "No tags found";
3572
3988
  return tags.map((t) => chalk11.bold(t.name)).join(", ");
@@ -3576,6 +3992,173 @@ function formatTagsMarkdown(tags) {
3576
3992
  return tags.map((t) => `- ${t.name}`).join("\n");
3577
3993
  }
3578
3994
 
3995
+ // src/commands/members.ts
3996
+ import chalk12 from "chalk";
3997
+ async function listMembers(config) {
3998
+ const client = new ClickUpClient(config);
3999
+ return client.getWorkspaceMembers(config.teamId);
4000
+ }
4001
+ function formatMembers(members) {
4002
+ if (members.length === 0) return "No members found";
4003
+ return members.map((m) => `${chalk12.bold(m.username)} ${chalk12.dim(`(${m.id})`)} ${m.email}`).join("\n");
4004
+ }
4005
+ function formatMembersMarkdown(members) {
4006
+ if (members.length === 0) return "No members found";
4007
+ return members.map((m) => `- **${m.username}** (${m.id}) - ${m.email}`).join("\n");
4008
+ }
4009
+
4010
+ // src/commands/fields.ts
4011
+ import chalk13 from "chalk";
4012
+ async function listFields(config, listId) {
4013
+ const client = new ClickUpClient(config);
4014
+ return client.getListCustomFields(listId);
4015
+ }
4016
+ function formatFields(fields) {
4017
+ if (fields.length === 0) return "No custom fields";
4018
+ return fields.map((f) => {
4019
+ const options = f.type_config?.options?.map((o) => o.name).join(", ");
4020
+ const optStr = options ? ` ${chalk13.dim(`[${options}]`)}` : "";
4021
+ return `${chalk13.bold(f.name)} ${chalk13.dim(f.type)}${f.required ? chalk13.yellow(" (required)") : ""}${optStr}`;
4022
+ }).join("\n");
4023
+ }
4024
+ function formatFieldsMarkdown(fields) {
4025
+ if (fields.length === 0) return "No custom fields";
4026
+ return fields.map((f) => {
4027
+ const options = f.type_config?.options?.map((o) => o.name).join(", ");
4028
+ const optStr = options ? ` [${options}]` : "";
4029
+ return `- **${f.name}** (${f.type})${f.required ? " - required" : ""}${optStr}`;
4030
+ }).join("\n");
4031
+ }
4032
+
4033
+ // src/commands/duplicate.ts
4034
+ var PRIORITY_MAP2 = { urgent: 1, high: 2, normal: 3, low: 4 };
4035
+ async function duplicateTask(config, taskId) {
4036
+ const client = new ClickUpClient(config);
4037
+ const task = await client.getTask(taskId);
4038
+ const created = await client.createTask(task.list.id, {
4039
+ name: `${task.name} (copy)`,
4040
+ description: task.description,
4041
+ markdown_content: task.markdown_content,
4042
+ priority: task.priority ? PRIORITY_MAP2[task.priority.priority.toLowerCase()] : void 0,
4043
+ tags: task.tags?.map((t) => t.name),
4044
+ time_estimate: task.time_estimate ?? void 0
4045
+ });
4046
+ return { id: created.id, name: created.name, url: created.url };
4047
+ }
4048
+
4049
+ // src/commands/bulk.ts
4050
+ async function bulkUpdateStatus(config, taskIds, status) {
4051
+ const client = new ClickUpClient(config);
4052
+ const failed = [];
4053
+ for (const id of taskIds) {
4054
+ try {
4055
+ await client.updateTask(id, { status });
4056
+ } catch (err) {
4057
+ const reason = err instanceof Error ? err.message : String(err);
4058
+ failed.push({ id, reason });
4059
+ }
4060
+ }
4061
+ return { updated: taskIds.length - failed.length, failed };
4062
+ }
4063
+
4064
+ // src/commands/goals.ts
4065
+ import chalk14 from "chalk";
4066
+ async function listGoals(config) {
4067
+ const client = new ClickUpClient(config);
4068
+ return client.getGoals(config.teamId);
4069
+ }
4070
+ async function createGoal(config, name, opts) {
4071
+ const client = new ClickUpClient(config);
4072
+ return client.createGoal(config.teamId, name, opts);
4073
+ }
4074
+ async function updateGoal(config, goalId, updates) {
4075
+ const client = new ClickUpClient(config);
4076
+ return client.updateGoal(goalId, updates);
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
+ }
4086
+ async function listKeyResults(config, goalId) {
4087
+ const client = new ClickUpClient(config);
4088
+ return client.getKeyResults(goalId);
4089
+ }
4090
+ async function createKeyResult(config, goalId, name, type, target) {
4091
+ const client = new ClickUpClient(config);
4092
+ return client.createKeyResult(goalId, name, type, target);
4093
+ }
4094
+ async function updateKeyResult(config, keyResultId, updates) {
4095
+ const client = new ClickUpClient(config);
4096
+ return client.updateKeyResult(keyResultId, {
4097
+ steps_current: updates.progress,
4098
+ note: updates.note
4099
+ });
4100
+ }
4101
+ function formatGoals(goals) {
4102
+ if (goals.length === 0) return "No goals found";
4103
+ return goals.map((g) => {
4104
+ const pct = Math.round(g.percent_completed * 100);
4105
+ const owner = g.owner ? ` ${chalk14.dim(`@${g.owner.username}`)}` : "";
4106
+ return `${chalk14.bold(g.name)} ${chalk14.dim(`(${g.id})`)} ${chalk14.cyan(`${pct}%`)}${owner}`;
4107
+ }).join("\n");
4108
+ }
4109
+ function formatGoalsMarkdown(goals) {
4110
+ if (goals.length === 0) return "No goals found";
4111
+ return goals.map((g) => {
4112
+ const pct = Math.round(g.percent_completed * 100);
4113
+ const owner = g.owner ? ` - @${g.owner.username}` : "";
4114
+ return `- **${g.name}** (${g.id}) - ${pct}%${owner}`;
4115
+ }).join("\n");
4116
+ }
4117
+ function formatKeyResults(keyResults) {
4118
+ if (keyResults.length === 0) return "No key results found";
4119
+ return keyResults.map((kr) => {
4120
+ const pct = Math.round(kr.percent_completed * 100);
4121
+ return `${chalk14.bold(kr.name)} ${chalk14.dim(`(${kr.id})`)} ${chalk14.cyan(`${kr.steps_current}/${kr.steps_end}`)} ${chalk14.dim(`${pct}%`)}`;
4122
+ }).join("\n");
4123
+ }
4124
+ function formatKeyResultsMarkdown(keyResults) {
4125
+ if (keyResults.length === 0) return "No key results found";
4126
+ return keyResults.map((kr) => {
4127
+ const pct = Math.round(kr.percent_completed * 100);
4128
+ return `- **${kr.name}** (${kr.id}) - ${kr.steps_current}/${kr.steps_end} (${pct}%)`;
4129
+ }).join("\n");
4130
+ }
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
+
3579
4162
  // src/index.ts
3580
4163
  var require2 = createRequire(import.meta.url);
3581
4164
  var { version } = require2("../package.json");
@@ -3654,7 +4237,7 @@ program.command("update <taskId>").description("Update a task").option("-n, --na
3654
4237
  }
3655
4238
  })
3656
4239
  );
3657
- 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(
3658
4241
  wrapAction(async (opts) => {
3659
4242
  const config = loadConfig();
3660
4243
  if (opts.assignee === "me") {
@@ -4160,6 +4743,202 @@ program.command("tags <spaceId>").description("List tags in a space").option("--
4160
4743
  }
4161
4744
  })
4162
4745
  );
4746
+ program.command("tag-create <spaceId> <name>").description("Create a tag in a space").option("--fg <color>", "Foreground color (hex)").option("--bg <color>", "Background color (hex)").option("--json", "Force JSON output even in terminal").action(
4747
+ wrapAction(
4748
+ async (spaceId, name, opts) => {
4749
+ const config = loadConfig();
4750
+ await createSpaceTag(config, spaceId, name, opts.fg, opts.bg);
4751
+ if (shouldOutputJson(opts.json ?? false)) {
4752
+ console.log(JSON.stringify({ success: true, spaceId, tag: name }, null, 2));
4753
+ } else {
4754
+ console.log(`Created tag "${name}" in space ${spaceId}`);
4755
+ }
4756
+ }
4757
+ )
4758
+ );
4759
+ program.command("tag-delete <spaceId> <name>").description("Delete a tag from a space").option("--json", "Force JSON output even in terminal").action(
4760
+ wrapAction(async (spaceId, name, opts) => {
4761
+ const config = loadConfig();
4762
+ await deleteSpaceTag(config, spaceId, name);
4763
+ if (shouldOutputJson(opts.json ?? false)) {
4764
+ console.log(JSON.stringify({ success: true, spaceId, tag: name }, null, 2));
4765
+ } else {
4766
+ console.log(`Deleted tag "${name}" from space ${spaceId}`);
4767
+ }
4768
+ })
4769
+ );
4770
+ program.command("members").description("List workspace members").option("--json", "Force JSON output even in terminal").action(
4771
+ wrapAction(async (opts) => {
4772
+ const config = loadConfig();
4773
+ const members = await listMembers(config);
4774
+ if (shouldOutputJson(opts.json ?? false)) {
4775
+ console.log(JSON.stringify(members, null, 2));
4776
+ } else if (isTTY()) {
4777
+ console.log(formatMembers(members));
4778
+ } else {
4779
+ console.log(formatMembersMarkdown(members));
4780
+ }
4781
+ })
4782
+ );
4783
+ program.command("fields <listId>").description("List custom fields for a list").option("--json", "Force JSON output even in terminal").action(
4784
+ wrapAction(async (listId, opts) => {
4785
+ const config = loadConfig();
4786
+ const fields = await listFields(config, listId);
4787
+ if (shouldOutputJson(opts.json ?? false)) {
4788
+ console.log(JSON.stringify(fields, null, 2));
4789
+ } else if (isTTY()) {
4790
+ console.log(formatFields(fields));
4791
+ } else {
4792
+ console.log(formatFieldsMarkdown(fields));
4793
+ }
4794
+ })
4795
+ );
4796
+ program.command("duplicate <taskId>").description("Duplicate a task").option("--json", "Force JSON output even in terminal").action(
4797
+ wrapAction(async (taskId, opts) => {
4798
+ const config = loadConfig();
4799
+ const result = await duplicateTask(config, taskId);
4800
+ if (shouldOutputJson(opts.json ?? false)) {
4801
+ console.log(JSON.stringify(result, null, 2));
4802
+ } else {
4803
+ console.log(`Duplicated as "${result.name}" (${result.id})`);
4804
+ }
4805
+ })
4806
+ );
4807
+ var bulkCmd = program.command("bulk").description("Bulk task operations");
4808
+ bulkCmd.command("status <status> <taskIds...>").description("Update status of multiple tasks").option("--json", "Force JSON output even in terminal").action(
4809
+ wrapAction(async (status, taskIds, opts) => {
4810
+ const config = loadConfig();
4811
+ const result = await bulkUpdateStatus(config, taskIds, status);
4812
+ if (shouldOutputJson(opts.json ?? false)) {
4813
+ console.log(JSON.stringify(result, null, 2));
4814
+ } else {
4815
+ console.log(`Updated ${result.updated} tasks to "${status}"`);
4816
+ if (result.failed.length > 0) {
4817
+ for (const f of result.failed) {
4818
+ console.log(` Failed ${f.id}: ${f.reason}`);
4819
+ }
4820
+ }
4821
+ }
4822
+ })
4823
+ );
4824
+ program.command("goals").description("List goals in your workspace").option("--json", "Force JSON output even in terminal").action(
4825
+ wrapAction(async (opts) => {
4826
+ const config = loadConfig();
4827
+ const goals = await listGoals(config);
4828
+ if (shouldOutputJson(opts.json ?? false)) {
4829
+ console.log(JSON.stringify(goals, null, 2));
4830
+ } else if (isTTY()) {
4831
+ console.log(formatGoals(goals));
4832
+ } else {
4833
+ console.log(formatGoalsMarkdown(goals));
4834
+ }
4835
+ })
4836
+ );
4837
+ program.command("goal-create <name>").description("Create a goal").option("-d, --description <text>", "Goal description").option("--color <hex>", "Goal color (hex)").option("--json", "Force JSON output even in terminal").action(
4838
+ wrapAction(
4839
+ async (name, opts) => {
4840
+ const config = loadConfig();
4841
+ const goal = await createGoal(config, name, {
4842
+ description: opts.description,
4843
+ color: opts.color
4844
+ });
4845
+ if (shouldOutputJson(opts.json ?? false)) {
4846
+ console.log(JSON.stringify(goal, null, 2));
4847
+ } else {
4848
+ console.log(`Created goal "${goal.name}" (${goal.id})`);
4849
+ }
4850
+ }
4851
+ )
4852
+ );
4853
+ program.command("goal-update <goalId>").description("Update a goal").option("-n, --name <text>", "New goal name").option("-d, --description <text>", "New description").option("--color <hex>", "New color (hex)").option("--json", "Force JSON output even in terminal").action(
4854
+ wrapAction(
4855
+ async (goalId, opts) => {
4856
+ const config = loadConfig();
4857
+ const goal = await updateGoal(config, goalId, {
4858
+ name: opts.name,
4859
+ description: opts.description,
4860
+ color: opts.color
4861
+ });
4862
+ if (shouldOutputJson(opts.json ?? false)) {
4863
+ console.log(JSON.stringify(goal, null, 2));
4864
+ } else {
4865
+ console.log(`Updated goal "${goal.name}" (${goal.id})`);
4866
+ }
4867
+ }
4868
+ )
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
+ );
4881
+ program.command("key-results <goalId>").description("List key results for a goal").option("--json", "Force JSON output even in terminal").action(
4882
+ wrapAction(async (goalId, opts) => {
4883
+ const config = loadConfig();
4884
+ const krs = await listKeyResults(config, goalId);
4885
+ if (shouldOutputJson(opts.json ?? false)) {
4886
+ console.log(JSON.stringify(krs, null, 2));
4887
+ } else if (isTTY()) {
4888
+ console.log(formatKeyResults(krs));
4889
+ } else {
4890
+ console.log(formatKeyResultsMarkdown(krs));
4891
+ }
4892
+ })
4893
+ );
4894
+ program.command("key-result-create <goalId> <name>").description("Create a key result on a goal").option("--type <type>", "Key result type (number or percentage)", "number").option("--target <n>", "Target value", "100").option("--json", "Force JSON output even in terminal").action(
4895
+ wrapAction(
4896
+ async (goalId, name, opts) => {
4897
+ const config = loadConfig();
4898
+ const target = Number(opts.target ?? 100);
4899
+ if (!Number.isFinite(target) || target <= 0) {
4900
+ throw new Error("--target must be a positive number");
4901
+ }
4902
+ const kr = await createKeyResult(config, goalId, name, opts.type ?? "number", target);
4903
+ if (shouldOutputJson(opts.json ?? false)) {
4904
+ console.log(JSON.stringify(kr, null, 2));
4905
+ } else {
4906
+ console.log(`Created key result "${kr.name}" (${kr.id})`);
4907
+ }
4908
+ }
4909
+ )
4910
+ );
4911
+ program.command("key-result-update <keyResultId>").description("Update a key result").option("--progress <n>", "Current progress value").option("--note <text>", "Progress note").option("--json", "Force JSON output even in terminal").action(
4912
+ wrapAction(
4913
+ async (keyResultId, opts) => {
4914
+ const config = loadConfig();
4915
+ const updates = {};
4916
+ if (opts.progress !== void 0) {
4917
+ const p = Number(opts.progress);
4918
+ if (!Number.isFinite(p)) throw new Error("--progress must be a number");
4919
+ updates.progress = p;
4920
+ }
4921
+ if (opts.note !== void 0) updates.note = opts.note;
4922
+ const kr = await updateKeyResult(config, keyResultId, updates);
4923
+ if (shouldOutputJson(opts.json ?? false)) {
4924
+ console.log(JSON.stringify(kr, null, 2));
4925
+ } else {
4926
+ console.log(`Updated key result "${kr.name}" (${kr.id})`);
4927
+ }
4928
+ }
4929
+ )
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
+ );
4163
4942
  program.command("docs [query]").description("List workspace docs (optionally filter by name)").option("--json", "Force JSON output even in terminal").action(
4164
4943
  wrapAction(async (query, opts) => {
4165
4944
  const config = loadConfig();
@@ -4263,6 +5042,77 @@ program.command("doc-page-edit <docId> <pageId>").description("Edit a doc page")
4263
5042
  }
4264
5043
  )
4265
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
+ );
4266
5116
  var configCmd = program.command("config").description("Manage CLI configuration");
4267
5117
  configCmd.command("get <key>").description("Print a config value").action(
4268
5118
  wrapAction(async (key) => {