@krodak/clickup-cli 0.15.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
- "description": "Claude Code skills for the cu (ClickUp CLI) tool - manage tasks, sprints, initiatives, and comments",
4
- "version": "0.6.1",
3
+ "description": "ClickUp CLI skills for managing tasks, sprints, comments, checklists, custom fields, tags, and time tracking via the cu command",
4
+ "version": "0.17.0",
5
5
  "author": {
6
6
  "name": "Krzysztof Rodak"
7
7
  },
package/README.md CHANGED
@@ -13,7 +13,7 @@ npm install -g @krodak/clickup-cli && cu init
13
13
 
14
14
  ## Talk to your agent
15
15
 
16
- Install `cu`, install the skill file, and your agent works with ClickUp. No API knowledge needed.
16
+ Install the CLI, add the skill file to your agent, and it works with ClickUp. No API knowledge needed.
17
17
 
18
18
  > **"Read task abc123, do the work, then mark it in review and leave a comment with the commit hash."**
19
19
 
@@ -27,188 +27,249 @@ Install `cu`, install the skill file, and your agent works with ClickUp. No API
27
27
 
28
28
  The agent reads the skill file, picks the right `cu` commands, and handles everything. You don't need to learn the CLI - the agent does.
29
29
 
30
- ## Setup
30
+ ## Install
31
31
 
32
32
  You need Node 22+ and a ClickUp personal API token (`pk_...` from [ClickUp Settings > Apps](https://app.clickup.com/settings/apps)).
33
33
 
34
34
  <details open>
35
- <summary>&nbsp;<img src="https://cdn.simpleicons.org/openai/999999" height="16" align="center">&nbsp;<strong>OpenCode</strong></summary>
36
-
37
- **1. Install the CLI**
35
+ <summary>&nbsp;<img src="https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white" height="18" align="center">&nbsp;<strong>npm</strong></summary>
38
36
 
39
37
  ```bash
40
38
  npm install -g @krodak/clickup-cli
41
39
  cu init
42
40
  ```
43
41
 
44
- **2. Install the skill**
42
+ </details>
43
+
44
+ <details>
45
+ <summary>&nbsp;<img src="https://img.shields.io/badge/Homebrew-FBB040?logo=homebrew&logoColor=black" height="18" align="center">&nbsp;<strong>Homebrew</strong></summary>
45
46
 
46
47
  ```bash
47
- mkdir -p ~/.config/opencode/skills/clickup
48
- cp $(npm root -g)/@krodak/clickup-cli/skills/clickup-cli/SKILL.md ~/.config/opencode/skills/clickup/SKILL.md
48
+ brew tap krodak/tap
49
+ brew install clickup-cli
50
+ cu init
49
51
  ```
50
52
 
51
53
  </details>
52
54
 
53
- <details>
54
- <summary>&nbsp;<img src="https://cdn.simpleicons.org/anthropic/D97757" height="16" align="center">&nbsp;<strong>Claude Code</strong></summary>
55
+ ## Set up your agent
56
+
57
+ The package includes a [skill file](https://agentskills.io) that teaches agents all available commands and when to use them. All three major coding agents support skills natively:
58
+
59
+ <details open>
60
+ <summary>&nbsp;<img src="https://img.shields.io/badge/Claude_Code-D97757?logo=anthropic&logoColor=white" height="18" align="center">&nbsp;<strong>Claude Code</strong></summary>
55
61
 
56
- **1. Install the CLI**
62
+ **Install as a [plugin](https://docs.anthropic.com/en/docs/claude-code/plugins)** (recommended):
57
63
 
58
64
  ```bash
59
- npm install -g @krodak/clickup-cli
60
- cu init
65
+ claude plugin add $(npm root -g)/@krodak/clickup-cli
61
66
  ```
62
67
 
63
- **2. Install the skill**
68
+ This registers the skill under the `clickup-cli:` namespace. Claude loads it automatically when you work with ClickUp tasks.
69
+
70
+ **Or install as a personal skill** (no namespace prefix):
64
71
 
65
72
  ```bash
73
+ SKILL=$(npm root -g)/@krodak/clickup-cli/skills/clickup-cli
66
74
  mkdir -p ~/.claude/skills/clickup
67
- cp $(npm root -g)/@krodak/clickup-cli/skills/clickup-cli/SKILL.md ~/.claude/skills/clickup/SKILL.md
75
+ cp "$SKILL/SKILL.md" ~/.claude/skills/clickup/SKILL.md
68
76
  ```
69
77
 
70
- Then reference it in your `CLAUDE.md` or project instructions.
71
-
72
78
  </details>
73
79
 
74
80
  <details>
75
- <summary>&nbsp;<img src="https://cdn.simpleicons.org/anthropic/D97757" height="16" align="center">&nbsp;<strong>Claude Desktop</strong></summary>
81
+ <summary>&nbsp;<img src="https://img.shields.io/badge/Codex-412991?logo=openai&logoColor=white" height="18" align="center">&nbsp;<strong>Codex</strong></summary>
76
82
 
77
- Claude Desktop doesn't run CLI tools directly. Use it alongside a coding agent (Claude Code, OpenCode) that has `cu` installed, or run `cu` commands manually in your terminal.
83
+ Codex supports [agent skills](https://developers.openai.com/codex/skills) across CLI, IDE extension, and web. Skills use the same `SKILL.md` format with YAML frontmatter.
78
84
 
79
- **Install the CLI:**
85
+ **Install as a user skill** (available across all your projects):
80
86
 
81
87
  ```bash
82
- npm install -g @krodak/clickup-cli
83
- cu init
88
+ SKILL=$(npm root -g)/@krodak/clickup-cli/skills/clickup-cli
89
+ mkdir -p ~/.agents/skills/clickup
90
+ cp "$SKILL/SKILL.md" ~/.agents/skills/clickup/SKILL.md
84
91
  ```
85
92
 
86
- </details>
87
-
88
- <details>
89
- <summary>&nbsp;<img src="https://cdn.simpleicons.org/homebrew/FBB040" height="16" align="center">&nbsp;<strong>Homebrew</strong></summary>
93
+ **Or install as a project skill** (checked into your repo):
90
94
 
91
95
  ```bash
92
- brew tap krodak/tap
93
- brew install clickup-cli
94
- cu init
96
+ SKILL=$(npm root -g)/@krodak/clickup-cli/skills/clickup-cli
97
+ mkdir -p .agents/skills/clickup
98
+ cp "$SKILL/SKILL.md" .agents/skills/clickup/SKILL.md
95
99
  ```
96
100
 
97
- Then install the skill for your agent (see OpenCode or Claude Code sections above).
101
+ You can also use the built-in installer: `$skill-installer clickup`
98
102
 
99
103
  </details>
100
104
 
101
105
  <details>
102
- <summary>&nbsp;<img src="https://cdn.simpleicons.org/openai/999999" height="16" align="center">&nbsp;<strong>Codex / Other agents</strong></summary>
103
-
104
- **1. Install the CLI**
106
+ <summary>&nbsp;<img src="https://img.shields.io/badge/OpenCode-24292e?logoColor=white" height="18" align="center">&nbsp;<strong>OpenCode</strong></summary>
105
107
 
106
108
  ```bash
107
- npm install -g @krodak/clickup-cli
108
- cu init
109
+ SKILL=$(npm root -g)/@krodak/clickup-cli/skills/clickup-cli
110
+ mkdir -p ~/.config/opencode/skills/clickup
111
+ cp "$SKILL/SKILL.md" ~/.config/opencode/skills/clickup/SKILL.md
109
112
  ```
110
113
 
111
- **2. Add the skill to your agent**
114
+ </details>
115
+
116
+ <details>
117
+ <summary>&nbsp;<strong>Other agents</strong></summary>
112
118
 
113
- Copy `skills/clickup-cli/SKILL.md` into your agent's system prompt or project instructions. It's a standalone markdown document that teaches the agent all available commands.
119
+ The skill file follows the [Agent Skills](https://agentskills.io) open standard. Copy `skills/clickup-cli/SKILL.md` into your agent's skill directory, system prompt, or `AGENTS.md`.
114
120
 
115
121
  </details>
116
122
 
117
- ## Commands
123
+ ## API Coverage
124
+
125
+ [Full command reference with examples and flags](docs/commands.md).
118
126
 
119
- 36 commands total. [Full reference with examples and flags](docs/commands.md).
127
+ Status: :white_check_mark: implemented | :construction: planned | :no_entry_sign: won't add
120
128
 
121
129
  ### Tasks
122
130
 
123
- | Command | Description |
124
- | --------------------- | ----------------------------------------------------------- |
125
- | `cu tasks` | List my tasks (filter by status, name, type, list, space) |
126
- | `cu task <id>` | Task details with custom fields and checklists |
127
- | `cu subtasks <id>` | List subtasks of a task |
128
- | `cu create` | Create a task or subtask |
129
- | `cu update <id>` | Update task fields (status, name, priority, assignee, etc.) |
130
- | `cu delete <id>` | Delete a task (requires confirmation) |
131
- | `cu assign <id>` | Assign/unassign users |
132
- | `cu depend <id>` | Add/remove task dependencies |
133
- | `cu move <id>` | Add/remove task from lists |
134
- | `cu open <query>` | Open task in browser by ID or name |
135
- | `cu search <query>` | Search my tasks by name |
136
- | _`cu duplicate <id>`_ | _Copy a task - coming soon_ |
137
- | _`cu bulk`_ | _Bulk task operations - coming soon_ |
131
+ | Feature | Command | Status |
132
+ | -------------------- | ---------------------- | ------------------ |
133
+ | List my tasks | `cu tasks` | :white_check_mark: |
134
+ | Get task details | `cu task <id>` | :white_check_mark: |
135
+ | Create task | `cu create` | :white_check_mark: |
136
+ | Update task | `cu update <id>` | :white_check_mark: |
137
+ | Delete task | `cu delete <id>` | :white_check_mark: |
138
+ | Search tasks | `cu search <query>` | :white_check_mark: |
139
+ | Open in browser | `cu open <query>` | :white_check_mark: |
140
+ | List subtasks | `cu subtasks <id>` | :white_check_mark: |
141
+ | Assign / unassign | `cu assign <id>` | :white_check_mark: |
142
+ | Duplicate task | `cu duplicate <id>` | :construction: |
143
+ | Create from template | `cu create --template` | :construction: |
144
+ | Bulk operations | `cu bulk` | :construction: |
145
+
146
+ ### Dependencies & Relations
147
+
148
+ | Feature | Command | Status |
149
+ | -------------------- | ------------------------- | ------------------ |
150
+ | Add dependency | `cu depend <id>` | :white_check_mark: |
151
+ | Remove dependency | `cu depend <id> --remove` | :white_check_mark: |
152
+ | Add/remove task link | `cu link <id> <linksTo>` | :white_check_mark: |
153
+
154
+ ### Multi-list
155
+
156
+ | Feature | Command | Status |
157
+ | ---------------- | ----------------------- | ------------------ |
158
+ | Add task to list | `cu move <id> --add` | :white_check_mark: |
159
+ | Remove from list | `cu move <id> --remove` | :white_check_mark: |
138
160
 
139
161
  ### Sprints & Planning
140
162
 
141
- | Command | Description |
142
- | ------------- | ----------------------------------------------- |
143
- | `cu sprint` | My tasks in the active sprint (auto-detected) |
144
- | `cu sprints` | List all sprints |
145
- | `cu assigned` | All my tasks grouped by status |
146
- | `cu summary` | Standup helper: completed, in progress, overdue |
147
- | `cu overdue` | Tasks past their due date |
148
- | `cu inbox` | Recently updated tasks |
163
+ | Feature | Command | Status |
164
+ | ------------------------ | ------------- | ------------------ |
165
+ | Active sprint tasks | `cu sprint` | :white_check_mark: |
166
+ | List all sprints | `cu sprints` | :white_check_mark: |
167
+ | Assigned tasks by status | `cu assigned` | :white_check_mark: |
168
+ | Standup summary | `cu summary` | :white_check_mark: |
169
+ | Overdue tasks | `cu overdue` | :white_check_mark: |
170
+ | Recently updated | `cu inbox` | :white_check_mark: |
149
171
 
150
172
  ### Comments
151
173
 
152
- | Command | Description |
153
- | -------------------------- | -------------------------------- |
154
- | `cu comment <id>` | Post a comment on a task |
155
- | `cu comments <id>` | List comments on a task |
156
- | `cu comment-edit <id>` | Edit an existing comment |
157
- | `cu activity <id>` | Task details + comments combined |
158
- | _`cu comment-delete <id>`_ | _Delete a comment - coming soon_ |
174
+ | Feature | Command | Status |
175
+ | ------------------------ | ----------------------------------- | ------------------ |
176
+ | List comments | `cu comments <id>` | :white_check_mark: |
177
+ | Post comment | `cu comment <id>` | :white_check_mark: |
178
+ | Edit comment | `cu comment-edit <id>` | :white_check_mark: |
179
+ | Task + comments combined | `cu activity <id>` | :white_check_mark: |
180
+ | Delete comment | `cu comment-delete <id>` | :white_check_mark: |
181
+ | Threaded replies | `cu replies <id>` / `cu reply <id>` | :white_check_mark: |
159
182
 
160
- ### Custom Fields
183
+ ### Checklists
161
184
 
162
- | Command | Description |
163
- | ---------------------- | ----------------------------------------------------------------------------------- |
164
- | `cu field <id>` | Set/remove custom field values (text, number, dropdown, date, checkbox, url, email) |
165
- | _`cu fields <listId>`_ | _List available custom fields for a list - coming soon_ |
185
+ | Feature | Command | Status |
186
+ | ---------------- | ---------------------------------------- | ------------------ |
187
+ | View checklists | `cu checklist view <id>` | :white_check_mark: |
188
+ | Create checklist | `cu checklist create <id> <name>` | :white_check_mark: |
189
+ | Delete checklist | `cu checklist delete <id>` | :white_check_mark: |
190
+ | Add item | `cu checklist add-item <id> <name>` | :white_check_mark: |
191
+ | Edit item | `cu checklist edit-item <id> <itemId>` | :white_check_mark: |
192
+ | Delete item | `cu checklist delete-item <id> <itemId>` | :white_check_mark: |
166
193
 
167
- ### Checklists
194
+ ### Custom Fields
168
195
 
169
- | Command | Description |
170
- | ---------------------------------------- | ------------------------------------------------ |
171
- | `cu checklist view <id>` | View all checklists on a task |
172
- | `cu checklist create <id> <name>` | Create a checklist |
173
- | `cu checklist delete <id>` | Delete a checklist |
174
- | `cu checklist add-item <id> <name>` | Add item to a checklist |
175
- | `cu checklist edit-item <id> <itemId>` | Edit a checklist item (name, resolved, assignee) |
176
- | `cu checklist delete-item <id> <itemId>` | Delete a checklist item |
196
+ | Feature | Command | Status |
197
+ | --------------------- | ------------------------ | ------------------ |
198
+ | Set field value | `cu field <id> --set` | :white_check_mark: |
199
+ | Remove field value | `cu field <id> --remove` | :white_check_mark: |
200
+ | List available fields | `cu fields <listId>` | :construction: |
177
201
 
178
202
  ### Tags
179
203
 
180
- | Command | Description |
181
- | --------------------- | --------------------------------------------------- |
182
- | `cu tag <id>` | Add/remove tags on a task |
183
- | _`cu tags <spaceId>`_ | _List/create/delete space-level tags - coming soon_ |
204
+ | Feature | Command | Status |
205
+ | ----------------------- | ------------------- | ------------------ |
206
+ | Add/remove tag on task | `cu tag <id>` | :white_check_mark: |
207
+ | List space tags | `cu tags <spaceId>` | :construction: |
208
+ | Create/delete space tag | | :construction: |
209
+
210
+ ### Time Tracking
211
+
212
+ | Feature | Command | Status |
213
+ | -------------- | ----------------------------- | ------------------ |
214
+ | Start timer | `cu time start <id>` | :white_check_mark: |
215
+ | Stop timer | `cu time stop` | :white_check_mark: |
216
+ | Timer status | `cu time status` | :white_check_mark: |
217
+ | Log time entry | `cu time log <id> <duration>` | :white_check_mark: |
218
+ | List entries | `cu time list` | :white_check_mark: |
219
+ | Update entry | `cu time update <id>` | :construction: |
220
+ | Delete entry | `cu time delete <id>` | :construction: |
184
221
 
185
222
  ### Workspace
186
223
 
187
- | Command | Description |
188
- | ------------------------ | ----------------------------------------- |
189
- | `cu spaces` | List workspace spaces |
190
- | `cu lists <spaceId>` | Lists in a space (including folder lists) |
191
- | `cu auth` | Check authentication status |
192
- | _`cu folders <spaceId>`_ | _List folders in a space - coming soon_ |
193
- | _`cu members`_ | _List workspace members - coming soon_ |
224
+ | Feature | Command | Status |
225
+ | ------------ | ---------------------- | ------------------ |
226
+ | List spaces | `cu spaces` | :white_check_mark: |
227
+ | List lists | `cu lists <spaceId>` | :white_check_mark: |
228
+ | Check auth | `cu auth` | :white_check_mark: |
229
+ | List folders | `cu folders <spaceId>` | :construction: |
230
+ | List members | `cu members` | :construction: |
194
231
 
195
- ### Time Tracking
232
+ ### Goals & Key Results
233
+
234
+ | Feature | Command | Status |
235
+ | ------------------ | ---------- | -------------- |
236
+ | List goals | `cu goals` | :construction: |
237
+ | Create/update goal | | :construction: |
238
+ | Key results CRUD | | :construction: |
239
+
240
+ ### Docs
241
+
242
+ | Feature | Command | Status |
243
+ | ----------------- | ----------------- | -------------- |
244
+ | Search docs | `cu docs <query>` | :construction: |
245
+ | View page content | `cu doc <id>` | :construction: |
246
+
247
+ ### Attachments
248
+
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
+
254
+ ### :no_entry_sign: Won't add
196
255
 
197
- | Command | Description |
198
- | ----------------------------- | ----------------------------------------------- |
199
- | `cu time start <id>` | Start tracking time on a task |
200
- | `cu time stop` | Stop the running timer |
201
- | `cu time status` | Show the currently running timer |
202
- | `cu time log <id> <duration>` | Log a manual time entry (e.g. "2h", "30m") |
203
- | `cu time list` | List recent time entries (default: last 7 days) |
256
+ | Feature | Why |
257
+ | --------------------- | --------------------------------------------------------------------------- |
258
+ | Webhooks | Server-side. A CLI can't listen for events. |
259
+ | OAuth flow | `cu init` already handles auth with API tokens. |
260
+ | Guest/ACL | Enterprise admin. Not what you reach for in a terminal. |
261
+ | Chat/DM | Use the ClickUp app. Chat doesn't belong in a CLI. |
262
+ | Audit logs | Enterprise admin. |
263
+ | User/group management | Too destructive for a CLI - removing someone from a workspace is permanent. |
264
+ | View CRUD | Views are visual layouts. Configure them in the UI. |
204
265
 
205
266
  ### Setup
206
267
 
207
- | Command | Description |
208
- | ----------------------- | --------------------------------- |
209
- | `cu init` | First-time setup wizard |
210
- | `cu config` | Get/set config values |
211
- | `cu completion <shell>` | Shell completions (bash/zsh/fish) |
268
+ | Feature | Command | Status |
269
+ | ----------------- | ----------------------- | ------------------ |
270
+ | First-time setup | `cu init` | :white_check_mark: |
271
+ | Get/set config | `cu config` | :white_check_mark: |
272
+ | Shell completions | `cu completion <shell>` | :white_check_mark: |
212
273
 
213
274
  ## Output Modes
214
275
 
package/dist/index.js CHANGED
@@ -204,6 +204,25 @@ var ClickUpClient = class {
204
204
  body: JSON.stringify(body)
205
205
  });
206
206
  }
207
+ async deleteComment(commentId) {
208
+ await this.request(`/comment/${commentId}`, { method: "DELETE" });
209
+ }
210
+ async getThreadedComments(commentId) {
211
+ const data = await this.request(`/comment/${commentId}/reply`);
212
+ return data.comments ?? [];
213
+ }
214
+ async createThreadedComment(commentId, text) {
215
+ await this.request(`/comment/${commentId}/reply`, {
216
+ method: "POST",
217
+ body: JSON.stringify({ comment_text: text })
218
+ });
219
+ }
220
+ async addTaskLink(taskId, linksTo) {
221
+ await this.request(`/task/${taskId}/link/${linksTo}`, { method: "POST" });
222
+ }
223
+ async deleteTaskLink(taskId, linksTo) {
224
+ await this.request(`/task/${taskId}/link/${linksTo}`, { method: "DELETE" });
225
+ }
207
226
  async getListCustomFields(listId) {
208
227
  const data = await this.request(`/list/${listId}/field`);
209
228
  return data.fields ?? [];
@@ -295,6 +314,37 @@ var ClickUpClient = class {
295
314
  method: "DELETE"
296
315
  });
297
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
+ }
298
348
  };
299
349
 
300
350
  // src/config.ts
@@ -527,6 +577,12 @@ function formatTaskDetailMarkdown(task) {
527
577
  lines.push("");
528
578
  }
529
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
+ }
530
586
  return lines.join("\n");
531
587
  }
532
588
  function formatUpdateConfirmation(id, name) {
@@ -673,6 +729,13 @@ function formatTaskDetail(task) {
673
729
  }
674
730
  }
675
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
+ }
676
739
  if (task.text_content?.trim()) {
677
740
  lines.push("");
678
741
  lines.push(descriptionPreview(task.text_content));
@@ -1808,7 +1871,7 @@ function bashCompletion() {
1808
1871
  cword=$COMP_CWORD
1809
1872
  fi
1810
1873
 
1811
- local commands="init auth tasks task update create sprint sprints subtasks comment comment-edit comments activity lists spaces inbox assigned open search summary overdue assign depend 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"
1812
1875
 
1813
1876
  if [[ $cword -eq 1 ]]; then
1814
1877
  COMPREPLY=($(compgen -W "$commands --help --version" -- "$cur"))
@@ -1917,6 +1980,21 @@ function bashCompletion() {
1917
1980
  comment-edit)
1918
1981
  COMPREPLY=($(compgen -W "-m --message --resolved --unresolved --json" -- "$cur"))
1919
1982
  ;;
1983
+ comment-delete)
1984
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
1985
+ ;;
1986
+ replies)
1987
+ COMPREPLY=($(compgen -W "--json" -- "$cur"))
1988
+ ;;
1989
+ reply)
1990
+ COMPREPLY=($(compgen -W "-m --message --json" -- "$cur"))
1991
+ ;;
1992
+ link)
1993
+ COMPREPLY=($(compgen -W "--remove --json" -- "$cur"))
1994
+ ;;
1995
+ attach)
1996
+ COMPREPLY=($(compgen -f -- "$cur"))
1997
+ ;;
1920
1998
  config)
1921
1999
  if [[ $cword -eq 2 ]]; then
1922
2000
  COMPREPLY=($(compgen -W "get set path" -- "$cur"))
@@ -1972,6 +2050,11 @@ _cu() {
1972
2050
  'checklist:Manage checklists on a task'
1973
2051
  'time:Track time on tasks'
1974
2052
  'comment-edit:Edit an existing comment'
2053
+ 'comment-delete:Delete a comment'
2054
+ 'replies:List threaded replies on a comment'
2055
+ 'reply:Reply to a comment'
2056
+ 'link:Add or remove a link between two tasks'
2057
+ 'attach:Upload a file attachment to a task'
1975
2058
  'config:Manage CLI configuration'
1976
2059
  'completion:Output shell completion script'
1977
2060
  )
@@ -2262,6 +2345,35 @@ _cu() {
2262
2345
  '--unresolved[Mark comment as unresolved]' \\
2263
2346
  '--json[Force JSON output]'
2264
2347
  ;;
2348
+ comment-delete)
2349
+ _arguments \\
2350
+ '1:comment_id:' \\
2351
+ '--json[Force JSON output]'
2352
+ ;;
2353
+ replies)
2354
+ _arguments \\
2355
+ '1:comment_id:' \\
2356
+ '--json[Force JSON output]'
2357
+ ;;
2358
+ reply)
2359
+ _arguments \\
2360
+ '1:comment_id:' \\
2361
+ '(-m --message)'{-m,--message}'[Reply text]:text:' \\
2362
+ '--json[Force JSON output]'
2363
+ ;;
2364
+ link)
2365
+ _arguments \\
2366
+ '1:task_id:' \\
2367
+ '2:links_to:' \\
2368
+ '--remove[Remove the link instead of adding it]' \\
2369
+ '--json[Force JSON output]'
2370
+ ;;
2371
+ attach)
2372
+ _arguments \\
2373
+ '1:task_id:' \\
2374
+ '2:file_path:_files' \\
2375
+ '--json[Force JSON output]'
2376
+ ;;
2265
2377
  config)
2266
2378
  local -a config_cmds
2267
2379
  config_cmds=(
@@ -2331,6 +2443,11 @@ complete -c cu -n __fish_use_subcommand -a tag -d 'Add or remove tags from a tas
2331
2443
  complete -c cu -n __fish_use_subcommand -a checklist -d 'Manage checklists on a task'
2332
2444
  complete -c cu -n __fish_use_subcommand -a time -d 'Track time on tasks'
2333
2445
  complete -c cu -n __fish_use_subcommand -a comment-edit -d 'Edit an existing comment'
2446
+ complete -c cu -n __fish_use_subcommand -a comment-delete -d 'Delete a comment'
2447
+ complete -c cu -n __fish_use_subcommand -a replies -d 'List threaded replies on a comment'
2448
+ complete -c cu -n __fish_use_subcommand -a reply -d 'Reply to a comment'
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'
2334
2451
  complete -c cu -n __fish_use_subcommand -a config -d 'Manage CLI configuration'
2335
2452
  complete -c cu -n __fish_use_subcommand -a completion -d 'Output shell completion script'
2336
2453
 
@@ -2463,6 +2580,19 @@ complete -c cu -n '__fish_seen_subcommand_from log; and __fish_seen_subcommand_f
2463
2580
  complete -c cu -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l days -d 'Number of days to look back'
2464
2581
  complete -c cu -n '__fish_seen_subcommand_from list; and __fish_seen_subcommand_from time' -l task -d 'Filter by task ID'
2465
2582
 
2583
+ complete -c cu -n '__fish_seen_subcommand_from comment-delete' -l json -d 'Force JSON output'
2584
+
2585
+ complete -c cu -n '__fish_seen_subcommand_from replies' -l json -d 'Force JSON output'
2586
+
2587
+ complete -c cu -n '__fish_seen_subcommand_from reply' -s m -l message -d 'Reply text'
2588
+ complete -c cu -n '__fish_seen_subcommand_from reply' -l json -d 'Force JSON output'
2589
+
2590
+ complete -c cu -n '__fish_seen_subcommand_from link' -l remove -d 'Remove the link'
2591
+ complete -c cu -n '__fish_seen_subcommand_from link' -l json -d 'Force JSON output'
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
+
2466
2596
  complete -c cu -n '__fish_seen_subcommand_from comment-edit' -s m -l message -d 'New comment text'
2467
2597
  complete -c cu -n '__fish_seen_subcommand_from comment-edit' -l resolved -d 'Mark comment as resolved'
2468
2598
  complete -c cu -n '__fish_seen_subcommand_from comment-edit' -l unresolved -d 'Mark comment as unresolved'
@@ -2775,8 +2905,58 @@ async function editComment(config, commentId, text, resolved) {
2775
2905
  await client.updateComment(commentId, text, resolved);
2776
2906
  }
2777
2907
 
2778
- // src/commands/time.ts
2908
+ // src/commands/comment-delete.ts
2909
+ async function deleteComment(config, commentId) {
2910
+ const client = new ClickUpClient(config);
2911
+ await client.deleteComment(commentId);
2912
+ }
2913
+
2914
+ // src/commands/replies.ts
2779
2915
  import chalk6 from "chalk";
2916
+ async function getReplies(config, commentId) {
2917
+ const client = new ClickUpClient(config);
2918
+ return client.getThreadedComments(commentId);
2919
+ }
2920
+ async function createReply(config, commentId, text) {
2921
+ if (!text.trim()) throw new Error("Reply text cannot be empty");
2922
+ const client = new ClickUpClient(config);
2923
+ await client.createThreadedComment(commentId, text);
2924
+ }
2925
+ function formatReplies(replies) {
2926
+ if (replies.length === 0) return "No replies";
2927
+ return replies.map((r) => {
2928
+ const user = r.user?.username ?? "Unknown";
2929
+ const date = new Date(Number(r.date)).toLocaleString();
2930
+ return `${chalk6.bold(user)} ${chalk6.dim(date)}
2931
+ ${r.comment_text}`;
2932
+ }).join("\n\n");
2933
+ }
2934
+
2935
+ // src/commands/link.ts
2936
+ async function manageTaskLink(config, taskId, linksTo, remove) {
2937
+ const client = new ClickUpClient(config);
2938
+ if (remove) {
2939
+ await client.deleteTaskLink(taskId, linksTo);
2940
+ return `Removed link between ${taskId} and ${linksTo}`;
2941
+ }
2942
+ await client.addTaskLink(taskId, linksTo);
2943
+ return `Linked ${taskId} to ${linksTo}`;
2944
+ }
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
+
2958
+ // src/commands/time.ts
2959
+ import chalk7 from "chalk";
2780
2960
  function formatDuration2(ms) {
2781
2961
  const totalMinutes = Math.round(Math.abs(ms) / 6e4);
2782
2962
  const hours = Math.floor(totalMinutes / 60);
@@ -2828,8 +3008,8 @@ function formatTimeEntry(entry) {
2828
3008
  const isRunning = entry.duration < 0;
2829
3009
  const elapsed = isRunning ? Date.now() - Number(entry.start) : entry.duration;
2830
3010
  const durationStr = formatDuration2(elapsed);
2831
- const status = isRunning ? chalk6.green("RUNNING") : "";
2832
- lines.push(`${chalk6.bold(taskName)} ${chalk6.dim(taskId)} ${status}`);
3011
+ const status = isRunning ? chalk7.green("RUNNING") : "";
3012
+ lines.push(`${chalk7.bold(taskName)} ${chalk7.dim(taskId)} ${status}`);
2833
3013
  lines.push(
2834
3014
  ` ${durationStr} - ${formatTimestamp2(entry.start)}${entry.description ? ` - ${entry.description}` : ""}`
2835
3015
  );
@@ -2997,6 +3177,39 @@ program.command("comment-edit <commentId>").description("Edit an existing commen
2997
3177
  }
2998
3178
  )
2999
3179
  );
3180
+ program.command("comment-delete <commentId>").description("Delete a comment").option("--json", "Force JSON output even in terminal").action(
3181
+ wrapAction(async (commentId, opts) => {
3182
+ const config = loadConfig();
3183
+ await deleteComment(config, commentId);
3184
+ if (shouldOutputJson(opts.json ?? false)) {
3185
+ console.log(JSON.stringify({ success: true, commentId }, null, 2));
3186
+ } else {
3187
+ console.log(`Deleted comment ${commentId}`);
3188
+ }
3189
+ })
3190
+ );
3191
+ program.command("replies <commentId>").description("List threaded replies on a comment").option("--json", "Force JSON output even in terminal").action(
3192
+ wrapAction(async (commentId, opts) => {
3193
+ const config = loadConfig();
3194
+ const replies = await getReplies(config, commentId);
3195
+ if (shouldOutputJson(opts.json ?? false)) {
3196
+ console.log(JSON.stringify(replies, null, 2));
3197
+ } else {
3198
+ console.log(formatReplies(replies));
3199
+ }
3200
+ })
3201
+ );
3202
+ program.command("reply <commentId>").description("Reply to a comment").requiredOption("-m, --message <text>", "Reply text").option("--json", "Force JSON output even in terminal").action(
3203
+ wrapAction(async (commentId, opts) => {
3204
+ const config = loadConfig();
3205
+ await createReply(config, commentId, opts.message);
3206
+ if (shouldOutputJson(opts.json ?? false)) {
3207
+ console.log(JSON.stringify({ success: true, commentId }, null, 2));
3208
+ } else {
3209
+ console.log(`Replied to comment ${commentId}`);
3210
+ }
3211
+ })
3212
+ );
3000
3213
  program.command("activity <taskId>").description("Show task details and comments combined").option("--json", "Force JSON output even in terminal").action(
3001
3214
  wrapAction(async (taskId, opts) => {
3002
3215
  const config = loadConfig();
@@ -3097,6 +3310,37 @@ program.command("depend <taskId>").description("Add or remove task dependencies"
3097
3310
  }
3098
3311
  })
3099
3312
  );
3313
+ program.command("link <taskId> <linksTo>").description("Add or remove a link between two tasks").option("--remove", "Remove the link instead of adding it").option("--json", "Force JSON output even in terminal").action(
3314
+ wrapAction(
3315
+ async (taskId, linksTo, opts) => {
3316
+ const config = loadConfig();
3317
+ const result = await manageTaskLink(config, taskId, linksTo, opts.remove ?? false);
3318
+ if (shouldOutputJson(opts.json ?? false)) {
3319
+ console.log(
3320
+ JSON.stringify(
3321
+ { success: true, taskId, linksTo, action: opts.remove ? "removed" : "added" },
3322
+ null,
3323
+ 2
3324
+ )
3325
+ );
3326
+ } else {
3327
+ console.log(result);
3328
+ }
3329
+ }
3330
+ )
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
+ );
3100
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(
3101
3345
  wrapAction(async (taskId, opts) => {
3102
3346
  const config = loadConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krodak/clickup-cli",
3
- "version": "0.15.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, 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, 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`)
@@ -73,6 +73,11 @@ All commands support `--help` for full flag details.
73
73
  | `cu checklist add-item <checklistId> <name> [--json]` | Add item to a checklist |
74
74
  | `cu checklist edit-item <checklistId> <itemId> [--name n] [--resolved] [--unresolved] [--assignee id] [--json]` | Edit a checklist item |
75
75
  | `cu checklist delete-item <checklistId> <itemId> [--json]` | Delete a checklist item |
76
+ | `cu comment-delete <commentId> [--json]` | Delete a comment |
77
+ | `cu replies <commentId> [--json]` | List threaded replies on a comment |
78
+ | `cu reply <commentId> -m text [--json]` | Reply to a comment |
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 |
76
81
  | `cu time start <taskId> [-d desc] [--json]` | Start tracking time on a task |
77
82
  | `cu time stop [--json]` | Stop the running timer |
78
83
  | `cu time status [--json]` | Show currently running timer |
@@ -83,40 +88,44 @@ All commands support `--help` for full flag details.
83
88
 
84
89
  ## Quick Reference
85
90
 
86
- | Topic | Detail |
87
- | ----------------------- | ------------------------------------------------------------------------------------------------------ |
88
- | Task IDs | Stable alphanumeric strings (e.g. `abc123def`) |
89
- | `--type` | Filter by task type: `task` (regular), or custom type name/ID (e.g. `initiative`, `Bug`) |
90
- | `--list` on create | Optional when `--parent` is given (auto-detected) |
91
- | `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr. |
92
- | `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
93
- | `--due-date` | `YYYY-MM-DD` format |
94
- | `--assignee` | User ID or `me` (on `cu create`, `cu update`, `cu assign`) |
95
- | `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
96
- | `--time-estimate` | Duration format: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
97
- | `--custom-item-id` | Custom task type ID for `cu create` (e.g. `1` for initiative) |
98
- | `--on` / `--blocks` | Task dependency direction (used with `cu depend`) |
99
- | `--to` / `--remove` | List ID to add/remove task (used with `cu move`) |
100
- | `cu field --set` | Supports: text, number, checkbox (true/false), dropdown (option name), date (YYYY-MM-DD), url, email |
101
- | `cu field` | Field names resolved case-insensitively; errors list available fields/options |
102
- | `cu delete` | DESTRUCTIVE. Requires `--confirm` in non-interactive mode. Cannot be undone |
103
- | `cu tag --add/--remove` | Comma-separated tag names (e.g. `--add "bug,frontend"`) |
104
- | `--space` | Partial name match or exact ID |
105
- | `--name` | Partial match, case-insensitive |
106
- | `--include-closed` | Include closed/done tasks (on `tasks`, `assigned`, `subtasks`, `sprint`, `search`, `inbox`, `overdue`) |
107
- | `cu assign --to me` | Shorthand for your own user ID |
108
- | `cu search` | Matches all query words against task name, case-insensitive |
109
- | `cu sprint` | Auto-detects active sprint via view API and date range parsing |
110
- | `cu summary` | Categories: completed (done/complete/closed within N hours), in progress, overdue |
111
- | `cu overdue` | Excludes closed tasks, sorted most overdue first |
112
- | `cu open` | Tries task ID first, falls back to name search |
113
- | `cu checklist` | Full CRUD for task checklists: view, create, delete, add-item, edit-item, delete-item |
114
- | `cu time` | Track time: start/stop timer, log entries, list history. Duration format: "2h", "30m", "1h30m" |
115
- | `cu comment-edit` | Edit comment text and resolution status |
116
- | `cu task` | Shows custom fields and checklists in detail view |
117
- | `cu lists` | Discovers list IDs needed for `--list` and `cu create -l` |
118
- | Errors | stderr with exit code 1 |
119
- | Parsing | Strict - excess/unknown arguments rejected |
91
+ | Topic | Detail |
92
+ | ------------------------- | ------------------------------------------------------------------------------------------------------ |
93
+ | Task IDs | Stable alphanumeric strings (e.g. `abc123def`) |
94
+ | `--type` | Filter by task type: `task` (regular), or custom type name/ID (e.g. `initiative`, `Bug`) |
95
+ | `--list` on create | Optional when `--parent` is given (auto-detected) |
96
+ | `--status` | Fuzzy matching: exact > starts-with > contains. Prints match to stderr. |
97
+ | `--priority` | Names (`urgent`, `high`, `normal`, `low`) or numbers (1-4) |
98
+ | `--due-date` | `YYYY-MM-DD` format |
99
+ | `--assignee` | User ID or `me` (on `cu create`, `cu update`, `cu assign`) |
100
+ | `--tags` | Comma-separated (e.g. `--tags "bug,frontend"`) |
101
+ | `--time-estimate` | Duration format: `"2h"`, `"30m"`, `"1h30m"`, or raw milliseconds |
102
+ | `--custom-item-id` | Custom task type ID for `cu create` (e.g. `1` for initiative) |
103
+ | `--on` / `--blocks` | Task dependency direction (used with `cu depend`) |
104
+ | `--to` / `--remove` | List ID to add/remove task (used with `cu move`) |
105
+ | `cu field --set` | Supports: text, number, checkbox (true/false), dropdown (option name), date (YYYY-MM-DD), url, email |
106
+ | `cu field` | Field names resolved case-insensitively; errors list available fields/options |
107
+ | `cu delete` | DESTRUCTIVE. Requires `--confirm` in non-interactive mode. Cannot be undone |
108
+ | `cu tag --add/--remove` | Comma-separated tag names (e.g. `--add "bug,frontend"`) |
109
+ | `--space` | Partial name match or exact ID |
110
+ | `--name` | Partial match, case-insensitive |
111
+ | `--include-closed` | Include closed/done tasks (on `tasks`, `assigned`, `subtasks`, `sprint`, `search`, `inbox`, `overdue`) |
112
+ | `cu assign --to me` | Shorthand for your own user ID |
113
+ | `cu search` | Matches all query words against task name, case-insensitive |
114
+ | `cu sprint` | Auto-detects active sprint via view API and date range parsing |
115
+ | `cu summary` | Categories: completed (done/complete/closed within N hours), in progress, overdue |
116
+ | `cu overdue` | Excludes closed tasks, sorted most overdue first |
117
+ | `cu open` | Tries task ID first, falls back to name search |
118
+ | `cu checklist` | Full CRUD for task checklists: view, create, delete, add-item, edit-item, delete-item |
119
+ | `cu time` | Track time: start/stop timer, log entries, list history. Duration format: "2h", "30m", "1h30m" |
120
+ | `cu comment-edit` | Edit comment text and resolution status |
121
+ | `cu comment-delete` | Delete a comment |
122
+ | `cu replies` / `cu reply` | View and post threaded comment replies |
123
+ | `cu link` | Link/unlink tasks (different from dependencies) |
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 |
126
+ | `cu lists` | Discovers list IDs needed for `--list` and `cu create -l` |
127
+ | Errors | stderr with exit code 1 |
128
+ | Parsing | Strict - excess/unknown arguments rejected |
120
129
 
121
130
  ## Agent Workflow Examples
122
131
 
@@ -170,6 +179,12 @@ cu checklist create abc123def "QA Steps" # add checklist
170
179
  cu checklist add-item <clId> "Run unit tests" # add item
171
180
  cu checklist edit-item <clId> <itemId> --resolved # check off item
172
181
  cu comment-edit <commentId> -m "Updated findings" # edit a comment
182
+ cu comment-delete <commentId> # delete a comment
183
+ cu replies <commentId> # view threaded replies
184
+ cu reply <commentId> -m "Agreed, fixing" # reply to a comment
185
+ cu link abc123 def456 # link two tasks
186
+ cu link abc123 def456 --remove # unlink two tasks
187
+ cu attach abc123def ./screenshot.png # upload file to task
173
188
  cu time start abc123def -d "Working on feature" # start timer
174
189
  cu time status # check running timer
175
190
  cu time stop # stop timer