@krodak/clickup-cli 1.34.0 → 1.36.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clickup-cli",
3
3
  "description": "ClickUp CLI skills for managing tasks, sprints, comments, checklists, custom fields, tags, and time tracking via the cup command",
4
- "version": "1.34.0",
4
+ "version": "1.36.0",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/dist/index.js CHANGED
@@ -873,7 +873,7 @@ var ClickUpClient = class {
873
873
  body: JSON.stringify({ name })
874
874
  });
875
875
  }
876
- async createCustomField(teamId, name, type, opts) {
876
+ buildCustomFieldBody(name, type, opts) {
877
877
  const typeConfig = {};
878
878
  if (opts?.options?.length) {
879
879
  typeConfig.options = opts.options.map((optName, i) => ({
@@ -881,7 +881,7 @@ var ClickUpClient = class {
881
881
  orderindex: i
882
882
  }));
883
883
  }
884
- const body = {
884
+ return {
885
885
  name,
886
886
  type,
887
887
  type_config: typeConfig,
@@ -895,11 +895,22 @@ var ClickUpClient = class {
895
895
  members: [],
896
896
  groups: []
897
897
  };
898
+ }
899
+ async createCustomField(teamId, name, type, opts) {
900
+ const body = this.buildCustomFieldBody(name, type, opts);
898
901
  const data = await this.request(
899
- `/field?workspace_id=${teamId}`,
902
+ `/team/${teamId}/field`,
900
903
  { method: "POST", body: JSON.stringify(body) }
901
904
  );
902
- return data.data;
905
+ return data.field;
906
+ }
907
+ async createListCustomField(listId, name, type, opts) {
908
+ const body = this.buildCustomFieldBody(name, type, opts);
909
+ const data = await this.request(
910
+ `/list/${listId}/field`,
911
+ { method: "POST", body: JSON.stringify(body) }
912
+ );
913
+ return data.field;
903
914
  }
904
915
  chatChannelsPath(suffix = "") {
905
916
  return `/workspaces/${this.teamId}/chat/channels${suffix}`;
@@ -4172,6 +4183,18 @@ var commandMetadata = [
4172
4183
  }
4173
4184
  ]
4174
4185
  },
4186
+ {
4187
+ name: "attach-get",
4188
+ description: "Download task attachment(s) by ID or title",
4189
+ flags: ["-o", "--output", "--dir", "--all", "--force", "--json"],
4190
+ quickReference: [
4191
+ {
4192
+ section: "read",
4193
+ usage: "attach-get <taskId> [selector]",
4194
+ description: "Download task attachment(s)"
4195
+ }
4196
+ ]
4197
+ },
4175
4198
  {
4176
4199
  name: "task-members",
4177
4200
  description: "List members with access to a task",
@@ -4487,13 +4510,23 @@ var commandMetadata = [
4487
4510
  },
4488
4511
  {
4489
4512
  name: "field-create",
4490
- description: "Create a custom field in your workspace",
4491
- flags: ["-t", "--type", "-d", "--description", "--options", "--required", "--json"],
4513
+ description: "Create a custom field in your workspace or on one or more lists",
4514
+ flags: [
4515
+ "-t",
4516
+ "--type",
4517
+ "-d",
4518
+ "--description",
4519
+ "--options",
4520
+ "--required",
4521
+ "--list",
4522
+ "--lists",
4523
+ "--json"
4524
+ ],
4492
4525
  quickReference: [
4493
4526
  {
4494
4527
  section: "write",
4495
4528
  usage: "field-create <name>",
4496
- description: "Create a custom field in your workspace"
4529
+ description: "Create a custom field in your workspace or on one or more lists"
4497
4530
  }
4498
4531
  ]
4499
4532
  },
@@ -6949,6 +6982,83 @@ async function attachFile(config, taskId, filePath) {
6949
6982
  return client.createTaskAttachment(taskId, filePath);
6950
6983
  }
6951
6984
 
6985
+ // src/commands/attach-get.ts
6986
+ function sanitizeFilename(name) {
6987
+ const base = name.replace(/[/\\]/g, "_").replace(/^\.+/, "");
6988
+ return base.length > 0 ? base : "attachment";
6989
+ }
6990
+ function selectAttachments(attachments, selector, all) {
6991
+ if (all) return attachments;
6992
+ if (attachments.length === 0) {
6993
+ throw new Error("No attachments found on this task");
6994
+ }
6995
+ if (!selector) {
6996
+ if (attachments.length === 1) return [attachments[0]];
6997
+ const list = attachments.map((a) => ` ${a.id} ${a.title}`).join("\n");
6998
+ throw new Error(
6999
+ `Task has ${attachments.length} attachments. Specify one by ID or title, or use --all:
7000
+ ${list}`
7001
+ );
7002
+ }
7003
+ const lower = selector.toLowerCase();
7004
+ const byId = attachments.find((a) => a.id === selector);
7005
+ if (byId) return [byId];
7006
+ const byExactTitle = attachments.find((a) => a.title.toLowerCase() === lower);
7007
+ if (byExactTitle) return [byExactTitle];
7008
+ const byPartial = attachments.filter((a) => a.title.toLowerCase().includes(lower));
7009
+ if (byPartial.length === 1) return [byPartial[0]];
7010
+ if (byPartial.length > 1) {
7011
+ const list = byPartial.map((a) => ` ${a.id} ${a.title}`).join("\n");
7012
+ throw new Error(`Multiple attachments match "${selector}":
7013
+ ${list}`);
7014
+ }
7015
+ const available = attachments.map((a) => ` ${a.id} ${a.title}`).join("\n");
7016
+ throw new Error(`No attachment matching "${selector}". Available:
7017
+ ${available}`);
7018
+ }
7019
+ async function fileExists(path) {
7020
+ const { access } = await import("fs/promises");
7021
+ try {
7022
+ await access(path);
7023
+ return true;
7024
+ } catch {
7025
+ return false;
7026
+ }
7027
+ }
7028
+ async function downloadAttachment(attachment, targetPath, force) {
7029
+ const { writeFile } = await import("fs/promises");
7030
+ if (!force && await fileExists(targetPath)) {
7031
+ throw new Error(`File already exists: ${targetPath} (use --force to overwrite)`);
7032
+ }
7033
+ const res = await fetch(attachment.url, { signal: AbortSignal.timeout(6e4) });
7034
+ if (!res.ok) {
7035
+ throw new Error(
7036
+ `Failed to download "${attachment.title}": HTTP ${res.status} ${res.statusText}`
7037
+ );
7038
+ }
7039
+ const buffer = Buffer.from(await res.arrayBuffer());
7040
+ await writeFile(targetPath, buffer);
7041
+ return { title: attachment.title, path: targetPath, size: buffer.length };
7042
+ }
7043
+ async function attachGet(config, taskId, selector, opts) {
7044
+ const { resolve: resolve2 } = await import("path");
7045
+ const client = new ClickUpClient(config);
7046
+ const attachments = await client.getTaskAttachments(taskId);
7047
+ const selected = selectAttachments(attachments, selector, opts.all ?? false);
7048
+ const results = [];
7049
+ for (const att of selected) {
7050
+ let targetPath;
7051
+ if (opts.output && !opts.all) {
7052
+ targetPath = resolve2(opts.output);
7053
+ } else {
7054
+ const dir = opts.dir ?? ".";
7055
+ targetPath = resolve2(dir, sanitizeFilename(att.title));
7056
+ }
7057
+ results.push(await downloadAttachment(att, targetPath, opts.force ?? false));
7058
+ }
7059
+ return results;
7060
+ }
7061
+
6952
7062
  // src/commands/docs.ts
6953
7063
  var DOC_COLUMNS = [
6954
7064
  { key: "id", label: "ID", maxWidth: 15 },
@@ -7354,21 +7464,6 @@ function formatFieldsMarkdown(fields) {
7354
7464
  }).join("\n");
7355
7465
  }
7356
7466
 
7357
- // src/commands/duplicate.ts
7358
- async function duplicateTask(config, taskId) {
7359
- const client = new ClickUpClient(config);
7360
- const task = await client.getTask(taskId);
7361
- const created = await client.createTask(task.list.id, {
7362
- name: `${task.name} (copy)`,
7363
- description: task.description,
7364
- markdown_content: task.markdown_content,
7365
- priority: task.priority ? parsePriority(task.priority.priority.toLowerCase()) : void 0,
7366
- tags: task.tags?.map((t) => t.name),
7367
- time_estimate: task.time_estimate ?? void 0
7368
- });
7369
- return { id: created.id, name: created.name, url: created.url };
7370
- }
7371
-
7372
7467
  // src/util/batch.ts
7373
7468
  async function runInBatches(items, concurrency, fn) {
7374
7469
  if (!Number.isInteger(concurrency) || concurrency < 1) {
@@ -7391,6 +7486,66 @@ async function runInBatches(items, concurrency, fn) {
7391
7486
  return results;
7392
7487
  }
7393
7488
 
7489
+ // src/commands/field-create.ts
7490
+ var FIELD_CREATE_CONCURRENCY = 5;
7491
+ var VALID_FIELD_TYPES = [
7492
+ "text",
7493
+ "short_text",
7494
+ "number",
7495
+ "date",
7496
+ "checkbox",
7497
+ "drop_down",
7498
+ "labels",
7499
+ "email",
7500
+ "phone",
7501
+ "url",
7502
+ "currency"
7503
+ ];
7504
+ function resolveFieldScope(list, lists) {
7505
+ if (list && lists) throw new Error("Cannot use --list and --lists together");
7506
+ if (list) return { mode: "single", listId: list };
7507
+ if (lists) {
7508
+ const listIds = lists.split(",").map((id) => id.trim()).filter(Boolean);
7509
+ if (listIds.length === 0) throw new Error("--lists requires at least one list ID");
7510
+ return { mode: "bulk", listIds };
7511
+ }
7512
+ return { mode: "workspace" };
7513
+ }
7514
+ function validateFieldType(type, options) {
7515
+ if (!VALID_FIELD_TYPES.includes(type)) {
7516
+ throw new Error(`Invalid field type "${type}". Valid types: ${VALID_FIELD_TYPES.join(", ")}`);
7517
+ }
7518
+ if ((type === "drop_down" || type === "labels") && !options?.length) {
7519
+ throw new Error(`--options is required for ${type} fields (comma-separated values)`);
7520
+ }
7521
+ }
7522
+ async function createFieldAcrossLists(config, name, type, listIds, opts) {
7523
+ const client = new ClickUpClient(config);
7524
+ const outcomes = await runInBatches(
7525
+ listIds,
7526
+ FIELD_CREATE_CONCURRENCY,
7527
+ (listId) => client.createListCustomField(listId, name, type, opts)
7528
+ );
7529
+ return outcomes.map(
7530
+ (outcome) => outcome.ok ? { listId: outcome.item, ok: true, fieldId: outcome.result.id } : { listId: outcome.item, ok: false, error: outcome.error.message }
7531
+ );
7532
+ }
7533
+
7534
+ // src/commands/duplicate.ts
7535
+ async function duplicateTask(config, taskId) {
7536
+ const client = new ClickUpClient(config);
7537
+ const task = await client.getTask(taskId);
7538
+ const created = await client.createTask(task.list.id, {
7539
+ name: `${task.name} (copy)`,
7540
+ description: task.description,
7541
+ markdown_content: task.markdown_content,
7542
+ priority: task.priority ? parsePriority(task.priority.priority.toLowerCase()) : void 0,
7543
+ tags: task.tags?.map((t) => t.name),
7544
+ time_estimate: task.time_estimate ?? void 0
7545
+ });
7546
+ return { id: created.id, name: created.name, url: created.url };
7547
+ }
7548
+
7394
7549
  // src/commands/bulk.ts
7395
7550
  var BULK_CONCURRENCY = 5;
7396
7551
  function toBulkResult(outcomes) {
@@ -9128,6 +9283,21 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
9128
9283
  }
9129
9284
  })
9130
9285
  );
9286
+ program.command("attach-get <taskId> [selector]").description("Download task attachment(s) by ID or title").option("-o, --output <path>", "Output file path (single attachment only)").option("--dir <dir>", "Directory to save into (default: current dir)").option("--all", "Download all attachments").option("--force", "Overwrite existing files").option("--json", "Force JSON output even in terminal").action(
9287
+ wrapAction(
9288
+ async (taskId, selector, opts) => {
9289
+ const config = loadConfig(getProfileName());
9290
+ const results = await attachGet(config, taskId, selector, opts);
9291
+ if (shouldOutputJson(opts.json ?? false)) {
9292
+ console.log(JSON.stringify(results, null, 2));
9293
+ } else {
9294
+ for (const r of results) {
9295
+ console.log(`Downloaded "${r.title}" -> ${r.path} (${r.size} bytes)`);
9296
+ }
9297
+ }
9298
+ }
9299
+ )
9300
+ );
9131
9301
  program.command("task-members <taskId>").description("List members with access to a task").option("--json", "Force JSON output even in terminal").action(
9132
9302
  wrapAction(async (taskId, opts) => {
9133
9303
  const config = loadConfig(getProfileName());
@@ -9454,44 +9624,35 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
9454
9624
  }
9455
9625
  })
9456
9626
  );
9457
- program.command("field-create <name>").description("Create a custom field in your workspace").requiredOption(
9627
+ program.command("field-create <name>").description("Create a custom field in your workspace or on one or more lists").requiredOption(
9458
9628
  "-t, --type <type>",
9459
9629
  "Field type (text, number, date, checkbox, drop_down, labels, email, phone, url, currency, short_text)"
9460
- ).option("-d, --description <text>", "Field description").option("--options <items>", "Comma-separated options for drop_down or labels types").option("--required", "Make the field required").option("--json", "Force JSON output even in terminal").action(
9630
+ ).option("-d, --description <text>", "Field description").option("--options <items>", "Comma-separated options for drop_down or labels types").option("--required", "Make the field required").option("--list <listId>", "Create the field scoped to a single list").option("--lists <ids>", "Comma-separated list IDs to create the same field on each").option("--json", "Force JSON output even in terminal").action(
9461
9631
  wrapAction(
9462
9632
  async (name, opts) => {
9463
9633
  if (!name.trim()) throw new Error("Field name cannot be empty");
9464
- const validTypes = [
9465
- "text",
9466
- "short_text",
9467
- "number",
9468
- "date",
9469
- "checkbox",
9470
- "drop_down",
9471
- "labels",
9472
- "email",
9473
- "phone",
9474
- "url",
9475
- "currency"
9476
- ];
9477
- if (!validTypes.includes(opts.type)) {
9478
- throw new Error(
9479
- `Invalid field type "${opts.type}". Valid types: ${validTypes.join(", ")}`
9480
- );
9481
- }
9482
- const config = loadConfig(getProfileName());
9483
- const client = new ClickUpClient(config);
9634
+ const scope = resolveFieldScope(opts.list, opts.lists);
9484
9635
  const options = opts.options ? opts.options.split(",").map((o) => o.trim()).filter(Boolean) : void 0;
9485
- if ((opts.type === "drop_down" || opts.type === "labels") && !options?.length) {
9486
- throw new Error(
9487
- `--options is required for ${opts.type} fields (comma-separated values)`
9488
- );
9489
- }
9490
- const field = await client.createCustomField(config.teamId, name, opts.type, {
9636
+ validateFieldType(opts.type, options);
9637
+ const config = loadConfig(getProfileName());
9638
+ const fieldOpts = {
9491
9639
  description: opts.description,
9492
9640
  required: opts.required,
9493
9641
  options
9494
- });
9642
+ };
9643
+ if (scope.mode === "bulk") {
9644
+ const results = await createFieldAcrossLists(
9645
+ config,
9646
+ name,
9647
+ opts.type,
9648
+ scope.listIds,
9649
+ fieldOpts
9650
+ );
9651
+ outputListFieldResults(results, opts.json ?? false);
9652
+ return;
9653
+ }
9654
+ const client = new ClickUpClient(config);
9655
+ const field = scope.mode === "single" ? await client.createListCustomField(scope.listId, name, opts.type, fieldOpts) : await client.createCustomField(config.teamId, name, opts.type, fieldOpts);
9495
9656
  if (shouldOutputJson(opts.json ?? false)) {
9496
9657
  console.log(JSON.stringify(field, null, 2));
9497
9658
  } else {
@@ -9511,6 +9672,21 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
9511
9672
  }
9512
9673
  })
9513
9674
  );
9675
+ function outputListFieldResults(results, forceJson) {
9676
+ if (shouldOutputJson(forceJson)) {
9677
+ console.log(JSON.stringify(results, null, 2));
9678
+ return;
9679
+ }
9680
+ for (const result of results) {
9681
+ if (result.ok) {
9682
+ console.log(`\u2713 ${result.listId}: created field ${result.fieldId}`);
9683
+ } else {
9684
+ console.error(`\u2717 ${result.listId}: ${result.error}`);
9685
+ }
9686
+ }
9687
+ const created = results.filter((r) => r.ok).length;
9688
+ console.log(`Created on ${created}/${results.length} lists`);
9689
+ }
9514
9690
  function outputBulkResult(result, forceJson, operation) {
9515
9691
  if (shouldOutputJson(forceJson)) {
9516
9692
  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": "1.34.0",
3
+ "version": "1.36.0",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -3,11 +3,11 @@ name: clickup
3
3
  description: 'Use when managing ClickUp tasks, sprints, or comments via the `cup` CLI tool. Triggers: task queries, status updates, sprint tracking, creating subtasks, posting comments, threaded replies, standup summaries, searching tasks, checking overdue items, assigning tasks, listing spaces and lists, opening tasks in browser, checking auth or config, setting custom fields, deleting tasks, managing tags, managing checklists, editing comments, task links, time tracking, attachments, file uploads, listing members, listing fields, duplicating tasks, bulk operations, goals, key results, saved filters, favorites.'
4
4
  ---
5
5
 
6
- # ClickUp CLI (`cup`) - skill version 1.34.0
6
+ # ClickUp CLI (`cup`) - skill version 1.36.0
7
7
 
8
8
  Reference for AI agents using the `cup` CLI tool. Covers task management, sprint tracking, comments, time tracking, custom fields, goals, docs, and project workflows.
9
9
 
10
- > **Version check:** Run `cup --version`. If your installed version is older than 1.34.0, update with `npm install -g @krodak/clickup-cli` and refresh this skill with `cup skill`.
10
+ > **Version check:** Run `cup --version`. If your installed version is older than 1.36.0, update with `npm install -g @krodak/clickup-cli` and refresh this skill with `cup skill`.
11
11
 
12
12
  ## Install & Configure
13
13
 
@@ -107,55 +107,56 @@ All commands support `--help` for full flag details. All commands support `--jso
107
107
 
108
108
  ### Read
109
109
 
110
- | Command | What it returns |
111
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
112
- | `cup tasks [--status s] [--name q] [--type t] [--list id] [--space id] [--all] [--include-closed] [--assignee id\|me] [--tag t] [--due-before d] [--due-after d] [--created-after d] [--created-before d] [--field "Name" val]` | Tasks assigned to you (filter by status, name, type, list, space, assignee, tag, dates, custom fields). `--all` for all assignees |
113
- | `cup assigned [--status s] [--include-closed]` | All my tasks grouped by status |
114
- | `cup sprint [--status s] [--space nameOrId] [--folder id] [--include-closed]` | Tasks in active sprint (auto-detected) |
115
- | `cup sprints [--space nameOrId]` | List all sprints (marks active with \*) |
116
- | `cup search <query> [--status s] [--list id] [--space id] [--all] [--include-closed] [--assignee id\|me] [--tag t] [--due-before d] [--due-after d] [--created-after d] [--created-before d] [--field "Name" val]` | Search your tasks by name. `--all` for all assignees |
117
- | `cup task <id>` | Single task details (custom fields, checklists, attachments, deps, links) |
118
- | `cup subtasks <id> [--status s] [--name q] [--include-closed]` | Subtasks of a task |
119
- | `cup comments <id>` | Comments on a task |
120
- | `cup activity <id>` | Task details + comment history combined |
121
- | `cup inbox [--days n] [--include-closed]` | Tasks updated in last n days (default 30) |
122
- | `cup summary [--hours n]` | Standup: completed, in-progress, overdue |
123
- | `cup overdue [--all] [--include-closed]` | Tasks past due date (most overdue first) |
124
- | `cup spaces [--name partial] [--my] [--archived]` | List/filter workspace spaces |
125
- | `cup lists <spaceId> [--name partial] [--archived]` | Lists in a space (including folder lists) |
126
- | `cup folders <spaceId> [--name partial] [--archived]` | Folders in a space (with their lists) |
127
- | `cup time-in-status <id>` | Show how long a task has been in each status |
128
- | `cup members` | Workspace members (username, ID, email) |
129
- | `cup groups` | User groups/teams (handle, name, UUID, member count) for `--group-assignee` flags |
130
- | `cup fields <listId>` | Custom fields on a list (type, required, options) |
131
- | `cup attachments <taskId>` | List attachments on a task (name, size, URL) |
132
- | `cup task-members <taskId>` | List members with access to a task |
133
- | `cup plan` | Show workspace plan and usage |
134
- | `cup tags <spaceId>` | Tags available in a space |
135
- | `cup goals` | Workspace goals with progress |
136
- | `cup key-results <goalId>` | Key results for a goal |
137
- | `cup docs [query]` | Workspace docs (optionally filter by name) |
138
- | `cup doc <docId> [pageId]` | Doc metadata + page tree, or a specific page |
139
- | `cup doc-pages <docId>` | All pages in a doc with content |
140
- | `cup task-types` | Custom task types (for `--custom-item-id`) |
141
- | `cup templates` | Task templates (for `--template`) |
142
- | `cup list-templates` | List templates (for `list-from-template`) |
143
- | `cup folder-templates` | Folder templates |
144
- | `cup views <listId>` | List views on a list |
145
- | `cup view <viewId>` | Get view details |
146
- | `cup open <query>` | Open task in browser by ID or name |
147
- | `cup auth` | Check authentication status |
148
- | `cup list-comments <listId>` | Comments on a list |
149
- | `cup view-comments <viewId>` | Comments on a view |
150
- | `cup webhook list` | List webhooks in workspace |
151
- | `cup shared` | Shared spaces, folders, and lists |
152
- | `cup chat channels [--all] [--type type]` | List chat channels |
153
- | `cup chat channel <id>` | Show channel details |
154
- | `cup chat messages <channelId> [--limit n]` | List channel messages |
155
- | `cup chat members <channelId>` | List channel members |
156
- | `cup chat followers <channelId>` | List channel followers |
157
- | `cup chat replies <messageId>` | List message replies |
158
- | `cup chat reactions <messageId>` | List reactions on a message |
110
+ | Command | What it returns |
111
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
112
+ | `cup tasks [--status s] [--name q] [--type t] [--list id] [--space id] [--all] [--include-closed] [--assignee id\|me] [--tag t] [--due-before d] [--due-after d] [--created-after d] [--created-before d] [--field "Name" val]` | Tasks assigned to you (filter by status, name, type, list, space, assignee, tag, dates, custom fields). `--all` for all assignees |
113
+ | `cup assigned [--status s] [--include-closed]` | All my tasks grouped by status |
114
+ | `cup sprint [--status s] [--space nameOrId] [--folder id] [--include-closed]` | Tasks in active sprint (auto-detected) |
115
+ | `cup sprints [--space nameOrId]` | List all sprints (marks active with \*) |
116
+ | `cup search <query> [--status s] [--list id] [--space id] [--all] [--include-closed] [--assignee id\|me] [--tag t] [--due-before d] [--due-after d] [--created-after d] [--created-before d] [--field "Name" val]` | Search your tasks by name. `--all` for all assignees |
117
+ | `cup task <id>` | Single task details (custom fields, checklists, attachments, deps, links) |
118
+ | `cup subtasks <id> [--status s] [--name q] [--include-closed]` | Subtasks of a task |
119
+ | `cup comments <id>` | Comments on a task |
120
+ | `cup activity <id>` | Task details + comment history combined |
121
+ | `cup inbox [--days n] [--include-closed]` | Tasks updated in last n days (default 30) |
122
+ | `cup summary [--hours n]` | Standup: completed, in-progress, overdue |
123
+ | `cup overdue [--all] [--include-closed]` | Tasks past due date (most overdue first) |
124
+ | `cup spaces [--name partial] [--my] [--archived]` | List/filter workspace spaces |
125
+ | `cup lists <spaceId> [--name partial] [--archived]` | Lists in a space (including folder lists) |
126
+ | `cup folders <spaceId> [--name partial] [--archived]` | Folders in a space (with their lists) |
127
+ | `cup time-in-status <id>` | Show how long a task has been in each status |
128
+ | `cup members` | Workspace members (username, ID, email) |
129
+ | `cup groups` | User groups/teams (handle, name, UUID, member count) for `--group-assignee` flags |
130
+ | `cup fields <listId>` | Custom fields on a list (type, required, options) |
131
+ | `cup attachments <taskId>` | List attachments on a task (name, size, URL) |
132
+ | `cup attach-get <taskId> [idOrTitle] [-o path] [--all] [--dir d] [--force]` | Download task attachment(s). No selector + 1 attachment downloads it; multiple requires a selector or `--all`. Saves to the attachment's filename by default |
133
+ | `cup task-members <taskId>` | List members with access to a task |
134
+ | `cup plan` | Show workspace plan and usage |
135
+ | `cup tags <spaceId>` | Tags available in a space |
136
+ | `cup goals` | Workspace goals with progress |
137
+ | `cup key-results <goalId>` | Key results for a goal |
138
+ | `cup docs [query]` | Workspace docs (optionally filter by name) |
139
+ | `cup doc <docId> [pageId]` | Doc metadata + page tree, or a specific page |
140
+ | `cup doc-pages <docId>` | All pages in a doc with content |
141
+ | `cup task-types` | Custom task types (for `--custom-item-id`) |
142
+ | `cup templates` | Task templates (for `--template`) |
143
+ | `cup list-templates` | List templates (for `list-from-template`) |
144
+ | `cup folder-templates` | Folder templates |
145
+ | `cup views <listId>` | List views on a list |
146
+ | `cup view <viewId>` | Get view details |
147
+ | `cup open <query>` | Open task in browser by ID or name |
148
+ | `cup auth` | Check authentication status |
149
+ | `cup list-comments <listId>` | Comments on a list |
150
+ | `cup view-comments <viewId>` | Comments on a view |
151
+ | `cup webhook list` | List webhooks in workspace |
152
+ | `cup shared` | Shared spaces, folders, and lists |
153
+ | `cup chat channels [--all] [--type type]` | List chat channels |
154
+ | `cup chat channel <id>` | Show channel details |
155
+ | `cup chat messages <channelId> [--limit n]` | List channel messages |
156
+ | `cup chat members <channelId>` | List channel members |
157
+ | `cup chat followers <channelId>` | List channel followers |
158
+ | `cup chat replies <messageId>` | List message replies |
159
+ | `cup chat reactions <messageId>` | List reactions on a message |
159
160
 
160
161
  ### Write
161
162
 
@@ -172,7 +173,7 @@ All commands support `--help` for full flag details. All commands support `--jso
172
173
  | `cup depend <id> [--on taskId] [--blocks taskId] [--remove]` | Add/remove dependencies |
173
174
  | `cup move <id> [--to listId\|sprint:current] [--remove listId]` | Add/remove task from lists. **`--to` + `--remove` together changes the task's _home_ list** (uses v3 `home_list` endpoint with auto status mapping). `--to` alone adds multi-list membership. `--to` accepts `sprint:current`. |
174
175
  | `cup field <id> [--set "Name" value] [--remove "Name"]` | Set/remove custom field values |
175
- | `cup field-create <name> -t <type> [-d desc] [--options "a,b,c"] [--required]` | Create a custom field |
176
+ | `cup field-create <name> -t <type> [-d desc] [--options "a,b,c"] [--required] [--list id] [--lists id1,id2]` | Create a custom field — workspace-wide (default), on one list (`--list`), or bulk across lists (`--lists`, parallel, per-list reporting) |
176
177
  | `cup tag <id> [--add tags] [--remove tags]` | Add/remove tags on a task |
177
178
  | `cup link <taskId> <linksTo> [--remove]` | Link/unlink tasks |
178
179
  | `cup attach <taskId> <filePath>` | Upload file attachment |