@krodak/clickup-cli 1.29.0 → 1.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +17 -16
- package/dist/index.js +513 -1
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +15 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clickup-cli",
|
|
3
3
|
"description": "ClickUp CLI skills for managing tasks, sprints, comments, checklists, custom fields, tags, and time tracking via the cup command",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.30.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Krzysztof Rodak"
|
|
7
7
|
},
|
package/README.md
CHANGED
|
@@ -171,22 +171,23 @@ npx skills add https://github.com/krodak/clickup-cli
|
|
|
171
171
|
|
|
172
172
|
Full CRUD for the core ClickUp workflow:
|
|
173
173
|
|
|
174
|
-
| Area | Capabilities
|
|
175
|
-
| -------------------- |
|
|
176
|
-
| ✅ **Tasks** | Create, read, update, delete, duplicate, search, subtasks, assign, dependencies, links, multi-list, bulk operations (status, assign, due-date, tag, priority, field, move) |
|
|
177
|
-
| 💬 **Comments** | Post, edit, delete by ID or by task scope for your own comments, threaded replies, notify all
|
|
178
|
-
| 🗨️ **Chat** | List channels, send messages, replies, reactions, channel management
|
|
179
|
-
| 📄 **Docs** | List, read, create, edit, delete (v3 API)
|
|
180
|
-
| ⏱️ **Time Tracking** | Start/stop timer, log entries, list/update/delete history
|
|
181
|
-
| ☑️ **Checklists** | View, create, delete, add/edit/delete items
|
|
182
|
-
| 🔧 **Custom Fields** | List, create, set, remove values (text, number, dropdown, labels, date, checkbox, url, email, rating, progress, relationship, people)
|
|
183
|
-
| 🏷️ **Tags** | Add/remove on tasks, space-level create/update/delete
|
|
184
|
-
| 🎯 **Goals & OKRs** | Goals CRUD, key results CRUD
|
|
185
|
-
| 🏃 **Sprints** | Auto-detect active sprint, `sprint:current` pseudo-ID for move/create, flexible date parsing, config override, favorite sprint folders
|
|
186
|
-
| ⭐ **Favorites** | Local favorites for quick access to sprint folders, spaces, lists, folders, views, tasks
|
|
187
|
-
| 👁️ **Views** | List, get, create, update, delete views on lists
|
|
188
|
-
|
|
|
189
|
-
|
|
|
174
|
+
| Area | Capabilities |
|
|
175
|
+
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
176
|
+
| ✅ **Tasks** | Create, read, update, delete, duplicate, merge, search, subtasks, assign, dependencies, links, multi-list, bulk operations (status, assign, due-date, tag, priority, field, move) |
|
|
177
|
+
| 💬 **Comments** | Post, edit, delete by ID or by task scope for your own comments, threaded replies, notify all, list and view comments |
|
|
178
|
+
| 🗨️ **Chat** | List channels, send messages, replies, reactions, channel management |
|
|
179
|
+
| 📄 **Docs** | List, read, create, edit, delete (v3 API) |
|
|
180
|
+
| ⏱️ **Time Tracking** | Start/stop timer, log entries, list/update/delete history, per-user time estimates |
|
|
181
|
+
| ☑️ **Checklists** | View, create, delete, add/edit/delete items |
|
|
182
|
+
| 🔧 **Custom Fields** | List, create, set, remove values (text, number, dropdown, labels, date, checkbox, url, email, rating, progress, relationship, people) |
|
|
183
|
+
| 🏷️ **Tags** | Add/remove on tasks, space-level create/update/delete |
|
|
184
|
+
| 🎯 **Goals & OKRs** | Goals CRUD, key results CRUD |
|
|
185
|
+
| 🏃 **Sprints** | Auto-detect active sprint, `sprint:current` pseudo-ID for move/create, flexible date parsing, config override, favorite sprint folders |
|
|
186
|
+
| ⭐ **Favorites** | Local favorites for quick access to sprint folders, spaces, lists, folders, views, tasks |
|
|
187
|
+
| 👁️ **Views** | List, get, create, update, delete views on lists |
|
|
188
|
+
| 🔗 **Webhooks** | List, create, update, delete webhooks; scope to space, folder, list, or task |
|
|
189
|
+
| 🏢 **Workspace** | Spaces, folders, lists (full CRUD + rename + from template), members, task types, templates, plan, shared hierarchy |
|
|
190
|
+
| 📎 **Attachments** | Upload files to tasks, list task attachments, shown in detail views |
|
|
190
191
|
|
|
191
192
|
[Full API coverage details](docs/api-coverage.md) | [Command reference](docs/commands.md)
|
|
192
193
|
|
package/dist/index.js
CHANGED
|
@@ -1000,6 +1000,77 @@ var ClickUpClient = class {
|
|
|
1000
1000
|
{ method: "DELETE" }
|
|
1001
1001
|
);
|
|
1002
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
|
+
}
|
|
1003
1074
|
};
|
|
1004
1075
|
|
|
1005
1076
|
// src/config.ts
|
|
@@ -4045,7 +4116,12 @@ var commandMetadata = [
|
|
|
4045
4116
|
description: "List my recent time entries (--all for team)"
|
|
4046
4117
|
},
|
|
4047
4118
|
{ section: "write", usage: "time update <timeEntryId>", description: "Update a time entry" },
|
|
4048
|
-
{ 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
|
+
}
|
|
4049
4125
|
]
|
|
4050
4126
|
},
|
|
4051
4127
|
{
|
|
@@ -4501,6 +4577,92 @@ var commandMetadata = [
|
|
|
4501
4577
|
{ section: "read", usage: "favorite list", description: "List saved favorites" }
|
|
4502
4578
|
]
|
|
4503
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
|
+
},
|
|
4504
4666
|
{
|
|
4505
4667
|
name: "chat",
|
|
4506
4668
|
description: "Chat channels and messaging",
|
|
@@ -7776,6 +7938,222 @@ function formatFavoritesMarkdown(favorites) {
|
|
|
7776
7938
|
return lines.join("\n");
|
|
7777
7939
|
}
|
|
7778
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
|
+
|
|
7779
8157
|
// src/index.ts
|
|
7780
8158
|
var require2 = createRequire(import.meta.url);
|
|
7781
8159
|
var { version } = require2("../package.json");
|
|
@@ -9861,6 +10239,140 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
9861
10239
|
}
|
|
9862
10240
|
})
|
|
9863
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
|
+
);
|
|
9864
10376
|
program.command("completion <shell>").description("Output shell completion script (bash, zsh, fish)").action(
|
|
9865
10377
|
wrapAction(async (shell) => {
|
|
9866
10378
|
const script = generateCompletion(shell, programName);
|
package/package.json
CHANGED
|
@@ -3,11 +3,11 @@ name: clickup
|
|
|
3
3
|
description: 'Use when managing ClickUp tasks, sprints, or comments via the `cup` CLI tool. Triggers: task queries, status updates, sprint tracking, creating subtasks, posting comments, threaded replies, standup summaries, searching tasks, checking overdue items, assigning tasks, listing spaces and lists, opening tasks in browser, checking auth or config, setting custom fields, deleting tasks, managing tags, managing checklists, editing comments, task links, time tracking, attachments, file uploads, listing members, listing fields, duplicating tasks, bulk operations, goals, key results, saved filters, favorites.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# ClickUp CLI (`cup`) - skill version 1.
|
|
6
|
+
# ClickUp CLI (`cup`) - skill version 1.30.1
|
|
7
7
|
|
|
8
8
|
Reference for AI agents using the `cup` CLI tool. Covers task management, sprint tracking, comments, time tracking, custom fields, goals, docs, and project workflows.
|
|
9
9
|
|
|
10
|
-
> **Version check:** Run `cup --version`. If your installed version is older than 1.
|
|
10
|
+
> **Version check:** Run `cup --version`. If your installed version is older than 1.30.1, update with `npm install -g @krodak/clickup-cli` and refresh this skill with `cup skill`.
|
|
11
11
|
|
|
12
12
|
## Install & Configure
|
|
13
13
|
|
|
@@ -144,6 +144,10 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
144
144
|
| `cup view <viewId>` | Get view details |
|
|
145
145
|
| `cup open <query>` | Open task in browser by ID or name |
|
|
146
146
|
| `cup auth` | Check authentication status |
|
|
147
|
+
| `cup list-comments <listId>` | Comments on a list |
|
|
148
|
+
| `cup view-comments <viewId>` | Comments on a view |
|
|
149
|
+
| `cup webhook list` | List webhooks in workspace |
|
|
150
|
+
| `cup shared` | Shared spaces, folders, and lists |
|
|
147
151
|
| `cup chat channels [--all] [--type type]` | List chat channels |
|
|
148
152
|
| `cup chat channel <id>` | Show channel details |
|
|
149
153
|
| `cup chat messages <channelId> [--limit n]` | List channel messages |
|
|
@@ -220,6 +224,13 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
220
224
|
| `cup view-create <listId> <name> -t <type> [--group-by field]` | Create a view on a list |
|
|
221
225
|
| `cup view-update <viewId> [-n name] [--group-by field]` | Update a view |
|
|
222
226
|
| `cup view-delete <viewId> [--confirm]` | Delete a view (DESTRUCTIVE) |
|
|
227
|
+
| `cup list-comment <listId> -m text [--notify-all]` | Post comment on a list |
|
|
228
|
+
| `cup view-comment <viewId> -m text [--notify-all]` | Post comment on a view |
|
|
229
|
+
| `cup webhook create --url <url> --events <events> [--space\|--folder\|--list\|--task <id>]` | Create a webhook |
|
|
230
|
+
| `cup webhook update <webhookId> [--url u] [--events e] [--status s]` | Update a webhook |
|
|
231
|
+
| `cup webhook delete <webhookId> [--confirm]` | Delete a webhook (DESTRUCTIVE) |
|
|
232
|
+
| `cup merge <sourceTaskId> <intoTaskId> [--confirm]` | Merge task into another (DESTRUCTIVE, source deleted) |
|
|
233
|
+
| `cup time estimate-by-user <taskId> <userId> <duration> [--replace]` | Set per-user time estimate |
|
|
223
234
|
| `cup chat send <channelId> -m <text> [--post --title t]` | Send message to channel |
|
|
224
235
|
| `cup chat reply <messageId> -m <text>` | Reply to a message |
|
|
225
236
|
| `cup chat react <messageId> --emoji <name>` | Add reaction |
|
|
@@ -288,7 +299,7 @@ When running in a terminal (not piped), task-listing commands (`cup tasks`, `cup
|
|
|
288
299
|
- **Enter** to confirm and view details of selected tasks
|
|
289
300
|
- After viewing details, prompted to open tasks in browser
|
|
290
301
|
|
|
291
|
-
Other interactive prompts: sprint selection (when multiple match), workspace selection (`cup init`), agent selection (`cup skill`), destructive action confirmations (`cup delete`, `cup list-delete`, `cup folder-delete`, `cup space-delete`, `cup archive`, `cup view-delete`).
|
|
302
|
+
Other interactive prompts: sprint selection (when multiple match), workspace selection (`cup init`), agent selection (`cup skill`), destructive action confirmations (`cup delete`, `cup list-delete`, `cup folder-delete`, `cup space-delete`, `cup archive`, `cup view-delete`, `cup merge`, `cup webhook delete`).
|
|
292
303
|
|
|
293
304
|
When piped or called with `--json`, all commands output non-interactive markdown or JSON. Agents should always use `--json` for structured data or pipe for markdown.
|
|
294
305
|
|
|
@@ -426,4 +437,4 @@ cup summary --hours 48 # wider window
|
|
|
426
437
|
|
|
427
438
|
## DELETE SAFETY
|
|
428
439
|
|
|
429
|
-
IMPORTANT: Always confirm with the user before running `cup delete`, `cup list-delete`, `cup folder-delete`,
|
|
440
|
+
IMPORTANT: Always confirm with the user before running `cup delete`, `cup list-delete`, `cup folder-delete`, `cup space-delete`, `cup merge`, or `cup webhook delete`. These are destructive, irreversible operations. Even when using `--confirm` flag, verify the ID is correct with the user first.
|