fizzy-cli 0.3.0 → 0.5.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 - 2026-01-25
4
+
5
+ ### Features
6
+
7
+ #### Comment Management
8
+
9
+ - `fizzy comment list <card_number>` - List all comments on a card
10
+ - `fizzy comment show <card_number> <comment_id>` - Display a specific comment
11
+ - `fizzy comment add <card_number> <body>` - Create a new comment on a card
12
+
13
+ ### Fixes
14
+
15
+ - Fixed error handling in notification read-all command
16
+ - Removed global flag usage in some commands
17
+
18
+ ## 0.4.0 - 2026-01-19
19
+
20
+ ### Features
21
+
22
+ #### Card Management
23
+
24
+ - `fizzy card not-now <card_number>` - Move a card to "Not Now" status
25
+ - `fizzy card untriage <card_number>` - Send a card back to triage
26
+ - `fizzy card watch <card_number>` - Subscribe to card notifications
27
+ - `fizzy card unwatch <card_number>` - Unsubscribe from card notifications
28
+ - `fizzy card golden <card_number>` - Mark a card as golden
29
+ - `fizzy card ungolden <card_number>` - Remove golden status from a card
30
+
3
31
  ## 0.3.0 - 2026-01-11
4
32
 
5
33
  ### Features
@@ -9,13 +9,6 @@ import (
9
9
  "github.com/spf13/cobra"
10
10
  )
11
11
 
12
- var (
13
- boardName string
14
- boardAllAccess bool
15
- boardAutoPostponePeriod int
16
- boardPublicDescription string
17
- )
18
-
19
12
  var boardCreateCmd = &cobra.Command{
20
13
  Use: "create",
21
14
  Short: "Create a new board",
@@ -33,11 +26,17 @@ func handleCreateBoard(cmd *cobra.Command) error {
33
26
  return fmt.Errorf("API client not available")
34
27
  }
35
28
 
29
+ // Read flag values directly from command
30
+ name, _ := cmd.Flags().GetString("name")
31
+ allAccess, _ := cmd.Flags().GetBool("all-access")
32
+ autoPostponePeriod, _ := cmd.Flags().GetInt("auto-postpone-period")
33
+ publicDescription, _ := cmd.Flags().GetString("description")
34
+
36
35
  payload := api.CreateBoardPayload{
37
- Name: boardName,
38
- AllAccess: boardAllAccess,
39
- AutoPostponePeriod: boardAutoPostponePeriod,
40
- PublicDescription: boardPublicDescription,
36
+ Name: name,
37
+ AllAccess: allAccess,
38
+ AutoPostponePeriod: autoPostponePeriod,
39
+ PublicDescription: publicDescription,
41
40
  }
42
41
 
43
42
  _, err := a.Client.PostBoards(context.Background(), payload)
@@ -45,16 +44,16 @@ func handleCreateBoard(cmd *cobra.Command) error {
45
44
  return fmt.Errorf("creating board: %w", err)
46
45
  }
47
46
 
48
- fmt.Printf("✓ Board '%s' created successfully\n", boardName)
47
+ fmt.Printf("✓ Board '%s' created successfully\n", name)
49
48
  return nil
50
49
  }
51
50
 
52
51
  func init() {
53
- boardCreateCmd.Flags().StringVarP(&boardName, "name", "n", "", "Board name (required)")
52
+ boardCreateCmd.Flags().StringP("name", "n", "", "Board name (required)")
54
53
  boardCreateCmd.MarkFlagRequired("name")
55
- boardCreateCmd.Flags().BoolVar(&boardAllAccess, "all-access", false, "Allow all access to the board")
56
- boardCreateCmd.Flags().IntVar(&boardAutoPostponePeriod, "auto-postpone-period", 0, "Auto postpone period in days")
57
- boardCreateCmd.Flags().StringVar(&boardPublicDescription, "description", "", "Public description of the board")
54
+ boardCreateCmd.Flags().Bool("all-access", false, "Allow all access to the board")
55
+ boardCreateCmd.Flags().Int("auto-postpone-period", 0, "Auto postpone period in days")
56
+ boardCreateCmd.Flags().String("description", "", "Public description of the board")
58
57
 
59
58
  boardCmd.AddCommand(boardCreateCmd)
60
59
  }
@@ -54,11 +54,6 @@ func TestBoardCreateCommandSuccess(t *testing.T) {
54
54
  cmd.SetContext(testApp.ToContext(context.Background()))
55
55
  cmd.ParseFlags([]string{"--name", "Test Board"})
56
56
 
57
- boardName = "Test Board"
58
- boardAllAccess = false
59
- boardAutoPostponePeriod = 0
60
- boardPublicDescription = ""
61
-
62
57
  if err := handleCreateBoard(cmd); err != nil {
63
58
  t.Fatalf("handleCreateBoard failed: %v", err)
64
59
  }
@@ -101,11 +96,6 @@ func TestBoardCreateCommandWithAllFlags(t *testing.T) {
101
96
  "--description", "Team project",
102
97
  })
103
98
 
104
- boardName = "Test Board"
105
- boardAllAccess = true
106
- boardAutoPostponePeriod = 7
107
- boardPublicDescription = "Team project"
108
-
109
99
  if err := handleCreateBoard(cmd); err != nil {
110
100
  t.Fatalf("handleCreateBoard failed: %v", err)
111
101
  }
@@ -125,11 +115,6 @@ func TestBoardCreateCommandAPIError(t *testing.T) {
125
115
  cmd.SetContext(testApp.ToContext(context.Background()))
126
116
  cmd.ParseFlags([]string{"--name", "Test Board"})
127
117
 
128
- boardName = "Test Board"
129
- boardAllAccess = false
130
- boardAutoPostponePeriod = 0
131
- boardPublicDescription = ""
132
-
133
118
  err := handleCreateBoard(cmd)
134
119
  if err == nil {
135
120
  t.Errorf("expected error for API failure")
@@ -146,8 +131,6 @@ func TestBoardCreateCommandNoClient(t *testing.T) {
146
131
  cmd.SetContext(testApp.ToContext(context.Background()))
147
132
  cmd.ParseFlags([]string{"--name", "Test Board"})
148
133
 
149
- boardName = "Test Board"
150
-
151
134
  err := handleCreateBoard(cmd)
152
135
  if err == nil {
153
136
  t.Errorf("expected error when client not available")
@@ -9,16 +9,6 @@ import (
9
9
  "github.com/spf13/cobra"
10
10
  )
11
11
 
12
- var (
13
- cardTitle string
14
- cardDescription string
15
- cardStatus string
16
- cardImageURL string
17
- cardTagIDs []string
18
- cardCreatedAt string
19
- cardLastActiveAt string
20
- )
21
-
22
12
  var cardCreateCmd = &cobra.Command{
23
13
  Use: "create",
24
14
  Short: "Create a new card",
@@ -40,14 +30,23 @@ func handleCreateCard(cmd *cobra.Command) error {
40
30
  return fmt.Errorf("no board selected")
41
31
  }
42
32
 
33
+ // Read flag values directly from command
34
+ title, _ := cmd.Flags().GetString("title")
35
+ description, _ := cmd.Flags().GetString("description")
36
+ status, _ := cmd.Flags().GetString("status")
37
+ imageURL, _ := cmd.Flags().GetString("image-url")
38
+ tagIDs, _ := cmd.Flags().GetStringSlice("tag-id")
39
+ createdAt, _ := cmd.Flags().GetString("created-at")
40
+ lastActiveAt, _ := cmd.Flags().GetString("last-active-at")
41
+
43
42
  payload := api.CreateCardPayload{
44
- Title: cardTitle,
45
- Description: cardDescription,
46
- Status: cardStatus,
47
- ImageURL: cardImageURL,
48
- TagIDS: cardTagIDs,
49
- CreatedAt: cardCreatedAt,
50
- LastActiveAt: cardLastActiveAt,
43
+ Title: title,
44
+ Description: description,
45
+ Status: status,
46
+ ImageURL: imageURL,
47
+ TagIDS: tagIDs,
48
+ CreatedAt: createdAt,
49
+ LastActiveAt: lastActiveAt,
51
50
  }
52
51
 
53
52
  _, err := a.Client.PostCards(context.Background(), payload)
@@ -55,19 +54,19 @@ func handleCreateCard(cmd *cobra.Command) error {
55
54
  return fmt.Errorf("creating card: %w", err)
56
55
  }
57
56
 
58
- fmt.Printf("✓ Card '%s' created successfully\n", cardTitle)
57
+ fmt.Printf("✓ Card '%s' created successfully\n", title)
59
58
  return nil
60
59
  }
61
60
 
62
61
  func init() {
63
- cardCreateCmd.Flags().StringVarP(&cardTitle, "title", "t", "", "Card title (required)")
62
+ cardCreateCmd.Flags().StringP("title", "t", "", "Card title (required)")
64
63
  cardCreateCmd.MarkFlagRequired("title")
65
- cardCreateCmd.Flags().StringVarP(&cardDescription, "description", "d", "", "Card description")
66
- cardCreateCmd.Flags().StringVar(&cardStatus, "status", "", "Card status")
67
- cardCreateCmd.Flags().StringVar(&cardImageURL, "image-url", "", "Card image URL")
68
- cardCreateCmd.Flags().StringSliceVar(&cardTagIDs, "tag-id", []string{}, "Tag ID (can be used multiple times)")
69
- cardCreateCmd.Flags().StringVar(&cardCreatedAt, "created-at", "", "Creation timestamp")
70
- cardCreateCmd.Flags().StringVar(&cardLastActiveAt, "last-active-at", "", "Last active timestamp")
64
+ cardCreateCmd.Flags().StringP("description", "d", "", "Card description")
65
+ cardCreateCmd.Flags().String("status", "", "Card status")
66
+ cardCreateCmd.Flags().String("image-url", "", "Card image URL")
67
+ cardCreateCmd.Flags().StringSlice("tag-id", []string{}, "Tag ID (can be used multiple times)")
68
+ cardCreateCmd.Flags().String("created-at", "", "Creation timestamp")
69
+ cardCreateCmd.Flags().String("last-active-at", "", "Last active timestamp")
71
70
 
72
71
  cardCmd.AddCommand(cardCreateCmd)
73
72
  }
@@ -58,14 +58,10 @@ func TestCardCreateCommandSuccess(t *testing.T) {
58
58
 
59
59
  cmd := cardCreateCmd
60
60
  cmd.SetContext(testApp.ToContext(context.Background()))
61
-
62
- cardTitle = "Implement feature"
63
- cardDescription = "A detailed description"
64
- cardStatus = ""
65
- cardImageURL = ""
66
- cardTagIDs = []string{}
67
- cardCreatedAt = ""
68
- cardLastActiveAt = ""
61
+ cmd.ParseFlags([]string{
62
+ "--title", "Implement feature",
63
+ "--description", "A detailed description",
64
+ })
69
65
 
70
66
  if err := handleCreateCard(cmd); err != nil {
71
67
  t.Fatalf("handleCreateCard failed: %v", err)
@@ -108,14 +104,13 @@ func TestCardCreateCommandWithAllFields(t *testing.T) {
108
104
 
109
105
  cmd := cardCreateCmd
110
106
  cmd.SetContext(testApp.ToContext(context.Background()))
111
-
112
- cardTitle = "Fix bug"
113
- cardDescription = ""
114
- cardStatus = "in_progress"
115
- cardImageURL = "https://example.com/image.jpg"
116
- cardTagIDs = []string{"tag-1", "tag-2"}
117
- cardCreatedAt = ""
118
- cardLastActiveAt = ""
107
+ cmd.ParseFlags([]string{
108
+ "--title", "Fix bug",
109
+ "--status", "in_progress",
110
+ "--image-url", "https://example.com/image.jpg",
111
+ "--tag-id", "tag-1",
112
+ "--tag-id", "tag-2",
113
+ })
119
114
 
120
115
  if err := handleCreateCard(cmd); err != nil {
121
116
  t.Fatalf("handleCreateCard failed: %v", err)
@@ -131,14 +126,9 @@ func TestCardCreateCommandNoBoard(t *testing.T) {
131
126
 
132
127
  cmd := cardCreateCmd
133
128
  cmd.SetContext(testApp.ToContext(context.Background()))
134
-
135
- cardTitle = "Test"
136
- cardDescription = ""
137
- cardStatus = ""
138
- cardImageURL = ""
139
- cardTagIDs = []string{}
140
- cardCreatedAt = ""
141
- cardLastActiveAt = ""
129
+ cmd.ParseFlags([]string{
130
+ "--title", "Test",
131
+ })
142
132
 
143
133
  err := handleCreateCard(cmd)
144
134
  if err == nil {
@@ -154,14 +144,9 @@ func TestCardCreateCommandNoClient(t *testing.T) {
154
144
 
155
145
  cmd := cardCreateCmd
156
146
  cmd.SetContext(testApp.ToContext(context.Background()))
157
-
158
- cardTitle = "Test"
159
- cardDescription = ""
160
- cardStatus = ""
161
- cardImageURL = ""
162
- cardTagIDs = []string{}
163
- cardCreatedAt = ""
164
- cardLastActiveAt = ""
147
+ cmd.ParseFlags([]string{
148
+ "--title", "Test",
149
+ })
165
150
 
166
151
  err := handleCreateCard(cmd)
167
152
  if err == nil {
@@ -187,14 +172,9 @@ func TestCardCreateCommandAPIError(t *testing.T) {
187
172
 
188
173
  cmd := cardCreateCmd
189
174
  cmd.SetContext(testApp.ToContext(context.Background()))
190
-
191
- cardTitle = "Test"
192
- cardDescription = ""
193
- cardStatus = ""
194
- cardImageURL = ""
195
- cardTagIDs = []string{}
196
- cardCreatedAt = ""
197
- cardLastActiveAt = ""
175
+ cmd.ParseFlags([]string{
176
+ "--title", "Test",
177
+ })
198
178
 
199
179
  err := handleCreateCard(cmd)
200
180
  if err == nil {
@@ -0,0 +1,46 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "strconv"
7
+
8
+ "github.com/rogeriopvl/fizzy/internal/app"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ var cardGoldenCmd = &cobra.Command{
13
+ Use: "golden <card_number>",
14
+ Short: "Mark a card as golden",
15
+ Long: `Mark an existing card as golden`,
16
+ Args: cobra.ExactArgs(1),
17
+ Run: func(cmd *cobra.Command, args []string) {
18
+ if err := handleGoldenCard(cmd, args[0]); err != nil {
19
+ fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
20
+ }
21
+ },
22
+ }
23
+
24
+ func handleGoldenCard(cmd *cobra.Command, cardNumber string) error {
25
+ cardNum, err := strconv.Atoi(cardNumber)
26
+ if err != nil {
27
+ return fmt.Errorf("invalid card number: %w", err)
28
+ }
29
+
30
+ a := app.FromContext(cmd.Context())
31
+ if a == nil || a.Client == nil {
32
+ return fmt.Errorf("API client not available")
33
+ }
34
+
35
+ _, err = a.Client.PostCardGoldenness(context.Background(), cardNum)
36
+ if err != nil {
37
+ return fmt.Errorf("marking card as golden: %w", err)
38
+ }
39
+
40
+ fmt.Printf("✓ Card #%d marked as golden\n", cardNum)
41
+ return nil
42
+ }
43
+
44
+ func init() {
45
+ cardCmd.AddCommand(cardGoldenCmd)
46
+ }
@@ -0,0 +1,92 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "net/http"
6
+ "net/http/httptest"
7
+ "testing"
8
+
9
+ "github.com/rogeriopvl/fizzy/internal/app"
10
+ "github.com/rogeriopvl/fizzy/internal/testutil"
11
+ )
12
+
13
+ func TestCardGoldenCommandSuccess(t *testing.T) {
14
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15
+ if r.URL.Path != "/cards/123/goldness" {
16
+ t.Errorf("expected /cards/123/goldness, got %s", r.URL.Path)
17
+ }
18
+ if r.Method != http.MethodPost {
19
+ t.Errorf("expected POST, got %s", r.Method)
20
+ }
21
+
22
+ auth := r.Header.Get("Authorization")
23
+ if auth != "Bearer test-token" {
24
+ t.Errorf("expected Bearer test-token, got %s", auth)
25
+ }
26
+
27
+ w.WriteHeader(http.StatusNoContent)
28
+ }))
29
+ defer server.Close()
30
+
31
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
32
+ testApp := &app.App{Client: client}
33
+
34
+ cmd := cardGoldenCmd
35
+ cmd.SetContext(testApp.ToContext(context.Background()))
36
+
37
+ if err := handleGoldenCard(cmd, "123"); err != nil {
38
+ t.Fatalf("handleGoldenCard failed: %v", err)
39
+ }
40
+ }
41
+
42
+ func TestCardGoldenCommandInvalidCardNumber(t *testing.T) {
43
+ testApp := &app.App{}
44
+
45
+ cmd := cardGoldenCmd
46
+ cmd.SetContext(testApp.ToContext(context.Background()))
47
+
48
+ err := handleGoldenCard(cmd, "not-a-number")
49
+ if err == nil {
50
+ t.Errorf("expected error for invalid card number")
51
+ }
52
+ if err.Error() != "invalid card number: strconv.Atoi: parsing \"not-a-number\": invalid syntax" {
53
+ t.Errorf("expected invalid card number error, got %v", err)
54
+ }
55
+ }
56
+
57
+ func TestCardGoldenCommandNoClient(t *testing.T) {
58
+ testApp := &app.App{}
59
+
60
+ cmd := cardGoldenCmd
61
+ cmd.SetContext(testApp.ToContext(context.Background()))
62
+
63
+ err := handleGoldenCard(cmd, "123")
64
+ if err == nil {
65
+ t.Errorf("expected error when client not available")
66
+ }
67
+ if err.Error() != "API client not available" {
68
+ t.Errorf("expected 'client not available' error, got %v", err)
69
+ }
70
+ }
71
+
72
+ func TestCardGoldenCommandAPIError(t *testing.T) {
73
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
74
+ w.WriteHeader(http.StatusInternalServerError)
75
+ w.Write([]byte("Internal Server Error"))
76
+ }))
77
+ defer server.Close()
78
+
79
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
80
+ testApp := &app.App{Client: client}
81
+
82
+ cmd := cardGoldenCmd
83
+ cmd.SetContext(testApp.ToContext(context.Background()))
84
+
85
+ err := handleGoldenCard(cmd, "123")
86
+ if err == nil {
87
+ t.Errorf("expected error for API failure")
88
+ }
89
+ if err.Error() != "marking card as golden: unexpected status code 500: Internal Server Error" {
90
+ t.Errorf("expected API error, got %v", err)
91
+ }
92
+ }
@@ -0,0 +1,46 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "strconv"
7
+
8
+ "github.com/rogeriopvl/fizzy/internal/app"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ var cardNotNowCmd = &cobra.Command{
13
+ Use: "not-now <card_number>",
14
+ Short: "Move a card to Not Now status",
15
+ Long: `Move an existing card to the "Not Now" status`,
16
+ Args: cobra.ExactArgs(1),
17
+ Run: func(cmd *cobra.Command, args []string) {
18
+ if err := handleNotNowCard(cmd, args[0]); err != nil {
19
+ fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
20
+ }
21
+ },
22
+ }
23
+
24
+ func handleNotNowCard(cmd *cobra.Command, cardNumber string) error {
25
+ cardNum, err := strconv.Atoi(cardNumber)
26
+ if err != nil {
27
+ return fmt.Errorf("invalid card number: %w", err)
28
+ }
29
+
30
+ a := app.FromContext(cmd.Context())
31
+ if a == nil || a.Client == nil {
32
+ return fmt.Errorf("API client not available")
33
+ }
34
+
35
+ _, err = a.Client.PostCardNotNow(context.Background(), cardNum)
36
+ if err != nil {
37
+ return fmt.Errorf("moving card to not now: %w", err)
38
+ }
39
+
40
+ fmt.Printf("✓ Card #%d moved to Not Now successfully\n", cardNum)
41
+ return nil
42
+ }
43
+
44
+ func init() {
45
+ cardCmd.AddCommand(cardNotNowCmd)
46
+ }
@@ -0,0 +1,92 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "net/http"
6
+ "net/http/httptest"
7
+ "testing"
8
+
9
+ "github.com/rogeriopvl/fizzy/internal/app"
10
+ "github.com/rogeriopvl/fizzy/internal/testutil"
11
+ )
12
+
13
+ func TestCardNotNowCommandSuccess(t *testing.T) {
14
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
15
+ if r.URL.Path != "/cards/123/not_now" {
16
+ t.Errorf("expected /cards/123/not_now, got %s", r.URL.Path)
17
+ }
18
+ if r.Method != http.MethodPost {
19
+ t.Errorf("expected POST, got %s", r.Method)
20
+ }
21
+
22
+ auth := r.Header.Get("Authorization")
23
+ if auth != "Bearer test-token" {
24
+ t.Errorf("expected Bearer test-token, got %s", auth)
25
+ }
26
+
27
+ w.WriteHeader(http.StatusNoContent)
28
+ }))
29
+ defer server.Close()
30
+
31
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
32
+ testApp := &app.App{Client: client}
33
+
34
+ cmd := cardNotNowCmd
35
+ cmd.SetContext(testApp.ToContext(context.Background()))
36
+
37
+ if err := handleNotNowCard(cmd, "123"); err != nil {
38
+ t.Fatalf("handleNotNowCard failed: %v", err)
39
+ }
40
+ }
41
+
42
+ func TestCardNotNowCommandInvalidCardNumber(t *testing.T) {
43
+ testApp := &app.App{}
44
+
45
+ cmd := cardNotNowCmd
46
+ cmd.SetContext(testApp.ToContext(context.Background()))
47
+
48
+ err := handleNotNowCard(cmd, "not-a-number")
49
+ if err == nil {
50
+ t.Errorf("expected error for invalid card number")
51
+ }
52
+ if err.Error() != "invalid card number: strconv.Atoi: parsing \"not-a-number\": invalid syntax" {
53
+ t.Errorf("expected invalid card number error, got %v", err)
54
+ }
55
+ }
56
+
57
+ func TestCardNotNowCommandNoClient(t *testing.T) {
58
+ testApp := &app.App{}
59
+
60
+ cmd := cardNotNowCmd
61
+ cmd.SetContext(testApp.ToContext(context.Background()))
62
+
63
+ err := handleNotNowCard(cmd, "123")
64
+ if err == nil {
65
+ t.Errorf("expected error when client not available")
66
+ }
67
+ if err.Error() != "API client not available" {
68
+ t.Errorf("expected 'client not available' error, got %v", err)
69
+ }
70
+ }
71
+
72
+ func TestCardNotNowCommandAPIError(t *testing.T) {
73
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
74
+ w.WriteHeader(http.StatusInternalServerError)
75
+ w.Write([]byte("Internal Server Error"))
76
+ }))
77
+ defer server.Close()
78
+
79
+ client := testutil.NewTestClient(server.URL, "", "", "test-token")
80
+ testApp := &app.App{Client: client}
81
+
82
+ cmd := cardNotNowCmd
83
+ cmd.SetContext(testApp.ToContext(context.Background()))
84
+
85
+ err := handleNotNowCard(cmd, "123")
86
+ if err == nil {
87
+ t.Errorf("expected error for API failure")
88
+ }
89
+ if err.Error() != "moving card to not now: unexpected status code 500: Internal Server Error" {
90
+ t.Errorf("expected API error, got %v", err)
91
+ }
92
+ }
@@ -0,0 +1,51 @@
1
+ package cmd
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "strconv"
7
+ "strings"
8
+
9
+ "github.com/rogeriopvl/fizzy/internal/app"
10
+ "github.com/spf13/cobra"
11
+ )
12
+
13
+ var cardTagCmd = &cobra.Command{
14
+ Use: "tag <card_number> <tag_title>",
15
+ Short: "Toggle a tag on or off for a card",
16
+ Long: `Toggle a tag on or off for a card. If the tag doesn't exist, it will be created.
17
+
18
+ The tag title can be specified with or without a leading # symbol.`,
19
+ Args: cobra.ExactArgs(2),
20
+ Run: func(cmd *cobra.Command, args []string) {
21
+ if err := handleTagCard(cmd, args[0], args[1]); err != nil {
22
+ fmt.Fprintf(cmd.OutOrStderr(), "Error: %v\n", err)
23
+ }
24
+ },
25
+ }
26
+
27
+ func handleTagCard(cmd *cobra.Command, cardNumber, tagTitle string) error {
28
+ cardNum, err := strconv.Atoi(cardNumber)
29
+ if err != nil {
30
+ return fmt.Errorf("invalid card number: %w", err)
31
+ }
32
+
33
+ a := app.FromContext(cmd.Context())
34
+ if a == nil || a.Client == nil {
35
+ return fmt.Errorf("API client not available")
36
+ }
37
+
38
+ tagTitle = strings.TrimPrefix(tagTitle, "#")
39
+
40
+ _, err = a.Client.PostCardTagging(context.Background(), cardNum, tagTitle)
41
+ if err != nil {
42
+ return fmt.Errorf("toggling tag on card: %w", err)
43
+ }
44
+
45
+ fmt.Printf("✓ Tag '%s' toggled on card #%d\n", tagTitle, cardNum)
46
+ return nil
47
+ }
48
+
49
+ func init() {
50
+ cardCmd.AddCommand(cardTagCmd)
51
+ }