@krodak/clickup-cli 0.13.1 → 0.13.2
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/README.md +4 -3
- package/dist/index.js +86 -78
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +14 -14
package/README.md
CHANGED
|
@@ -201,6 +201,7 @@ cu update abc123 -n "New task name"
|
|
|
201
201
|
cu update abc123 -d "Updated description with **markdown**"
|
|
202
202
|
cu update abc123 --priority high
|
|
203
203
|
cu update abc123 --due-date 2025-03-15
|
|
204
|
+
cu update abc123 --assignee me
|
|
204
205
|
cu update abc123 --assignee 12345
|
|
205
206
|
cu update abc123 -n "New name" -s "done" --priority urgent
|
|
206
207
|
cu update abc123 --time-estimate 2h
|
|
@@ -216,7 +217,7 @@ cu update abc123 -s "in progress" --json
|
|
|
216
217
|
| `--priority <level>` | Priority: `urgent`, `high`, `normal`, `low` (or 1-4) |
|
|
217
218
|
| `--due-date <date>` | Due date (`YYYY-MM-DD`) |
|
|
218
219
|
| `--time-estimate <duration>` | Time estimate (e.g. `"2h"`, `"30m"`, `"1h30m"`) |
|
|
219
|
-
| `--assignee <userId>` | Add assignee by
|
|
220
|
+
| `--assignee <userId>` | Add assignee by user ID or `"me"` |
|
|
220
221
|
| `--parent <taskId>` | Set parent task (makes this a subtask) |
|
|
221
222
|
| `--json` | Force JSON output even in terminal |
|
|
222
223
|
|
|
@@ -229,7 +230,7 @@ cu create -n "Fix login bug" -l <listId>
|
|
|
229
230
|
cu create -n "Subtask name" -p <parentTaskId> # --list auto-detected
|
|
230
231
|
cu create -n "Task" -l <listId> -d "desc" -s "open"
|
|
231
232
|
cu create -n "Task" -l <listId> --priority high --due-date 2025-06-01
|
|
232
|
-
cu create -n "Task" -l <listId> --assignee
|
|
233
|
+
cu create -n "Task" -l <listId> --assignee me --tags "bug,frontend"
|
|
233
234
|
cu create -n "Initiative" -l <listId> --custom-item-id 1
|
|
234
235
|
cu create -n "Task" -l <listId> --time-estimate 2h
|
|
235
236
|
cu create -n "Fix bug" -l <listId> --json
|
|
@@ -245,7 +246,7 @@ cu create -n "Fix bug" -l <listId> --json
|
|
|
245
246
|
| `--priority <level>` | no | Priority: `urgent`, `high`, `normal`, `low` (or 1-4) |
|
|
246
247
|
| `--due-date <date>` | no | Due date (`YYYY-MM-DD`) |
|
|
247
248
|
| `--time-estimate <duration>` | no | Time estimate (e.g. `"2h"`, `"30m"`, `"1h30m"`) |
|
|
248
|
-
| `--assignee <userId>` | no | Assignee by
|
|
249
|
+
| `--assignee <userId>` | no | Assignee by user ID or `"me"` |
|
|
249
250
|
| `--tags <tags>` | no | Comma-separated tag names |
|
|
250
251
|
| `--custom-item-id <id>` | no | Custom task type ID (e.g. for creating initiatives) |
|
|
251
252
|
| `--json` | no | Force JSON output even in terminal |
|
package/dist/index.js
CHANGED
|
@@ -4,72 +4,6 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
|
|
7
|
-
// src/config.ts
|
|
8
|
-
import fs from "fs";
|
|
9
|
-
import { homedir } from "os";
|
|
10
|
-
import { join } from "path";
|
|
11
|
-
function configDir() {
|
|
12
|
-
const xdg = process.env.XDG_CONFIG_HOME;
|
|
13
|
-
if (xdg) return join(xdg, "cu");
|
|
14
|
-
return join(homedir(), ".config", "cu");
|
|
15
|
-
}
|
|
16
|
-
function configPath() {
|
|
17
|
-
return join(configDir(), "config.json");
|
|
18
|
-
}
|
|
19
|
-
function loadConfig() {
|
|
20
|
-
const envToken = process.env.CU_API_TOKEN?.trim();
|
|
21
|
-
const envTeamId = process.env.CU_TEAM_ID?.trim();
|
|
22
|
-
let fileToken;
|
|
23
|
-
let fileTeamId;
|
|
24
|
-
const path = configPath();
|
|
25
|
-
if (fs.existsSync(path)) {
|
|
26
|
-
const raw = fs.readFileSync(path, "utf-8");
|
|
27
|
-
let parsed;
|
|
28
|
-
try {
|
|
29
|
-
parsed = JSON.parse(raw);
|
|
30
|
-
} catch {
|
|
31
|
-
throw new Error(`Config file at ${path} contains invalid JSON. Please check the file syntax.`);
|
|
32
|
-
}
|
|
33
|
-
fileToken = parsed.apiToken?.trim();
|
|
34
|
-
fileTeamId = parsed.teamId?.trim();
|
|
35
|
-
}
|
|
36
|
-
const apiToken = envToken || fileToken;
|
|
37
|
-
if (!apiToken) {
|
|
38
|
-
throw new Error("Config missing required field: apiToken.\nSet CU_API_TOKEN or run: cu init");
|
|
39
|
-
}
|
|
40
|
-
if (!apiToken.startsWith("pk_")) {
|
|
41
|
-
throw new Error("Config apiToken must start with pk_. The configured token does not.");
|
|
42
|
-
}
|
|
43
|
-
const teamId = envTeamId || fileTeamId;
|
|
44
|
-
if (!teamId) {
|
|
45
|
-
throw new Error("Config missing required field: teamId.\nSet CU_TEAM_ID or run: cu init");
|
|
46
|
-
}
|
|
47
|
-
return { apiToken, teamId };
|
|
48
|
-
}
|
|
49
|
-
function loadRawConfig() {
|
|
50
|
-
const path = configPath();
|
|
51
|
-
if (!fs.existsSync(path)) return {};
|
|
52
|
-
try {
|
|
53
|
-
return JSON.parse(fs.readFileSync(path, "utf-8"));
|
|
54
|
-
} catch {
|
|
55
|
-
return {};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function getConfigPath() {
|
|
59
|
-
return configPath();
|
|
60
|
-
}
|
|
61
|
-
function writeConfig(config) {
|
|
62
|
-
const dir = configDir();
|
|
63
|
-
if (!fs.existsSync(dir)) {
|
|
64
|
-
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
65
|
-
}
|
|
66
|
-
const filePath = join(dir, "config.json");
|
|
67
|
-
fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", {
|
|
68
|
-
encoding: "utf-8",
|
|
69
|
-
mode: 384
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
7
|
// src/api.ts
|
|
74
8
|
var BASE_URL = "https://api.clickup.com/api/v2";
|
|
75
9
|
var MAX_PAGES = 100;
|
|
@@ -261,6 +195,72 @@ var ClickUpClient = class {
|
|
|
261
195
|
}
|
|
262
196
|
};
|
|
263
197
|
|
|
198
|
+
// src/config.ts
|
|
199
|
+
import fs from "fs";
|
|
200
|
+
import { homedir } from "os";
|
|
201
|
+
import { join } from "path";
|
|
202
|
+
function configDir() {
|
|
203
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
204
|
+
if (xdg) return join(xdg, "cu");
|
|
205
|
+
return join(homedir(), ".config", "cu");
|
|
206
|
+
}
|
|
207
|
+
function configPath() {
|
|
208
|
+
return join(configDir(), "config.json");
|
|
209
|
+
}
|
|
210
|
+
function loadConfig() {
|
|
211
|
+
const envToken = process.env.CU_API_TOKEN?.trim();
|
|
212
|
+
const envTeamId = process.env.CU_TEAM_ID?.trim();
|
|
213
|
+
let fileToken;
|
|
214
|
+
let fileTeamId;
|
|
215
|
+
const path = configPath();
|
|
216
|
+
if (fs.existsSync(path)) {
|
|
217
|
+
const raw = fs.readFileSync(path, "utf-8");
|
|
218
|
+
let parsed;
|
|
219
|
+
try {
|
|
220
|
+
parsed = JSON.parse(raw);
|
|
221
|
+
} catch {
|
|
222
|
+
throw new Error(`Config file at ${path} contains invalid JSON. Please check the file syntax.`);
|
|
223
|
+
}
|
|
224
|
+
fileToken = parsed.apiToken?.trim();
|
|
225
|
+
fileTeamId = parsed.teamId?.trim();
|
|
226
|
+
}
|
|
227
|
+
const apiToken = envToken || fileToken;
|
|
228
|
+
if (!apiToken) {
|
|
229
|
+
throw new Error("Config missing required field: apiToken.\nSet CU_API_TOKEN or run: cu init");
|
|
230
|
+
}
|
|
231
|
+
if (!apiToken.startsWith("pk_")) {
|
|
232
|
+
throw new Error("Config apiToken must start with pk_. The configured token does not.");
|
|
233
|
+
}
|
|
234
|
+
const teamId = envTeamId || fileTeamId;
|
|
235
|
+
if (!teamId) {
|
|
236
|
+
throw new Error("Config missing required field: teamId.\nSet CU_TEAM_ID or run: cu init");
|
|
237
|
+
}
|
|
238
|
+
return { apiToken, teamId };
|
|
239
|
+
}
|
|
240
|
+
function loadRawConfig() {
|
|
241
|
+
const path = configPath();
|
|
242
|
+
if (!fs.existsSync(path)) return {};
|
|
243
|
+
try {
|
|
244
|
+
return JSON.parse(fs.readFileSync(path, "utf-8"));
|
|
245
|
+
} catch {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function getConfigPath() {
|
|
250
|
+
return configPath();
|
|
251
|
+
}
|
|
252
|
+
function writeConfig(config) {
|
|
253
|
+
const dir = configDir();
|
|
254
|
+
if (!fs.existsSync(dir)) {
|
|
255
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
256
|
+
}
|
|
257
|
+
const filePath = join(dir, "config.json");
|
|
258
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", {
|
|
259
|
+
encoding: "utf-8",
|
|
260
|
+
mode: 384
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
264
|
// src/date.ts
|
|
265
265
|
function formatDate(ms) {
|
|
266
266
|
const d = new Date(Number(ms));
|
|
@@ -754,9 +754,16 @@ function parseDueDate(value) {
|
|
|
754
754
|
}
|
|
755
755
|
function parseAssigneeId(value) {
|
|
756
756
|
const id = Number(value);
|
|
757
|
-
if (!Number.isInteger(id)) throw new Error(
|
|
757
|
+
if (!Number.isInteger(id)) throw new Error('Assignee must be a numeric user ID or "me"');
|
|
758
758
|
return id;
|
|
759
759
|
}
|
|
760
|
+
async function resolveAssigneeId(client, value) {
|
|
761
|
+
if (value === "me") {
|
|
762
|
+
const user = await client.getMe();
|
|
763
|
+
return user.id;
|
|
764
|
+
}
|
|
765
|
+
return parseAssigneeId(value);
|
|
766
|
+
}
|
|
760
767
|
function parseTimeEstimate(value) {
|
|
761
768
|
const pattern = /^(?:(\d+)h)?(?:(\d+)m)?$/i;
|
|
762
769
|
const match = value.match(pattern);
|
|
@@ -1583,13 +1590,6 @@ function configPath2() {
|
|
|
1583
1590
|
}
|
|
1584
1591
|
|
|
1585
1592
|
// src/commands/assign.ts
|
|
1586
|
-
async function resolveUserId(client, value) {
|
|
1587
|
-
if (value === "me") {
|
|
1588
|
-
const user = await client.getMe();
|
|
1589
|
-
return user.id;
|
|
1590
|
-
}
|
|
1591
|
-
return parseAssigneeId(value);
|
|
1592
|
-
}
|
|
1593
1593
|
async function assignTask(config, taskId, opts) {
|
|
1594
1594
|
if (!opts.to && !opts.remove) {
|
|
1595
1595
|
throw new Error("Provide at least one of: --to, --remove");
|
|
@@ -1598,10 +1598,10 @@ async function assignTask(config, taskId, opts) {
|
|
|
1598
1598
|
const add = [];
|
|
1599
1599
|
const rem = [];
|
|
1600
1600
|
if (opts.to) {
|
|
1601
|
-
add.push(await
|
|
1601
|
+
add.push(await resolveAssigneeId(client, opts.to));
|
|
1602
1602
|
}
|
|
1603
1603
|
if (opts.remove) {
|
|
1604
|
-
rem.push(await
|
|
1604
|
+
rem.push(await resolveAssigneeId(client, opts.remove));
|
|
1605
1605
|
}
|
|
1606
1606
|
return client.updateTask(taskId, {
|
|
1607
1607
|
assignees: {
|
|
@@ -2509,9 +2509,13 @@ program.command("task <taskId>").description("Get task details").option("--json"
|
|
|
2509
2509
|
}
|
|
2510
2510
|
})
|
|
2511
2511
|
);
|
|
2512
|
-
program.command("update <taskId>").description("Update a task").option("-n, --name <text>", "New task name").option("-d, --description <text>", "New description (markdown supported)").option("-s, --status <status>", 'New status (e.g. "in progress", "done")').option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--assignee <userId>",
|
|
2512
|
+
program.command("update <taskId>").description("Update a task").option("-n, --name <text>", "New task name").option("-d, --description <text>", "New description (markdown supported)").option("-s, --status <status>", 'New status (e.g. "in progress", "done")').option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--assignee <userId>", 'Add assignee by user ID or "me"').option("--parent <taskId>", "Set parent task (makes this a subtask)").option("--json", "Force JSON output even in terminal").action(
|
|
2513
2513
|
wrapAction(async (taskId, opts) => {
|
|
2514
2514
|
const config = loadConfig();
|
|
2515
|
+
if (opts.assignee === "me") {
|
|
2516
|
+
const client = new ClickUpClient(config);
|
|
2517
|
+
opts.assignee = String(await resolveAssigneeId(client, "me"));
|
|
2518
|
+
}
|
|
2515
2519
|
const payload = buildUpdatePayload(opts);
|
|
2516
2520
|
const result = await updateTask(config, taskId, payload);
|
|
2517
2521
|
if (shouldOutputJson(opts.json ?? false)) {
|
|
@@ -2521,9 +2525,13 @@ program.command("update <taskId>").description("Update a task").option("-n, --na
|
|
|
2521
2525
|
}
|
|
2522
2526
|
})
|
|
2523
2527
|
);
|
|
2524
|
-
program.command("create").description("Create a new task").option("-l, --list <listId>", "Target list ID (auto-detected from --parent if omitted)").requiredOption("-n, --name <name>", "Task name").option("-d, --description <text>", "Task description (markdown supported)").option("-p, --parent <taskId>", "Parent task ID (list auto-detected from parent)").option("-s, --status <status>", "Initial status").option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--assignee <userId>",
|
|
2528
|
+
program.command("create").description("Create a new task").option("-l, --list <listId>", "Target list ID (auto-detected from --parent if omitted)").requiredOption("-n, --name <name>", "Task name").option("-d, --description <text>", "Task description (markdown supported)").option("-p, --parent <taskId>", "Parent task ID (list auto-detected from parent)").option("-s, --status <status>", "Initial status").option("--priority <level>", "Priority: urgent, high, normal, low (or 1-4)").option("--due-date <date>", "Due date (YYYY-MM-DD)").option("--assignee <userId>", 'Assignee user ID or "me"').option("--tags <tags>", "Comma-separated tag names").option("--custom-item-id <id>", "Custom task type ID (use to create initiatives)").option("--time-estimate <duration>", 'Time estimate (e.g. "2h", "30m", "1h30m")').option("--json", "Force JSON output even in terminal").action(
|
|
2525
2529
|
wrapAction(async (opts) => {
|
|
2526
2530
|
const config = loadConfig();
|
|
2531
|
+
if (opts.assignee === "me") {
|
|
2532
|
+
const client = new ClickUpClient(config);
|
|
2533
|
+
opts.assignee = String(await resolveAssigneeId(client, "me"));
|
|
2534
|
+
}
|
|
2527
2535
|
const result = await createTask(config, opts);
|
|
2528
2536
|
if (shouldOutputJson(opts.json ?? false)) {
|
|
2529
2537
|
console.log(JSON.stringify(result, null, 2));
|
package/package.json
CHANGED
|
@@ -55,19 +55,19 @@ All commands support `--help` for full flag details.
|
|
|
55
55
|
|
|
56
56
|
### Write
|
|
57
57
|
|
|
58
|
-
| Command
|
|
59
|
-
|
|
|
60
|
-
| `cu update <id> [-n name] [-d desc] [-s status] [--priority p] [--due-date d] [--time-estimate t] [--assignee id] [--parent id] [--json]` | Update task fields (desc supports markdown) |
|
|
61
|
-
| `cu create -n name [-l listId] [-p parentId] [-d desc] [-s status] [--priority p] [--due-date d] [--time-estimate t] [--assignee id] [--tags t] [--custom-item-id n] [--json]` | Create task (desc supports markdown) |
|
|
62
|
-
| `cu comment <id> -m text [--json]`
|
|
63
|
-
| `cu assign <id> [--to userId\|me] [--remove userId\|me] [--json]`
|
|
64
|
-
| `cu depend <id> [--on taskId] [--blocks taskId] [--remove] [--json]`
|
|
65
|
-
| `cu move <id> [--to listId] [--remove listId] [--json]`
|
|
66
|
-
| `cu field <id> [--set "Name" value] [--remove "Name"] [--json]`
|
|
67
|
-
| `cu delete <id> [--confirm] [--json]`
|
|
68
|
-
| `cu tag <id> [--add tags] [--remove tags] [--json]`
|
|
69
|
-
| `cu config get <key>` / `cu config set <key> <value>` / `cu config path`
|
|
70
|
-
| `cu completion <shell>`
|
|
58
|
+
| Command | What it does |
|
|
59
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- |
|
|
60
|
+
| `cu update <id> [-n name] [-d desc] [-s status] [--priority p] [--due-date d] [--time-estimate t] [--assignee id\|me] [--parent id] [--json]` | Update task fields (desc supports markdown) |
|
|
61
|
+
| `cu create -n name [-l listId] [-p parentId] [-d desc] [-s status] [--priority p] [--due-date d] [--time-estimate t] [--assignee id\|me] [--tags t] [--custom-item-id n] [--json]` | Create task (desc supports markdown) |
|
|
62
|
+
| `cu comment <id> -m text [--json]` | Post comment on task |
|
|
63
|
+
| `cu assign <id> [--to userId\|me] [--remove userId\|me] [--json]` | Assign/unassign users |
|
|
64
|
+
| `cu depend <id> [--on taskId] [--blocks taskId] [--remove] [--json]` | Add/remove task dependencies |
|
|
65
|
+
| `cu move <id> [--to listId] [--remove listId] [--json]` | Add/remove task from lists |
|
|
66
|
+
| `cu field <id> [--set "Name" value] [--remove "Name"] [--json]` | Set/remove custom field values |
|
|
67
|
+
| `cu delete <id> [--confirm] [--json]` | Delete a task (DESTRUCTIVE, irreversible) |
|
|
68
|
+
| `cu tag <id> [--add tags] [--remove tags] [--json]` | Add/remove tags on a task |
|
|
69
|
+
| `cu config get <key>` / `cu config set <key> <value>` / `cu config path` | Manage CLI config |
|
|
70
|
+
| `cu completion <shell>` | Shell completions (bash/zsh/fish) |
|
|
71
71
|
|
|
72
72
|
## Quick Reference
|
|
73
73
|
|
|
@@ -79,7 +79,7 @@ All commands support `--help` for full flag details.
|
|
|
79
79
|
| `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr. |
|
|
80
80
|
| `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
|
|
81
81
|
| `--due-date` | `YYYY-MM-DD` format |
|
|
82
|
-
| `--assignee` |
|
|
82
|
+
| `--assignee` | User ID or `me` (on `cu create`, `cu update`, `cu assign`) |
|
|
83
83
|
| `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
|
|
84
84
|
| `--time-estimate` | Duration format: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
|
|
85
85
|
| `--custom-item-id` | Custom task type ID for `cu create` (e.g. `1` for initiative) |
|