@krodak/clickup-cli 0.13.0 → 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
@@ -98,7 +98,7 @@ When output is piped (no TTY), all commands output **Markdown** by default - opt
98
98
 
99
99
  ## Commands
100
100
 
101
- 30 commands total. All support `--help` for full flag details.
101
+ 29 commands total. All support `--help` for full flag details.
102
102
 
103
103
  ### `cu init`
104
104
 
@@ -110,32 +110,21 @@ cu init
110
110
 
111
111
  ### `cu tasks`
112
112
 
113
- List tasks assigned to me.
113
+ List tasks assigned to me. By default shows all task types. Use `--type` to filter by task type.
114
114
 
115
115
  ```bash
116
116
  cu tasks
117
117
  cu tasks --status "in progress"
118
118
  cu tasks --name "login"
119
+ cu tasks --type task # regular tasks only
120
+ cu tasks --type initiative # initiatives only
121
+ cu tasks --type "Bug" # custom task type by name
119
122
  cu tasks --list <listId>
120
123
  cu tasks --space <spaceId>
121
124
  cu tasks --include-closed
122
125
  cu tasks --json
123
126
  ```
124
127
 
125
- ### `cu initiatives`
126
-
127
- List initiatives assigned to me.
128
-
129
- ```bash
130
- cu initiatives
131
- cu initiatives --status "to do"
132
- cu initiatives --name "auth"
133
- cu initiatives --list <listId>
134
- cu initiatives --space <spaceId>
135
- cu initiatives --include-closed
136
- cu initiatives --json
137
- ```
138
-
139
128
  ### `cu sprint`
140
129
 
141
130
  List my tasks in the currently active sprint (auto-detected from sprint folder date ranges).
@@ -192,7 +181,7 @@ cu task abc123 --json
192
181
 
193
182
  ### `cu subtasks <id>`
194
183
 
195
- List subtasks of a task or initiative.
184
+ List subtasks of a task.
196
185
 
197
186
  ```bash
198
187
  cu subtasks abc123
@@ -212,6 +201,7 @@ cu update abc123 -n "New task name"
212
201
  cu update abc123 -d "Updated description with **markdown**"
213
202
  cu update abc123 --priority high
214
203
  cu update abc123 --due-date 2025-03-15
204
+ cu update abc123 --assignee me
215
205
  cu update abc123 --assignee 12345
216
206
  cu update abc123 -n "New name" -s "done" --priority urgent
217
207
  cu update abc123 --time-estimate 2h
@@ -227,7 +217,7 @@ cu update abc123 -s "in progress" --json
227
217
  | `--priority <level>` | Priority: `urgent`, `high`, `normal`, `low` (or 1-4) |
228
218
  | `--due-date <date>` | Due date (`YYYY-MM-DD`) |
229
219
  | `--time-estimate <duration>` | Time estimate (e.g. `"2h"`, `"30m"`, `"1h30m"`) |
230
- | `--assignee <userId>` | Add assignee by numeric user ID |
220
+ | `--assignee <userId>` | Add assignee by user ID or `"me"` |
231
221
  | `--parent <taskId>` | Set parent task (makes this a subtask) |
232
222
  | `--json` | Force JSON output even in terminal |
233
223
 
@@ -240,7 +230,7 @@ cu create -n "Fix login bug" -l <listId>
240
230
  cu create -n "Subtask name" -p <parentTaskId> # --list auto-detected
241
231
  cu create -n "Task" -l <listId> -d "desc" -s "open"
242
232
  cu create -n "Task" -l <listId> --priority high --due-date 2025-06-01
243
- cu create -n "Task" -l <listId> --assignee 12345 --tags "bug,frontend"
233
+ cu create -n "Task" -l <listId> --assignee me --tags "bug,frontend"
244
234
  cu create -n "Initiative" -l <listId> --custom-item-id 1
245
235
  cu create -n "Task" -l <listId> --time-estimate 2h
246
236
  cu create -n "Fix bug" -l <listId> --json
@@ -256,9 +246,9 @@ cu create -n "Fix bug" -l <listId> --json
256
246
  | `--priority <level>` | no | Priority: `urgent`, `high`, `normal`, `low` (or 1-4) |
257
247
  | `--due-date <date>` | no | Due date (`YYYY-MM-DD`) |
258
248
  | `--time-estimate <duration>` | no | Time estimate (e.g. `"2h"`, `"30m"`, `"1h30m"`) |
259
- | `--assignee <userId>` | no | Assignee by numeric user ID |
249
+ | `--assignee <userId>` | no | Assignee by user ID or `"me"` |
260
250
  | `--tags <tags>` | no | Comma-separated tag names |
261
- | `--custom-item-id <id>` | no | Custom task type ID (for creating initiatives) |
251
+ | `--custom-item-id <id>` | no | Custom task type ID (e.g. for creating initiatives) |
262
252
  | `--json` | no | Force JSON output even in terminal |
263
253
 
264
254
  ### `cu delete <id>`
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;
@@ -190,6 +124,12 @@ var ClickUpClient = class {
190
124
  const data = await this.request(`/team/${teamId}/space?archived=false`);
191
125
  return data.spaces ?? [];
192
126
  }
127
+ async getCustomTaskTypes(teamId) {
128
+ const data = await this.request(
129
+ `/team/${teamId}/custom_item`
130
+ );
131
+ return data.custom_items ?? [];
132
+ }
193
133
  async getLists(spaceId) {
194
134
  const data = await this.request(`/space/${spaceId}/list?archived=false`);
195
135
  return data.lists ?? [];
@@ -255,6 +195,72 @@ var ClickUpClient = class {
255
195
  }
256
196
  };
257
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
+
258
264
  // src/date.ts
259
265
  function formatDate(ms) {
260
266
  const d = new Date(Number(ms));
@@ -372,11 +378,11 @@ function formatDuration(ms) {
372
378
  }
373
379
  function formatTaskDetailMarkdown(task) {
374
380
  const lines = [`# ${task.name}`, ""];
375
- const isInitiative2 = (task.custom_item_id ?? 0) !== 0;
381
+ const isInitiative = (task.custom_item_id ?? 0) !== 0;
376
382
  const fields = [
377
383
  ["ID", task.id],
378
384
  ["Status", task.status.status],
379
- ["Type", isInitiative2 ? "initiative" : "task"],
385
+ ["Type", isInitiative ? "initiative" : "task"],
380
386
  ["List", task.list.name],
381
387
  ["URL", task.url],
382
388
  [
@@ -505,8 +511,8 @@ function formatCustomFieldValue(field) {
505
511
  }
506
512
  function formatTaskDetail(task) {
507
513
  const lines = [];
508
- const isInitiative2 = (task.custom_item_id ?? 0) !== 0;
509
- const typeLabel = isInitiative2 ? "initiative" : "task";
514
+ const isInitiative = (task.custom_item_id ?? 0) !== 0;
515
+ const typeLabel = isInitiative ? "initiative" : "task";
510
516
  lines.push(chalk2.bold.underline(task.name));
511
517
  lines.push("");
512
518
  const fields = [
@@ -631,19 +637,21 @@ function isDoneStatus(status) {
631
637
  const lower = status.toLowerCase();
632
638
  return DONE_PATTERNS.some((p) => lower.includes(p));
633
639
  }
634
- function isInitiative(task) {
635
- return (task.custom_item_id ?? 0) !== 0;
636
- }
637
640
  function formatDueDate(ms) {
638
641
  if (!ms) return "";
639
642
  return formatDate(ms);
640
643
  }
641
- function summarize(task) {
644
+ function resolveTaskType(task, typeMap) {
645
+ const id = task.custom_item_id ?? 0;
646
+ if (id === 0) return "task";
647
+ return typeMap.get(id) ?? `type_${id}`;
648
+ }
649
+ function summarize(task, typeMap) {
642
650
  return {
643
651
  id: task.id,
644
652
  name: task.name,
645
653
  status: task.status.status,
646
- task_type: isInitiative(task) ? "initiative" : "task",
654
+ task_type: resolveTaskType(task, typeMap ?? /* @__PURE__ */ new Map()),
647
655
  priority: task.priority?.priority ?? "none",
648
656
  due_date: formatDueDate(task.due_date),
649
657
  list: task.list.name,
@@ -651,16 +659,42 @@ function summarize(task) {
651
659
  ...task.parent ? { parent: task.parent } : {}
652
660
  };
653
661
  }
662
+ function buildTypeMap(types) {
663
+ const map = /* @__PURE__ */ new Map();
664
+ for (const t of types) {
665
+ map.set(t.id, t.name);
666
+ }
667
+ return map;
668
+ }
669
+ function resolveTypeFilter(typeFilter, typeMap) {
670
+ if (typeFilter === "task") return 0;
671
+ const asNum = Number(typeFilter);
672
+ if (Number.isFinite(asNum)) return asNum;
673
+ const lower = typeFilter.toLowerCase();
674
+ for (const [id, name] of typeMap) {
675
+ if (name.toLowerCase() === lower) return id;
676
+ }
677
+ const available = ["task", ...Array.from(typeMap.values())].join(", ");
678
+ throw new Error(`Unknown task type "${typeFilter}". Available types: ${available}`);
679
+ }
654
680
  async function fetchMyTasks(config, opts = {}) {
655
681
  const client = new ClickUpClient(config);
656
682
  const { typeFilter, name, ...apiFilters } = opts;
657
- const allTasks = await client.getMyTasks(config.teamId, apiFilters);
658
- let filtered = typeFilter === "initiative" ? allTasks.filter(isInitiative) : typeFilter === "task" ? allTasks.filter((t) => !isInitiative(t)) : allTasks;
683
+ const [allTasks, customTypes] = await Promise.all([
684
+ client.getMyTasks(config.teamId, apiFilters),
685
+ client.getCustomTaskTypes(config.teamId)
686
+ ]);
687
+ const typeMap = buildTypeMap(customTypes);
688
+ let filtered = allTasks;
689
+ if (typeFilter) {
690
+ const targetId = resolveTypeFilter(typeFilter, typeMap);
691
+ filtered = allTasks.filter((t) => (t.custom_item_id ?? 0) === targetId);
692
+ }
659
693
  if (name) {
660
694
  const query = name.toLowerCase();
661
695
  filtered = filtered.filter((t) => t.name.toLowerCase().includes(query));
662
696
  }
663
- return filtered.map(summarize);
697
+ return filtered.map((t) => summarize(t, typeMap));
664
698
  }
665
699
  async function printTasks(tasks, forceJson, config) {
666
700
  if (shouldOutputJson(forceJson)) {
@@ -720,9 +754,16 @@ function parseDueDate(value) {
720
754
  }
721
755
  function parseAssigneeId(value) {
722
756
  const id = Number(value);
723
- 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"');
724
758
  return id;
725
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
+ }
726
767
  function parseTimeEstimate(value) {
727
768
  const pattern = /^(?:(\d+)h)?(?:(\d+)m)?$/i;
728
769
  const match = value.match(pattern);
@@ -920,10 +961,12 @@ function findRelatedSpaces(mySpaceIds, allSpaces) {
920
961
  async function runSprintCommand(config, opts) {
921
962
  const client = new ClickUpClient(config);
922
963
  process.stderr.write("Detecting active sprint...\n");
923
- const [myTasks, allSpaces] = await Promise.all([
964
+ const [myTasks, allSpaces, customTypes] = await Promise.all([
924
965
  client.getMyTasks(config.teamId),
925
- client.getSpaces(config.teamId)
966
+ client.getSpaces(config.teamId),
967
+ client.getCustomTaskTypes(config.teamId)
926
968
  ]);
969
+ const typeMap = buildTypeMap(customTypes);
927
970
  let spaces;
928
971
  if (opts.space) {
929
972
  spaces = allSpaces.filter(
@@ -966,7 +1009,7 @@ async function runSprintCommand(config, opts) {
966
1009
  sprintTasks = sprintTasks.filter((t) => !isDoneStatus(t.status.status));
967
1010
  }
968
1011
  const filtered = opts.status ? sprintTasks.filter((t) => t.status.status.toLowerCase() === opts.status.toLowerCase()) : sprintTasks;
969
- const summaries = filtered.map(summarize);
1012
+ const summaries = filtered.map((t) => summarize(t, typeMap));
970
1013
  await printTasks(summaries, opts.json ?? false, config);
971
1014
  }
972
1015
 
@@ -1059,13 +1102,17 @@ async function listSprints(config, opts = {}) {
1059
1102
  // src/commands/subtasks.ts
1060
1103
  async function fetchSubtasks(config, taskId, options = {}) {
1061
1104
  const client = new ClickUpClient(config);
1062
- const parent = await client.getTask(taskId);
1105
+ const [parent, customTypes] = await Promise.all([
1106
+ client.getTask(taskId),
1107
+ client.getCustomTaskTypes(config.teamId)
1108
+ ]);
1109
+ const typeMap = buildTypeMap(customTypes);
1063
1110
  const tasks = await client.getTasksFromList(
1064
1111
  parent.list.id,
1065
1112
  { parent: taskId, subtasks: "false" },
1066
1113
  { includeClosed: options.includeClosed }
1067
1114
  );
1068
- return tasks.map(summarize);
1115
+ return tasks.map((t) => summarize(t, typeMap));
1069
1116
  }
1070
1117
 
1071
1118
  // src/commands/comment.ts
@@ -1171,9 +1218,9 @@ var TIME_PERIODS = [
1171
1218
  { key: "last_month", label: "Last month" },
1172
1219
  { key: "older", label: "Older" }
1173
1220
  ];
1174
- function summarizeWithDate(task) {
1221
+ function summarizeWithDate(task, typeMap) {
1175
1222
  return {
1176
- ...summarize(task),
1223
+ ...summarize(task, typeMap),
1177
1224
  date_updated: task.date_updated ?? "0"
1178
1225
  };
1179
1226
  }
@@ -1210,12 +1257,13 @@ function groupTasks(tasks, now) {
1210
1257
  }
1211
1258
  async function fetchInbox(config, days = 30, opts = {}) {
1212
1259
  const client = new ClickUpClient(config);
1213
- const tasks = await client.getMyTasks(config.teamId, {
1214
- subtasks: true,
1215
- includeClosed: opts.includeClosed
1216
- });
1260
+ const [tasks, customTypes] = await Promise.all([
1261
+ client.getMyTasks(config.teamId, { subtasks: true, includeClosed: opts.includeClosed }),
1262
+ client.getCustomTaskTypes(config.teamId)
1263
+ ]);
1264
+ const typeMap = buildTypeMap(customTypes);
1217
1265
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
1218
- return tasks.filter((t) => Number(t.date_updated ?? 0) > cutoff).sort((a, b) => Number(b.date_updated ?? 0) - Number(a.date_updated ?? 0)).map(summarizeWithDate);
1266
+ return tasks.filter((t) => Number(t.date_updated ?? 0) > cutoff).sort((a, b) => Number(b.date_updated ?? 0) - Number(a.date_updated ?? 0)).map((t) => summarizeWithDate(t, typeMap));
1219
1267
  }
1220
1268
  async function printInbox(tasks, forceJson, config) {
1221
1269
  const now = Date.now();
@@ -1322,9 +1370,11 @@ function groupByStatus(tasks, includeClosed) {
1322
1370
  }
1323
1371
  async function runAssignedCommand(config, opts) {
1324
1372
  const client = new ClickUpClient(config);
1325
- const allTasks = await client.getMyTasks(config.teamId, {
1326
- includeClosed: opts.includeClosed
1327
- });
1373
+ const [allTasks, customTypes] = await Promise.all([
1374
+ client.getMyTasks(config.teamId, { includeClosed: opts.includeClosed }),
1375
+ client.getCustomTaskTypes(config.teamId)
1376
+ ]);
1377
+ const typeMap = buildTypeMap(customTypes);
1328
1378
  let groups = groupByStatus(allTasks, opts.includeClosed ?? false);
1329
1379
  if (opts.status) {
1330
1380
  const lower = opts.status.toLowerCase();
@@ -1333,7 +1383,7 @@ async function runAssignedCommand(config, opts) {
1333
1383
  if (shouldOutputJson(opts.json ?? false)) {
1334
1384
  const result = {};
1335
1385
  for (const group of groups) {
1336
- result[group.status.toLowerCase()] = group.tasks.map(summarize);
1386
+ result[group.status.toLowerCase()] = group.tasks.map((t) => summarize(t, typeMap));
1337
1387
  }
1338
1388
  console.log(JSON.stringify(result, null, 2));
1339
1389
  return;
@@ -1341,7 +1391,7 @@ async function runAssignedCommand(config, opts) {
1341
1391
  if (!isTTY()) {
1342
1392
  const mdGroups = groups.map((g) => ({
1343
1393
  label: g.status,
1344
- tasks: g.tasks.map((t) => summarize(t))
1394
+ tasks: g.tasks.map((t) => summarize(t, typeMap))
1345
1395
  }));
1346
1396
  console.log(formatGroupedTasksMarkdown(mdGroups));
1347
1397
  return;
@@ -1352,7 +1402,7 @@ async function runAssignedCommand(config, opts) {
1352
1402
  }
1353
1403
  const pickerGroups = groups.map((g) => ({
1354
1404
  label: g.status.toUpperCase(),
1355
- tasks: g.tasks.map(summarize)
1405
+ tasks: g.tasks.map((t) => summarize(t, typeMap))
1356
1406
  }));
1357
1407
  const selected = await groupedTaskPicker(pickerGroups);
1358
1408
  await showDetailsAndOpen(selected, (id) => client.getTask(id));
@@ -1435,7 +1485,7 @@ function isOverdue(task, now) {
1435
1485
  const due = Number(task.due_date);
1436
1486
  return !isNaN(due) && due < now;
1437
1487
  }
1438
- function categorizeTasks(tasks, hoursBack) {
1488
+ function categorizeTasks(tasks, hoursBack, typeMap) {
1439
1489
  const now = Date.now();
1440
1490
  const cutoff = now - hoursBack * 60 * 60 * 1e3;
1441
1491
  const completed = [];
@@ -1444,13 +1494,13 @@ function categorizeTasks(tasks, hoursBack) {
1444
1494
  for (const task of tasks) {
1445
1495
  const done = isDoneStatus(task.status.status);
1446
1496
  if (done && isCompletedRecently(task, cutoff)) {
1447
- completed.push(summarize(task));
1497
+ completed.push(summarize(task, typeMap));
1448
1498
  }
1449
1499
  if (!done && isInProgress(task)) {
1450
- inProgress.push(summarize(task));
1500
+ inProgress.push(summarize(task, typeMap));
1451
1501
  }
1452
1502
  if (!done && isOverdue(task, now)) {
1453
- overdue.push(summarize(task));
1503
+ overdue.push(summarize(task, typeMap));
1454
1504
  }
1455
1505
  }
1456
1506
  return { completed, inProgress, overdue };
@@ -1466,8 +1516,12 @@ ${label} (${tasks.length})`);
1466
1516
  }
1467
1517
  async function runSummaryCommand(config, opts) {
1468
1518
  const client = new ClickUpClient(config);
1469
- const allTasks = await client.getMyTasks(config.teamId, { includeClosed: true });
1470
- const result = categorizeTasks(allTasks, opts.hours);
1519
+ const [allTasks, customTypes] = await Promise.all([
1520
+ client.getMyTasks(config.teamId, { includeClosed: true }),
1521
+ client.getCustomTaskTypes(config.teamId)
1522
+ ]);
1523
+ const typeMap = buildTypeMap(customTypes);
1524
+ const result = categorizeTasks(allTasks, opts.hours, typeMap);
1471
1525
  if (shouldOutputJson(opts.json)) {
1472
1526
  console.log(JSON.stringify(result, null, 2));
1473
1527
  return;
@@ -1493,9 +1547,13 @@ function isOverdue2(task, now) {
1493
1547
  }
1494
1548
  async function fetchOverdueTasks(config, opts = {}) {
1495
1549
  const client = new ClickUpClient(config);
1496
- const allTasks = await client.getMyTasks(config.teamId, { includeClosed: opts.includeClosed });
1550
+ const [allTasks, customTypes] = await Promise.all([
1551
+ client.getMyTasks(config.teamId, { includeClosed: opts.includeClosed }),
1552
+ client.getCustomTaskTypes(config.teamId)
1553
+ ]);
1554
+ const typeMap = buildTypeMap(customTypes);
1497
1555
  const now = Date.now();
1498
- return allTasks.filter((t) => isOverdue2(t, now) && (opts.includeClosed || !isDoneStatus(t.status.status))).sort((a, b) => Number(a.due_date) - Number(b.due_date)).map(summarize);
1556
+ return allTasks.filter((t) => isOverdue2(t, now) && (opts.includeClosed || !isDoneStatus(t.status.status))).sort((a, b) => Number(a.due_date) - Number(b.due_date)).map((t) => summarize(t, typeMap));
1499
1557
  }
1500
1558
 
1501
1559
  // src/commands/config.ts
@@ -1532,13 +1590,6 @@ function configPath2() {
1532
1590
  }
1533
1591
 
1534
1592
  // src/commands/assign.ts
1535
- async function resolveUserId(client, value) {
1536
- if (value === "me") {
1537
- const user = await client.getMe();
1538
- return user.id;
1539
- }
1540
- return parseAssigneeId(value);
1541
- }
1542
1593
  async function assignTask(config, taskId, opts) {
1543
1594
  if (!opts.to && !opts.remove) {
1544
1595
  throw new Error("Provide at least one of: --to, --remove");
@@ -1547,10 +1598,10 @@ async function assignTask(config, taskId, opts) {
1547
1598
  const add = [];
1548
1599
  const rem = [];
1549
1600
  if (opts.to) {
1550
- add.push(await resolveUserId(client, opts.to));
1601
+ add.push(await resolveAssigneeId(client, opts.to));
1551
1602
  }
1552
1603
  if (opts.remove) {
1553
- rem.push(await resolveUserId(client, opts.remove));
1604
+ rem.push(await resolveAssigneeId(client, opts.remove));
1554
1605
  }
1555
1606
  return client.updateTask(taskId, {
1556
1607
  assignees: {
@@ -1633,7 +1684,7 @@ function bashCompletion() {
1633
1684
  cword=$COMP_CWORD
1634
1685
  fi
1635
1686
 
1636
- local commands="init auth tasks initiatives task update create sprint sprints subtasks comment comments activity lists spaces inbox assigned open search summary overdue assign depend move field delete tag config completion"
1687
+ local commands="init auth tasks task update create sprint sprints subtasks comment comments activity lists spaces inbox assigned open search summary overdue assign depend move field delete tag config completion"
1637
1688
 
1638
1689
  if [[ $cword -eq 1 ]]; then
1639
1690
  COMPREPLY=($(compgen -W "$commands --help --version" -- "$cur"))
@@ -1654,8 +1705,8 @@ function bashCompletion() {
1654
1705
  esac
1655
1706
 
1656
1707
  case "$cmd" in
1657
- tasks|initiatives)
1658
- COMPREPLY=($(compgen -W "--status --list --space --name --include-closed --json" -- "$cur"))
1708
+ tasks)
1709
+ COMPREPLY=($(compgen -W "--status --list --space --name --type --include-closed --json" -- "$cur"))
1659
1710
  ;;
1660
1711
  task)
1661
1712
  COMPREPLY=($(compgen -W "--json" -- "$cur"))
@@ -1758,7 +1809,6 @@ _cu() {
1758
1809
  'init:Set up cu for the first time'
1759
1810
  'auth:Validate API token and show current user'
1760
1811
  'tasks:List tasks assigned to me'
1761
- 'initiatives:List initiatives assigned to me'
1762
1812
  'task:Get task details'
1763
1813
  'update:Update a task'
1764
1814
  'create:Create a new task'
@@ -1798,12 +1848,13 @@ _cu() {
1798
1848
  ;;
1799
1849
  args)
1800
1850
  case $words[1] in
1801
- tasks|initiatives)
1851
+ tasks)
1802
1852
  _arguments \\
1803
1853
  '--status[Filter by status]:status:(open "in progress" "in review" done closed)' \\
1804
1854
  '--list[Filter by list ID]:list_id:' \\
1805
1855
  '--space[Filter by space ID]:space_id:' \\
1806
1856
  '--name[Filter by name]:query:' \\
1857
+ '--type[Filter by task type]:type:' \\
1807
1858
  '--include-closed[Include done/closed tasks]' \\
1808
1859
  '--json[Force JSON output]'
1809
1860
  ;;
@@ -2011,7 +2062,6 @@ complete -c cu -n __fish_use_subcommand -s V -l version -d 'Show version'
2011
2062
  complete -c cu -n __fish_use_subcommand -a init -d 'Set up cu for the first time'
2012
2063
  complete -c cu -n __fish_use_subcommand -a auth -d 'Validate API token and show current user'
2013
2064
  complete -c cu -n __fish_use_subcommand -a tasks -d 'List tasks assigned to me'
2014
- complete -c cu -n __fish_use_subcommand -a initiatives -d 'List initiatives assigned to me'
2015
2065
  complete -c cu -n __fish_use_subcommand -a task -d 'Get task details'
2016
2066
  complete -c cu -n __fish_use_subcommand -a update -d 'Update a task'
2017
2067
  complete -c cu -n __fish_use_subcommand -a create -d 'Create a new task'
@@ -2040,12 +2090,13 @@ complete -c cu -n __fish_use_subcommand -a completion -d 'Output shell completio
2040
2090
 
2041
2091
  complete -c cu -n '__fish_seen_subcommand_from auth' -l json -d 'Force JSON output'
2042
2092
 
2043
- complete -c cu -n '__fish_seen_subcommand_from tasks initiatives' -l status -d 'Filter by status'
2044
- complete -c cu -n '__fish_seen_subcommand_from tasks initiatives' -l list -d 'Filter by list ID'
2045
- complete -c cu -n '__fish_seen_subcommand_from tasks initiatives' -l space -d 'Filter by space ID'
2046
- complete -c cu -n '__fish_seen_subcommand_from tasks initiatives' -l name -d 'Filter by name'
2047
- complete -c cu -n '__fish_seen_subcommand_from tasks initiatives' -l include-closed -d 'Include done/closed tasks'
2048
- complete -c cu -n '__fish_seen_subcommand_from tasks initiatives' -l json -d 'Force JSON output'
2093
+ complete -c cu -n '__fish_seen_subcommand_from tasks' -l status -d 'Filter by status'
2094
+ complete -c cu -n '__fish_seen_subcommand_from tasks' -l list -d 'Filter by list ID'
2095
+ complete -c cu -n '__fish_seen_subcommand_from tasks' -l space -d 'Filter by space ID'
2096
+ complete -c cu -n '__fish_seen_subcommand_from tasks' -l name -d 'Filter by name'
2097
+ complete -c cu -n '__fish_seen_subcommand_from tasks' -l type -d 'Filter by task type'
2098
+ complete -c cu -n '__fish_seen_subcommand_from tasks' -l include-closed -d 'Include done/closed tasks'
2099
+ complete -c cu -n '__fish_seen_subcommand_from tasks' -l json -d 'Force JSON output'
2049
2100
 
2050
2101
  complete -c cu -n '__fish_seen_subcommand_from task' -l json -d 'Force JSON output'
2051
2102
 
@@ -2183,9 +2234,11 @@ async function searchTasks(config, query, opts = {}) {
2183
2234
  throw new Error("Search query cannot be empty");
2184
2235
  }
2185
2236
  const client = new ClickUpClient(config);
2186
- const allTasks = await client.getMyTasks(config.teamId, {
2187
- includeClosed: opts.includeClosed
2188
- });
2237
+ const [allTasks, customTypes] = await Promise.all([
2238
+ client.getMyTasks(config.teamId, { includeClosed: opts.includeClosed }),
2239
+ client.getCustomTaskTypes(config.teamId)
2240
+ ]);
2241
+ const typeMap = buildTypeMap(customTypes);
2189
2242
  const words = trimmed.toLowerCase().split(/\s+/);
2190
2243
  let matched = allTasks.filter((task) => {
2191
2244
  const name = task.name.toLowerCase();
@@ -2204,7 +2257,7 @@ async function searchTasks(config, query, opts = {}) {
2204
2257
  matched = matched.filter((t) => t.status.status.toLowerCase() === opts.status.toLowerCase());
2205
2258
  }
2206
2259
  }
2207
- return matched.map(summarize);
2260
+ return matched.map((t) => summarize(t, typeMap));
2208
2261
  }
2209
2262
 
2210
2263
  // src/commands/depend.ts
@@ -2426,25 +2479,14 @@ program.command("auth").description("Validate API token and show current user").
2426
2479
  }
2427
2480
  })
2428
2481
  );
2429
- program.command("tasks").description("List tasks assigned to me").option("--status <status>", 'Filter by status (e.g. "in progress")').option("--list <listId>", "Filter by list ID").option("--space <spaceId>", "Filter by space ID").option("--name <partial>", "Filter by name (case-insensitive contains)").option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").action(
2430
- wrapAction(async (opts) => {
2431
- const config = loadConfig();
2432
- const tasks = await fetchMyTasks(config, {
2433
- typeFilter: "task",
2434
- statuses: opts.status ? [opts.status] : void 0,
2435
- listIds: opts.list ? [opts.list] : void 0,
2436
- spaceIds: opts.space ? [opts.space] : void 0,
2437
- name: opts.name,
2438
- includeClosed: opts.includeClosed
2439
- });
2440
- await printTasks(tasks, opts.json ?? false, config);
2441
- })
2442
- );
2443
- program.command("initiatives").description("List initiatives assigned to me").option("--status <status>", "Filter by status").option("--list <listId>", "Filter by list ID").option("--space <spaceId>", "Filter by space ID").option("--name <partial>", "Filter by name (case-insensitive contains)").option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").action(
2482
+ program.command("tasks").description("List tasks assigned to me").option("--status <status>", 'Filter by status (e.g. "in progress")').option("--list <listId>", "Filter by list ID").option("--space <spaceId>", "Filter by space ID").option("--name <partial>", "Filter by name (case-insensitive contains)").option(
2483
+ "--type <type>",
2484
+ 'Filter by task type (e.g. "task", "initiative", or custom type name/ID)'
2485
+ ).option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").action(
2444
2486
  wrapAction(async (opts) => {
2445
2487
  const config = loadConfig();
2446
2488
  const tasks = await fetchMyTasks(config, {
2447
- typeFilter: "initiative",
2489
+ typeFilter: opts.type,
2448
2490
  statuses: opts.status ? [opts.status] : void 0,
2449
2491
  listIds: opts.list ? [opts.list] : void 0,
2450
2492
  spaceIds: opts.space ? [opts.space] : void 0,
@@ -2467,9 +2509,13 @@ program.command("task <taskId>").description("Get task details").option("--json"
2467
2509
  }
2468
2510
  })
2469
2511
  );
2470
- 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(
2471
2513
  wrapAction(async (taskId, opts) => {
2472
2514
  const config = loadConfig();
2515
+ if (opts.assignee === "me") {
2516
+ const client = new ClickUpClient(config);
2517
+ opts.assignee = String(await resolveAssigneeId(client, "me"));
2518
+ }
2473
2519
  const payload = buildUpdatePayload(opts);
2474
2520
  const result = await updateTask(config, taskId, payload);
2475
2521
  if (shouldOutputJson(opts.json ?? false)) {
@@ -2479,9 +2525,13 @@ program.command("update <taskId>").description("Update a task").option("-n, --na
2479
2525
  }
2480
2526
  })
2481
2527
  );
2482
- 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(
2483
2529
  wrapAction(async (opts) => {
2484
2530
  const config = loadConfig();
2531
+ if (opts.assignee === "me") {
2532
+ const client = new ClickUpClient(config);
2533
+ opts.assignee = String(await resolveAssigneeId(client, "me"));
2534
+ }
2485
2535
  const result = await createTask(config, opts);
2486
2536
  if (shouldOutputJson(opts.json ?? false)) {
2487
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.0",
3
+ "version": "0.13.2",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: clickup
3
- description: 'Use when managing ClickUp tasks, initiatives, sprints, or comments via the `cu` CLI tool. Triggers: task queries, status updates, sprint tracking, creating subtasks, posting comments, 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.'
3
+ description: 'Use when managing ClickUp tasks, sprints, or comments via the `cu` CLI tool. Triggers: task queries, status updates, sprint tracking, creating subtasks, posting comments, 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.'
4
4
  ---
5
5
 
6
6
  # ClickUp CLI (`cu`)
7
7
 
8
- Reference for AI agents using the `cu` CLI tool. Covers task management, sprint tracking, initiatives, comments, and project workflows.
8
+ Reference for AI agents using the `cu` CLI tool. Covers task management, sprint tracking, comments, and project workflows.
9
9
 
10
- Keywords: ClickUp, task management, sprint, initiative, project management, agile, backlog, subtasks, standup, overdue, search
10
+ Keywords: ClickUp, task management, sprint, project management, agile, backlog, subtasks, standup, overdue, search
11
11
 
12
12
  ## Setup
13
13
 
@@ -34,75 +34,74 @@ All commands support `--help` for full flag details.
34
34
 
35
35
  ### Read
36
36
 
37
- | Command | What it returns |
38
- | --------------------------------------------------------------------------------------------- | -------------------------------------------------- |
39
- | `cu tasks [--status s] [--name q] [--list id] [--space id] [--include-closed] [--json]` | My tasks (workspace-wide) |
40
- | `cu initiatives [--status s] [--name q] [--list id] [--space id] [--include-closed] [--json]` | My initiatives |
41
- | `cu assigned [--status s] [--include-closed] [--json]` | All my tasks grouped by status |
42
- | `cu sprint [--status s] [--space nameOrId] [--include-closed] [--json]` | Tasks in active sprint (auto-detected) |
43
- | `cu sprints [--space nameOrId] [--json]` | List all sprints (marks active with \*) |
44
- | `cu search <query> [--status s] [--include-closed] [--json]` | Search my tasks by name (multi-word, fuzzy status) |
45
- | `cu task <id> [--json]` | Single task details |
46
- | `cu subtasks <id> [--status s] [--name q] [--include-closed] [--json]` | Subtasks of a task or initiative |
47
- | `cu comments <id> [--json]` | Comments on a task |
48
- | `cu activity <id> [--json]` | Task details + comment history combined |
49
- | `cu inbox [--days n] [--include-closed] [--json]` | Tasks updated in last n days (default 30) |
50
- | `cu summary [--hours n] [--json]` | Standup helper: completed, in-progress, overdue |
51
- | `cu overdue [--include-closed] [--json]` | Tasks past their due date |
52
- | `cu spaces [--name partial] [--my] [--json]` | List/filter workspace spaces |
53
- | `cu lists <spaceId> [--name partial] [--json]` | Lists in a space (including folder lists) |
54
- | `cu open <query> [--json]` | Open task in browser by ID or name |
55
- | `cu auth [--json]` | Check authentication status |
37
+ | Command | What it returns |
38
+ | -------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
39
+ | `cu tasks [--status s] [--name q] [--type t] [--list id] [--space id] [--include-closed] [--json]` | My tasks (all types, or filter with --type) |
40
+ | `cu assigned [--status s] [--include-closed] [--json]` | All my tasks grouped by status |
41
+ | `cu sprint [--status s] [--space nameOrId] [--include-closed] [--json]` | Tasks in active sprint (auto-detected) |
42
+ | `cu sprints [--space nameOrId] [--json]` | List all sprints (marks active with \*) |
43
+ | `cu search <query> [--status s] [--include-closed] [--json]` | Search my tasks by name (multi-word, fuzzy status) |
44
+ | `cu task <id> [--json]` | Single task details |
45
+ | `cu subtasks <id> [--status s] [--name q] [--include-closed] [--json]` | Subtasks of a task |
46
+ | `cu comments <id> [--json]` | Comments on a task |
47
+ | `cu activity <id> [--json]` | Task details + comment history combined |
48
+ | `cu inbox [--days n] [--include-closed] [--json]` | Tasks updated in last n days (default 30) |
49
+ | `cu summary [--hours n] [--json]` | Standup helper: completed, in-progress, overdue |
50
+ | `cu overdue [--include-closed] [--json]` | Tasks past their due date |
51
+ | `cu spaces [--name partial] [--my] [--json]` | List/filter workspace spaces |
52
+ | `cu lists <spaceId> [--name partial] [--json]` | Lists in a space (including folder lists) |
53
+ | `cu open <query> [--json]` | Open task in browser by ID or name |
54
+ | `cu auth [--json]` | Check authentication status |
56
55
 
57
56
  ### Write
58
57
 
59
- | Command | What it does |
60
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------- |
61
- | `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) |
62
- | `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) |
63
- | `cu comment <id> -m text [--json]` | Post comment on task |
64
- | `cu assign <id> [--to userId\|me] [--remove userId\|me] [--json]` | Assign/unassign users |
65
- | `cu depend <id> [--on taskId] [--blocks taskId] [--remove] [--json]` | Add/remove task dependencies |
66
- | `cu move <id> [--to listId] [--remove listId] [--json]` | Add/remove task from lists |
67
- | `cu field <id> [--set "Name" value] [--remove "Name"] [--json]` | Set/remove custom field values |
68
- | `cu delete <id> [--confirm] [--json]` | Delete a task (DESTRUCTIVE, irreversible) |
69
- | `cu tag <id> [--add tags] [--remove tags] [--json]` | Add/remove tags on a task |
70
- | `cu config get <key>` / `cu config set <key> <value>` / `cu config path` | Manage CLI config |
71
- | `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) |
72
71
 
73
72
  ## Quick Reference
74
73
 
75
- | Topic | Detail |
76
- | ----------------------- | --------------------------------------------------------------------------------------------------------------------- |
77
- | Task IDs | Stable alphanumeric strings (e.g. `abc123def`) |
78
- | Initiatives | Detected via `custom_item_id !== 0` |
79
- | `--list` on create | Optional when `--parent` is given (auto-detected) |
80
- | `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr. |
81
- | `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
82
- | `--due-date` | `YYYY-MM-DD` format |
83
- | `--assignee` | Numeric user ID (find via `cu task <id> --json`) |
84
- | `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
85
- | `--time-estimate` | Duration format: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
86
- | `--custom-item-id` | Custom task type ID (e.g. `1` for initiative) |
87
- | `--on` / `--blocks` | Task dependency direction (used with `cu depend`) |
88
- | `--to` / `--remove` | List ID to add/remove task (used with `cu move`) |
89
- | `cu field --set` | Supports: text, number, checkbox (true/false), dropdown (option name), date (YYYY-MM-DD), url, email |
90
- | `cu field` | Field names resolved case-insensitively; errors list available fields/options |
91
- | `cu delete` | DESTRUCTIVE. Requires `--confirm` in non-interactive mode. Cannot be undone |
92
- | `cu tag --add/--remove` | Comma-separated tag names (e.g. `--add "bug,frontend"`) |
93
- | `--space` | Partial name match or exact ID |
94
- | `--name` | Partial match, case-insensitive |
95
- | `--include-closed` | Include closed/done tasks (on `tasks`, `initiatives`, `assigned`, `subtasks`, `sprint`, `search`, `inbox`, `overdue`) |
96
- | `cu assign --to me` | Shorthand for your own user ID |
97
- | `cu search` | Matches all query words against task name, case-insensitive |
98
- | `cu sprint` | Auto-detects active sprint via view API and date range parsing |
99
- | `cu summary` | Categories: completed (done/complete/closed within N hours), in progress, overdue |
100
- | `cu overdue` | Excludes closed tasks, sorted most overdue first |
101
- | `cu open` | Tries task ID first, falls back to name search |
102
- | `cu task` | Shows custom fields in detail view |
103
- | `cu lists` | Discovers list IDs needed for `--list` and `cu create -l` |
104
- | Errors | stderr with exit code 1 |
105
- | Parsing | Strict - excess/unknown arguments rejected |
74
+ | Topic | Detail |
75
+ | ----------------------- | ------------------------------------------------------------------------------------------------------ |
76
+ | Task IDs | Stable alphanumeric strings (e.g. `abc123def`) |
77
+ | `--type` | Filter by task type: `task` (regular), or custom type name/ID (e.g. `initiative`, `Bug`) |
78
+ | `--list` on create | Optional when `--parent` is given (auto-detected) |
79
+ | `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr. |
80
+ | `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
81
+ | `--due-date` | `YYYY-MM-DD` format |
82
+ | `--assignee` | User ID or `me` (on `cu create`, `cu update`, `cu assign`) |
83
+ | `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
84
+ | `--time-estimate` | Duration format: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
85
+ | `--custom-item-id` | Custom task type ID for `cu create` (e.g. `1` for initiative) |
86
+ | `--on` / `--blocks` | Task dependency direction (used with `cu depend`) |
87
+ | `--to` / `--remove` | List ID to add/remove task (used with `cu move`) |
88
+ | `cu field --set` | Supports: text, number, checkbox (true/false), dropdown (option name), date (YYYY-MM-DD), url, email |
89
+ | `cu field` | Field names resolved case-insensitively; errors list available fields/options |
90
+ | `cu delete` | DESTRUCTIVE. Requires `--confirm` in non-interactive mode. Cannot be undone |
91
+ | `cu tag --add/--remove` | Comma-separated tag names (e.g. `--add "bug,frontend"`) |
92
+ | `--space` | Partial name match or exact ID |
93
+ | `--name` | Partial match, case-insensitive |
94
+ | `--include-closed` | Include closed/done tasks (on `tasks`, `assigned`, `subtasks`, `sprint`, `search`, `inbox`, `overdue`) |
95
+ | `cu assign --to me` | Shorthand for your own user ID |
96
+ | `cu search` | Matches all query words against task name, case-insensitive |
97
+ | `cu sprint` | Auto-detects active sprint via view API and date range parsing |
98
+ | `cu summary` | Categories: completed (done/complete/closed within N hours), in progress, overdue |
99
+ | `cu overdue` | Excludes closed tasks, sorted most overdue first |
100
+ | `cu open` | Tries task ID first, falls back to name search |
101
+ | `cu task` | Shows custom fields in detail view |
102
+ | `cu lists` | Discovers list IDs needed for `--list` and `cu create -l` |
103
+ | Errors | stderr with exit code 1 |
104
+ | Parsing | Strict - excess/unknown arguments rejected |
106
105
 
107
106
  ## Agent Workflow Examples
108
107
 
@@ -121,6 +120,8 @@ cu activity abc123def # task + comments combined
121
120
  ```bash
122
121
  cu tasks --status "in progress" # by status
123
122
  cu tasks --name "login" # by partial name
123
+ cu tasks --type initiative # initiatives only
124
+ cu tasks --type task # regular tasks only
124
125
  cu search "payment flow" # multi-word search
125
126
  cu search auth --status "prog" # fuzzy status match
126
127
  cu sprint # current sprint