@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 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 numeric user ID |
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 12345 --tags "bug,frontend"
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 numeric user ID |
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("Assignee must be a numeric user ID");
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 resolveUserId(client, opts.to));
1601
+ add.push(await resolveAssigneeId(client, opts.to));
1602
1602
  }
1603
1603
  if (opts.remove) {
1604
- rem.push(await resolveUserId(client, opts.remove));
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>", "Add assignee by user ID").option("--parent <taskId>", "Set parent task (makes this a subtask)").option("--json", "Force JSON output even in terminal").action(
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>", "Assignee user ID").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(
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -55,19 +55,19 @@ All commands support `--help` for full flag details.
55
55
 
56
56
  ### Write
57
57
 
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] [--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]` | 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) |
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` | Numeric user ID (find via `cu task <id> --json`) |
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) |