@krodak/clickup-cli 1.28.0 → 1.30.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
@@ -445,6 +445,28 @@ var ClickUpClient = class {
445
445
  async deleteTask(taskId) {
446
446
  await this.request(this.taskPath(taskId), { method: "DELETE" });
447
447
  }
448
+ async deleteList(listId) {
449
+ await this.request(`/list/${listId}`, { method: "DELETE" });
450
+ }
451
+ async deleteFolder(folderId) {
452
+ await this.request(`/folder/${folderId}`, { method: "DELETE" });
453
+ }
454
+ async deleteSpace(spaceId) {
455
+ await this.request(`/space/${spaceId}`, { method: "DELETE" });
456
+ }
457
+ async getTaskMembers(taskId) {
458
+ const data = await this.request(this.taskPath(taskId, "/member"));
459
+ return readCollectionField(data, "members", "task members");
460
+ }
461
+ async getWorkspacePlan() {
462
+ return this.request(`/team/${this.teamId}/plan`);
463
+ }
464
+ async getTaskAttachments(taskId) {
465
+ const data = await this.requestV3(
466
+ `/workspaces/${this.teamId}/tasks/${taskId}/attachments`
467
+ );
468
+ return expectArrayField(data, "data", "task attachments");
469
+ }
448
470
  async addTagToTask(taskId, tagName) {
449
471
  await this.request(this.taskPath(taskId, `/tag/${encodeURIComponent(tagName)}`), {
450
472
  method: "POST"
@@ -978,6 +1000,77 @@ var ClickUpClient = class {
978
1000
  { method: "DELETE" }
979
1001
  );
980
1002
  }
1003
+ async mergeTasks(taskId, mergeWithTaskIds) {
1004
+ await this.request(this.taskPath(taskId, "/merge"), {
1005
+ method: "POST",
1006
+ body: JSON.stringify({ merge_with: mergeWithTaskIds })
1007
+ });
1008
+ }
1009
+ async updateTimeEstimatesByUser(taskId, estimates) {
1010
+ return this.requestV3(`/workspaces/${this.teamId}/tasks/${taskId}/time_estimates_by_user`, {
1011
+ method: "PATCH",
1012
+ body: JSON.stringify({ time_estimates_by_user: estimates })
1013
+ });
1014
+ }
1015
+ async replaceTimeEstimatesByUser(taskId, estimates) {
1016
+ return this.requestV3(`/workspaces/${this.teamId}/tasks/${taskId}/time_estimates_by_user`, {
1017
+ method: "PUT",
1018
+ body: JSON.stringify({ time_estimates_by_user: estimates })
1019
+ });
1020
+ }
1021
+ async getSharedHierarchy() {
1022
+ return this.request(`/team/${this.teamId}/shared`);
1023
+ }
1024
+ async getWebhooks() {
1025
+ const data = await this.request(`/team/${this.teamId}/webhook`);
1026
+ return readCollectionField(data, "webhooks", "webhooks");
1027
+ }
1028
+ async createWebhook(endpoint, events, opts) {
1029
+ const body = { endpoint, events };
1030
+ if (opts?.taskId) body.task_id = opts.taskId;
1031
+ if (opts?.listId) body.list_id = opts.listId;
1032
+ if (opts?.folderId) body.folder_id = opts.folderId;
1033
+ if (opts?.spaceId) body.space_id = opts.spaceId;
1034
+ const data = await this.request(`/team/${this.teamId}/webhook`, {
1035
+ method: "POST",
1036
+ body: JSON.stringify(body)
1037
+ });
1038
+ return expectRecordField(data, "webhook", "webhook");
1039
+ }
1040
+ async updateWebhook(webhookId, opts) {
1041
+ const data = await this.request(`/webhook/${webhookId}`, {
1042
+ method: "PUT",
1043
+ body: JSON.stringify(opts)
1044
+ });
1045
+ return expectRecordField(data, "webhook", "webhook");
1046
+ }
1047
+ async deleteWebhook(webhookId) {
1048
+ await this.request(`/webhook/${webhookId}`, { method: "DELETE" });
1049
+ }
1050
+ async getListComments(listId) {
1051
+ const data = await this.request(`/list/${listId}/comment`);
1052
+ return readCollectionField(data, "comments", "list comments");
1053
+ }
1054
+ async postListComment(listId, commentText, notifyAll) {
1055
+ const body = { comment_text: commentText };
1056
+ if (notifyAll) body.notify_all = true;
1057
+ return this.request(`/list/${listId}/comment`, {
1058
+ method: "POST",
1059
+ body: JSON.stringify(body)
1060
+ });
1061
+ }
1062
+ async getViewComments(viewId) {
1063
+ const data = await this.request(`/view/${viewId}/comment`);
1064
+ return readCollectionField(data, "comments", "view comments");
1065
+ }
1066
+ async postViewComment(viewId, commentText, notifyAll) {
1067
+ const body = { comment_text: commentText };
1068
+ if (notifyAll) body.notify_all = true;
1069
+ return this.request(`/view/${viewId}/comment`, {
1070
+ method: "POST",
1071
+ body: JSON.stringify(body)
1072
+ });
1073
+ }
981
1074
  };
982
1075
 
983
1076
  // src/config.ts
@@ -3888,6 +3981,60 @@ var commandMetadata = [
3888
3981
  flags: ["--confirm", "--json"],
3889
3982
  quickReference: [{ section: "write", usage: "delete <taskId>", description: "Delete a task" }]
3890
3983
  },
3984
+ {
3985
+ name: "list-delete",
3986
+ description: "Delete a list (requires confirmation)",
3987
+ flags: ["--confirm", "--json"],
3988
+ quickReference: [
3989
+ { section: "write", usage: "list-delete <listId>", description: "Delete a list" }
3990
+ ]
3991
+ },
3992
+ {
3993
+ name: "folder-delete",
3994
+ description: "Delete a folder (requires confirmation)",
3995
+ flags: ["--confirm", "--json"],
3996
+ quickReference: [
3997
+ { section: "write", usage: "folder-delete <folderId>", description: "Delete a folder" }
3998
+ ]
3999
+ },
4000
+ {
4001
+ name: "space-delete",
4002
+ description: "Delete a space (requires confirmation)",
4003
+ flags: ["--confirm", "--json"],
4004
+ quickReference: [
4005
+ { section: "write", usage: "space-delete <spaceId>", description: "Delete a space" }
4006
+ ]
4007
+ },
4008
+ {
4009
+ name: "attachments",
4010
+ description: "List attachments on a task",
4011
+ flags: ["--json"],
4012
+ quickReference: [
4013
+ {
4014
+ section: "read",
4015
+ usage: "attachments <taskId>",
4016
+ description: "List attachments on a task"
4017
+ }
4018
+ ]
4019
+ },
4020
+ {
4021
+ name: "task-members",
4022
+ description: "List members with access to a task",
4023
+ flags: ["--json"],
4024
+ quickReference: [
4025
+ {
4026
+ section: "read",
4027
+ usage: "task-members <taskId>",
4028
+ description: "List members with access to a task"
4029
+ }
4030
+ ]
4031
+ },
4032
+ {
4033
+ name: "plan",
4034
+ description: "Show workspace plan",
4035
+ flags: ["--json"],
4036
+ quickReference: [{ section: "read", usage: "plan", description: "Show workspace plan" }]
4037
+ },
3891
4038
  {
3892
4039
  name: "tag",
3893
4040
  description: "Add or remove tags from a task",
@@ -3969,7 +4116,12 @@ var commandMetadata = [
3969
4116
  description: "List my recent time entries (--all for team)"
3970
4117
  },
3971
4118
  { section: "write", usage: "time update <timeEntryId>", description: "Update a time entry" },
3972
- { section: "write", usage: "time delete <timeEntryId>", description: "Delete a time entry" }
4119
+ { section: "write", usage: "time delete <timeEntryId>", description: "Delete a time entry" },
4120
+ {
4121
+ section: "write",
4122
+ usage: "time estimate-by-user <taskId> <userId> <duration>",
4123
+ description: "Set per-user time estimate"
4124
+ }
3973
4125
  ]
3974
4126
  },
3975
4127
  {
@@ -4425,6 +4577,92 @@ var commandMetadata = [
4425
4577
  { section: "read", usage: "favorite list", description: "List saved favorites" }
4426
4578
  ]
4427
4579
  },
4580
+ {
4581
+ name: "list-comments",
4582
+ description: "List comments on a list",
4583
+ flags: ["--json"],
4584
+ quickReference: [
4585
+ {
4586
+ section: "read",
4587
+ usage: "list-comments <listId>",
4588
+ description: "List comments on a list"
4589
+ }
4590
+ ]
4591
+ },
4592
+ {
4593
+ name: "list-comment",
4594
+ description: "Post a comment on a list",
4595
+ flags: ["-m", "--message", "--notify-all", "--json"],
4596
+ quickReference: [
4597
+ {
4598
+ section: "write",
4599
+ usage: "list-comment <listId>",
4600
+ description: "Post a comment on a list"
4601
+ }
4602
+ ]
4603
+ },
4604
+ {
4605
+ name: "view-comments",
4606
+ description: "List comments on a view",
4607
+ flags: ["--json"],
4608
+ quickReference: [
4609
+ {
4610
+ section: "read",
4611
+ usage: "view-comments <viewId>",
4612
+ description: "List comments on a view"
4613
+ }
4614
+ ]
4615
+ },
4616
+ {
4617
+ name: "view-comment",
4618
+ description: "Post a comment on a view",
4619
+ flags: ["-m", "--message", "--notify-all", "--json"],
4620
+ quickReference: [
4621
+ {
4622
+ section: "write",
4623
+ usage: "view-comment <viewId>",
4624
+ description: "Post a comment on a view"
4625
+ }
4626
+ ]
4627
+ },
4628
+ {
4629
+ name: "webhook",
4630
+ description: "Manage webhooks",
4631
+ quickReference: [
4632
+ { section: "read", usage: "webhook list", description: "List webhooks" },
4633
+ {
4634
+ section: "write",
4635
+ usage: "webhook create",
4636
+ description: "Create a webhook"
4637
+ },
4638
+ { section: "write", usage: "webhook update <webhookId>", description: "Update a webhook" },
4639
+ { section: "write", usage: "webhook delete <webhookId>", description: "Delete a webhook" }
4640
+ ]
4641
+ },
4642
+ {
4643
+ name: "merge",
4644
+ description: "Merge a task into another (source becomes subtask of target)",
4645
+ flags: ["--confirm", "--json"],
4646
+ quickReference: [
4647
+ {
4648
+ section: "write",
4649
+ usage: "merge <sourceTaskId> <intoTaskId>",
4650
+ description: "Merge a task into another"
4651
+ }
4652
+ ]
4653
+ },
4654
+ {
4655
+ name: "shared",
4656
+ description: "Show shared spaces, folders, and lists",
4657
+ flags: ["--json"],
4658
+ quickReference: [
4659
+ {
4660
+ section: "read",
4661
+ usage: "shared",
4662
+ description: "Show shared spaces, folders, and lists"
4663
+ }
4664
+ ]
4665
+ },
4428
4666
  {
4429
4667
  name: "chat",
4430
4668
  description: "Chat channels and messaging",
@@ -6150,6 +6388,144 @@ async function deleteTaskCommand(config, taskId, opts) {
6150
6388
  return { taskId, deleted: true };
6151
6389
  }
6152
6390
 
6391
+ // src/commands/list-delete.ts
6392
+ async function deleteListCommand(config, listId, opts) {
6393
+ const client = new ClickUpClient(config);
6394
+ if (!opts.confirm) {
6395
+ if (!isTTY()) {
6396
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
6397
+ }
6398
+ const list = await client.getListWithStatuses(listId);
6399
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
6400
+ const confirmed = await confirm3({
6401
+ message: `Delete list "${list.name}" (${listId})? This cannot be undone.`,
6402
+ default: false
6403
+ });
6404
+ if (!confirmed) {
6405
+ throw new Error("Cancelled");
6406
+ }
6407
+ }
6408
+ await client.deleteList(listId);
6409
+ return { listId, deleted: true };
6410
+ }
6411
+
6412
+ // src/commands/folder-delete.ts
6413
+ async function deleteFolderCommand(config, folderId, opts) {
6414
+ const client = new ClickUpClient(config);
6415
+ if (!opts.confirm) {
6416
+ if (!isTTY()) {
6417
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
6418
+ }
6419
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
6420
+ const confirmed = await confirm3({
6421
+ message: `Delete folder ${folderId}? This cannot be undone.`,
6422
+ default: false
6423
+ });
6424
+ if (!confirmed) {
6425
+ throw new Error("Cancelled");
6426
+ }
6427
+ }
6428
+ await client.deleteFolder(folderId);
6429
+ return { folderId, deleted: true };
6430
+ }
6431
+
6432
+ // src/commands/space-delete.ts
6433
+ async function deleteSpaceCommand(config, spaceId, opts) {
6434
+ const client = new ClickUpClient(config);
6435
+ if (!opts.confirm) {
6436
+ if (!isTTY()) {
6437
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
6438
+ }
6439
+ const space = await client.getSpaceWithStatuses(spaceId);
6440
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
6441
+ const confirmed = await confirm3({
6442
+ message: `Delete space "${space.name}" (${spaceId})? This cannot be undone.`,
6443
+ default: false
6444
+ });
6445
+ if (!confirmed) {
6446
+ throw new Error("Cancelled");
6447
+ }
6448
+ }
6449
+ await client.deleteSpace(spaceId);
6450
+ return { spaceId, deleted: true };
6451
+ }
6452
+
6453
+ // src/commands/attachments.ts
6454
+ function formatSize(size) {
6455
+ if (size < 1024) return `${size} B`;
6456
+ if (size < 1048576) return `${(size / 1024).toFixed(1)} KB`;
6457
+ return `${(size / 1048576).toFixed(1)} MB`;
6458
+ }
6459
+ var ATTACHMENT_COLUMNS = [
6460
+ { key: "title", label: "Title", maxWidth: 40 },
6461
+ { key: "extension", label: "Ext", maxWidth: 10 },
6462
+ { key: "size", label: "Size", maxWidth: 10 },
6463
+ { key: "date", label: "Date", maxWidth: 12 },
6464
+ { key: "url", label: "URL", maxWidth: 60 }
6465
+ ];
6466
+ function toRow(a) {
6467
+ return {
6468
+ title: a.title,
6469
+ extension: a.extension,
6470
+ size: formatSize(a.size),
6471
+ date: new Date(a.date_created).toLocaleDateString(),
6472
+ url: a.url
6473
+ };
6474
+ }
6475
+ async function listTaskAttachments(config, taskId) {
6476
+ const client = new ClickUpClient(config);
6477
+ return client.getTaskAttachments(taskId);
6478
+ }
6479
+ function formatAttachmentsTable(attachments) {
6480
+ if (attachments.length === 0) return "No attachments found";
6481
+ const rows = attachments.map(toRow);
6482
+ return formatTable(rows, ATTACHMENT_COLUMNS);
6483
+ }
6484
+ function formatAttachmentsMarkdown(attachments) {
6485
+ if (attachments.length === 0) return "No attachments found";
6486
+ return attachments.map((a) => `- **${a.title}** (${a.extension}, ${formatSize(a.size)}) \u2014 ${a.url}`).join("\n");
6487
+ }
6488
+
6489
+ // src/commands/task-members.ts
6490
+ var TASK_MEMBER_COLUMNS = [
6491
+ { key: "username", label: "Username", maxWidth: 25 },
6492
+ { key: "id", label: "ID", maxWidth: 15 },
6493
+ { key: "email", label: "Email", maxWidth: 40 }
6494
+ ];
6495
+ async function listTaskMembers(config, taskId) {
6496
+ const client = new ClickUpClient(config);
6497
+ return client.getTaskMembers(taskId);
6498
+ }
6499
+ function formatTaskMembers(members) {
6500
+ if (members.length === 0) return "No task members found";
6501
+ const rows = members.map((m) => ({
6502
+ username: m.username,
6503
+ id: String(m.id),
6504
+ email: m.email
6505
+ }));
6506
+ return formatTable(rows, TASK_MEMBER_COLUMNS);
6507
+ }
6508
+ function formatTaskMembersMarkdown(members) {
6509
+ if (members.length === 0) return "No task members found";
6510
+ return members.map((m) => `- **${m.username}** (${m.id}) - ${m.email}`).join("\n");
6511
+ }
6512
+
6513
+ // src/commands/plan.ts
6514
+ import chalk9 from "chalk";
6515
+ async function getWorkspacePlanCommand(config) {
6516
+ const client = new ClickUpClient(config);
6517
+ return client.getWorkspacePlan();
6518
+ }
6519
+ function formatPlan(plan) {
6520
+ return [
6521
+ `${chalk9.bold("Plan:")} ${plan.name}`,
6522
+ `${chalk9.bold("Plan ID:")} ${plan.plan_id}`
6523
+ ].join("\n");
6524
+ }
6525
+ function formatPlanMarkdown(plan) {
6526
+ return [`**Plan:** ${plan.name}`, `**Plan ID:** ${plan.plan_id}`].join("\n");
6527
+ }
6528
+
6153
6529
  // src/commands/archive.ts
6154
6530
  async function archiveTaskCommand(config, taskId, opts) {
6155
6531
  const client = new ClickUpClient(config);
@@ -6210,7 +6586,7 @@ async function manageTags(config, taskId, opts) {
6210
6586
  }
6211
6587
 
6212
6588
  // src/commands/checklist.ts
6213
- import chalk9 from "chalk";
6589
+ import chalk10 from "chalk";
6214
6590
  async function viewChecklists(config, taskId) {
6215
6591
  const client = new ClickUpClient(config);
6216
6592
  const task = await client.getTask(taskId);
@@ -6260,19 +6636,19 @@ function formatChecklists(checklists) {
6260
6636
  const lines = [];
6261
6637
  const renderItem = (item, depth) => {
6262
6638
  const indent = " ".repeat(depth + 1);
6263
- const check = item.resolved ? chalk9.green("[x]") : chalk9.dim("[ ]");
6264
- const name = item.resolved ? chalk9.dim(item.name) : item.name;
6265
- const assignee = item.assignee ? chalk9.dim(` @${item.assignee.username}`) : "";
6639
+ const check = item.resolved ? chalk10.green("[x]") : chalk10.dim("[ ]");
6640
+ const name = item.resolved ? chalk10.dim(item.name) : item.name;
6641
+ const assignee = item.assignee ? chalk10.dim(` @${item.assignee.username}`) : "";
6266
6642
  lines.push(`${indent}${check} ${name}${assignee}`);
6267
- lines.push(chalk9.dim(`${indent} item-id: ${item.id}`));
6643
+ lines.push(chalk10.dim(`${indent} item-id: ${item.id}`));
6268
6644
  for (const child of sortByOrder(item.children ?? [])) {
6269
6645
  renderItem(child, depth + 1);
6270
6646
  }
6271
6647
  };
6272
6648
  for (const cl of checklists) {
6273
6649
  const { total, resolved } = countItems(cl.items);
6274
- lines.push(chalk9.bold(`${cl.name} (${resolved}/${total})`));
6275
- lines.push(chalk9.dim(` ID: ${cl.id}`));
6650
+ lines.push(chalk10.bold(`${cl.name} (${resolved}/${total})`));
6651
+ lines.push(chalk10.dim(` ID: ${cl.id}`));
6276
6652
  for (const item of sortByOrder(cl.items)) renderItem(item, 0);
6277
6653
  }
6278
6654
  return lines.join("\n");
@@ -6338,7 +6714,7 @@ async function deleteCommentByTaskSelection(config, taskId, options) {
6338
6714
  }
6339
6715
 
6340
6716
  // src/commands/replies.ts
6341
- import chalk10 from "chalk";
6717
+ import chalk11 from "chalk";
6342
6718
  async function getReplies(config, commentId) {
6343
6719
  const client = new ClickUpClient(config);
6344
6720
  return client.getThreadedComments(commentId);
@@ -6354,7 +6730,7 @@ function formatReplies(replies) {
6354
6730
  return replies.map((r) => {
6355
6731
  const user = r.user?.username ?? "Unknown";
6356
6732
  const date = formatTimestamp(Number(r.date));
6357
- return `${chalk10.bold(user)} ${chalk10.dim(date)}
6733
+ return `${chalk11.bold(user)} ${chalk11.dim(date)}
6358
6734
  ${r.comment_text}`;
6359
6735
  }).join("\n\n");
6360
6736
  }
@@ -6417,7 +6793,7 @@ function formatDocsMarkdown(docs) {
6417
6793
  }
6418
6794
 
6419
6795
  // src/commands/doc.ts
6420
- import chalk11 from "chalk";
6796
+ import chalk12 from "chalk";
6421
6797
  async function getDocInfo(config, docId) {
6422
6798
  const client = new ClickUpClient(config);
6423
6799
  const [doc, pages] = await Promise.all([
@@ -6429,14 +6805,14 @@ async function getDocInfo(config, docId) {
6429
6805
  function formatDocInfo(doc, pages, indent = 0) {
6430
6806
  const lines = [];
6431
6807
  if (indent === 0) {
6432
- lines.push(`${chalk11.bold(doc.name)} ${chalk11.dim(doc.id)}`);
6808
+ lines.push(`${chalk12.bold(doc.name)} ${chalk12.dim(doc.id)}`);
6433
6809
  if (pages.length === 0) {
6434
6810
  lines.push(" (no pages)");
6435
6811
  }
6436
6812
  }
6437
6813
  for (const page of pages) {
6438
6814
  const prefix = " ".repeat(indent + 1);
6439
- lines.push(`${prefix}${page.name} ${chalk11.dim(page.id)}`);
6815
+ lines.push(`${prefix}${page.name} ${chalk12.dim(page.id)}`);
6440
6816
  if (page.pages && page.pages.length > 0) {
6441
6817
  lines.push(formatDocInfo(doc, page.pages, indent + 1));
6442
6818
  }
@@ -6515,7 +6891,7 @@ async function deleteDocPage(config, docId, pageId) {
6515
6891
  }
6516
6892
 
6517
6893
  // src/commands/folders.ts
6518
- import chalk12 from "chalk";
6894
+ import chalk13 from "chalk";
6519
6895
  async function listFolders(config, spaceId, nameFilter, archived) {
6520
6896
  const client = new ClickUpClient(config);
6521
6897
  const folders = await client.getFolders(spaceId, archived);
@@ -6534,9 +6910,9 @@ async function listFolders(config, spaceId, nameFilter, archived) {
6534
6910
  function formatFolders(folders) {
6535
6911
  if (folders.length === 0) return "No folders found";
6536
6912
  return folders.map((f) => {
6537
- const header = `${chalk12.bold(f.name)} ${chalk12.dim(f.id)}`;
6913
+ const header = `${chalk13.bold(f.name)} ${chalk13.dim(f.id)}`;
6538
6914
  if (f.lists.length === 0) return header;
6539
- const listLines = f.lists.map((l) => ` ${chalk12.dim(">")} ${l.name} ${chalk12.dim(l.id)}`);
6915
+ const listLines = f.lists.map((l) => ` ${chalk13.dim(">")} ${l.name} ${chalk13.dim(l.id)}`);
6540
6916
  return [header, ...listLines].join("\n");
6541
6917
  }).join("\n\n");
6542
6918
  }
@@ -6551,13 +6927,13 @@ function formatFoldersMarkdown(folders) {
6551
6927
  }
6552
6928
 
6553
6929
  // src/commands/time.ts
6554
- import chalk13 from "chalk";
6930
+ import chalk14 from "chalk";
6555
6931
  var TIME_COLUMNS = [
6556
6932
  { key: "task", label: "Task", maxWidth: 35 },
6557
6933
  { key: "duration", label: "Duration", maxWidth: 10 },
6558
6934
  { key: "date", label: "Date", maxWidth: 20 },
6559
6935
  { key: "description", label: "Description", maxWidth: 30 },
6560
- { key: "status", label: "", maxWidth: 10, format: (v) => v === "RUNNING" ? chalk13.green(v) : "" }
6936
+ { key: "status", label: "", maxWidth: 10, format: (v) => v === "RUNNING" ? chalk14.green(v) : "" }
6561
6937
  ];
6562
6938
  async function startTimer(config, taskId, description) {
6563
6939
  const client = new ClickUpClient(config);
@@ -6653,7 +7029,7 @@ function formatTimeEntriesMarkdown(entries) {
6653
7029
  }
6654
7030
 
6655
7031
  // src/commands/tags.ts
6656
- import chalk14 from "chalk";
7032
+ import chalk15 from "chalk";
6657
7033
  var TAG_COLUMNS = [
6658
7034
  { key: "name", label: "Name", maxWidth: 40 },
6659
7035
  { key: "fg", label: "FG", maxWidth: 10 },
@@ -6686,13 +7062,13 @@ function formatTags(tags) {
6686
7062
  if (tags.length === 0) return "No tags found";
6687
7063
  if (isTTY()) {
6688
7064
  const rows = tags.map((t) => ({
6689
- name: t.tag_bg ? chalk14.bgHex(t.tag_bg).hex(t.tag_fg || "#ffffff")(` ${t.name} `) : chalk14.bold(t.name),
7065
+ name: t.tag_bg ? chalk15.bgHex(t.tag_bg).hex(t.tag_fg || "#ffffff")(` ${t.name} `) : chalk15.bold(t.name),
6690
7066
  fg: t.tag_fg || "",
6691
7067
  bg: t.tag_bg || ""
6692
7068
  }));
6693
7069
  return formatTable(rows, TAG_COLUMNS);
6694
7070
  }
6695
- return tags.map((t) => chalk14.bold(t.name)).join(", ");
7071
+ return tags.map((t) => chalk15.bold(t.name)).join(", ");
6696
7072
  }
6697
7073
  function formatTagsMarkdown(tags) {
6698
7074
  if (tags.length === 0) return "No tags found";
@@ -6727,7 +7103,7 @@ function formatMembersMarkdown(members) {
6727
7103
  }
6728
7104
 
6729
7105
  // src/commands/fields.ts
6730
- import chalk15 from "chalk";
7106
+ import chalk16 from "chalk";
6731
7107
  var FIELD_COLUMNS = [
6732
7108
  { key: "id", label: "ID", maxWidth: 20 },
6733
7109
  { key: "name", label: "Name", maxWidth: 30 },
@@ -6736,7 +7112,7 @@ var FIELD_COLUMNS = [
6736
7112
  key: "required",
6737
7113
  label: "Required",
6738
7114
  maxWidth: 10,
6739
- format: (v) => v === "yes" ? chalk15.yellow(v) : chalk15.dim(v)
7115
+ format: (v) => v === "yes" ? chalk16.yellow(v) : chalk16.dim(v)
6740
7116
  },
6741
7117
  { key: "options", label: "Options", maxWidth: 40 }
6742
7118
  ];
@@ -6894,13 +7270,13 @@ async function bulkMove(config, listId, taskIds) {
6894
7270
  }
6895
7271
 
6896
7272
  // src/commands/goals.ts
6897
- import chalk16 from "chalk";
7273
+ import chalk17 from "chalk";
6898
7274
  function colorProgress(value) {
6899
7275
  const num = parseInt(value, 10);
6900
7276
  if (isNaN(num)) return value;
6901
- if (num >= 75) return chalk16.green(value);
6902
- if (num >= 25) return chalk16.yellow(value);
6903
- return chalk16.red(value);
7277
+ if (num >= 75) return chalk17.green(value);
7278
+ if (num >= 25) return chalk17.yellow(value);
7279
+ return chalk17.red(value);
6904
7280
  }
6905
7281
  var GOAL_COLUMNS = [
6906
7282
  { key: "id", label: "ID", maxWidth: 15 },
@@ -6994,14 +7370,14 @@ function formatKeyResultsMarkdown(keyResults) {
6994
7370
  }
6995
7371
 
6996
7372
  // src/commands/task-types.ts
6997
- import chalk17 from "chalk";
7373
+ import chalk18 from "chalk";
6998
7374
  async function listTaskTypes(config) {
6999
7375
  const client = new ClickUpClient(config);
7000
7376
  return client.getCustomTaskTypes(config.teamId);
7001
7377
  }
7002
7378
  function formatTaskTypes(types) {
7003
7379
  if (types.length === 0) return "No custom task types";
7004
- return types.map((t) => `${chalk17.bold(t.name)} ${chalk17.dim(`(${t.id})`)}`).join("\n");
7380
+ return types.map((t) => `${chalk18.bold(t.name)} ${chalk18.dim(`(${t.id})`)}`).join("\n");
7005
7381
  }
7006
7382
  function formatTaskTypesMarkdown(types) {
7007
7383
  if (types.length === 0) return "No custom task types";
@@ -7009,14 +7385,14 @@ function formatTaskTypesMarkdown(types) {
7009
7385
  }
7010
7386
 
7011
7387
  // src/commands/templates.ts
7012
- import chalk18 from "chalk";
7388
+ import chalk19 from "chalk";
7013
7389
  async function listTemplates(config) {
7014
7390
  const client = new ClickUpClient(config);
7015
7391
  return client.getTaskTemplates(config.teamId);
7016
7392
  }
7017
7393
  function formatTemplates(templates) {
7018
7394
  if (templates.length === 0) return "No task templates";
7019
- return templates.map((t) => `${chalk18.bold(t.name)} ${chalk18.dim(`(${t.id})`)}`).join("\n");
7395
+ return templates.map((t) => `${chalk19.bold(t.name)} ${chalk19.dim(`(${t.id})`)}`).join("\n");
7020
7396
  }
7021
7397
  function formatTemplatesMarkdown(templates) {
7022
7398
  if (templates.length === 0) return "No task templates";
@@ -7024,14 +7400,14 @@ function formatTemplatesMarkdown(templates) {
7024
7400
  }
7025
7401
 
7026
7402
  // src/commands/list-templates.ts
7027
- import chalk19 from "chalk";
7403
+ import chalk20 from "chalk";
7028
7404
  async function listListTemplates(config) {
7029
7405
  const client = new ClickUpClient(config);
7030
7406
  return client.getListTemplates(config.teamId);
7031
7407
  }
7032
7408
  function formatListTemplates(templates) {
7033
7409
  if (templates.length === 0) return "No list templates";
7034
- return templates.map((t) => `${chalk19.bold(t.name)} ${chalk19.dim(`(${t.id})`)}`).join("\n");
7410
+ return templates.map((t) => `${chalk20.bold(t.name)} ${chalk20.dim(`(${t.id})`)}`).join("\n");
7035
7411
  }
7036
7412
  function formatListTemplatesMarkdown(templates) {
7037
7413
  if (templates.length === 0) return "No list templates";
@@ -7039,14 +7415,14 @@ function formatListTemplatesMarkdown(templates) {
7039
7415
  }
7040
7416
 
7041
7417
  // src/commands/folder-templates.ts
7042
- import chalk20 from "chalk";
7418
+ import chalk21 from "chalk";
7043
7419
  async function listFolderTemplates(config) {
7044
7420
  const client = new ClickUpClient(config);
7045
7421
  return client.getFolderTemplates(config.teamId);
7046
7422
  }
7047
7423
  function formatFolderTemplates(templates) {
7048
7424
  if (templates.length === 0) return "No folder templates";
7049
- return templates.map((t) => `${chalk20.bold(t.name)} ${chalk20.dim(`(${t.id})`)}`).join("\n");
7425
+ return templates.map((t) => `${chalk21.bold(t.name)} ${chalk21.dim(`(${t.id})`)}`).join("\n");
7050
7426
  }
7051
7427
  function formatFolderTemplatesMarkdown(templates) {
7052
7428
  if (templates.length === 0) return "No folder templates";
@@ -7069,7 +7445,7 @@ async function createListFromTemplate(config, name, opts) {
7069
7445
  }
7070
7446
 
7071
7447
  // src/commands/views.ts
7072
- import chalk21 from "chalk";
7448
+ import chalk22 from "chalk";
7073
7449
  async function listViews(config, id, container = "list") {
7074
7450
  const client = new ClickUpClient(config);
7075
7451
  if (container === "space") return client.getSpaceViews(id);
@@ -7080,7 +7456,7 @@ async function listViews(config, id, container = "list") {
7080
7456
  }
7081
7457
  function formatViews(views) {
7082
7458
  if (views.length === 0) return "No views";
7083
- return views.map((v) => `${chalk21.bold(v.name)} ${chalk21.dim(`(${v.id})`)} ${chalk21.dim(v.type)}`).join("\n");
7459
+ return views.map((v) => `${chalk22.bold(v.name)} ${chalk22.dim(`(${v.id})`)} ${chalk22.dim(v.type)}`).join("\n");
7084
7460
  }
7085
7461
  function formatViewsMarkdown(views) {
7086
7462
  if (views.length === 0) return "No views";
@@ -7088,23 +7464,23 @@ function formatViewsMarkdown(views) {
7088
7464
  }
7089
7465
 
7090
7466
  // src/commands/chat.ts
7091
- import chalk22 from "chalk";
7467
+ import chalk23 from "chalk";
7092
7468
  function channelName(c) {
7093
7469
  return c.name || "DM";
7094
7470
  }
7095
7471
  function colorChannelType(type) {
7096
- if (type === "CHANNEL") return chalk22.cyan(type);
7097
- if (type === "DM") return chalk22.dim(type);
7098
- if (type === "GROUP_DM") return chalk22.blue(type);
7472
+ if (type === "CHANNEL") return chalk23.cyan(type);
7473
+ if (type === "DM") return chalk23.dim(type);
7474
+ if (type === "GROUP_DM") return chalk23.blue(type);
7099
7475
  return type;
7100
7476
  }
7101
7477
  function colorVisibility(v) {
7102
- if (v === "PUBLIC") return chalk22.green(v);
7103
- return chalk22.dim(v);
7478
+ if (v === "PUBLIC") return chalk23.green(v);
7479
+ return chalk23.dim(v);
7104
7480
  }
7105
7481
  var CHANNEL_COLUMNS = [
7106
- { key: "name", label: "Name", maxWidth: 40, format: (v) => chalk22.bold(v) },
7107
- { key: "id", label: "ID", maxWidth: 20, format: (v) => chalk22.dim(v) },
7482
+ { key: "name", label: "Name", maxWidth: 40, format: (v) => chalk23.bold(v) },
7483
+ { key: "id", label: "ID", maxWidth: 20, format: (v) => chalk23.dim(v) },
7108
7484
  { key: "type", label: "Type", maxWidth: 12, format: (v) => colorChannelType(v) },
7109
7485
  { key: "visibility", label: "Visibility", maxWidth: 10, format: (v) => colorVisibility(v) },
7110
7486
  { key: "topic", label: "Topic", maxWidth: 40 }
@@ -7129,26 +7505,26 @@ function formatChannelsMarkdown(channels) {
7129
7505
  }
7130
7506
  function formatChannelDetail(channel) {
7131
7507
  const lines = [];
7132
- lines.push(chalk22.bold.underline(channelName(channel)));
7508
+ lines.push(chalk23.bold.underline(channelName(channel)));
7133
7509
  lines.push("");
7134
7510
  const fields = [
7135
- ["ID", chalk22.dim(channel.id)],
7511
+ ["ID", chalk23.dim(channel.id)],
7136
7512
  ["Type", colorChannelType(channel.type)],
7137
7513
  ["Visibility", colorVisibility(channel.visibility)]
7138
7514
  ];
7139
7515
  if (channel.topic) fields.push(["Topic", channel.topic]);
7140
7516
  if (channel.description) fields.push(["Description", channel.description]);
7141
- fields.push(["Archived", channel.archived ? chalk22.yellow("Yes") : "No"]);
7517
+ fields.push(["Archived", channel.archived ? chalk23.yellow("Yes") : "No"]);
7142
7518
  fields.push(["Created", formatDate(channel.created_at)]);
7143
7519
  const maxLabel = Math.max(...fields.map(([k]) => k.length));
7144
7520
  for (const [label, value] of fields) {
7145
- lines.push(` ${chalk22.bold(label.padEnd(maxLabel + 1))} ${value}`);
7521
+ lines.push(` ${chalk23.bold(label.padEnd(maxLabel + 1))} ${value}`);
7146
7522
  }
7147
7523
  return lines.join("\n");
7148
7524
  }
7149
7525
  var CHAT_MEMBER_COLUMNS = [
7150
- { key: "name", label: "Name", maxWidth: 30, format: (v) => chalk22.bold(v) },
7151
- { key: "id", label: "ID", maxWidth: 15, format: (v) => chalk22.dim(v) },
7526
+ { key: "name", label: "Name", maxWidth: 30, format: (v) => chalk23.bold(v) },
7527
+ { key: "id", label: "ID", maxWidth: 15, format: (v) => chalk23.dim(v) },
7152
7528
  { key: "email", label: "Email", maxWidth: 40 },
7153
7529
  { key: "type", label: "Type", maxWidth: 12 }
7154
7530
  ];
@@ -7171,22 +7547,22 @@ function formatChatMembersMarkdown(members) {
7171
7547
  }
7172
7548
 
7173
7549
  // src/commands/chat-message.ts
7174
- import chalk23 from "chalk";
7175
- var separator = chalk23.dim("-".repeat(60));
7550
+ import chalk24 from "chalk";
7551
+ var separator = chalk24.dim("-".repeat(60));
7176
7552
  function formatMessages(messages) {
7177
7553
  if (messages.length === 0) return "No messages";
7178
7554
  const lines = [];
7179
7555
  for (let i = 0; i < messages.length; i++) {
7180
7556
  const msg = messages[i];
7181
7557
  if (i > 0) lines.push(separator);
7182
- const meta = [chalk23.bold(`@${msg.user_id}`), chalk23.dim(formatTimestamp(msg.date))];
7558
+ const meta = [chalk24.bold(`@${msg.user_id}`), chalk24.dim(formatTimestamp(msg.date))];
7183
7559
  if (msg.replies_count) {
7184
- meta.push(chalk23.dim(`${msg.replies_count} replies`));
7560
+ meta.push(chalk24.dim(`${msg.replies_count} replies`));
7185
7561
  }
7186
- meta.push(chalk23.dim(`(${msg.id})`));
7562
+ meta.push(chalk24.dim(`(${msg.id})`));
7187
7563
  lines.push(meta.join(" "));
7188
7564
  if (msg.type === "post" && msg.post_data?.title) {
7189
- lines.push(chalk23.cyan.bold(msg.post_data.title));
7565
+ lines.push(chalk24.cyan.bold(msg.post_data.title));
7190
7566
  }
7191
7567
  lines.push(msg.content);
7192
7568
  }
@@ -7205,7 +7581,7 @@ ${msg.content}`;
7205
7581
  }
7206
7582
 
7207
7583
  // src/commands/chat-reaction.ts
7208
- import chalk24 from "chalk";
7584
+ import chalk25 from "chalk";
7209
7585
  var EMOJI_MAP = {
7210
7586
  thumbsup: "\u{1F44D}",
7211
7587
  thumbsdown: "\u{1F44E}",
@@ -7244,8 +7620,8 @@ function formatReactions(reactions) {
7244
7620
  const lines = [];
7245
7621
  for (const [emoji, users] of groups) {
7246
7622
  const icon = emojiChar(emoji);
7247
- const userList = users.map((u) => chalk24.bold(`@${u}`)).join(", ");
7248
- lines.push(`${icon} ${chalk24.dim(emoji)} ${chalk24.dim(`(${users.length})`)} \u2014 ${userList}`);
7623
+ const userList = users.map((u) => chalk25.bold(`@${u}`)).join(", ");
7624
+ lines.push(`${icon} ${chalk25.dim(emoji)} ${chalk25.dim(`(${users.length})`)} \u2014 ${userList}`);
7249
7625
  }
7250
7626
  return lines.join("\n");
7251
7627
  }
@@ -7260,20 +7636,20 @@ function formatReactionsMarkdown(reactions) {
7260
7636
  }
7261
7637
 
7262
7638
  // src/commands/view.ts
7263
- import chalk25 from "chalk";
7639
+ import chalk26 from "chalk";
7264
7640
  async function getView(config, viewId) {
7265
7641
  const client = new ClickUpClient(config);
7266
7642
  return client.getView(viewId);
7267
7643
  }
7268
7644
  function formatView(view) {
7269
7645
  const lines = [];
7270
- lines.push(chalk25.bold.underline(view.name));
7646
+ lines.push(chalk26.bold.underline(view.name));
7271
7647
  lines.push("");
7272
- lines.push(` ${chalk25.bold("ID")} ${view.id}`);
7273
- lines.push(` ${chalk25.bold("Type")} ${view.type}`);
7274
- if (view.visibility) lines.push(` ${chalk25.bold("Visibility")} ${view.visibility}`);
7275
- if (view.date_created) lines.push(` ${chalk25.bold("Created")} ${formatDate(view.date_created)}`);
7276
- if (view.protected !== void 0) lines.push(` ${chalk25.bold("Protected")} ${view.protected}`);
7648
+ lines.push(` ${chalk26.bold("ID")} ${view.id}`);
7649
+ lines.push(` ${chalk26.bold("Type")} ${view.type}`);
7650
+ if (view.visibility) lines.push(` ${chalk26.bold("Visibility")} ${view.visibility}`);
7651
+ if (view.date_created) lines.push(` ${chalk26.bold("Created")} ${formatDate(view.date_created)}`);
7652
+ if (view.protected !== void 0) lines.push(` ${chalk26.bold("Protected")} ${view.protected}`);
7277
7653
  return lines.join("\n");
7278
7654
  }
7279
7655
  function formatViewMarkdown(view) {
@@ -7562,6 +7938,222 @@ function formatFavoritesMarkdown(favorites) {
7562
7938
  return lines.join("\n");
7563
7939
  }
7564
7940
 
7941
+ // src/commands/merge.ts
7942
+ async function mergeCommand(config, sourceTaskId, intoTaskId, opts) {
7943
+ const client = new ClickUpClient(config);
7944
+ if (!opts.confirm) {
7945
+ if (!isTTY()) {
7946
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
7947
+ }
7948
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
7949
+ const confirmed = await confirm3({
7950
+ message: `Merge task ${sourceTaskId} into ${intoTaskId}? The source task becomes a subtask of the target.`,
7951
+ default: false
7952
+ });
7953
+ if (!confirmed) {
7954
+ throw new Error("Cancelled");
7955
+ }
7956
+ }
7957
+ await client.mergeTasks(intoTaskId, [sourceTaskId]);
7958
+ return { sourceTaskId, intoTaskId, merged: true };
7959
+ }
7960
+
7961
+ // src/commands/webhook.ts
7962
+ import chalk27 from "chalk";
7963
+ var WEBHOOK_COLUMNS = [
7964
+ { key: "id", label: "ID" },
7965
+ { key: "endpoint", label: "Endpoint", maxWidth: 45 },
7966
+ { key: "events", label: "Events", maxWidth: 30 },
7967
+ {
7968
+ key: "status",
7969
+ label: "Status",
7970
+ maxWidth: 10,
7971
+ format: (v) => v === "active" ? chalk27.green(v) : chalk27.dim(v)
7972
+ },
7973
+ { key: "scope", label: "Scope", maxWidth: 15 }
7974
+ ];
7975
+ function webhookScope(w) {
7976
+ if (w.task_id) return `task:${w.task_id}`;
7977
+ if (w.list_id) return `list:${w.list_id}`;
7978
+ if (w.folder_id) return `folder:${w.folder_id}`;
7979
+ if (w.space_id) return `space:${w.space_id}`;
7980
+ return "workspace";
7981
+ }
7982
+ async function fetchWebhooks(config) {
7983
+ const client = new ClickUpClient(config);
7984
+ return client.getWebhooks();
7985
+ }
7986
+ async function createWebhookCommand(config, opts) {
7987
+ const client = new ClickUpClient(config);
7988
+ const events = opts.events.split(",").map((e) => e.trim());
7989
+ const scopeOpts = {};
7990
+ if (opts.space) scopeOpts.spaceId = opts.space;
7991
+ if (opts.folder) scopeOpts.folderId = opts.folder;
7992
+ if (opts.list) scopeOpts.listId = opts.list;
7993
+ if (opts.task) scopeOpts.taskId = opts.task;
7994
+ return client.createWebhook(opts.url, events, scopeOpts);
7995
+ }
7996
+ async function updateWebhookCommand(config, webhookId, opts) {
7997
+ const client = new ClickUpClient(config);
7998
+ const payload = {};
7999
+ if (opts.url) payload.endpoint = opts.url;
8000
+ if (opts.events) payload.events = opts.events.split(",").map((e) => e.trim());
8001
+ if (opts.status) payload.status = opts.status;
8002
+ return client.updateWebhook(webhookId, payload);
8003
+ }
8004
+ async function deleteWebhookCommand(config, webhookId, opts) {
8005
+ const client = new ClickUpClient(config);
8006
+ if (!opts.confirm) {
8007
+ if (!isTTY()) {
8008
+ throw new Error("Destructive operation requires --confirm flag in non-interactive mode");
8009
+ }
8010
+ const { confirm: confirm3 } = await import("@inquirer/prompts");
8011
+ const confirmed = await confirm3({
8012
+ message: `Delete webhook ${webhookId}? This cannot be undone.`,
8013
+ default: false
8014
+ });
8015
+ if (!confirmed) {
8016
+ throw new Error("Cancelled");
8017
+ }
8018
+ }
8019
+ await client.deleteWebhook(webhookId);
8020
+ return { webhookId, deleted: true };
8021
+ }
8022
+ function formatWebhooks(webhooks) {
8023
+ if (webhooks.length === 0) return "No webhooks found.";
8024
+ const rows = webhooks.map((w) => ({
8025
+ id: w.id,
8026
+ endpoint: w.endpoint,
8027
+ events: w.events.join(", "),
8028
+ status: w.status,
8029
+ scope: webhookScope(w)
8030
+ }));
8031
+ return formatTable(rows, WEBHOOK_COLUMNS);
8032
+ }
8033
+ function formatWebhooksMarkdown(webhooks) {
8034
+ if (webhooks.length === 0) return "No webhooks found.";
8035
+ const lines = [];
8036
+ for (const w of webhooks) {
8037
+ lines.push(`## ${w.id}`);
8038
+ lines.push("");
8039
+ lines.push(`- **Endpoint:** ${w.endpoint}`);
8040
+ lines.push(`- **Events:** ${w.events.join(", ")}`);
8041
+ lines.push(`- **Status:** ${w.status}`);
8042
+ lines.push(`- **Scope:** ${webhookScope(w)}`);
8043
+ lines.push("");
8044
+ }
8045
+ return lines.join("\n");
8046
+ }
8047
+
8048
+ // src/commands/time-estimate-by-user.ts
8049
+ async function timeEstimateByUserCommand(config, taskId, userId, duration, opts) {
8050
+ const client = new ClickUpClient(config);
8051
+ const timeMs = parseTimeEstimate(duration);
8052
+ const estimates = [{ assignee: userId, time: timeMs }];
8053
+ if (opts.replace) {
8054
+ return client.replaceTimeEstimatesByUser(taskId, estimates);
8055
+ }
8056
+ return client.updateTimeEstimatesByUser(taskId, estimates);
8057
+ }
8058
+
8059
+ // src/commands/shared.ts
8060
+ import chalk28 from "chalk";
8061
+ async function fetchSharedHierarchy(config) {
8062
+ const client = new ClickUpClient(config);
8063
+ return client.getSharedHierarchy();
8064
+ }
8065
+ function formatSharedHierarchy(hierarchy) {
8066
+ const { spaces, folders, lists } = hierarchy.shared;
8067
+ if (spaces.length === 0 && folders.length === 0 && lists.length === 0) {
8068
+ return "No shared items found.";
8069
+ }
8070
+ const lines = [];
8071
+ if (spaces.length > 0) {
8072
+ lines.push(chalk28.bold("Spaces"));
8073
+ for (const s of spaces) {
8074
+ lines.push(` ${s.name} ${chalk28.dim(s.id)}`);
8075
+ }
8076
+ }
8077
+ if (folders.length > 0) {
8078
+ if (lines.length > 0) lines.push("");
8079
+ lines.push(chalk28.bold("Folders"));
8080
+ for (const f of folders) {
8081
+ lines.push(` ${f.name} ${chalk28.dim(f.id)}`);
8082
+ }
8083
+ }
8084
+ if (lists.length > 0) {
8085
+ if (lines.length > 0) lines.push("");
8086
+ lines.push(chalk28.bold("Lists"));
8087
+ for (const l of lists) {
8088
+ lines.push(` ${l.name} ${chalk28.dim(l.id)}`);
8089
+ }
8090
+ }
8091
+ return lines.join("\n");
8092
+ }
8093
+ function formatSharedHierarchyMarkdown(hierarchy) {
8094
+ const { spaces, folders, lists } = hierarchy.shared;
8095
+ if (spaces.length === 0 && folders.length === 0 && lists.length === 0) {
8096
+ return "No shared items found.";
8097
+ }
8098
+ const lines = ["# Shared Hierarchy", ""];
8099
+ if (spaces.length > 0) {
8100
+ lines.push("## Spaces", "");
8101
+ for (const s of spaces) {
8102
+ lines.push(`- **${s.name}** (${s.id})`);
8103
+ }
8104
+ lines.push("");
8105
+ }
8106
+ if (folders.length > 0) {
8107
+ lines.push("## Folders", "");
8108
+ for (const f of folders) {
8109
+ lines.push(`- **${f.name}** (${f.id})`);
8110
+ }
8111
+ lines.push("");
8112
+ }
8113
+ if (lists.length > 0) {
8114
+ lines.push("## Lists", "");
8115
+ for (const l of lists) {
8116
+ lines.push(`- **${l.name}** (${l.id})`);
8117
+ }
8118
+ lines.push("");
8119
+ }
8120
+ return lines.join("\n");
8121
+ }
8122
+
8123
+ // src/commands/list-comments.ts
8124
+ async function fetchListComments(config, listId) {
8125
+ const client = new ClickUpClient(config);
8126
+ const comments = await client.getListComments(listId);
8127
+ return comments.map((c) => ({
8128
+ id: c.id,
8129
+ user: c.user.username,
8130
+ date: c.date,
8131
+ text: c.comment_text
8132
+ }));
8133
+ }
8134
+ async function postListCommentCommand(config, listId, text, notifyAll) {
8135
+ if (!text.trim()) throw new Error("Comment text cannot be empty");
8136
+ const client = new ClickUpClient(config);
8137
+ return client.postListComment(listId, text, notifyAll);
8138
+ }
8139
+
8140
+ // src/commands/view-comments.ts
8141
+ async function fetchViewComments(config, viewId) {
8142
+ const client = new ClickUpClient(config);
8143
+ const comments = await client.getViewComments(viewId);
8144
+ return comments.map((c) => ({
8145
+ id: c.id,
8146
+ user: c.user.username,
8147
+ date: c.date,
8148
+ text: c.comment_text
8149
+ }));
8150
+ }
8151
+ async function postViewCommentCommand(config, viewId, text, notifyAll) {
8152
+ if (!text.trim()) throw new Error("Comment text cannot be empty");
8153
+ const client = new ClickUpClient(config);
8154
+ return client.postViewComment(viewId, text, notifyAll);
8155
+ }
8156
+
7565
8157
  // src/index.ts
7566
8158
  var require2 = createRequire(import.meta.url);
7567
8159
  var { version } = require2("../package.json");
@@ -8187,6 +8779,78 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
8187
8779
  }
8188
8780
  })
8189
8781
  );
8782
+ program.command("list-delete <listId>").description("Delete a list (requires confirmation)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8783
+ wrapAction(async (listId, opts) => {
8784
+ const config = loadConfig(getProfileName());
8785
+ const result = await deleteListCommand(config, listId, opts);
8786
+ if (shouldOutputJson(opts.json ?? false)) {
8787
+ console.log(JSON.stringify(result, null, 2));
8788
+ } else {
8789
+ console.log(`Deleted list ${result.listId}`);
8790
+ }
8791
+ })
8792
+ );
8793
+ program.command("folder-delete <folderId>").description("Delete a folder (requires confirmation)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8794
+ wrapAction(async (folderId, opts) => {
8795
+ const config = loadConfig(getProfileName());
8796
+ const result = await deleteFolderCommand(config, folderId, opts);
8797
+ if (shouldOutputJson(opts.json ?? false)) {
8798
+ console.log(JSON.stringify(result, null, 2));
8799
+ } else {
8800
+ console.log(`Deleted folder ${result.folderId}`);
8801
+ }
8802
+ })
8803
+ );
8804
+ program.command("space-delete <spaceId>").description("Delete a space (requires confirmation)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8805
+ wrapAction(async (spaceId, opts) => {
8806
+ const config = loadConfig(getProfileName());
8807
+ const result = await deleteSpaceCommand(config, spaceId, opts);
8808
+ if (shouldOutputJson(opts.json ?? false)) {
8809
+ console.log(JSON.stringify(result, null, 2));
8810
+ } else {
8811
+ console.log(`Deleted space ${result.spaceId}`);
8812
+ }
8813
+ })
8814
+ );
8815
+ program.command("attachments <taskId>").description("List attachments on a task").option("--json", "Force JSON output even in terminal").action(
8816
+ wrapAction(async (taskId, opts) => {
8817
+ const config = loadConfig(getProfileName());
8818
+ const attachments = await listTaskAttachments(config, taskId);
8819
+ if (shouldOutputJson(opts.json ?? false)) {
8820
+ console.log(JSON.stringify(attachments, null, 2));
8821
+ } else if (isTTY()) {
8822
+ console.log(formatAttachmentsTable(attachments));
8823
+ } else {
8824
+ console.log(formatAttachmentsMarkdown(attachments));
8825
+ }
8826
+ })
8827
+ );
8828
+ program.command("task-members <taskId>").description("List members with access to a task").option("--json", "Force JSON output even in terminal").action(
8829
+ wrapAction(async (taskId, opts) => {
8830
+ const config = loadConfig(getProfileName());
8831
+ const members = await listTaskMembers(config, taskId);
8832
+ if (shouldOutputJson(opts.json ?? false)) {
8833
+ console.log(JSON.stringify(members, null, 2));
8834
+ } else if (isTTY()) {
8835
+ console.log(formatTaskMembers(members));
8836
+ } else {
8837
+ console.log(formatTaskMembersMarkdown(members));
8838
+ }
8839
+ })
8840
+ );
8841
+ program.command("plan").description("Show workspace plan").option("--json", "Force JSON output even in terminal").action(
8842
+ wrapAction(async (opts) => {
8843
+ const config = loadConfig(getProfileName());
8844
+ const plan = await getWorkspacePlanCommand(config);
8845
+ if (shouldOutputJson(opts.json ?? false)) {
8846
+ console.log(JSON.stringify(plan, null, 2));
8847
+ } else if (isTTY()) {
8848
+ console.log(formatPlan(plan));
8849
+ } else {
8850
+ console.log(formatPlanMarkdown(plan));
8851
+ }
8852
+ })
8853
+ );
8190
8854
  program.command("archive <taskId>").description("Archive a task (or unarchive with --unarchive)").option("--unarchive", "Unarchive instead of archiving").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
8191
8855
  wrapAction(
8192
8856
  async (taskId, opts) => {
@@ -9575,6 +10239,140 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
9575
10239
  }
9576
10240
  })
9577
10241
  );
10242
+ program.command("merge <sourceTaskId> <intoTaskId>").description("Merge a task into another (source becomes subtask of target)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
10243
+ wrapAction(
10244
+ async (sourceTaskId, intoTaskId, opts) => {
10245
+ const config = loadConfig(getProfileName());
10246
+ const result = await mergeCommand(config, sourceTaskId, intoTaskId, opts);
10247
+ if (shouldOutputJson(opts.json ?? false)) {
10248
+ console.log(JSON.stringify(result, null, 2));
10249
+ } else {
10250
+ console.log(`Merged task ${result.sourceTaskId} into ${result.intoTaskId}`);
10251
+ }
10252
+ }
10253
+ )
10254
+ );
10255
+ timeCmd.command("estimate-by-user <taskId> <userId> <duration>").description(
10256
+ 'Set per-user time estimate (e.g. "2h", "30m"). Use --replace to overwrite all estimates.'
10257
+ ).option("--replace", "Replace all estimates (PUT) instead of updating (PATCH)").option("--json", "Force JSON output even in terminal").action(
10258
+ wrapAction(
10259
+ async (taskId, userId, duration, opts) => {
10260
+ const config = loadConfig(getProfileName());
10261
+ const result = await timeEstimateByUserCommand(config, taskId, userId, duration, opts);
10262
+ if (shouldOutputJson(opts.json ?? false)) {
10263
+ console.log(JSON.stringify(result, null, 2));
10264
+ } else {
10265
+ console.log(
10266
+ `${opts.replace ? "Replaced" : "Updated"} time estimate for user ${userId} on task ${taskId}`
10267
+ );
10268
+ }
10269
+ }
10270
+ )
10271
+ );
10272
+ program.command("shared").description("Show shared spaces, folders, and lists").option("--json", "Force JSON output even in terminal").action(
10273
+ wrapAction(async (opts) => {
10274
+ const config = loadConfig(getProfileName());
10275
+ const hierarchy = await fetchSharedHierarchy(config);
10276
+ if (shouldOutputJson(opts.json ?? false)) {
10277
+ console.log(JSON.stringify(hierarchy, null, 2));
10278
+ } else if (isTTY()) {
10279
+ console.log(formatSharedHierarchy(hierarchy));
10280
+ } else {
10281
+ console.log(formatSharedHierarchyMarkdown(hierarchy));
10282
+ }
10283
+ })
10284
+ );
10285
+ program.command("list-comments <listId>").description("List comments on a list").option("--json", "Force JSON output even in terminal").action(
10286
+ wrapAction(async (listId, opts) => {
10287
+ const config = loadConfig(getProfileName());
10288
+ const comments = await fetchListComments(config, listId);
10289
+ printComments(comments, opts.json ?? false);
10290
+ })
10291
+ );
10292
+ program.command("list-comment <listId>").description("Post a comment on a list").requiredOption("-m, --message <text>", "Comment text").option("--notify-all", "Notify all assignees").option("--json", "Force JSON output even in terminal").action(
10293
+ wrapAction(
10294
+ async (listId, opts) => {
10295
+ const config = loadConfig(getProfileName());
10296
+ const result = await postListCommentCommand(config, listId, opts.message, opts.notifyAll);
10297
+ if (shouldOutputJson(opts.json ?? false)) {
10298
+ console.log(JSON.stringify(result, null, 2));
10299
+ } else {
10300
+ console.log(formatCommentConfirmation(result.id));
10301
+ }
10302
+ }
10303
+ )
10304
+ );
10305
+ program.command("view-comments <viewId>").description("List comments on a view").option("--json", "Force JSON output even in terminal").action(
10306
+ wrapAction(async (viewId, opts) => {
10307
+ const config = loadConfig(getProfileName());
10308
+ const comments = await fetchViewComments(config, viewId);
10309
+ printComments(comments, opts.json ?? false);
10310
+ })
10311
+ );
10312
+ program.command("view-comment <viewId>").description("Post a comment on a view").requiredOption("-m, --message <text>", "Comment text").option("--notify-all", "Notify all assignees").option("--json", "Force JSON output even in terminal").action(
10313
+ wrapAction(
10314
+ async (viewId, opts) => {
10315
+ const config = loadConfig(getProfileName());
10316
+ const result = await postViewCommentCommand(config, viewId, opts.message, opts.notifyAll);
10317
+ if (shouldOutputJson(opts.json ?? false)) {
10318
+ console.log(JSON.stringify(result, null, 2));
10319
+ } else {
10320
+ console.log(formatCommentConfirmation(result.id));
10321
+ }
10322
+ }
10323
+ )
10324
+ );
10325
+ const webhookCmd = program.command("webhook").description("Manage webhooks");
10326
+ webhookCmd.command("list").description("List webhooks in your workspace").option("--json", "Force JSON output even in terminal").action(
10327
+ wrapAction(async (opts) => {
10328
+ const config = loadConfig(getProfileName());
10329
+ const webhooks = await fetchWebhooks(config);
10330
+ if (shouldOutputJson(opts.json ?? false)) {
10331
+ console.log(JSON.stringify(webhooks, null, 2));
10332
+ } else if (isTTY()) {
10333
+ console.log(formatWebhooks(webhooks));
10334
+ } else {
10335
+ console.log(formatWebhooksMarkdown(webhooks));
10336
+ }
10337
+ })
10338
+ );
10339
+ webhookCmd.command("create").description("Create a webhook").requiredOption("--url <url>", "Webhook endpoint URL").requiredOption("--events <events>", "Comma-separated event names").option("--space <id>", "Scope to a space").option("--folder <id>", "Scope to a folder").option("--list <id>", "Scope to a list").option("--task <id>", "Scope to a task").option("--json", "Force JSON output even in terminal").action(
10340
+ wrapAction(
10341
+ async (opts) => {
10342
+ const config = loadConfig(getProfileName());
10343
+ const result = await createWebhookCommand(config, opts);
10344
+ if (shouldOutputJson(opts.json ?? false)) {
10345
+ console.log(JSON.stringify(result, null, 2));
10346
+ } else {
10347
+ console.log(`Created webhook ${result.id} \u2192 ${result.endpoint}`);
10348
+ }
10349
+ }
10350
+ )
10351
+ );
10352
+ webhookCmd.command("update <webhookId>").description("Update a webhook").option("--url <url>", "New endpoint URL").option("--events <events>", "New comma-separated event names").option("--status <status>", "New status (active or inactive)").option("--json", "Force JSON output even in terminal").action(
10353
+ wrapAction(
10354
+ async (webhookId, opts) => {
10355
+ const config = loadConfig(getProfileName());
10356
+ const result = await updateWebhookCommand(config, webhookId, opts);
10357
+ if (shouldOutputJson(opts.json ?? false)) {
10358
+ console.log(JSON.stringify(result, null, 2));
10359
+ } else {
10360
+ console.log(`Updated webhook ${result.id}`);
10361
+ }
10362
+ }
10363
+ )
10364
+ );
10365
+ webhookCmd.command("delete <webhookId>").description("Delete a webhook (requires confirmation)").option("--confirm", "Skip confirmation prompt (required in non-interactive mode)").option("--json", "Force JSON output even in terminal").action(
10366
+ wrapAction(async (webhookId, opts) => {
10367
+ const config = loadConfig(getProfileName());
10368
+ const result = await deleteWebhookCommand(config, webhookId, opts);
10369
+ if (shouldOutputJson(opts.json ?? false)) {
10370
+ console.log(JSON.stringify(result, null, 2));
10371
+ } else {
10372
+ console.log(`Deleted webhook ${result.webhookId}`);
10373
+ }
10374
+ })
10375
+ );
9578
10376
  program.command("completion <shell>").description("Output shell completion script (bash, zsh, fish)").action(
9579
10377
  wrapAction(async (shell) => {
9580
10378
  const script = generateCompletion(shell, programName);