@krodak/clickup-cli 0.9.1 → 0.11.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/README.md +54 -22
- package/dist/index.js +165 -42
- package/package.json +1 -1
- package/skills/clickup-cli/SKILL.md +58 -50
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
|
-
|
|
101
|
+
25 commands total. All support `--help` for full flag details.
|
|
102
102
|
|
|
103
103
|
### `cu init`
|
|
104
104
|
|
|
@@ -118,6 +118,7 @@ cu tasks --status "in progress"
|
|
|
118
118
|
cu tasks --name "login"
|
|
119
119
|
cu tasks --list <listId>
|
|
120
120
|
cu tasks --space <spaceId>
|
|
121
|
+
cu tasks --include-closed
|
|
121
122
|
cu tasks --json
|
|
122
123
|
```
|
|
123
124
|
|
|
@@ -131,6 +132,7 @@ cu initiatives --status "to do"
|
|
|
131
132
|
cu initiatives --name "auth"
|
|
132
133
|
cu initiatives --list <listId>
|
|
133
134
|
cu initiatives --space <spaceId>
|
|
135
|
+
cu initiatives --include-closed
|
|
134
136
|
cu initiatives --json
|
|
135
137
|
```
|
|
136
138
|
|
|
@@ -141,6 +143,7 @@ List my tasks in the currently active sprint (auto-detected from sprint folder d
|
|
|
141
143
|
```bash
|
|
142
144
|
cu sprint
|
|
143
145
|
cu sprint --status "in progress"
|
|
146
|
+
cu sprint --include-closed
|
|
144
147
|
cu sprint --json
|
|
145
148
|
```
|
|
146
149
|
|
|
@@ -159,6 +162,7 @@ All tasks assigned to me, grouped by pipeline stage (code review, in progress, t
|
|
|
159
162
|
|
|
160
163
|
```bash
|
|
161
164
|
cu assigned
|
|
165
|
+
cu assigned --status "in progress"
|
|
162
166
|
cu assigned --include-closed
|
|
163
167
|
cu assigned --json
|
|
164
168
|
```
|
|
@@ -190,6 +194,8 @@ List subtasks of a task or initiative.
|
|
|
190
194
|
|
|
191
195
|
```bash
|
|
192
196
|
cu subtasks abc123
|
|
197
|
+
cu subtasks abc123 --status "in progress"
|
|
198
|
+
cu subtasks abc123 --name "auth"
|
|
193
199
|
cu subtasks abc123 --include-closed
|
|
194
200
|
cu subtasks abc123 --json
|
|
195
201
|
```
|
|
@@ -206,18 +212,20 @@ cu update abc123 --priority high
|
|
|
206
212
|
cu update abc123 --due-date 2025-03-15
|
|
207
213
|
cu update abc123 --assignee 12345
|
|
208
214
|
cu update abc123 -n "New name" -s "done" --priority urgent
|
|
215
|
+
cu update abc123 --time-estimate 2h
|
|
209
216
|
cu update abc123 -s "in progress" --json
|
|
210
217
|
```
|
|
211
218
|
|
|
212
|
-
| Flag
|
|
213
|
-
|
|
|
214
|
-
| `-n, --name <text>`
|
|
215
|
-
| `-d, --description <text>`
|
|
216
|
-
| `-s, --status <status>`
|
|
217
|
-
| `--priority <level>`
|
|
218
|
-
| `--due-date <date>`
|
|
219
|
-
| `--
|
|
220
|
-
| `--
|
|
219
|
+
| Flag | Description |
|
|
220
|
+
| ---------------------------- | --------------------------------------------------------------------------- |
|
|
221
|
+
| `-n, --name <text>` | New task name |
|
|
222
|
+
| `-d, --description <text>` | New description (markdown supported) |
|
|
223
|
+
| `-s, --status <status>` | New status, supports fuzzy matching (e.g. `"prog"` matches `"in progress"`) |
|
|
224
|
+
| `--priority <level>` | Priority: `urgent`, `high`, `normal`, `low` (or 1-4) |
|
|
225
|
+
| `--due-date <date>` | Due date (`YYYY-MM-DD`) |
|
|
226
|
+
| `--time-estimate <duration>` | Time estimate (e.g. `"2h"`, `"30m"`, `"1h30m"`) |
|
|
227
|
+
| `--assignee <userId>` | Add assignee by numeric user ID |
|
|
228
|
+
| `--json` | Force JSON output even in terminal |
|
|
221
229
|
|
|
222
230
|
### `cu create`
|
|
223
231
|
|
|
@@ -229,21 +237,25 @@ cu create -n "Subtask name" -p <parentTaskId> # --list auto-detected
|
|
|
229
237
|
cu create -n "Task" -l <listId> -d "desc" -s "open"
|
|
230
238
|
cu create -n "Task" -l <listId> --priority high --due-date 2025-06-01
|
|
231
239
|
cu create -n "Task" -l <listId> --assignee 12345 --tags "bug,frontend"
|
|
240
|
+
cu create -n "Initiative" -l <listId> --custom-item-id 1
|
|
241
|
+
cu create -n "Task" -l <listId> --time-estimate 2h
|
|
232
242
|
cu create -n "Fix bug" -l <listId> --json
|
|
233
243
|
```
|
|
234
244
|
|
|
235
|
-
| Flag
|
|
236
|
-
|
|
|
237
|
-
| `-n, --name <name>`
|
|
238
|
-
| `-l, --list <listId>`
|
|
239
|
-
| `-p, --parent <taskId>`
|
|
240
|
-
| `-d, --description <text>`
|
|
241
|
-
| `-s, --status <status>`
|
|
242
|
-
| `--priority <level>`
|
|
243
|
-
| `--due-date <date>`
|
|
244
|
-
| `--
|
|
245
|
-
| `--
|
|
246
|
-
| `--
|
|
245
|
+
| Flag | Required | Description |
|
|
246
|
+
| ---------------------------- | ---------------- | ---------------------------------------------------- |
|
|
247
|
+
| `-n, --name <name>` | yes | Task name |
|
|
248
|
+
| `-l, --list <listId>` | if no `--parent` | Target list ID |
|
|
249
|
+
| `-p, --parent <taskId>` | no | Parent task (list auto-detected) |
|
|
250
|
+
| `-d, --description <text>` | no | Description (markdown) |
|
|
251
|
+
| `-s, --status <status>` | no | Initial status |
|
|
252
|
+
| `--priority <level>` | no | Priority: `urgent`, `high`, `normal`, `low` (or 1-4) |
|
|
253
|
+
| `--due-date <date>` | no | Due date (`YYYY-MM-DD`) |
|
|
254
|
+
| `--time-estimate <duration>` | no | Time estimate (e.g. `"2h"`, `"30m"`, `"1h30m"`) |
|
|
255
|
+
| `--assignee <userId>` | no | Assignee by numeric user ID |
|
|
256
|
+
| `--tags <tags>` | no | Comma-separated tag names |
|
|
257
|
+
| `--custom-item-id <id>` | no | Custom task type ID (for creating initiatives) |
|
|
258
|
+
| `--json` | no | Force JSON output even in terminal |
|
|
247
259
|
|
|
248
260
|
### `cu comment <id>`
|
|
249
261
|
|
|
@@ -324,6 +336,7 @@ cu search "login bug"
|
|
|
324
336
|
cu search auth
|
|
325
337
|
cu search "payment flow" --json
|
|
326
338
|
cu search auth --status "prog" # fuzzy matches "in progress"
|
|
339
|
+
cu search "old task" --include-closed
|
|
327
340
|
```
|
|
328
341
|
|
|
329
342
|
### `cu summary`
|
|
@@ -368,6 +381,25 @@ cu assign abc123 --to me --json
|
|
|
368
381
|
| `--remove <userId>` | Remove assignee (user ID or `me`) |
|
|
369
382
|
| `--json` | Force JSON output |
|
|
370
383
|
|
|
384
|
+
### `cu depend <id>`
|
|
385
|
+
|
|
386
|
+
Add or remove task dependencies. Set a task as waiting on or blocking another task.
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
cu depend abc123 --on def456 # abc123 depends on (waits for) def456
|
|
390
|
+
cu depend abc123 --blocks def456 # abc123 blocks def456
|
|
391
|
+
cu depend abc123 --on def456 --remove # remove the dependency
|
|
392
|
+
cu depend abc123 --blocks def456 --remove
|
|
393
|
+
cu depend abc123 --on def456 --json
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Flag | Description |
|
|
397
|
+
| ------------------- | ------------------------------------------- |
|
|
398
|
+
| `--on <taskId>` | Task that this task depends on (waiting on) |
|
|
399
|
+
| `--blocks <taskId>` | Task that this task blocks |
|
|
400
|
+
| `--remove` | Remove the dependency instead of adding it |
|
|
401
|
+
| `--json` | Force JSON output |
|
|
402
|
+
|
|
371
403
|
### `cu auth`
|
|
372
404
|
|
|
373
405
|
Check authentication status. Validates your API token and shows your user info.
|
package/dist/index.js
CHANGED
|
@@ -223,6 +223,23 @@ var ClickUpClient = class {
|
|
|
223
223
|
async getViewTasks(viewId) {
|
|
224
224
|
return this.paginate((page) => `/view/${viewId}/task?page=${page}`);
|
|
225
225
|
}
|
|
226
|
+
async addDependency(taskId, opts) {
|
|
227
|
+
const body = {};
|
|
228
|
+
if (opts.dependsOn) body.depends_on = opts.dependsOn;
|
|
229
|
+
if (opts.dependencyOf) body.dependency_of = opts.dependencyOf;
|
|
230
|
+
await this.request(`/task/${taskId}/dependency`, {
|
|
231
|
+
method: "POST",
|
|
232
|
+
body: JSON.stringify(body)
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async deleteDependency(taskId, opts) {
|
|
236
|
+
const params = new URLSearchParams();
|
|
237
|
+
if (opts.dependsOn) params.set("depends_on", opts.dependsOn);
|
|
238
|
+
if (opts.dependencyOf) params.set("dependency_of", opts.dependencyOf);
|
|
239
|
+
await this.request(`/task/${taskId}/dependency?${params.toString()}`, {
|
|
240
|
+
method: "DELETE"
|
|
241
|
+
});
|
|
242
|
+
}
|
|
226
243
|
};
|
|
227
244
|
|
|
228
245
|
// src/output.ts
|
|
@@ -680,6 +697,18 @@ function parseAssigneeId(value) {
|
|
|
680
697
|
if (!Number.isInteger(id)) throw new Error("Assignee must be a numeric user ID");
|
|
681
698
|
return id;
|
|
682
699
|
}
|
|
700
|
+
function parseTimeEstimate(value) {
|
|
701
|
+
const pattern = /^(?:(\d+)h)?(?:(\d+)m)?$/i;
|
|
702
|
+
const match = value.match(pattern);
|
|
703
|
+
if (match && (match[1] || match[2])) {
|
|
704
|
+
const hours = Number(match[1] ?? 0);
|
|
705
|
+
const minutes = Number(match[2] ?? 0);
|
|
706
|
+
return (hours * 60 + minutes) * 60 * 1e3;
|
|
707
|
+
}
|
|
708
|
+
const ms = Number(value);
|
|
709
|
+
if (Number.isFinite(ms) && ms > 0) return ms;
|
|
710
|
+
throw new Error('Time estimate must be a duration (e.g. "2h", "30m", "1h30m") or milliseconds');
|
|
711
|
+
}
|
|
683
712
|
function buildUpdatePayload(opts) {
|
|
684
713
|
const payload = {};
|
|
685
714
|
if (opts.name !== void 0) payload.name = opts.name;
|
|
@@ -693,10 +722,13 @@ function buildUpdatePayload(opts) {
|
|
|
693
722
|
if (opts.assignee !== void 0) {
|
|
694
723
|
payload.assignees = { add: [parseAssigneeId(opts.assignee)] };
|
|
695
724
|
}
|
|
725
|
+
if (opts.timeEstimate !== void 0) {
|
|
726
|
+
payload.time_estimate = parseTimeEstimate(opts.timeEstimate);
|
|
727
|
+
}
|
|
696
728
|
return payload;
|
|
697
729
|
}
|
|
698
730
|
function hasUpdateFields(options) {
|
|
699
|
-
return options.name !== void 0 || options.description !== void 0 || options.status !== void 0 || options.priority !== void 0 || options.due_date !== void 0 || options.assignees !== void 0;
|
|
731
|
+
return options.name !== void 0 || options.description !== void 0 || options.status !== void 0 || options.priority !== void 0 || options.due_date !== void 0 || options.time_estimate !== void 0 || options.assignees !== void 0;
|
|
700
732
|
}
|
|
701
733
|
async function resolveStatus(client, taskId, statusInput) {
|
|
702
734
|
const task = await client.getTask(taskId);
|
|
@@ -716,7 +748,7 @@ async function resolveStatus(client, taskId, statusInput) {
|
|
|
716
748
|
async function updateTask(config, taskId, options) {
|
|
717
749
|
if (!hasUpdateFields(options))
|
|
718
750
|
throw new Error(
|
|
719
|
-
"Provide at least one of: --name, --description, --status, --priority, --due-date, --assignee"
|
|
751
|
+
"Provide at least one of: --name, --description, --status, --priority, --due-date, --time-estimate, --assignee"
|
|
720
752
|
);
|
|
721
753
|
const client = new ClickUpClient(config);
|
|
722
754
|
if (options.status !== void 0) {
|
|
@@ -756,6 +788,15 @@ async function createTask(config, options) {
|
|
|
756
788
|
if (options.tags !== void 0) {
|
|
757
789
|
payload.tags = options.tags.split(",").map((t) => t.trim());
|
|
758
790
|
}
|
|
791
|
+
if (options.customItemId !== void 0) {
|
|
792
|
+
const id = Number(options.customItemId);
|
|
793
|
+
if (!Number.isInteger(id) || id < 0)
|
|
794
|
+
throw new Error("Custom item ID must be a non-negative integer");
|
|
795
|
+
payload.custom_item_id = id;
|
|
796
|
+
}
|
|
797
|
+
if (options.timeEstimate !== void 0) {
|
|
798
|
+
payload.time_estimate = parseTimeEstimate(options.timeEstimate);
|
|
799
|
+
}
|
|
759
800
|
const task = await client.createTask(listId, payload);
|
|
760
801
|
return { id: task.id, name: task.name, url: task.url };
|
|
761
802
|
}
|
|
@@ -893,7 +934,10 @@ async function runSprintCommand(config, opts) {
|
|
|
893
934
|
} else {
|
|
894
935
|
allTasks = await client.getTasksFromList(activeList.id);
|
|
895
936
|
}
|
|
896
|
-
|
|
937
|
+
let sprintTasks = allTasks.filter((t) => t.assignees.some((a) => Number(a.id) === me.id));
|
|
938
|
+
if (!opts.includeClosed) {
|
|
939
|
+
sprintTasks = sprintTasks.filter((t) => !isDoneStatus(t.status.status));
|
|
940
|
+
}
|
|
897
941
|
const filtered = opts.status ? sprintTasks.filter((t) => t.status.status.toLowerCase() === opts.status.toLowerCase()) : sprintTasks;
|
|
898
942
|
const summaries = filtered.map(summarize);
|
|
899
943
|
await printTasks(summaries, opts.json ?? false, config);
|
|
@@ -956,7 +1000,7 @@ async function listSprints(config, opts = {}) {
|
|
|
956
1000
|
const lists = listsByFolder[i];
|
|
957
1001
|
allSprints.push(...buildSprintInfos(lists, folder.name, today));
|
|
958
1002
|
}
|
|
959
|
-
if (opts.json
|
|
1003
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
960
1004
|
console.log(JSON.stringify(allSprints, null, 2));
|
|
961
1005
|
return;
|
|
962
1006
|
}
|
|
@@ -973,6 +1017,15 @@ async function listSprints(config, opts = {}) {
|
|
|
973
1017
|
dates: dateStr
|
|
974
1018
|
};
|
|
975
1019
|
});
|
|
1020
|
+
if (!isTTY()) {
|
|
1021
|
+
const mdColumns = [
|
|
1022
|
+
{ key: "id", label: "ID" },
|
|
1023
|
+
{ key: "sprint", label: "Sprint" },
|
|
1024
|
+
{ key: "dates", label: "Dates" }
|
|
1025
|
+
];
|
|
1026
|
+
console.log(formatMarkdownTable(rows, mdColumns));
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
976
1029
|
console.log(formatTable(rows, SPRINT_COLUMNS));
|
|
977
1030
|
}
|
|
978
1031
|
|
|
@@ -1190,6 +1243,8 @@ async function listSpaces(config, opts) {
|
|
|
1190
1243
|
console.log(JSON.stringify(spaces, null, 2));
|
|
1191
1244
|
} else if (!isTTY()) {
|
|
1192
1245
|
console.log(formatSpacesMarkdown(spaces.map((s) => ({ id: s.id, name: s.name }))));
|
|
1246
|
+
} else if (spaces.length === 0) {
|
|
1247
|
+
console.log("No spaces found.");
|
|
1193
1248
|
} else {
|
|
1194
1249
|
const table = formatTable(
|
|
1195
1250
|
spaces.map((s) => ({ id: s.id, name: s.name })),
|
|
@@ -1254,7 +1309,11 @@ async function runAssignedCommand(config, opts) {
|
|
|
1254
1309
|
const allTasks = await client.getMyTasks(config.teamId, {
|
|
1255
1310
|
includeClosed: opts.includeClosed
|
|
1256
1311
|
});
|
|
1257
|
-
|
|
1312
|
+
let groups = groupByStatus(allTasks, opts.includeClosed ?? false);
|
|
1313
|
+
if (opts.status) {
|
|
1314
|
+
const lower = opts.status.toLowerCase();
|
|
1315
|
+
groups = groups.filter((g) => g.status.toLowerCase() === lower);
|
|
1316
|
+
}
|
|
1258
1317
|
if (shouldOutputJson(opts.json ?? false)) {
|
|
1259
1318
|
const result = {};
|
|
1260
1319
|
for (const group of groups) {
|
|
@@ -1511,10 +1570,20 @@ async function fetchActivity(config, taskId) {
|
|
|
1511
1570
|
return { task, comments };
|
|
1512
1571
|
}
|
|
1513
1572
|
function printActivity(result, forceJson) {
|
|
1514
|
-
if (forceJson
|
|
1573
|
+
if (shouldOutputJson(forceJson)) {
|
|
1515
1574
|
console.log(JSON.stringify(result, null, 2));
|
|
1516
1575
|
return;
|
|
1517
1576
|
}
|
|
1577
|
+
if (!isTTY()) {
|
|
1578
|
+
const taskMd = formatTaskDetailMarkdown(result.task);
|
|
1579
|
+
const commentsMd = formatCommentsMarkdown(result.comments);
|
|
1580
|
+
console.log(`${taskMd}
|
|
1581
|
+
|
|
1582
|
+
## Comments
|
|
1583
|
+
|
|
1584
|
+
${commentsMd}`);
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1518
1587
|
console.log(formatTaskDetail(result.task));
|
|
1519
1588
|
console.log("");
|
|
1520
1589
|
console.log(chalk4.bold("Comments"));
|
|
@@ -1957,7 +2026,9 @@ async function searchTasks(config, query, opts = {}) {
|
|
|
1957
2026
|
throw new Error("Search query cannot be empty");
|
|
1958
2027
|
}
|
|
1959
2028
|
const client = new ClickUpClient(config);
|
|
1960
|
-
const allTasks = await client.getMyTasks(config.teamId
|
|
2029
|
+
const allTasks = await client.getMyTasks(config.teamId, {
|
|
2030
|
+
includeClosed: opts.includeClosed
|
|
2031
|
+
});
|
|
1961
2032
|
const words = trimmed.toLowerCase().split(/\s+/);
|
|
1962
2033
|
let matched = allTasks.filter((task) => {
|
|
1963
2034
|
const name = task.name.toLowerCase();
|
|
@@ -1979,6 +2050,31 @@ async function searchTasks(config, query, opts = {}) {
|
|
|
1979
2050
|
return matched.map(summarize);
|
|
1980
2051
|
}
|
|
1981
2052
|
|
|
2053
|
+
// src/commands/depend.ts
|
|
2054
|
+
async function manageDependency(config, taskId, opts) {
|
|
2055
|
+
if (!opts.on && !opts.blocks) {
|
|
2056
|
+
throw new Error("Provide --on <taskId> or --blocks <taskId>");
|
|
2057
|
+
}
|
|
2058
|
+
if (opts.on && opts.blocks) {
|
|
2059
|
+
throw new Error("Provide only one of --on or --blocks per invocation");
|
|
2060
|
+
}
|
|
2061
|
+
const client = new ClickUpClient(config);
|
|
2062
|
+
if (opts.remove) {
|
|
2063
|
+
await client.deleteDependency(taskId, {
|
|
2064
|
+
dependsOn: opts.on,
|
|
2065
|
+
dependencyOf: opts.blocks
|
|
2066
|
+
});
|
|
2067
|
+
if (opts.on) return `Removed dependency: ${taskId} no longer depends on ${opts.on}`;
|
|
2068
|
+
return `Removed dependency: ${taskId} no longer blocks ${opts.blocks}`;
|
|
2069
|
+
}
|
|
2070
|
+
await client.addDependency(taskId, {
|
|
2071
|
+
dependsOn: opts.on,
|
|
2072
|
+
dependencyOf: opts.blocks
|
|
2073
|
+
});
|
|
2074
|
+
if (opts.on) return `Added dependency: ${taskId} depends on ${opts.on}`;
|
|
2075
|
+
return `Added dependency: ${taskId} blocks ${opts.blocks}`;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
1982
2078
|
// src/index.ts
|
|
1983
2079
|
var require2 = createRequire(import.meta.url);
|
|
1984
2080
|
var { version } = require2("../package.json");
|
|
@@ -2001,17 +2097,16 @@ program.command("auth").description("Validate API token and show current user").
|
|
|
2001
2097
|
wrapAction(async (opts) => {
|
|
2002
2098
|
const config = loadConfig();
|
|
2003
2099
|
const result = await checkAuth(config);
|
|
2004
|
-
if (opts.json
|
|
2100
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
2005
2101
|
console.log(JSON.stringify(result, null, 2));
|
|
2006
2102
|
} else if (result.authenticated && result.user) {
|
|
2007
2103
|
console.log(`Authenticated as @${result.user.username} (id: ${result.user.id})`);
|
|
2008
2104
|
} else {
|
|
2009
|
-
|
|
2010
|
-
process.exit(1);
|
|
2105
|
+
throw new Error(`Authentication failed: ${result.error ?? "unknown error"}`);
|
|
2011
2106
|
}
|
|
2012
2107
|
})
|
|
2013
2108
|
);
|
|
2014
|
-
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("--json", "Force JSON output even in terminal").action(
|
|
2109
|
+
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(
|
|
2015
2110
|
wrapAction(async (opts) => {
|
|
2016
2111
|
const config = loadConfig();
|
|
2017
2112
|
const tasks = await fetchMyTasks(config, {
|
|
@@ -2019,12 +2114,13 @@ program.command("tasks").description("List tasks assigned to me").option("--stat
|
|
|
2019
2114
|
statuses: opts.status ? [opts.status] : void 0,
|
|
2020
2115
|
listIds: opts.list ? [opts.list] : void 0,
|
|
2021
2116
|
spaceIds: opts.space ? [opts.space] : void 0,
|
|
2022
|
-
name: opts.name
|
|
2117
|
+
name: opts.name,
|
|
2118
|
+
includeClosed: opts.includeClosed
|
|
2023
2119
|
});
|
|
2024
2120
|
await printTasks(tasks, opts.json ?? false, config);
|
|
2025
2121
|
})
|
|
2026
2122
|
);
|
|
2027
|
-
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("--json", "Force JSON output even in terminal").action(
|
|
2123
|
+
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(
|
|
2028
2124
|
wrapAction(async (opts) => {
|
|
2029
2125
|
const config = loadConfig();
|
|
2030
2126
|
const tasks = await fetchMyTasks(config, {
|
|
@@ -2032,7 +2128,8 @@ program.command("initiatives").description("List initiatives assigned to me").op
|
|
|
2032
2128
|
statuses: opts.status ? [opts.status] : void 0,
|
|
2033
2129
|
listIds: opts.list ? [opts.list] : void 0,
|
|
2034
2130
|
spaceIds: opts.space ? [opts.space] : void 0,
|
|
2035
|
-
name: opts.name
|
|
2131
|
+
name: opts.name,
|
|
2132
|
+
includeClosed: opts.includeClosed
|
|
2036
2133
|
});
|
|
2037
2134
|
await printTasks(tasks, opts.json ?? false, config);
|
|
2038
2135
|
})
|
|
@@ -2050,34 +2147,36 @@ program.command("task <taskId>").description("Get task details").option("--json"
|
|
|
2050
2147
|
}
|
|
2051
2148
|
})
|
|
2052
2149
|
);
|
|
2053
|
-
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("--assignee <userId>", "Add assignee by user ID").option("--json", "Force JSON output even in terminal").action(
|
|
2150
|
+
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("--json", "Force JSON output even in terminal").action(
|
|
2054
2151
|
wrapAction(async (taskId, opts) => {
|
|
2055
2152
|
const config = loadConfig();
|
|
2056
2153
|
const payload = buildUpdatePayload(opts);
|
|
2057
2154
|
const result = await updateTask(config, taskId, payload);
|
|
2058
|
-
if (shouldOutputJson(false)) {
|
|
2155
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
2059
2156
|
console.log(JSON.stringify(result, null, 2));
|
|
2060
2157
|
} else {
|
|
2061
2158
|
console.log(formatUpdateConfirmation(result.id, result.name));
|
|
2062
2159
|
}
|
|
2063
2160
|
})
|
|
2064
2161
|
);
|
|
2065
|
-
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").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("--json", "Force JSON output even in terminal").action(
|
|
2162
|
+
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").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(
|
|
2066
2163
|
wrapAction(async (opts) => {
|
|
2067
2164
|
const config = loadConfig();
|
|
2068
2165
|
const result = await createTask(config, opts);
|
|
2069
|
-
if (shouldOutputJson(false)) {
|
|
2166
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
2070
2167
|
console.log(JSON.stringify(result, null, 2));
|
|
2071
2168
|
} else {
|
|
2072
2169
|
console.log(formatCreateConfirmation(result.id, result.name, result.url));
|
|
2073
2170
|
}
|
|
2074
2171
|
})
|
|
2075
2172
|
);
|
|
2076
|
-
program.command("sprint").description("List my tasks in the current active sprint (auto-detected)").option("--status <status>", "Filter by status").option("--space <nameOrId>", "Narrow sprint search to a specific space (partial name or ID)").option("--json", "Force JSON output even in terminal").action(
|
|
2077
|
-
wrapAction(
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2173
|
+
program.command("sprint").description("List my tasks in the current active sprint (auto-detected)").option("--status <status>", "Filter by status").option("--space <nameOrId>", "Narrow sprint search to a specific space (partial name or ID)").option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").action(
|
|
2174
|
+
wrapAction(
|
|
2175
|
+
async (opts) => {
|
|
2176
|
+
const config = loadConfig();
|
|
2177
|
+
await runSprintCommand(config, opts);
|
|
2178
|
+
}
|
|
2179
|
+
)
|
|
2081
2180
|
);
|
|
2082
2181
|
program.command("sprints").description("List all sprints in sprint folders").option("--space <nameOrId>", "Filter by space (partial name or ID)").option("--json", "Force JSON output even in terminal").action(
|
|
2083
2182
|
wrapAction(async (opts) => {
|
|
@@ -2085,18 +2184,28 @@ program.command("sprints").description("List all sprints in sprint folders").opt
|
|
|
2085
2184
|
await listSprints(config, opts);
|
|
2086
2185
|
})
|
|
2087
2186
|
);
|
|
2088
|
-
program.command("subtasks <taskId>").description("List subtasks of a task or initiative").option("--include-closed", "Include closed/done subtasks").option("--json", "Force JSON output even in terminal").action(
|
|
2089
|
-
wrapAction(
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2187
|
+
program.command("subtasks <taskId>").description("List subtasks of a task or initiative").option("--status <status>", "Filter by status").option("--name <partial>", "Filter by name (case-insensitive contains)").option("--include-closed", "Include closed/done subtasks").option("--json", "Force JSON output even in terminal").action(
|
|
2188
|
+
wrapAction(
|
|
2189
|
+
async (taskId, opts) => {
|
|
2190
|
+
const config = loadConfig();
|
|
2191
|
+
let tasks = await fetchSubtasks(config, taskId, { includeClosed: opts.includeClosed });
|
|
2192
|
+
if (opts.status) {
|
|
2193
|
+
const lower = opts.status.toLowerCase();
|
|
2194
|
+
tasks = tasks.filter((t) => t.status.toLowerCase() === lower);
|
|
2195
|
+
}
|
|
2196
|
+
if (opts.name) {
|
|
2197
|
+
const query = opts.name.toLowerCase();
|
|
2198
|
+
tasks = tasks.filter((t) => t.name.toLowerCase().includes(query));
|
|
2199
|
+
}
|
|
2200
|
+
await printTasks(tasks, opts.json ?? false, config);
|
|
2201
|
+
}
|
|
2202
|
+
)
|
|
2094
2203
|
);
|
|
2095
|
-
program.command("comment <taskId>").description("Post a comment on a task").requiredOption("-m, --message <text>", "Comment text").action(
|
|
2204
|
+
program.command("comment <taskId>").description("Post a comment on a task").requiredOption("-m, --message <text>", "Comment text").option("--json", "Force JSON output even in terminal").action(
|
|
2096
2205
|
wrapAction(async (taskId, opts) => {
|
|
2097
2206
|
const config = loadConfig();
|
|
2098
2207
|
const result = await postComment(config, taskId, opts.message);
|
|
2099
|
-
if (shouldOutputJson(false)) {
|
|
2208
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
2100
2209
|
console.log(JSON.stringify(result, null, 2));
|
|
2101
2210
|
} else {
|
|
2102
2211
|
console.log(formatCommentConfirmation(result.id));
|
|
@@ -2135,14 +2244,13 @@ program.command("inbox").description("Recently updated tasks grouped by time per
|
|
|
2135
2244
|
const config = loadConfig();
|
|
2136
2245
|
const days = Number(opts.days ?? 30);
|
|
2137
2246
|
if (!Number.isFinite(days) || days <= 0) {
|
|
2138
|
-
|
|
2139
|
-
process.exit(1);
|
|
2247
|
+
throw new Error("--days must be a positive number");
|
|
2140
2248
|
}
|
|
2141
2249
|
const tasks = await fetchInbox(config, days);
|
|
2142
2250
|
await printInbox(tasks, opts.json ?? false, config);
|
|
2143
2251
|
})
|
|
2144
2252
|
);
|
|
2145
|
-
program.command("assigned").description("Show all tasks assigned to me, grouped by status").option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").action(
|
|
2253
|
+
program.command("assigned").description("Show all tasks assigned to me, grouped by status").option("--status <status>", "Show only tasks with this status").option("--include-closed", "Include done/closed tasks").option("--json", "Force JSON output even in terminal").action(
|
|
2146
2254
|
wrapAction(async (opts) => {
|
|
2147
2255
|
const config = loadConfig();
|
|
2148
2256
|
await runAssignedCommand(config, opts);
|
|
@@ -2154,20 +2262,24 @@ program.command("open <query>").description("Open a task in the browser by ID or
|
|
|
2154
2262
|
await openTask(config, query, opts);
|
|
2155
2263
|
})
|
|
2156
2264
|
);
|
|
2157
|
-
program.command("search <query>").description("Search my tasks by name").option("--status <status>", "Filter by status").option("--json", "Force JSON output even in terminal").action(
|
|
2158
|
-
wrapAction(
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2265
|
+
program.command("search <query>").description("Search my tasks by name").option("--status <status>", "Filter by status").option("--include-closed", "Include done/closed tasks in search").option("--json", "Force JSON output even in terminal").action(
|
|
2266
|
+
wrapAction(
|
|
2267
|
+
async (query, opts) => {
|
|
2268
|
+
const config = loadConfig();
|
|
2269
|
+
const tasks = await searchTasks(config, query, {
|
|
2270
|
+
status: opts.status,
|
|
2271
|
+
includeClosed: opts.includeClosed
|
|
2272
|
+
});
|
|
2273
|
+
await printTasks(tasks, opts.json ?? false, config);
|
|
2274
|
+
}
|
|
2275
|
+
)
|
|
2163
2276
|
);
|
|
2164
2277
|
program.command("summary").description("Daily standup summary: completed, in-progress, overdue").option("--hours <n>", "Completed-tasks lookback in hours", "24").option("--json", "Force JSON output even in terminal").action(
|
|
2165
2278
|
wrapAction(async (opts) => {
|
|
2166
2279
|
const config = loadConfig();
|
|
2167
2280
|
const hours = Number(opts.hours ?? 24);
|
|
2168
2281
|
if (!Number.isFinite(hours) || hours <= 0) {
|
|
2169
|
-
|
|
2170
|
-
process.exit(1);
|
|
2282
|
+
throw new Error("--hours must be a positive number");
|
|
2171
2283
|
}
|
|
2172
2284
|
await runSummaryCommand(config, { hours, json: opts.json ?? false });
|
|
2173
2285
|
})
|
|
@@ -2190,6 +2302,17 @@ program.command("assign <taskId>").description("Assign or unassign users from a
|
|
|
2190
2302
|
}
|
|
2191
2303
|
})
|
|
2192
2304
|
);
|
|
2305
|
+
program.command("depend <taskId>").description("Add or remove task dependencies").option("--on <taskId>", "Task that this task depends on (waiting on)").option("--blocks <taskId>", "Task that this task blocks").option("--remove", "Remove the dependency instead of adding it").option("--json", "Force JSON output even in terminal").action(
|
|
2306
|
+
wrapAction(async (taskId, opts) => {
|
|
2307
|
+
const config = loadConfig();
|
|
2308
|
+
const message = await manageDependency(config, taskId, opts);
|
|
2309
|
+
if (shouldOutputJson(opts.json ?? false)) {
|
|
2310
|
+
console.log(JSON.stringify({ taskId, ...opts, message }, null, 2));
|
|
2311
|
+
} else {
|
|
2312
|
+
console.log(message);
|
|
2313
|
+
}
|
|
2314
|
+
})
|
|
2315
|
+
);
|
|
2193
2316
|
var configCmd = program.command("config").description("Manage CLI configuration");
|
|
2194
2317
|
configCmd.command("get <key>").description("Print a config value").action(
|
|
2195
2318
|
wrapAction(async (key) => {
|
package/package.json
CHANGED
|
@@ -34,62 +34,66 @@ All commands support `--help` for full flag details.
|
|
|
34
34
|
|
|
35
35
|
### Read
|
|
36
36
|
|
|
37
|
-
| Command
|
|
38
|
-
|
|
|
39
|
-
| `cu tasks [--status s] [--name q] [--list id] [--space id] [--json]` | My tasks (workspace-wide) |
|
|
40
|
-
| `cu initiatives [--status s] [--name q] [--list id] [--space id] [--json]` | My initiatives |
|
|
41
|
-
| `cu assigned [--include-closed] [--json]`
|
|
42
|
-
| `cu sprint [--status s] [--space nameOrId] [--json]` | Tasks in active sprint (auto-detected) |
|
|
43
|
-
| `cu sprints [--space nameOrId] [--json]`
|
|
44
|
-
| `cu search <query> [--status s] [--json]` | Search my tasks by name (multi-word, fuzzy status) |
|
|
45
|
-
| `cu task <id> [--json]`
|
|
46
|
-
| `cu subtasks <id> [--include-closed] [--json]`
|
|
47
|
-
| `cu comments <id> [--json]`
|
|
48
|
-
| `cu activity <id> [--json]`
|
|
49
|
-
| `cu inbox [--days n] [--json]`
|
|
50
|
-
| `cu summary [--hours n] [--json]`
|
|
51
|
-
| `cu overdue [--json]`
|
|
52
|
-
| `cu spaces [--name partial] [--my] [--json]`
|
|
53
|
-
| `cu lists <spaceId> [--name partial] [--json]`
|
|
54
|
-
| `cu open <query> [--json]`
|
|
55
|
-
| `cu auth [--json]`
|
|
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] [--json]` | Tasks updated in last n days (default 30) |
|
|
50
|
+
| `cu summary [--hours n] [--json]` | Standup helper: completed, in-progress, overdue |
|
|
51
|
+
| `cu overdue [--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 |
|
|
56
56
|
|
|
57
57
|
### Write
|
|
58
58
|
|
|
59
|
-
| Command
|
|
60
|
-
|
|
|
61
|
-
| `cu update <id> [-n name] [-d desc] [-s status] [--priority p] [--due-date d] [--assignee id] [--json]`
|
|
62
|
-
| `cu create -n name [-l listId] [-p parentId] [-d desc] [-s status] [--priority p] [--due-date d] [--assignee id] [--tags t] [--json]` | Create task (list auto-detected from parent) |
|
|
63
|
-
| `cu comment <id> -m text`
|
|
64
|
-
| `cu assign <id> [--to userId\|me] [--remove userId\|me] [--json]`
|
|
65
|
-
| `cu
|
|
66
|
-
| `cu
|
|
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] [--json]` | Update task fields |
|
|
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 (list auto-detected from parent) |
|
|
63
|
+
| `cu comment <id> -m text` | 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 config get <key>` / `cu config set <key> <value>` / `cu config path` | Manage CLI config |
|
|
67
|
+
| `cu completion <shell>` | Shell completions (bash/zsh/fish) |
|
|
67
68
|
|
|
68
69
|
## Quick Reference
|
|
69
70
|
|
|
70
|
-
| Topic | Detail
|
|
71
|
-
| ------------------- |
|
|
72
|
-
| Task IDs | Stable alphanumeric strings (e.g. `abc123def`)
|
|
73
|
-
| Initiatives | Detected via `custom_item_id !== 0`
|
|
74
|
-
| `--list` on create | Optional when `--parent` is given (auto-detected)
|
|
75
|
-
| `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr.
|
|
76
|
-
| `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4)
|
|
77
|
-
| `--due-date` | `YYYY-MM-DD` format
|
|
78
|
-
| `--assignee` | Numeric user ID (find via `cu task <id> --json`)
|
|
79
|
-
| `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`)
|
|
80
|
-
| `--
|
|
81
|
-
| `--
|
|
82
|
-
| `--
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `cu
|
|
87
|
-
| `cu
|
|
88
|
-
| `cu
|
|
89
|
-
| `cu
|
|
90
|
-
| `cu
|
|
91
|
-
|
|
|
92
|
-
|
|
|
71
|
+
| Topic | Detail |
|
|
72
|
+
| ------------------- | ------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| Task IDs | Stable alphanumeric strings (e.g. `abc123def`) |
|
|
74
|
+
| Initiatives | Detected via `custom_item_id !== 0` |
|
|
75
|
+
| `--list` on create | Optional when `--parent` is given (auto-detected) |
|
|
76
|
+
| `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr. |
|
|
77
|
+
| `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
|
|
78
|
+
| `--due-date` | `YYYY-MM-DD` format |
|
|
79
|
+
| `--assignee` | Numeric user ID (find via `cu task <id> --json`) |
|
|
80
|
+
| `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
|
|
81
|
+
| `--time-estimate` | Duration format: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
|
|
82
|
+
| `--custom-item-id` | Custom task type ID (e.g. `1` for initiative) |
|
|
83
|
+
| `--on` / `--blocks` | Task dependency direction (used with `cu depend`) |
|
|
84
|
+
| `--space` | Partial name match or exact ID |
|
|
85
|
+
| `--name` | Partial match, case-insensitive |
|
|
86
|
+
| `--include-closed` | Include closed/done tasks (on `tasks`, `initiatives`, `assigned`, `subtasks`, `sprint`, `search`) |
|
|
87
|
+
| `cu assign --to me` | Shorthand for your own user ID |
|
|
88
|
+
| `cu search` | Matches all query words against task name, case-insensitive |
|
|
89
|
+
| `cu sprint` | Auto-detects active sprint via view API and date range parsing |
|
|
90
|
+
| `cu summary` | Categories: completed (done/complete/closed within N hours), in progress, overdue |
|
|
91
|
+
| `cu overdue` | Excludes closed tasks, sorted most overdue first |
|
|
92
|
+
| `cu open` | Tries task ID first, falls back to name search |
|
|
93
|
+
| `cu task` | Shows custom fields in detail view |
|
|
94
|
+
| `cu lists` | Discovers list IDs needed for `--list` and `cu create -l` |
|
|
95
|
+
| Errors | stderr with exit code 1 |
|
|
96
|
+
| Parsing | Strict - excess/unknown arguments rejected |
|
|
93
97
|
|
|
94
98
|
## Agent Workflow Examples
|
|
95
99
|
|
|
@@ -121,10 +125,14 @@ cu inbox --days 7 # recently updated
|
|
|
121
125
|
```bash
|
|
122
126
|
cu update abc123def -s "done"
|
|
123
127
|
cu update abc123def --priority high --due-date 2025-03-15
|
|
128
|
+
cu update abc123def --time-estimate 2h
|
|
124
129
|
cu create -n "Fix the thing" -p abc123def
|
|
125
130
|
cu create -n "Fix bug" -l <listId> --priority urgent --tags "bug,frontend"
|
|
131
|
+
cu create -n "Q3 Roadmap" -l <listId> --custom-item-id 1 # create initiative
|
|
126
132
|
cu comment abc123def -m "Completed in PR #42"
|
|
127
133
|
cu assign abc123def --to me
|
|
134
|
+
cu depend task3 --on task2 # task3 waits for task2
|
|
135
|
+
cu depend task1 --blocks task2 # task1 blocks task2
|
|
128
136
|
```
|
|
129
137
|
|
|
130
138
|
### Discover workspace structure
|