Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/folder/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func runFolderCreate(cmd *cobra.Command, args []string) error {
fmt.Println(out)
} else {
fmt.Println("Folder created successfully!")
fmt.Printf("ID: %s\n", folder.ID)
fmt.Printf("ID: %d\n", folder.ID)
fmt.Printf("Title: %s\n", folder.Title)
}

Expand Down
15 changes: 10 additions & 5 deletions cmd/folder/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"os"
"strconv"
"strings"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -54,9 +55,13 @@ func runFolderDelete(cmd *cobra.Command, args []string) error {

client := api.NewClient(apiKey)

var folderID string
var folderID int
if len(args) > 0 {
folderID = args[0]
var err error
folderID, err = strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("invalid folder ID: %s", args[0])
}
} else {
// Interactive mode - let user select from folders
selected, err := prompt.SearchSelect(prompt.SearchSelectConfig{
Expand All @@ -71,7 +76,7 @@ func runFolderDelete(cmd *cobra.Command, args []string) error {
options := make([]prompt.SelectOption, len(folders.RoutineFolders))
for i, f := range folders.RoutineFolders {
options[i] = prompt.SelectOption{
ID: f.ID,
ID: fmt.Sprintf("%d", f.ID),
Title: f.Title,
Description: fmt.Sprintf("Index: %d", f.Index),
}
Expand All @@ -82,7 +87,7 @@ func runFolderDelete(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
folderID = selected.ID
folderID, _ = strconv.Atoi(selected.ID)
}

// Get folder details first to show what we're deleting
Expand All @@ -93,7 +98,7 @@ func runFolderDelete(cmd *cobra.Command, args []string) error {

// Confirm deletion unless --force is used
if !deleteForce {
fmt.Printf("Are you sure you want to delete folder '%s' (%s)?\n", folder.Title, folder.ID)
fmt.Printf("Are you sure you want to delete folder '%s' (%d)?\n", folder.Title, folder.ID)
fmt.Printf("This action cannot be undone. Routines inside will become unorganized.\n")
fmt.Print("Type 'yes' to confirm: ")

Expand Down
17 changes: 11 additions & 6 deletions cmd/folder/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package folder
import (
"fmt"
"os"
"strconv"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -38,9 +39,13 @@ func runGet(cmd *cobra.Command, args []string) error {

client := api.NewClient(apiKey)

var folderID string
var folderID int
if len(args) > 0 {
folderID = args[0]
var err error
folderID, err = strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("invalid folder ID: %s", args[0])
}
} else {
// Interactive mode - let user select from folders
selected, err := prompt.SearchSelect(prompt.SearchSelectConfig{
Expand All @@ -55,7 +60,7 @@ func runGet(cmd *cobra.Command, args []string) error {
options := make([]prompt.SelectOption, len(folders.RoutineFolders))
for i, f := range folders.RoutineFolders {
options[i] = prompt.SelectOption{
ID: f.ID,
ID: fmt.Sprintf("%d", f.ID),
Title: f.Title,
Description: fmt.Sprintf("Index: %d", f.Index),
}
Expand All @@ -66,7 +71,7 @@ func runGet(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
folderID = selected.ID
folderID, _ = strconv.Atoi(selected.ID)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle selected.ID conversion errors instead of discarding them

Ignoring Atoi errors can silently coerce to 0 and produce misleading “not found” behavior if ID formatting changes later.

Suggested patch
-		folderID, _ = strconv.Atoi(selected.ID)
+		var err error
+		folderID, err = strconv.Atoi(selected.ID)
+		if err != nil {
+			return fmt.Errorf("invalid selected folder ID: %q", selected.ID)
+		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
folderID, _ = strconv.Atoi(selected.ID)
var err error
folderID, err = strconv.Atoi(selected.ID)
if err != nil {
return fmt.Errorf("invalid selected folder ID: %q", selected.ID)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/folder/get.go` at line 74, The code currently ignores the error from
strconv.Atoi(selected.ID) causing a silent fallback to 0; update the conversion
to capture the error (e.g., folderID, err := strconv.Atoi(selected.ID)) and
handle it by returning or logging a clear error (for example return
fmt.Errorf("invalid folder ID %q: %w", selected.ID, err) or printing and
exiting), ensuring the calling function (where selected and folderID are used)
treats the conversion failure as an actual error rather than proceeding with 0.

}

// Search for the folder in the list
Expand All @@ -93,7 +98,7 @@ func runGet(cmd *cobra.Command, args []string) error {
}

if folder == nil {
return fmt.Errorf("folder not found: %s", folderID)
return fmt.Errorf("folder not found: %d", folderID)
}

// Determine output format
Expand Down Expand Up @@ -123,7 +128,7 @@ func runGet(cmd *cobra.Command, args []string) error {

func printFolderDetails(f *api.RoutineFolder, cfg *config.Config) {
fmt.Printf("Folder: %s\n", f.Title)
fmt.Printf("ID: %s\n", f.ID)
fmt.Printf("ID: %d\n", f.ID)
fmt.Printf("Index: %d\n", f.Index)
fmt.Printf("Created: %s\n", f.CreatedAt.Format(cfg.Display.DateFormat))
fmt.Printf("Updated: %s\n", f.UpdatedAt.Format(cfg.Display.DateFormat))
Expand Down
2 changes: 1 addition & 1 deletion cmd/folder/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func runList(cmd *cobra.Command, args []string) error {
for _, f := range allFolders {
updated := f.UpdatedAt.Format(cfg.Display.DateFormat)
table.AddRow(
f.ID,
fmt.Sprintf("%d", f.ID),
f.Title,
fmt.Sprintf("%d", f.Index),
updated,
Expand Down
15 changes: 10 additions & 5 deletions cmd/folder/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package folder
import (
"fmt"
"os"
"strconv"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -48,9 +49,13 @@ func runFolderUpdate(cmd *cobra.Command, args []string) error {

client := api.NewClient(apiKey)

var folderID string
var folderID int
if len(args) > 0 {
folderID = args[0]
var err error
folderID, err = strconv.Atoi(args[0])
if err != nil {
return fmt.Errorf("invalid folder ID: %s", args[0])
}
} else {
// Interactive mode - let user select from folders
selected, err := prompt.SearchSelect(prompt.SearchSelectConfig{
Expand All @@ -65,7 +70,7 @@ func runFolderUpdate(cmd *cobra.Command, args []string) error {
options := make([]prompt.SelectOption, len(folders.RoutineFolders))
for i, f := range folders.RoutineFolders {
options[i] = prompt.SelectOption{
ID: f.ID,
ID: fmt.Sprintf("%d", f.ID),
Title: f.Title,
Description: fmt.Sprintf("Index: %d", f.Index),
}
Expand All @@ -76,7 +81,7 @@ func runFolderUpdate(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
folderID = selected.ID
folderID, _ = strconv.Atoi(selected.ID)
}

// Determine output format
Expand Down Expand Up @@ -112,7 +117,7 @@ func runFolderUpdate(cmd *cobra.Command, args []string) error {
fmt.Println(out)
} else {
fmt.Println("Folder updated successfully!")
fmt.Printf("ID: %s\n", folder.ID)
fmt.Printf("ID: %d\n", folder.ID)
fmt.Printf("Title: %s\n", folder.Title)
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/routine/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func printRoutineDetails(r *api.Routine, cfg *config.Config, formatter output.Fo
fmt.Printf("ID: %s\n", r.ID)

if r.FolderID != nil {
fmt.Printf("Folder: %s\n", *r.FolderID)
fmt.Printf("Folder: %d\n", *r.FolderID)
}

fmt.Printf("Created: %s\n", r.CreatedAt.Format(cfg.Display.DateFormat))
Expand Down
9 changes: 7 additions & 2 deletions cmd/routine/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package routine
import (
"fmt"
"os"
"strconv"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -94,9 +95,13 @@ func runList(cmd *cobra.Command, args []string) error {

// Filter by folder if specified
if listFolder != "" {
folderID, err := strconv.Atoi(listFolder)
if err != nil {
return fmt.Errorf("invalid folder ID: %s", listFolder)
}
var filtered []api.Routine
for _, r := range allRoutines {
if r.FolderID != nil && *r.FolderID == listFolder {
if r.FolderID != nil && *r.FolderID == folderID {
filtered = append(filtered, r)
}
}
Expand All @@ -120,7 +125,7 @@ func runList(cmd *cobra.Command, args []string) error {
for _, r := range allRoutines {
folder := "-"
if r.FolderID != nil {
folder = *r.FolderID
folder = fmt.Sprintf("%d", *r.FolderID)
}

updated := r.UpdatedAt.Format(cfg.Display.DateFormat)
Expand Down
12 changes: 6 additions & 6 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,11 +403,11 @@ func (c *Client) CreateRoutineFolder(req *CreateRoutineFolderRequest) (*RoutineF
}

// GetRoutineFolder fetches a single routine folder by ID
func (c *Client) GetRoutineFolder(id string) (*RoutineFolder, error) {
func (c *Client) GetRoutineFolder(id int) (*RoutineFolder, error) {
var result RoutineFolderResponse
resp, err := c.httpClient.R().
SetResult(&result).
Get("/routine_folders/" + id)
Get(fmt.Sprintf("/routine_folders/%d", id))

if err != nil {
return nil, &APIError{
Expand Down Expand Up @@ -532,12 +532,12 @@ func (c *Client) DeleteRoutine(id string) error {
}

// UpdateRoutineFolder updates an existing routine folder
func (c *Client) UpdateRoutineFolder(id string, req *UpdateRoutineFolderRequest) (*RoutineFolder, error) {
func (c *Client) UpdateRoutineFolder(id int, req *UpdateRoutineFolderRequest) (*RoutineFolder, error) {
var result RoutineFolderResponse
resp, err := c.httpClient.R().
SetBody(req).
SetResult(&result).
Put("/routine_folders/" + id)
Put(fmt.Sprintf("/routine_folders/%d", id))

if err != nil {
return nil, &APIError{
Expand All @@ -554,9 +554,9 @@ func (c *Client) UpdateRoutineFolder(id string, req *UpdateRoutineFolderRequest)
}

// DeleteRoutineFolder deletes a routine folder by ID
func (c *Client) DeleteRoutineFolder(id string) error {
func (c *Client) DeleteRoutineFolder(id int) error {
resp, err := c.httpClient.R().
Delete("/routine_folders/" + id)
Delete(fmt.Sprintf("/routine_folders/%d", id))

if err != nil {
return &APIError{
Expand Down
4 changes: 2 additions & 2 deletions internal/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@ const (
type Routine struct {
ID string `json:"id"`
Title string `json:"title"`
FolderID *string `json:"folder_id,omitempty"`
FolderID *int `json:"folder_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Exercises []Exercise `json:"exercises"`
}

// RoutineFolder represents a folder for organizing routines
type RoutineFolder struct {
ID string `json:"id"`
ID int `json:"id"`
Title string `json:"title"`
Index int `json:"index"`
CreatedAt time.Time `json:"created_at"`
Expand Down