Skip to content

Commit 3eefa7c

Browse files
committed
feat: add missing API endpoints for full ClickUp API coverage
- Attachments: file upload (multipart/form-data) - Task Relationships: dependencies and task links - Task Extras: merge, time-in-status, add/remove from list - Time Tracking Legacy: task-level time tracking (4 endpoints) - Time Entry Extras: history, tag add/remove/update - Users: invite, get, edit, remove - Roles: list custom roles - Custom Task Types: list custom task types - Shared Hierarchy: list shared items - Workspace: seats and plan endpoints - Comments: threaded replies, view comments - Guest Assignments: add/remove guests to task/list/folder - Templates: list task templates, create from templates Adds ~35 new API endpoints with tests, bringing CLI to full 1:1 coverage of the ClickUp API v2 specification.
1 parent baaf78c commit 3eefa7c

41 files changed

Lines changed: 3234 additions & 80 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/attachment.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
6+
"github.com/blockful/clickup-cli/internal/output"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var attachmentCmd = &cobra.Command{
11+
Use: "attachment",
12+
Short: "Manage attachments",
13+
}
14+
15+
var attachmentCreateCmd = &cobra.Command{
16+
Use: "create",
17+
Short: "Create a task attachment",
18+
RunE: func(cmd *cobra.Command, args []string) error {
19+
client := getClient()
20+
ctx := context.Background()
21+
taskID, _ := cmd.Flags().GetString("task-id")
22+
filePath, _ := cmd.Flags().GetString("file")
23+
24+
if taskID == "" || filePath == "" {
25+
output.PrintError("VALIDATION_ERROR", "--task-id and --file are required")
26+
return &exitError{code: 1}
27+
}
28+
29+
resp, err := client.CreateTaskAttachment(ctx, taskID, filePath)
30+
if err != nil {
31+
return handleError(err)
32+
}
33+
output.JSON(resp)
34+
return nil
35+
},
36+
}
37+
38+
func init() {
39+
rootCmd.AddCommand(attachmentCmd)
40+
attachmentCmd.AddCommand(attachmentCreateCmd)
41+
42+
attachmentCreateCmd.Flags().String("task-id", "", "Task ID (required)")
43+
attachmentCreateCmd.Flags().String("file", "", "Path to file (required)")
44+
}

cmd/comment.go

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,25 @@ var commentCmd = &cobra.Command{
1515

1616
var commentListCmd = &cobra.Command{
1717
Use: "list",
18-
Short: "List comments on a task or list",
18+
Short: "List comments on a task, list, or view",
1919
RunE: func(cmd *cobra.Command, args []string) error {
2020
client := getClient()
2121
ctx := context.Background()
2222
taskID, _ := cmd.Flags().GetString("task")
2323
listID, _ := cmd.Flags().GetString("list")
24-
if taskID == "" && listID == "" {
25-
output.PrintError("VALIDATION_ERROR", "--task or --list is required")
24+
viewID, _ := cmd.Flags().GetString("view-id")
25+
if taskID == "" && listID == "" && viewID == "" {
26+
output.PrintError("VALIDATION_ERROR", "--task, --list, or --view-id is required")
2627
return &exitError{code: 1}
2728
}
29+
if viewID != "" {
30+
resp, err := client.ListViewComments(ctx, viewID)
31+
if err != nil {
32+
return handleError(err)
33+
}
34+
output.JSON(resp)
35+
return nil
36+
}
2837
if listID != "" {
2938
resp, err := client.ListListComments(ctx, listID)
3039
if err != nil {
@@ -44,14 +53,15 @@ var commentListCmd = &cobra.Command{
4453

4554
var commentCreateCmd = &cobra.Command{
4655
Use: "create",
47-
Short: "Add a comment to a task",
56+
Short: "Add a comment to a task, list, or view",
4857
RunE: func(cmd *cobra.Command, args []string) error {
4958
client := getClient()
5059
ctx := context.Background()
5160
taskID, _ := cmd.Flags().GetString("task")
5261
listID, _ := cmd.Flags().GetString("list")
53-
if taskID == "" && listID == "" {
54-
output.PrintError("VALIDATION_ERROR", "--task or --list is required")
62+
viewID, _ := cmd.Flags().GetString("view-id")
63+
if taskID == "" && listID == "" && viewID == "" {
64+
output.PrintError("VALIDATION_ERROR", "--task, --list, or --view-id is required")
5565
return &exitError{code: 1}
5666
}
5767
text, _ := cmd.Flags().GetString("text")
@@ -66,6 +76,14 @@ var commentCreateCmd = &cobra.Command{
6676
}
6777
req.NotifyAll, _ = cmd.Flags().GetBool("notify-all")
6878

79+
if viewID != "" {
80+
resp, err := client.CreateViewComment(ctx, viewID, req)
81+
if err != nil {
82+
return handleError(err)
83+
}
84+
output.JSON(resp)
85+
return nil
86+
}
6987
if listID != "" {
7088
resp, err := client.CreateListComment(ctx, listID, req)
7189
if err != nil {
@@ -135,12 +153,70 @@ var commentDeleteCmd = &cobra.Command{
135153
},
136154
}
137155

156+
var commentReplyCmd = &cobra.Command{
157+
Use: "reply",
158+
Short: "Manage threaded comment replies",
159+
}
160+
161+
var commentReplyListCmd = &cobra.Command{
162+
Use: "list",
163+
Short: "List threaded comments on a comment",
164+
RunE: func(cmd *cobra.Command, args []string) error {
165+
client := getClient()
166+
ctx := context.Background()
167+
commentID, _ := cmd.Flags().GetString("comment-id")
168+
if commentID == "" {
169+
output.PrintError("VALIDATION_ERROR", "--comment-id is required")
170+
return &exitError{code: 1}
171+
}
172+
resp, err := client.ListThreadedComments(ctx, commentID)
173+
if err != nil {
174+
return handleError(err)
175+
}
176+
output.JSON(resp)
177+
return nil
178+
},
179+
}
180+
181+
var commentReplyCreateCmd = &cobra.Command{
182+
Use: "create",
183+
Short: "Create a threaded comment reply",
184+
RunE: func(cmd *cobra.Command, args []string) error {
185+
client := getClient()
186+
ctx := context.Background()
187+
commentID, _ := cmd.Flags().GetString("comment-id")
188+
if commentID == "" {
189+
output.PrintError("VALIDATION_ERROR", "--comment-id is required")
190+
return &exitError{code: 1}
191+
}
192+
text, _ := cmd.Flags().GetString("text")
193+
if text == "" {
194+
output.PrintError("VALIDATION_ERROR", "--text is required")
195+
return &exitError{code: 1}
196+
}
197+
req := &api.CreateCommentRequest{CommentText: text}
198+
if cmd.Flags().Changed("assignee") {
199+
v, _ := cmd.Flags().GetInt("assignee")
200+
req.Assignee = api.IntPtr(v)
201+
}
202+
req.NotifyAll, _ = cmd.Flags().GetBool("notify-all")
203+
resp, err := client.CreateThreadedComment(ctx, commentID, req)
204+
if err != nil {
205+
return handleError(err)
206+
}
207+
output.JSON(resp)
208+
return nil
209+
},
210+
}
211+
138212
func init() {
139213
commentListCmd.Flags().String("task", "", "Task ID")
140214
commentListCmd.Flags().String("list", "", "List ID")
215+
commentListCmd.Flags().String("view-id", "", "View ID (chat view comments)")
141216

142217
commentCreateCmd.Flags().String("task", "", "Task ID")
143218
commentCreateCmd.Flags().String("list", "", "List ID")
219+
commentCreateCmd.Flags().String("view-id", "", "View ID (chat view comment)")
144220
commentCreateCmd.Flags().String("text", "", "Comment text")
145221
commentCreateCmd.Flags().Int("assignee", 0, "Assignee user ID")
146222
commentCreateCmd.Flags().Bool("notify-all", false, "Notify all")
@@ -152,9 +228,19 @@ func init() {
152228

153229
commentDeleteCmd.Flags().String("id", "", "Comment ID")
154230

231+
commentReplyListCmd.Flags().String("comment-id", "", "Comment ID")
232+
commentReplyCreateCmd.Flags().String("comment-id", "", "Comment ID")
233+
commentReplyCreateCmd.Flags().String("text", "", "Comment text")
234+
commentReplyCreateCmd.Flags().Int("assignee", 0, "Assignee user ID")
235+
commentReplyCreateCmd.Flags().Bool("notify-all", false, "Notify all")
236+
237+
commentReplyCmd.AddCommand(commentReplyListCmd)
238+
commentReplyCmd.AddCommand(commentReplyCreateCmd)
239+
155240
commentCmd.AddCommand(commentListCmd)
156241
commentCmd.AddCommand(commentCreateCmd)
157242
commentCmd.AddCommand(commentUpdateCmd)
158243
commentCmd.AddCommand(commentDeleteCmd)
244+
commentCmd.AddCommand(commentReplyCmd)
159245
rootCmd.AddCommand(commentCmd)
160246
}

cmd/custom_task_type.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
6+
"github.com/blockful/clickup-cli/internal/output"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var customTaskTypeCmd = &cobra.Command{
11+
Use: "custom-task-type",
12+
Short: "Manage custom task types",
13+
}
14+
15+
var customTaskTypeListCmd = &cobra.Command{
16+
Use: "list",
17+
Short: "List custom task types",
18+
RunE: func(cmd *cobra.Command, args []string) error {
19+
client := getClient()
20+
ctx := context.Background()
21+
wid := getWorkspaceID(cmd)
22+
resp, err := client.GetCustomTaskTypes(ctx, wid)
23+
if err != nil {
24+
return handleError(err)
25+
}
26+
output.JSON(resp)
27+
return nil
28+
},
29+
}
30+
31+
func init() {
32+
rootCmd.AddCommand(customTaskTypeCmd)
33+
customTaskTypeCmd.AddCommand(customTaskTypeListCmd)
34+
}

0 commit comments

Comments
 (0)