@krodak/clickup-cli 1.35.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.35.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}`;
@@ -4499,13 +4510,23 @@ var commandMetadata = [
4499
4510
  },
4500
4511
  {
4501
4512
  name: "field-create",
4502
- description: "Create a custom field in your workspace",
4503
- 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
+ ],
4504
4525
  quickReference: [
4505
4526
  {
4506
4527
  section: "write",
4507
4528
  usage: "field-create <name>",
4508
- description: "Create a custom field in your workspace"
4529
+ description: "Create a custom field in your workspace or on one or more lists"
4509
4530
  }
4510
4531
  ]
4511
4532
  },
@@ -7443,21 +7464,6 @@ function formatFieldsMarkdown(fields) {
7443
7464
  }).join("\n");
7444
7465
  }
7445
7466
 
7446
- // src/commands/duplicate.ts
7447
- async function duplicateTask(config, taskId) {
7448
- const client = new ClickUpClient(config);
7449
- const task = await client.getTask(taskId);
7450
- const created = await client.createTask(task.list.id, {
7451
- name: `${task.name} (copy)`,
7452
- description: task.description,
7453
- markdown_content: task.markdown_content,
7454
- priority: task.priority ? parsePriority(task.priority.priority.toLowerCase()) : void 0,
7455
- tags: task.tags?.map((t) => t.name),
7456
- time_estimate: task.time_estimate ?? void 0
7457
- });
7458
- return { id: created.id, name: created.name, url: created.url };
7459
- }
7460
-
7461
7467
  // src/util/batch.ts
7462
7468
  async function runInBatches(items, concurrency, fn) {
7463
7469
  if (!Number.isInteger(concurrency) || concurrency < 1) {
@@ -7480,6 +7486,66 @@ async function runInBatches(items, concurrency, fn) {
7480
7486
  return results;
7481
7487
  }
7482
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
+
7483
7549
  // src/commands/bulk.ts
7484
7550
  var BULK_CONCURRENCY = 5;
7485
7551
  function toBulkResult(outcomes) {
@@ -9558,44 +9624,35 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
9558
9624
  }
9559
9625
  })
9560
9626
  );
9561
- 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(
9562
9628
  "-t, --type <type>",
9563
9629
  "Field type (text, number, date, checkbox, drop_down, labels, email, phone, url, currency, short_text)"
9564
- ).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(
9565
9631
  wrapAction(
9566
9632
  async (name, opts) => {
9567
9633
  if (!name.trim()) throw new Error("Field name cannot be empty");
9568
- const validTypes = [
9569
- "text",
9570
- "short_text",
9571
- "number",
9572
- "date",
9573
- "checkbox",
9574
- "drop_down",
9575
- "labels",
9576
- "email",
9577
- "phone",
9578
- "url",
9579
- "currency"
9580
- ];
9581
- if (!validTypes.includes(opts.type)) {
9582
- throw new Error(
9583
- `Invalid field type "${opts.type}". Valid types: ${validTypes.join(", ")}`
9584
- );
9585
- }
9586
- const config = loadConfig(getProfileName());
9587
- const client = new ClickUpClient(config);
9634
+ const scope = resolveFieldScope(opts.list, opts.lists);
9588
9635
  const options = opts.options ? opts.options.split(",").map((o) => o.trim()).filter(Boolean) : void 0;
9589
- if ((opts.type === "drop_down" || opts.type === "labels") && !options?.length) {
9590
- throw new Error(
9591
- `--options is required for ${opts.type} fields (comma-separated values)`
9592
- );
9593
- }
9594
- const field = await client.createCustomField(config.teamId, name, opts.type, {
9636
+ validateFieldType(opts.type, options);
9637
+ const config = loadConfig(getProfileName());
9638
+ const fieldOpts = {
9595
9639
  description: opts.description,
9596
9640
  required: opts.required,
9597
9641
  options
9598
- });
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);
9599
9656
  if (shouldOutputJson(opts.json ?? false)) {
9600
9657
  console.log(JSON.stringify(field, null, 2));
9601
9658
  } else {
@@ -9615,6 +9672,21 @@ function buildProgram(programName = basename(process.argv[1] ?? "cup")) {
9615
9672
  }
9616
9673
  })
9617
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
+ }
9618
9690
  function outputBulkResult(result, forceJson, operation) {
9619
9691
  if (shouldOutputJson(forceJson)) {
9620
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.35.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.35.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.35.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
 
@@ -173,7 +173,7 @@ All commands support `--help` for full flag details. All commands support `--jso
173
173
  | `cup depend <id> [--on taskId] [--blocks taskId] [--remove]` | Add/remove dependencies |
174
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`. |
175
175
  | `cup field <id> [--set "Name" value] [--remove "Name"]` | Set/remove custom field values |
176
- | `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) |
177
177
  | `cup tag <id> [--add tags] [--remove tags]` | Add/remove tags on a task |
178
178
  | `cup link <taskId> <linksTo> [--remove]` | Link/unlink tasks |
179
179
  | `cup attach <taskId> <filePath>` | Upload file attachment |