@krodak/clickup-cli 1.23.0 → 1.24.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/.claude-plugin/plugin.json +1 -1
- package/README.md +15 -15
- package/dist/index.js +137 -67
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +81 -76
|
@@ -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.24.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Krzysztof Rodak"
|
|
7
7
|
},
|
package/README.md
CHANGED
|
@@ -171,21 +171,21 @@ 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 status
|
|
177
|
-
| 💬 **Comments** | Post, edit, delete by ID or by task scope for your own comments, threaded replies, notify all
|
|
178
|
-
| 📄 **Docs** | List, read, create, edit, delete (v3 API)
|
|
179
|
-
| ⏱️ **Time Tracking** | Start/stop timer, log entries, list/update/delete history
|
|
180
|
-
| ☑️ **Checklists** | View, create, delete, add/edit/delete items
|
|
181
|
-
| 🔧 **Custom Fields** | List, create, set, remove values (dropdown, labels, date, checkbox,
|
|
182
|
-
| 🏷️ **Tags** | Add/remove on tasks, space-level create/update/delete
|
|
183
|
-
| 🎯 **Goals & OKRs** | Goals CRUD, key results CRUD
|
|
184
|
-
| 🏃 **Sprints** | Auto-detect active sprint, `sprint:current` pseudo-ID for move/create, flexible date parsing, config override, favorite sprint folders
|
|
185
|
-
| ⭐ **Favorites** | Local favorites for quick access to sprint folders, spaces, lists, folders, views, tasks
|
|
186
|
-
| 👁️ **Views** | List, get, create, update, delete views on lists
|
|
187
|
-
| 🏢 **Workspace** | Spaces, folders, lists (read + create + rename + from template), members, task types, templates
|
|
188
|
-
| 📎 **Attachments** | Upload files to tasks, shown in detail views
|
|
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
|
+
| 📄 **Docs** | List, read, create, edit, delete (v3 API) |
|
|
179
|
+
| ⏱️ **Time Tracking** | Start/stop timer, log entries, list/update/delete history |
|
|
180
|
+
| ☑️ **Checklists** | View, create, delete, add/edit/delete items |
|
|
181
|
+
| 🔧 **Custom Fields** | List, create, set, remove values (text, number, dropdown, labels, date, checkbox, url, email, rating, progress, relationship, people) |
|
|
182
|
+
| 🏷️ **Tags** | Add/remove on tasks, space-level create/update/delete |
|
|
183
|
+
| 🎯 **Goals & OKRs** | Goals CRUD, key results CRUD |
|
|
184
|
+
| 🏃 **Sprints** | Auto-detect active sprint, `sprint:current` pseudo-ID for move/create, flexible date parsing, config override, favorite sprint folders |
|
|
185
|
+
| ⭐ **Favorites** | Local favorites for quick access to sprint folders, spaces, lists, folders, views, tasks |
|
|
186
|
+
| 👁️ **Views** | List, get, create, update, delete views on lists |
|
|
187
|
+
| 🏢 **Workspace** | Spaces, folders, lists (read + create + rename + from template), members, task types, templates |
|
|
188
|
+
| 📎 **Attachments** | Upload files to tasks, shown in detail views |
|
|
189
189
|
|
|
190
190
|
[Full API coverage details](docs/api-coverage.md) | [Command reference](docs/commands.md)
|
|
191
191
|
|
package/dist/index.js
CHANGED
|
@@ -7,13 +7,15 @@ import { Command } from "commander";
|
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
|
+
// src/util/guards.ts
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
// src/api.ts
|
|
11
16
|
var BASE_URL = "https://api.clickup.com/api/v2";
|
|
12
17
|
var BASE_URL_V3 = "https://api.clickup.com/api/v3";
|
|
13
18
|
var MAX_PAGES = 100;
|
|
14
|
-
function isRecord(value) {
|
|
15
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
|
-
}
|
|
17
19
|
function expectRecord(value, context) {
|
|
18
20
|
if (!isRecord(value)) {
|
|
19
21
|
throw new Error(`Unexpected API response: expected ${context} object`);
|
|
@@ -549,10 +551,12 @@ var ClickUpClient = class {
|
|
|
549
551
|
async deleteChecklist(checklistId) {
|
|
550
552
|
await this.request(`/checklist/${checklistId}`, { method: "DELETE" });
|
|
551
553
|
}
|
|
552
|
-
async createChecklistItem(checklistId, name) {
|
|
554
|
+
async createChecklistItem(checklistId, name, parent) {
|
|
555
|
+
const body = { name };
|
|
556
|
+
if (parent !== void 0) body.parent = parent;
|
|
553
557
|
const data = await this.request(
|
|
554
558
|
`/checklist/${checklistId}/checklist_item`,
|
|
555
|
-
{ method: "POST", body: JSON.stringify(
|
|
559
|
+
{ method: "POST", body: JSON.stringify(body) }
|
|
556
560
|
);
|
|
557
561
|
return expectRecordField(
|
|
558
562
|
data,
|
|
@@ -887,8 +891,44 @@ var ClickUpClient = class {
|
|
|
887
891
|
import fs from "fs";
|
|
888
892
|
import { homedir } from "os";
|
|
889
893
|
import { join } from "path";
|
|
890
|
-
|
|
891
|
-
|
|
894
|
+
var VALID_FAVORITE_TYPES = /* @__PURE__ */ new Set([
|
|
895
|
+
"sprint-folder",
|
|
896
|
+
"space",
|
|
897
|
+
"list",
|
|
898
|
+
"folder",
|
|
899
|
+
"view",
|
|
900
|
+
"task"
|
|
901
|
+
]);
|
|
902
|
+
function isFilterEntry(v) {
|
|
903
|
+
if (!isRecord(v)) return false;
|
|
904
|
+
if (!Array.isArray(v.command)) return false;
|
|
905
|
+
if (!v.command.every((c) => typeof c === "string")) return false;
|
|
906
|
+
if (v.description !== void 0 && typeof v.description !== "string") return false;
|
|
907
|
+
return true;
|
|
908
|
+
}
|
|
909
|
+
function parseFiltersMap(raw) {
|
|
910
|
+
if (!isRecord(raw)) return {};
|
|
911
|
+
const out = {};
|
|
912
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
913
|
+
if (isFilterEntry(value)) out[key] = value;
|
|
914
|
+
}
|
|
915
|
+
return out;
|
|
916
|
+
}
|
|
917
|
+
function isFavoriteEntry(v) {
|
|
918
|
+
if (!isRecord(v)) return false;
|
|
919
|
+
if (typeof v.type !== "string") return false;
|
|
920
|
+
if (!VALID_FAVORITE_TYPES.has(v.type)) return false;
|
|
921
|
+
if (typeof v.id !== "string") return false;
|
|
922
|
+
if (v.name !== void 0 && typeof v.name !== "string") return false;
|
|
923
|
+
return true;
|
|
924
|
+
}
|
|
925
|
+
function parseFavoritesMap(raw) {
|
|
926
|
+
if (!isRecord(raw)) return {};
|
|
927
|
+
const out = {};
|
|
928
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
929
|
+
if (isFavoriteEntry(value)) out[key] = value;
|
|
930
|
+
}
|
|
931
|
+
return out;
|
|
892
932
|
}
|
|
893
933
|
function readConfigString(parsed, key, path, strict) {
|
|
894
934
|
const value = parsed[key];
|
|
@@ -912,7 +952,7 @@ function parseConfigFile(raw, path, strictFields, strictRoot = strictFields) {
|
|
|
912
952
|
}
|
|
913
953
|
return {};
|
|
914
954
|
}
|
|
915
|
-
if (!
|
|
955
|
+
if (!isRecord(parsed)) {
|
|
916
956
|
if (strictRoot) {
|
|
917
957
|
throw new Error(`Config file at ${path} must contain a JSON object.`);
|
|
918
958
|
}
|
|
@@ -978,18 +1018,18 @@ function migrateToMultiProfile(parsed, filePath) {
|
|
|
978
1018
|
});
|
|
979
1019
|
return migrated;
|
|
980
1020
|
}
|
|
981
|
-
if (
|
|
1021
|
+
if (isRecord(parsed.profiles)) {
|
|
982
1022
|
const profiles = {};
|
|
983
1023
|
for (const [name, value] of Object.entries(parsed.profiles)) {
|
|
984
|
-
if (
|
|
1024
|
+
if (isRecord(value)) {
|
|
985
1025
|
const p = {};
|
|
986
1026
|
if (typeof value.apiToken === "string" && value.apiToken.trim())
|
|
987
1027
|
p.apiToken = value.apiToken.trim();
|
|
988
1028
|
if (typeof value.teamId === "string" && value.teamId.trim()) p.teamId = value.teamId.trim();
|
|
989
1029
|
if (typeof value.sprintFolderId === "string" && value.sprintFolderId.trim())
|
|
990
1030
|
p.sprintFolderId = value.sprintFolderId.trim();
|
|
991
|
-
if (
|
|
992
|
-
if (
|
|
1031
|
+
if (value.filters !== void 0) p.filters = parseFiltersMap(value.filters);
|
|
1032
|
+
if (value.favorites !== void 0) p.favorites = parseFavoritesMap(value.favorites);
|
|
993
1033
|
profiles[name] = p;
|
|
994
1034
|
}
|
|
995
1035
|
}
|
|
@@ -1010,7 +1050,7 @@ function parseRawConfig(filePath) {
|
|
|
1010
1050
|
`Config file at ${filePath} contains invalid JSON. Please check the file syntax.`
|
|
1011
1051
|
);
|
|
1012
1052
|
}
|
|
1013
|
-
if (!
|
|
1053
|
+
if (!isRecord(parsed)) {
|
|
1014
1054
|
throw new Error(`Config file at ${filePath} must contain a JSON object.`);
|
|
1015
1055
|
}
|
|
1016
1056
|
return { parsed, raw };
|
|
@@ -1100,7 +1140,7 @@ function loadMultiProfileConfig() {
|
|
|
1100
1140
|
} catch {
|
|
1101
1141
|
return { defaultProfile: "", profiles: {} };
|
|
1102
1142
|
}
|
|
1103
|
-
if (!
|
|
1143
|
+
if (!isRecord(parsed)) return { defaultProfile: "", profiles: {} };
|
|
1104
1144
|
if (isOldFormat(parsed)) {
|
|
1105
1145
|
return migrateToMultiProfile(parsed, path);
|
|
1106
1146
|
}
|
|
@@ -1165,7 +1205,7 @@ function loadRawConfig(profileName) {
|
|
|
1165
1205
|
} catch {
|
|
1166
1206
|
return {};
|
|
1167
1207
|
}
|
|
1168
|
-
if (!
|
|
1208
|
+
if (!isRecord(parsed)) {
|
|
1169
1209
|
throw new Error(`Config file at ${path} must contain a JSON object.`);
|
|
1170
1210
|
}
|
|
1171
1211
|
if (isOldFormat(parsed)) {
|
|
@@ -1565,7 +1605,7 @@ function formatCustomFieldValue(field) {
|
|
|
1565
1605
|
switch (field.type) {
|
|
1566
1606
|
case "drop_down": {
|
|
1567
1607
|
if (!options) return stringifyFieldValue(field.value);
|
|
1568
|
-
const match = options.find((o) => o.
|
|
1608
|
+
const match = options.find((o) => o.orderindex === Number(field.value));
|
|
1569
1609
|
return match?.name ?? stringifyFieldValue(field.value);
|
|
1570
1610
|
}
|
|
1571
1611
|
case "labels": {
|
|
@@ -5443,7 +5483,7 @@ function parseFieldValue(field, rawValue) {
|
|
|
5443
5483
|
const available = options.map((o) => o.name).join(", ");
|
|
5444
5484
|
throw new Error(`Label "${name}" not found. Available: ${available}`);
|
|
5445
5485
|
}
|
|
5446
|
-
ids.push(
|
|
5486
|
+
ids.push(option.id);
|
|
5447
5487
|
}
|
|
5448
5488
|
return ids;
|
|
5449
5489
|
}
|
|
@@ -5600,9 +5640,9 @@ async function deleteChecklist(config, checklistId) {
|
|
|
5600
5640
|
await client.deleteChecklist(checklistId);
|
|
5601
5641
|
return { checklistId };
|
|
5602
5642
|
}
|
|
5603
|
-
async function addChecklistItem(config, checklistId, name) {
|
|
5643
|
+
async function addChecklistItem(config, checklistId, name, parent) {
|
|
5604
5644
|
const client = new ClickUpClient(config);
|
|
5605
|
-
return client.createChecklistItem(checklistId, name);
|
|
5645
|
+
return client.createChecklistItem(checklistId, name, parent);
|
|
5606
5646
|
}
|
|
5607
5647
|
async function editChecklistItem(config, checklistId, checklistItemId, updates) {
|
|
5608
5648
|
const client = new ClickUpClient(config);
|
|
@@ -5613,31 +5653,59 @@ async function deleteChecklistItem(config, checklistId, checklistItemId) {
|
|
|
5613
5653
|
await client.deleteChecklistItem(checklistId, checklistItemId);
|
|
5614
5654
|
return { checklistId, checklistItemId };
|
|
5615
5655
|
}
|
|
5656
|
+
function sortByOrder(items) {
|
|
5657
|
+
return [...items].sort((a, b) => (a.orderindex ?? 0) - (b.orderindex ?? 0));
|
|
5658
|
+
}
|
|
5659
|
+
function countItems(items) {
|
|
5660
|
+
let total = 0;
|
|
5661
|
+
let resolved = 0;
|
|
5662
|
+
for (const item of items) {
|
|
5663
|
+
total++;
|
|
5664
|
+
if (item.resolved) resolved++;
|
|
5665
|
+
if (item.children?.length) {
|
|
5666
|
+
const nested = countItems(item.children);
|
|
5667
|
+
total += nested.total;
|
|
5668
|
+
resolved += nested.resolved;
|
|
5669
|
+
}
|
|
5670
|
+
}
|
|
5671
|
+
return { total, resolved };
|
|
5672
|
+
}
|
|
5616
5673
|
function formatChecklists(checklists) {
|
|
5617
5674
|
if (checklists.length === 0) return "No checklists";
|
|
5618
5675
|
const lines = [];
|
|
5676
|
+
const renderItem = (item, depth) => {
|
|
5677
|
+
const indent = " ".repeat(depth + 1);
|
|
5678
|
+
const check = item.resolved ? chalk9.green("[x]") : chalk9.dim("[ ]");
|
|
5679
|
+
const name = item.resolved ? chalk9.dim(item.name) : item.name;
|
|
5680
|
+
const assignee = item.assignee ? chalk9.dim(` @${item.assignee.username}`) : "";
|
|
5681
|
+
lines.push(`${indent}${check} ${name}${assignee}`);
|
|
5682
|
+
lines.push(chalk9.dim(`${indent} item-id: ${item.id}`));
|
|
5683
|
+
for (const child of sortByOrder(item.children ?? [])) {
|
|
5684
|
+
renderItem(child, depth + 1);
|
|
5685
|
+
}
|
|
5686
|
+
};
|
|
5619
5687
|
for (const cl of checklists) {
|
|
5620
|
-
const resolved = cl.items
|
|
5621
|
-
lines.push(chalk9.bold(`${cl.name} (${resolved}/${
|
|
5688
|
+
const { total, resolved } = countItems(cl.items);
|
|
5689
|
+
lines.push(chalk9.bold(`${cl.name} (${resolved}/${total})`));
|
|
5622
5690
|
lines.push(chalk9.dim(` ID: ${cl.id}`));
|
|
5623
|
-
for (const item of cl.items)
|
|
5624
|
-
const check = item.resolved ? chalk9.green("[x]") : chalk9.dim("[ ]");
|
|
5625
|
-
const name = item.resolved ? chalk9.dim(item.name) : item.name;
|
|
5626
|
-
const assignee = item.assignee ? chalk9.dim(` @${item.assignee.username}`) : "";
|
|
5627
|
-
lines.push(` ${check} ${name}${assignee}`);
|
|
5628
|
-
lines.push(chalk9.dim(` item-id: ${item.id}`));
|
|
5629
|
-
}
|
|
5691
|
+
for (const item of sortByOrder(cl.items)) renderItem(item, 0);
|
|
5630
5692
|
}
|
|
5631
5693
|
return lines.join("\n");
|
|
5632
5694
|
}
|
|
5633
5695
|
function formatChecklistsMarkdown(checklists) {
|
|
5634
5696
|
if (checklists.length === 0) return "No checklists";
|
|
5697
|
+
const renderItem = (item, depth) => {
|
|
5698
|
+
const indent = " ".repeat(depth);
|
|
5699
|
+
const lines = [`${indent}- [${item.resolved ? "x" : " "}] ${item.name}`];
|
|
5700
|
+
for (const child of sortByOrder(item.children ?? [])) {
|
|
5701
|
+
lines.push(...renderItem(child, depth + 1));
|
|
5702
|
+
}
|
|
5703
|
+
return lines;
|
|
5704
|
+
};
|
|
5635
5705
|
return checklists.map((cl) => {
|
|
5636
|
-
const resolved = cl.items
|
|
5637
|
-
const header = `### ${cl.name} (${resolved}/${
|
|
5638
|
-
const items = cl.items.
|
|
5639
|
-
(item) => `- [${item.resolved ? "x" : " "}] ${item.name}`
|
|
5640
|
-
);
|
|
5706
|
+
const { total, resolved } = countItems(cl.items);
|
|
5707
|
+
const header = `### ${cl.name} (${resolved}/${total})`;
|
|
5708
|
+
const items = sortByOrder(cl.items).flatMap((item) => renderItem(item, 0));
|
|
5641
5709
|
return [header, "", ...items].join("\n");
|
|
5642
5710
|
}).join("\n\n");
|
|
5643
5711
|
}
|
|
@@ -6124,31 +6192,34 @@ async function duplicateTask(config, taskId) {
|
|
|
6124
6192
|
return { id: created.id, name: created.name, url: created.url };
|
|
6125
6193
|
}
|
|
6126
6194
|
|
|
6127
|
-
// src/
|
|
6128
|
-
var BULK_CONCURRENCY = 5;
|
|
6195
|
+
// src/util/batch.ts
|
|
6129
6196
|
async function runInBatches(items, concurrency, fn) {
|
|
6197
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
6198
|
+
throw new Error(`concurrency must be a positive integer, got ${concurrency}`);
|
|
6199
|
+
}
|
|
6130
6200
|
const results = [];
|
|
6131
6201
|
for (let i = 0; i < items.length; i += concurrency) {
|
|
6132
6202
|
const batch = items.slice(i, i + concurrency);
|
|
6133
|
-
const
|
|
6134
|
-
|
|
6203
|
+
const settled = await Promise.allSettled(batch.map(fn));
|
|
6204
|
+
settled.forEach((res, idx) => {
|
|
6135
6205
|
const item = batch[idx];
|
|
6136
6206
|
if (res.status === "fulfilled") {
|
|
6137
|
-
results.push({ item, result: res.value });
|
|
6207
|
+
results.push({ item, ok: true, result: res.value });
|
|
6138
6208
|
} else {
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
error: res.reason instanceof Error ? res.reason : new Error(String(res.reason))
|
|
6142
|
-
});
|
|
6209
|
+
const error = res.reason instanceof Error ? res.reason : new Error(String(res.reason), { cause: res.reason });
|
|
6210
|
+
results.push({ item, ok: false, error });
|
|
6143
6211
|
}
|
|
6144
6212
|
});
|
|
6145
6213
|
}
|
|
6146
6214
|
return results;
|
|
6147
6215
|
}
|
|
6216
|
+
|
|
6217
|
+
// src/commands/bulk.ts
|
|
6218
|
+
var BULK_CONCURRENCY = 5;
|
|
6148
6219
|
function toBulkResult(outcomes) {
|
|
6149
6220
|
const failed = [];
|
|
6150
6221
|
for (const outcome of outcomes) {
|
|
6151
|
-
if (outcome.
|
|
6222
|
+
if (!outcome.ok) {
|
|
6152
6223
|
failed.push({ id: outcome.item, reason: outcome.error.message });
|
|
6153
6224
|
}
|
|
6154
6225
|
}
|
|
@@ -6484,14 +6555,14 @@ async function createView(config, listId, name, opts) {
|
|
|
6484
6555
|
}
|
|
6485
6556
|
|
|
6486
6557
|
// src/commands/view-update.ts
|
|
6487
|
-
var VALID_GROUP_BY_FIELDS2 = [
|
|
6558
|
+
var VALID_GROUP_BY_FIELDS2 = /* @__PURE__ */ new Set([
|
|
6488
6559
|
"status",
|
|
6489
6560
|
"assignee",
|
|
6490
6561
|
"priority",
|
|
6491
6562
|
"due_date",
|
|
6492
6563
|
"tag",
|
|
6493
6564
|
"sprint"
|
|
6494
|
-
];
|
|
6565
|
+
]);
|
|
6495
6566
|
async function updateView(config, viewId, opts) {
|
|
6496
6567
|
if (opts.name === void 0 && opts.groupBy === void 0) {
|
|
6497
6568
|
throw new Error("Provide at least one of: --name, --group-by");
|
|
@@ -6502,9 +6573,9 @@ async function updateView(config, viewId, opts) {
|
|
|
6502
6573
|
const payload = {};
|
|
6503
6574
|
if (opts.name) payload.name = opts.name;
|
|
6504
6575
|
if (opts.groupBy) {
|
|
6505
|
-
if (!VALID_GROUP_BY_FIELDS2.
|
|
6576
|
+
if (!VALID_GROUP_BY_FIELDS2.has(opts.groupBy)) {
|
|
6506
6577
|
throw new Error(
|
|
6507
|
-
`Invalid group-by field "${opts.groupBy}". Valid fields: ${VALID_GROUP_BY_FIELDS2.join(", ")}`
|
|
6578
|
+
`Invalid group-by field "${opts.groupBy}". Valid fields: ${[...VALID_GROUP_BY_FIELDS2].join(", ")}`
|
|
6508
6579
|
);
|
|
6509
6580
|
}
|
|
6510
6581
|
payload.grouping = { field: opts.groupBy };
|
|
@@ -7394,18 +7465,23 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
7394
7465
|
}
|
|
7395
7466
|
})
|
|
7396
7467
|
);
|
|
7397
|
-
checklistCmd.command("add-item <checklistId> <name>").description("Add an item to a checklist").option("--json", "Force JSON output even in terminal").action(
|
|
7398
|
-
wrapAction(
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7468
|
+
checklistCmd.command("add-item <checklistId> <name>").description("Add an item to a checklist").option("--parent <itemId>", "Nest under a parent checklist item ID").option("--json", "Force JSON output even in terminal").action(
|
|
7469
|
+
wrapAction(
|
|
7470
|
+
async (checklistId, name, opts) => {
|
|
7471
|
+
const config = loadConfig(getProfileName());
|
|
7472
|
+
const result = await addChecklistItem(config, checklistId, name, opts.parent);
|
|
7473
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
7474
|
+
console.log(JSON.stringify(result, null, 2));
|
|
7475
|
+
} else {
|
|
7476
|
+
console.log(`Added item "${name}" to checklist ${checklistId}`);
|
|
7477
|
+
}
|
|
7405
7478
|
}
|
|
7406
|
-
|
|
7479
|
+
)
|
|
7407
7480
|
);
|
|
7408
|
-
checklistCmd.command("edit-item <checklistId> <checklistItemId>").description("Edit a checklist item").option("--name <name>", "New item name").option("--resolved", "Mark item as resolved").option("--unresolved", "Mark item as unresolved").option("--assignee <userId>", 'Assign user by ID (use "null" to unassign)').option(
|
|
7481
|
+
checklistCmd.command("edit-item <checklistId> <checklistItemId>").description("Edit a checklist item").option("--name <name>", "New item name").option("--resolved", "Mark item as resolved").option("--unresolved", "Mark item as unresolved").option("--assignee <userId>", 'Assign user by ID (use "null" to unassign)').option(
|
|
7482
|
+
"--parent <itemId>",
|
|
7483
|
+
'Reparent item under another checklist item ID (use "null" to unnest)'
|
|
7484
|
+
).option("--json", "Force JSON output even in terminal").action(
|
|
7409
7485
|
wrapAction(
|
|
7410
7486
|
async (checklistId, checklistItemId, opts) => {
|
|
7411
7487
|
const config = loadConfig(getProfileName());
|
|
@@ -7416,6 +7492,9 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
7416
7492
|
if (opts.assignee !== void 0) {
|
|
7417
7493
|
updates.assignee = opts.assignee === "null" ? null : parseOptionalNumberOption(opts.assignee, "--assignee");
|
|
7418
7494
|
}
|
|
7495
|
+
if (opts.parent !== void 0) {
|
|
7496
|
+
updates.parent = opts.parent === "null" ? null : opts.parent;
|
|
7497
|
+
}
|
|
7419
7498
|
const result = await editChecklistItem(config, checklistId, checklistItemId, updates);
|
|
7420
7499
|
if (shouldOutputJson(opts.json ?? false)) {
|
|
7421
7500
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -7682,16 +7761,7 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
|
|
|
7682
7761
|
wrapAction(async (status, taskIds, opts) => {
|
|
7683
7762
|
const config = loadConfig(getProfileName());
|
|
7684
7763
|
const result = await bulkUpdateStatus(config, taskIds, status);
|
|
7685
|
-
|
|
7686
|
-
console.log(JSON.stringify(result, null, 2));
|
|
7687
|
-
} else {
|
|
7688
|
-
console.log(`Updated ${result.updated} tasks to "${status}"`);
|
|
7689
|
-
if (result.failed.length > 0) {
|
|
7690
|
-
for (const f of result.failed) {
|
|
7691
|
-
console.log(` Failed ${f.id}: ${f.reason}`);
|
|
7692
|
-
}
|
|
7693
|
-
}
|
|
7694
|
-
}
|
|
7764
|
+
outputBulkResult(result, opts.json ?? false, `status ${status}`);
|
|
7695
7765
|
})
|
|
7696
7766
|
);
|
|
7697
7767
|
bulkCmd.command("assign <taskIds...>").description("Bulk assign or unassign a user from tasks").option("--to <userId>", 'Add this user (user ID or "me")').option("--remove <userId>", 'Remove this user (user ID or "me")').option("--json", "Force JSON output even in terminal").action(
|
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.24.0
|
|
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.24.0, update with `npm install -g @krodak/clickup-cli` and refresh this skill with `cup skill`.
|
|
11
11
|
|
|
12
12
|
## Install & Configure
|
|
13
13
|
|
|
@@ -120,10 +120,11 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
120
120
|
| `cup activity <id>` | Task details + comment history combined |
|
|
121
121
|
| `cup inbox [--days n] [--include-closed]` | Tasks updated in last n days (default 30) |
|
|
122
122
|
| `cup summary [--hours n]` | Standup: completed, in-progress, overdue |
|
|
123
|
-
| `cup overdue [--include-closed]`
|
|
124
|
-
| `cup spaces [--name partial] [--my]`
|
|
125
|
-
| `cup lists <spaceId> [--name partial]`
|
|
126
|
-
| `cup folders <spaceId> [--name partial]`
|
|
123
|
+
| `cup overdue [--all] [--include-closed]` | Tasks past due date (most overdue first) |
|
|
124
|
+
| `cup spaces [--name partial] [--my] [--archived]` | List/filter workspace spaces |
|
|
125
|
+
| `cup lists <spaceId> [--name partial] [--archived]` | Lists in a space (including folder lists) |
|
|
126
|
+
| `cup folders <spaceId> [--name partial] [--archived]` | Folders in a space (with their lists) |
|
|
127
|
+
| `cup time-in-status <id>` | Show how long a task has been in each status |
|
|
127
128
|
| `cup members` | Workspace members (username, ID, email) |
|
|
128
129
|
| `cup fields <listId>` | Custom fields on a list (type, required, options) |
|
|
129
130
|
| `cup tags <spaceId>` | Tags available in a space |
|
|
@@ -143,76 +144,78 @@ All commands support `--help` for full flag details. All commands support `--jso
|
|
|
143
144
|
|
|
144
145
|
### Write
|
|
145
146
|
|
|
146
|
-
| Command
|
|
147
|
-
|
|
|
148
|
-
| `cup create -n name [-l listId\|sprint:current] [-p parentId] [-d desc] [-s status] [--priority p] [--due-date d] [--start-date d] [--time-estimate t] [--assignee id\|me] [--tags t] [--custom-item-id n] [--template id] [--field "Name" val]`
|
|
149
|
-
| `cup update <id> [-n name] [-d desc] [-s status] [--priority p] [--due-date d\|none] [--start-date d] [--time-estimate t] [--assignee id\|me] [--remove-assignee id\|me] [--parent id] [--detach] [--archive] [--unarchive] [--field "Name" val]` | Update task fields (including custom fields) |
|
|
150
|
-
| `cup comment <id> -m text [--notify-all]`
|
|
151
|
-
| `cup comment-edit <commentId> -m text [--resolved] [--unresolved]`
|
|
152
|
-
| `cup comment-delete <commentId>` or `cup comment-delete --task <taskId> --mine [--match text]`
|
|
153
|
-
| `cup replies <commentId>`
|
|
154
|
-
| `cup reply <commentId> -m text [--notify-all]`
|
|
155
|
-
| `cup assign <id> [--to ids\|me] [--remove ids\|me]`
|
|
156
|
-
| `cup depend <id> [--on taskId] [--blocks taskId] [--remove]`
|
|
157
|
-
| `cup move <id> [--to listId\|sprint:current] [--remove listId]`
|
|
158
|
-
| `cup field <id> [--set "Name" value] [--remove "Name"]`
|
|
159
|
-
| `cup field-create <name> -t <type> [-d desc] [--options "a,b,c"] [--required]`
|
|
160
|
-
| `cup tag <id> [--add tags] [--remove tags]`
|
|
161
|
-
| `cup link <taskId> <linksTo> [--remove]`
|
|
162
|
-
| `cup attach <taskId> <filePath>`
|
|
163
|
-
| `cup delete <id> [--confirm]`
|
|
164
|
-
| `cup duplicate <taskId>`
|
|
165
|
-
| `cup bulk status <status> <taskIds...>`
|
|
166
|
-
| `cup bulk assign <taskIds...> [--to userId\|me] [--remove userId\|me]`
|
|
167
|
-
| `cup bulk due-date <date\|none\|clear> <taskIds...>`
|
|
168
|
-
| `cup bulk tag <tagName> <taskIds...> [--
|
|
169
|
-
| `cup bulk
|
|
170
|
-
| `cup
|
|
171
|
-
| `cup
|
|
172
|
-
| `cup checklist
|
|
173
|
-
| `cup checklist
|
|
174
|
-
| `cup checklist
|
|
175
|
-
| `cup checklist
|
|
176
|
-
| `cup
|
|
177
|
-
| `cup
|
|
178
|
-
| `cup time
|
|
179
|
-
| `cup time
|
|
180
|
-
| `cup time
|
|
181
|
-
| `cup time
|
|
182
|
-
| `cup time
|
|
183
|
-
| `cup
|
|
184
|
-
| `cup
|
|
185
|
-
| `cup goal-
|
|
186
|
-
| `cup
|
|
187
|
-
| `cup
|
|
188
|
-
| `cup key-result-
|
|
189
|
-
| `cup
|
|
190
|
-
| `cup
|
|
191
|
-
| `cup doc-
|
|
192
|
-
| `cup doc-
|
|
193
|
-
| `cup doc-page-
|
|
194
|
-
| `cup
|
|
195
|
-
| `cup
|
|
196
|
-
| `cup
|
|
197
|
-
| `cup list-
|
|
198
|
-
| `cup folder-
|
|
199
|
-
| `cup
|
|
200
|
-
| `cup
|
|
201
|
-
| `cup
|
|
202
|
-
| `cup tag-
|
|
203
|
-
| `cup
|
|
204
|
-
| `cup
|
|
205
|
-
| `cup
|
|
206
|
-
| `cup view-
|
|
207
|
-
| `cup
|
|
208
|
-
| `cup
|
|
209
|
-
| `cup favorite
|
|
210
|
-
| `cup
|
|
211
|
-
| `cup
|
|
212
|
-
| `cup profile
|
|
213
|
-
| `cup profile
|
|
214
|
-
| `cup
|
|
215
|
-
| `cup
|
|
147
|
+
| Command | What it does |
|
|
148
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
149
|
+
| `cup create -n name [-l listId\|sprint:current] [-p parentId] [-d desc] [-s status] [--priority p] [--due-date d] [--start-date d] [--time-estimate t] [--assignee id\|me] [--tags t] [--custom-item-id n] [--template id] [--field "Name" val]` | Create task (`--list` accepts `sprint:current`, `--field` sets custom fields inline) |
|
|
150
|
+
| `cup update <id> [-n name] [-d desc] [-s status] [--priority p] [--due-date d\|none] [--start-date d] [--time-estimate t] [--assignee id\|me] [--remove-assignee id\|me] [--parent id] [--detach] [--archive] [--unarchive] [--type type] [--field "Name" val]` | Update task fields (including custom fields) |
|
|
151
|
+
| `cup comment <id> -m text [--notify-all]` | Post comment on task |
|
|
152
|
+
| `cup comment-edit <commentId> -m text [--resolved] [--unresolved]` | Edit a comment |
|
|
153
|
+
| `cup comment-delete <commentId>` or `cup comment-delete --task <taskId> --mine [--match text]` | Delete a comment by ID or delete one of your task comments |
|
|
154
|
+
| `cup replies <commentId>` | List threaded replies |
|
|
155
|
+
| `cup reply <commentId> -m text [--notify-all]` | Reply to a comment |
|
|
156
|
+
| `cup assign <id> [--to ids\|me] [--remove ids\|me]` | Assign/unassign users (`--to`/`--remove` accept comma-separated IDs) |
|
|
157
|
+
| `cup depend <id> [--on taskId] [--blocks taskId] [--remove]` | Add/remove dependencies |
|
|
158
|
+
| `cup move <id> [--to listId\|sprint:current] [--remove listId]` | Add/remove task from lists (`--to` accepts `sprint:current`) |
|
|
159
|
+
| `cup field <id> [--set "Name" value] [--remove "Name"]` | Set/remove custom field values |
|
|
160
|
+
| `cup field-create <name> -t <type> [-d desc] [--options "a,b,c"] [--required]` | Create a custom field |
|
|
161
|
+
| `cup tag <id> [--add tags] [--remove tags]` | Add/remove tags on a task |
|
|
162
|
+
| `cup link <taskId> <linksTo> [--remove]` | Link/unlink tasks |
|
|
163
|
+
| `cup attach <taskId> <filePath>` | Upload file attachment |
|
|
164
|
+
| `cup delete <id> [--confirm]` | Delete task (DESTRUCTIVE) |
|
|
165
|
+
| `cup duplicate <taskId>` | Duplicate a task |
|
|
166
|
+
| `cup bulk status <status> <taskIds...>` | Bulk update status |
|
|
167
|
+
| `cup bulk assign <taskIds...> [--to userId\|me] [--remove userId\|me]` | Bulk assign/unassign user from tasks |
|
|
168
|
+
| `cup bulk due-date <date\|none\|clear> <taskIds...>` | Bulk set or clear due dates |
|
|
169
|
+
| `cup bulk tag <tagName> <taskIds...> [--remove]` | Bulk add/remove tag from tasks |
|
|
170
|
+
| `cup bulk priority <taskIds...> --to <priority>` | Bulk set priority on many tasks |
|
|
171
|
+
| `cup bulk field <taskIds...> --set "Name" value` | Bulk set a custom field value |
|
|
172
|
+
| `cup bulk move <taskIds...> --to <listId>` | Bulk move tasks to a destination list |
|
|
173
|
+
| `cup checklist view <id>` | View checklists on a task |
|
|
174
|
+
| `cup checklist create <id> <name>` | Create a checklist |
|
|
175
|
+
| `cup checklist delete <checklistId>` | Delete a checklist |
|
|
176
|
+
| `cup checklist add-item <checklistId> <name> [--parent itemId]` | Add item to checklist (nest under parent via `--parent`) |
|
|
177
|
+
| `cup checklist edit-item <checklistId> <itemId> [--name n] [--resolved] [--unresolved] [--assignee id] [--parent itemId\|null]` | Edit checklist item (reparent with `--parent`, use `"null"` to unnest) |
|
|
178
|
+
| `cup checklist delete-item <checklistId> <itemId>` | Delete checklist item |
|
|
179
|
+
| `cup time start <taskId> [-d desc]` | Start timer |
|
|
180
|
+
| `cup time stop` | Stop running timer |
|
|
181
|
+
| `cup time status` | Show running timer |
|
|
182
|
+
| `cup time log <taskId> <duration> [-d desc]` | Log manual entry (e.g. "2h", "30m") |
|
|
183
|
+
| `cup time list [--days n] [--task id] [--all]` | List my recent time entries (--all for team) |
|
|
184
|
+
| `cup time update <timeEntryId> [-d desc] [--duration dur]` | Update time entry |
|
|
185
|
+
| `cup time delete <timeEntryId>` | Delete time entry |
|
|
186
|
+
| `cup goal-create <name> [-d desc] [--color hex]` | Create a goal |
|
|
187
|
+
| `cup goal-update <goalId> [-n name] [-d desc] [--color hex]` | Update a goal |
|
|
188
|
+
| `cup goal-delete <goalId>` | Delete a goal |
|
|
189
|
+
| `cup key-result-create <goalId> <name> [--type t] [--target n]` | Create key result |
|
|
190
|
+
| `cup key-result-update <keyResultId> [--progress n] [--note text]` | Update key result |
|
|
191
|
+
| `cup key-result-delete <keyResultId>` | Delete key result |
|
|
192
|
+
| `cup doc-create <title> [-c content]` | Create a doc |
|
|
193
|
+
| `cup doc-page-create <docId> <name> [-c content] [--parent-page pageId]` | Create doc page |
|
|
194
|
+
| `cup doc-page-edit <docId> <pageId> [--name text] [-c content]` | Edit doc page |
|
|
195
|
+
| `cup doc-delete <docId>` | Delete a doc |
|
|
196
|
+
| `cup doc-page-delete <docId> <pageId>` | Delete doc page |
|
|
197
|
+
| `cup space-create <name>` | Create a space |
|
|
198
|
+
| `cup list-create <spaceId> <name> [--folder folderId] [--copy-statuses-from id]` | Create a list in a space or folder |
|
|
199
|
+
| `cup folder-create <spaceId> <name>` | Create a folder in a space |
|
|
200
|
+
| `cup list-rename <listId> <newName>` | Rename a list |
|
|
201
|
+
| `cup folder-rename <folderId> <newName>` | Rename a folder |
|
|
202
|
+
| `cup space-rename <spaceId> <newName>` | Rename a space |
|
|
203
|
+
| `cup tag-create <spaceId> <name> [--fg color] [--bg color]` | Create space tag |
|
|
204
|
+
| `cup tag-update <spaceId> <tagName> --name <newName> [--fg c] [--bg c]` | Update space tag |
|
|
205
|
+
| `cup tag-delete <spaceId> <name>` | Delete space tag |
|
|
206
|
+
| `cup list-from-template <name> --template <id> [--space id] [--folder id]` | Create list from template |
|
|
207
|
+
| `cup view-create <listId> <name> -t <type> [--group-by field]` | Create a view on a list |
|
|
208
|
+
| `cup view-update <viewId> [-n name] [--group-by field]` | Update a view |
|
|
209
|
+
| `cup view-delete <viewId> [--confirm]` | Delete a view (DESTRUCTIVE) |
|
|
210
|
+
| `cup favorite add <type> <id> [alias] [-n name]` | Add a local favorite |
|
|
211
|
+
| `cup favorite remove <alias>` | Remove a favorite |
|
|
212
|
+
| `cup favorite list [--type t]` | List favorites (optionally filter by type) |
|
|
213
|
+
| `cup profile list [--json]` | List all profiles |
|
|
214
|
+
| `cup profile add <name>` | Add a new profile (interactive) |
|
|
215
|
+
| `cup profile remove <name>` | Remove a profile |
|
|
216
|
+
| `cup profile use <name>` | Set the default profile |
|
|
217
|
+
| `cup config get <key>` / `set <key> <value>` / `path` | Manage config |
|
|
218
|
+
| `cup completion <shell>` | Shell completions (bash/zsh/fish) |
|
|
216
219
|
|
|
217
220
|
## Global Flags
|
|
218
221
|
|
|
@@ -302,7 +305,9 @@ cup field abc123def --set "Story Points" 5
|
|
|
302
305
|
cup tag abc123def --add "bug,frontend"
|
|
303
306
|
cup checklist create abc123def "QA Steps"
|
|
304
307
|
cup checklist add-item <clId> "Run unit tests"
|
|
308
|
+
cup checklist add-item <clId> "Sub step" --parent <itemId> # nest under parent
|
|
305
309
|
cup checklist edit-item <clId> <itemId> --resolved
|
|
310
|
+
cup checklist edit-item <clId> <itemId> --parent <newParent> # reparent (use "null" to unnest)
|
|
306
311
|
cup link abc123 def456
|
|
307
312
|
cup attach abc123def ./screenshot.png
|
|
308
313
|
cup time start abc123def -d "Working on feature"
|