@krodak/clickup-cli 0.16.0 → 0.17.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 cu command",
4
- "version": "0.16.0",
4
+ "version": "0.17.0",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/README.md CHANGED
@@ -246,10 +246,10 @@ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign
246
246
 
247
247
  ### Attachments
248
248
 
249
- | Feature | Command | Status |
250
- | ---------------- | ----------------------- | -------------- |
251
- | Upload file | `cu attach <id> <file>` | :construction: |
252
- | List attachments | | :construction: |
249
+ | Feature | Command | Status |
250
+ | ---------------- | ------------------------- | ------------------ |
251
+ | Upload file | `cu attach <id> <file>` | :white_check_mark: |
252
+ | List attachments | shown inline in `cu task` | :white_check_mark: |
253
253
 
254
254
  ### :no_entry_sign: Won't add
255
255
 
package/dist/index.js CHANGED
@@ -314,6 +314,37 @@ var ClickUpClient = class {
314
314
  method: "DELETE"
315
315
  });
316
316
  }
317
+ async createTaskAttachment(taskId, filePath) {
318
+ const { readFile } = await import("fs/promises");
319
+ const { basename } = await import("path");
320
+ const fileBuffer = await readFile(filePath);
321
+ const fileName = basename(filePath);
322
+ const formData = new FormData();
323
+ formData.append("attachment", new Blob([fileBuffer]), fileName);
324
+ const res = await fetch(`${BASE_URL}/task/${taskId}/attachment`, {
325
+ method: "POST",
326
+ headers: { Authorization: this.apiToken },
327
+ body: formData,
328
+ signal: AbortSignal.timeout(6e4)
329
+ });
330
+ if (!res.ok) {
331
+ let msg;
332
+ try {
333
+ const data2 = await res.json();
334
+ msg = data2.err ?? `HTTP ${res.status}`;
335
+ } catch {
336
+ msg = `HTTP ${res.status}`;
337
+ }
338
+ throw new Error(`ClickUp API error ${res.status}: ${msg}`);
339
+ }
340
+ let data;
341
+ try {
342
+ data = await res.json();
343
+ } catch {
344
+ throw new Error(`ClickUp API error ${res.status}: response was not valid JSON`);
345
+ }
346
+ return data;
347
+ }
317
348
  };
318
349
 
319
350
  // src/config.ts
@@ -546,6 +577,12 @@ function formatTaskDetailMarkdown(task) {
546
577
  lines.push("");
547
578
  }
548
579
  }
580
+ if (task.attachments?.length) {
581
+ lines.push("", "## Attachments", "");
582
+ for (const att of task.attachments) {
583
+ lines.push(`- [${att.title}](${att.url})`);
584
+ }
585
+ }
549
586
  return lines.join("\n");
550
587
  }
551
588
  function formatUpdateConfirmation(id, name) {
@@ -692,6 +729,13 @@ function formatTaskDetail(task) {
692
729
  }
693
730
  }
694
731
  }
732
+ if (task.attachments?.length) {
733
+ lines.push("");
734
+ lines.push(chalk2.bold("Attachments"));
735
+ for (const att of task.attachments) {
736
+ lines.push(` ${att.title} ${chalk2.dim(att.url)}`);
737
+ }
738
+ }
695
739
  if (task.text_content?.trim()) {
696
740
  lines.push("");
697
741
  lines.push(descriptionPreview(task.text_content));
@@ -1827,7 +1871,7 @@ function bashCompletion() {
1827
1871
  cword=$COMP_CWORD
1828
1872
  fi
1829
1873
 
1830
- local commands="init auth tasks task update create sprint sprints subtasks comment comment-edit comment-delete comments replies reply activity lists spaces inbox assigned open search summary overdue assign depend link move field delete tag checklist time config completion"
1874
+ local commands="init auth tasks task update create sprint sprints subtasks comment comment-edit comment-delete comments replies reply activity lists spaces inbox assigned open search summary overdue assign depend link attach move field delete tag checklist time config completion"
1831
1875
 
1832
1876
  if [[ $cword -eq 1 ]]; then
1833
1877
  COMPREPLY=($(compgen -W "$commands --help --version" -- "$cur"))
@@ -1948,6 +1992,9 @@ function bashCompletion() {
1948
1992
  link)
1949
1993
  COMPREPLY=($(compgen -W "--remove --json" -- "$cur"))
1950
1994
  ;;
1995
+ attach)
1996
+ COMPREPLY=($(compgen -f -- "$cur"))
1997
+ ;;
1951
1998
  config)
1952
1999
  if [[ $cword -eq 2 ]]; then
1953
2000
  COMPREPLY=($(compgen -W "get set path" -- "$cur"))
@@ -2007,6 +2054,7 @@ _cu() {
2007
2054
  'replies:List threaded replies on a comment'
2008
2055
  'reply:Reply to a comment'
2009
2056
  'link:Add or remove a link between two tasks'
2057
+ 'attach:Upload a file attachment to a task'
2010
2058
  'config:Manage CLI configuration'
2011
2059
  'completion:Output shell completion script'
2012
2060
  )
@@ -2320,6 +2368,12 @@ _cu() {
2320
2368
  '--remove[Remove the link instead of adding it]' \\
2321
2369
  '--json[Force JSON output]'
2322
2370
  ;;
2371
+ attach)
2372
+ _arguments \\
2373
+ '1:task_id:' \\
2374
+ '2:file_path:_files' \\
2375
+ '--json[Force JSON output]'
2376
+ ;;
2323
2377
  config)
2324
2378
  local -a config_cmds
2325
2379
  config_cmds=(
@@ -2393,6 +2447,7 @@ complete -c cu -n __fish_use_subcommand -a comment-delete -d 'Delete a comment'
2393
2447
  complete -c cu -n __fish_use_subcommand -a replies -d 'List threaded replies on a comment'
2394
2448
  complete -c cu -n __fish_use_subcommand -a reply -d 'Reply to a comment'
2395
2449
  complete -c cu -n __fish_use_subcommand -a link -d 'Add or remove a link between two tasks'
2450
+ complete -c cu -n __fish_use_subcommand -a attach -d 'Upload a file attachment to a task'
2396
2451
  complete -c cu -n __fish_use_subcommand -a config -d 'Manage CLI configuration'
2397
2452
  complete -c cu -n __fish_use_subcommand -a completion -d 'Output shell completion script'
2398
2453
 
@@ -2535,6 +2590,9 @@ complete -c cu -n '__fish_seen_subcommand_from reply' -l json -d 'Force JSON out
2535
2590
  complete -c cu -n '__fish_seen_subcommand_from link' -l remove -d 'Remove the link'
2536
2591
  complete -c cu -n '__fish_seen_subcommand_from link' -l json -d 'Force JSON output'
2537
2592
 
2593
+ complete -c cu -n '__fish_seen_subcommand_from attach' -l json -d 'Force JSON output'
2594
+ complete -c cu -n '__fish_seen_subcommand_from attach' -F
2595
+
2538
2596
  complete -c cu -n '__fish_seen_subcommand_from comment-edit' -s m -l message -d 'New comment text'
2539
2597
  complete -c cu -n '__fish_seen_subcommand_from comment-edit' -l resolved -d 'Mark comment as resolved'
2540
2598
  complete -c cu -n '__fish_seen_subcommand_from comment-edit' -l unresolved -d 'Mark comment as unresolved'
@@ -2885,6 +2943,18 @@ async function manageTaskLink(config, taskId, linksTo, remove) {
2885
2943
  return `Linked ${taskId} to ${linksTo}`;
2886
2944
  }
2887
2945
 
2946
+ // src/commands/attach.ts
2947
+ async function attachFile(config, taskId, filePath) {
2948
+ const { access } = await import("fs/promises");
2949
+ try {
2950
+ await access(filePath);
2951
+ } catch {
2952
+ throw new Error(`File not found: ${filePath}`);
2953
+ }
2954
+ const client = new ClickUpClient(config);
2955
+ return client.createTaskAttachment(taskId, filePath);
2956
+ }
2957
+
2888
2958
  // src/commands/time.ts
2889
2959
  import chalk7 from "chalk";
2890
2960
  function formatDuration2(ms) {
@@ -3259,6 +3329,18 @@ program.command("link <taskId> <linksTo>").description("Add or remove a link bet
3259
3329
  }
3260
3330
  )
3261
3331
  );
3332
+ program.command("attach <taskId> <filePath>").description("Upload a file attachment to a task").option("--json", "Force JSON output even in terminal").action(
3333
+ wrapAction(async (taskId, filePath, opts) => {
3334
+ const config = loadConfig();
3335
+ const result = await attachFile(config, taskId, filePath);
3336
+ if (shouldOutputJson(opts.json ?? false)) {
3337
+ console.log(JSON.stringify(result, null, 2));
3338
+ } else {
3339
+ console.log(`Uploaded "${result.title}" to task ${taskId}`);
3340
+ console.log(` ${result.url}`);
3341
+ }
3342
+ })
3343
+ );
3262
3344
  program.command("move <taskId>").description("Add or remove a task from a list").option("--to <listId>", "Add task to this list").option("--remove <listId>", "Remove task from this list").option("--json", "Force JSON output even in terminal").action(
3263
3345
  wrapAction(async (taskId, opts) => {
3264
3346
  const config = loadConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "ClickUp CLI for AI agents and humans",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: clickup
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, 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.'
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, 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.'
4
4
  ---
5
5
 
6
6
  # ClickUp CLI (`cu`)
@@ -77,6 +77,7 @@ All commands support `--help` for full flag details.
77
77
  | `cu replies <commentId> [--json]` | List threaded replies on a comment |
78
78
  | `cu reply <commentId> -m text [--json]` | Reply to a comment |
79
79
  | `cu link <taskId> <linksTo> [--remove] [--json]` | Add or remove link between tasks |
80
+ | `cu attach <taskId> <filePath> [--json]` | Upload file attachment to a task |
80
81
  | `cu time start <taskId> [-d desc] [--json]` | Start tracking time on a task |
81
82
  | `cu time stop [--json]` | Stop the running timer |
82
83
  | `cu time status [--json]` | Show currently running timer |
@@ -120,7 +121,8 @@ All commands support `--help` for full flag details.
120
121
  | `cu comment-delete` | Delete a comment |
121
122
  | `cu replies` / `cu reply` | View and post threaded comment replies |
122
123
  | `cu link` | Link/unlink tasks (different from dependencies) |
123
- | `cu task` | Shows custom fields and checklists in detail view |
124
+ | `cu attach` | Upload files to tasks. Attachments shown in `cu task` detail view |
125
+ | `cu task` | Shows custom fields, checklists, and attachments in detail view |
124
126
  | `cu lists` | Discovers list IDs needed for `--list` and `cu create -l` |
125
127
  | Errors | stderr with exit code 1 |
126
128
  | Parsing | Strict - excess/unknown arguments rejected |
@@ -182,6 +184,7 @@ cu replies <commentId> # view threaded replies
182
184
  cu reply <commentId> -m "Agreed, fixing" # reply to a comment
183
185
  cu link abc123 def456 # link two tasks
184
186
  cu link abc123 def456 --remove # unlink two tasks
187
+ cu attach abc123def ./screenshot.png # upload file to task
185
188
  cu time start abc123def -d "Working on feature" # start timer
186
189
  cu time status # check running timer
187
190
  cu time stop # stop timer