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
30 changes: 0 additions & 30 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,36 +62,6 @@ changelog:
- Merge pull request
- Merge branch

homebrew_casks:
- name: hevycli
repository:
owner: obay
name: homebrew-tap
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
directory: Casks
homepage: "https://github.com/obay/hevycli"
description: "CLI for the Hevy fitness tracking platform"
binaries:
- hevycli
commit_author:
name: goreleaserbot
email: bot@goreleaser.com
commit_msg_template: "Brew cask update for {{ .ProjectName }} version {{ .Tag }}"

scoops:
- name: hevycli
repository:
owner: obay
name: scoop-bucket
token: "{{ .Env.SCOOP_BUCKET_TOKEN }}"
homepage: "https://github.com/obay/hevycli"
description: "CLI for the Hevy fitness tracking platform"
license: MIT
commit_author:
name: goreleaserbot
email: bot@goreleaser.com
commit_msg_template: "Scoop manifest update for {{ .ProjectName }} version {{ .Tag }}"

release:
github:
owner: obay
Expand Down
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
18 changes: 13 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,10 @@ func runFolderDelete(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
folderID = selected.ID
folderID, err = strconv.Atoi(selected.ID)
if err != nil {
return fmt.Errorf("invalid folder ID selected: %s", selected.ID)
}
}

// Get folder details first to show what we're deleting
Expand All @@ -93,7 +101,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
24 changes: 16 additions & 8 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 All @@ -19,8 +20,8 @@ var getCmd = &cobra.Command{
Long: `Get detailed information about a specific routine folder.

Examples:
hevycli folder get abc123-def456 # Get folder by ID
hevycli folder get abc123 -o json # Output as JSON`,
hevycli folder get 123 # Get folder by ID
hevycli folder get 123 -o json # Output as JSON`,
Args: cmdutil.RequireArgs(1, "<folder-id>"),
RunE: runGet,
}
Expand All @@ -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,10 @@ func runGet(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
folderID = selected.ID
folderID, err = strconv.Atoi(selected.ID)
if err != nil {
return fmt.Errorf("invalid folder ID selected: %s", selected.ID)
}
}

// Search for the folder in the list
Expand All @@ -93,7 +101,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 +131,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
18 changes: 13 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,10 @@ func runFolderUpdate(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
folderID = selected.ID
folderID, err = strconv.Atoi(selected.ID)
if err != nil {
return fmt.Errorf("invalid folder ID selected: %s", selected.ID)
}
}

// Determine output format
Expand Down Expand Up @@ -112,7 +120,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
30 changes: 22 additions & 8 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,14 @@ func (c *Client) CreateRoutine(req *CreateRoutineRequest) (*Routine, error) {
return nil, err
}

return &result.Routine, nil
if len(result.Routines) == 0 {
return nil, &APIError{
ErrorCode: "EMPTY_RESPONSE",
ErrorMessage: "API returned no routines in response",
}
}

return &result.Routines[0], nil
}

// UpdateRoutine updates an existing routine
Expand All @@ -377,7 +384,14 @@ func (c *Client) UpdateRoutine(id string, req *UpdateRoutineRequest) (*Routine,
return nil, err
}

return &result.Routine, nil
if len(result.Routines) == 0 {
return nil, &APIError{
ErrorCode: "EMPTY_RESPONSE",
ErrorMessage: "API returned no routines in response",
}
}

return &result.Routines[0], nil
}

// CreateRoutineFolder creates a new routine folder
Expand All @@ -403,11 +417,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 +546,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 +568,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
6 changes: 3 additions & 3 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 Expand Up @@ -280,7 +280,7 @@ type WorkoutResponse struct {

// RoutineResponse represents the response from POST/PUT /routines
type RoutineResponse struct {
Routine Routine `json:"routine"`
Routines []Routine `json:"routine"`
}

// RoutineFolderResponse represents the response from POST /routine_folders
Expand Down