Skip to content
1 change: 1 addition & 0 deletions SURFACE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ FLAG fizzy --quiet type=bool
FLAG fizzy --styled type=bool
FLAG fizzy --token type=string
FLAG fizzy --verbose type=bool
FLAG fizzy --version type=bool
FLAG fizzy account --agent type=bool
FLAG fizzy account --api-url type=string
FLAG fizzy account --count type=bool
Expand Down
6 changes: 3 additions & 3 deletions internal/commands/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ var authListCmd = &cobra.Command{
breadcrumb("login", "fizzy auth login <token>", "Log in"),
breadcrumb("signup", "fizzy signup", "Sign up"),
}
printSuccessWithBreadcrumbs([]any{}, "No profiles configured", breadcrumbs)
printList([]any{}, authProfileColumns, "No profiles configured", breadcrumbs)
return nil
}

Expand All @@ -247,7 +247,7 @@ var authListCmd = &cobra.Command{
breadcrumb("login", "fizzy auth login <token>", "Log in"),
breadcrumb("signup", "fizzy signup", "Sign up"),
}
printSuccessWithBreadcrumbs([]any{}, "No profiles configured", breadcrumbs)
printList([]any{}, authProfileColumns, "No profiles configured", breadcrumbs)
return nil
}

Expand Down Expand Up @@ -282,7 +282,7 @@ var authListCmd = &cobra.Command{
breadcrumb("switch", "fizzy auth switch <profile>", "Switch profile"),
}

printSuccessWithBreadcrumbs(entries, fmt.Sprintf("%d profile(s)", len(entries)), breadcrumbs)
printList(entries, authProfileColumns, fmt.Sprintf("%d profile(s)", len(entries)), breadcrumbs)
return nil
},
}
Expand Down
37 changes: 37 additions & 0 deletions internal/commands/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"

"github.com/basecamp/cli/credstore"
"github.com/basecamp/cli/output"
"github.com/basecamp/cli/profile"
"github.com/basecamp/fizzy-cli/internal/config"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -507,6 +509,41 @@ func TestAuthList(t *testing.T) {
t.Errorf("expected 0 profiles, got %d", len(profiles))
}
})

t.Run("renders styled output with next steps", func(t *testing.T) {
credDir := t.TempDir()
profileDir := t.TempDir()

os.Setenv("FIZZY_LIST_STYLED_NO_KR", "1")
defer os.Unsetenv("FIZZY_LIST_STYLED_NO_KR")
store := credstore.NewStore(credstore.StoreOptions{
ServiceName: "fizzy-list-styled-test",
DisableEnvVar: "FIZZY_LIST_STYLED_NO_KR",
FallbackDir: credDir,
})
profileStore := profile.NewStore(filepath.Join(profileDir, "config.json"))
profileStore.Create(&profile.Profile{Name: "acme", BaseURL: "https://app.fizzy.do"})
t1, _ := json.Marshal("token1")
store.Save("profile:acme", t1)

mock := NewMockClient()
SetTestModeWithSDK(mock)
SetTestCreds(store)
SetTestProfiles(profileStore)
SetTestFormat(output.FormatStyled)
defer resetTest()

err := authListCmd.RunE(authListCmd, []string{})
assertExitCode(t, err, 0)

raw := TestOutput()
if !strings.Contains(raw, "Profile") {
t.Fatalf("expected styled table output, got:\n%s", raw)
}
if !strings.Contains(raw, "Next steps:") {
t.Fatalf("expected next steps section, got:\n%s", raw)
}
})
}

func TestAuthSwitch(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var columnListCmd = &cobra.Command{

dataSlice := toSliceAny(items)
if dataSlice == nil {
printSuccess(items)
printDetail(items, "", nil)
return nil
}

Expand Down
7 changes: 7 additions & 0 deletions internal/commands/columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ var (
{Header: "Name", Field: "name"},
}

authProfileColumns = render.Columns{
{Header: "Profile", Field: "profile"},
{Header: "Active", Field: "active"},
{Header: "Board", Field: "board"},
{Header: "Base URL", Field: "base_url"},
}

notificationColumns = render.Columns{
{Header: "ID", Field: "id"},
{Header: "Message", Field: "message"},
Expand Down
21 changes: 2 additions & 19 deletions internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ type flagInfo struct {
Description string `json:"description"`
}

// commandsCmd emits a flat catalog of all commands with their flags.
// commandsCmd emits a catalog of all commands with their flags.
var commandsCmd = &cobra.Command{
Use: "commands",
Short: "List all available commands",
Long: "Lists all available commands with their flags in JSON format.",
Long: "Lists all available commands. Use --json for a structured command catalog.",
RunE: func(cmd *cobra.Command, args []string) error {
catalog := walkCommands(rootCmd, "fizzy")
printSuccess(catalog)
Expand Down Expand Up @@ -110,20 +110,3 @@ func agentHelp(cmd *cobra.Command, _ []string) {
data, _ := json.MarshalIndent(info, "", " ")
fmt.Fprintln(outWriter, string(data))
}

// installAgentHelp sets the custom help function when --agent is active.
func installAgentHelp() {
rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
if cfgAgent {
agentHelp(cmd, args)
return
}
// Banner on root help only
if cmd == rootCmd {
printBanner()
}
// Fall back to Cobra's default help
cmd.Root().SetHelpFunc(nil)
_ = cmd.Help()
})
}
64 changes: 64 additions & 0 deletions internal/commands/commands_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package commands

import (
"strings"
"testing"

"github.com/basecamp/cli/output"
)

func TestCommandsStyledOutputRendersHumanCatalog(t *testing.T) {
mock := NewMockClient()
SetTestModeWithSDK(mock)
SetTestFormat(output.FormatStyled)
defer resetTest()

if err := commandsCmd.RunE(commandsCmd, []string{}); err != nil {
t.Fatalf("unexpected error: %v", err)
}

raw := TestOutput()
if !strings.Contains(raw, "Name") {
t.Fatalf("expected styled catalog header, got:\n%s", raw)
}
if !strings.Contains(raw, "fizzy auth") {
t.Fatalf("expected styled catalog to include commands, got:\n%s", raw)
}
}

func TestCommandsJSONOutputReturnsStructuredCatalog(t *testing.T) {
mock := NewMockClient()
result := SetTestModeWithSDK(mock)
defer resetTest()

if err := commandsCmd.RunE(commandsCmd, []string{}); err != nil {
t.Fatalf("unexpected error: %v", err)
}

if result.Response == nil || !result.Response.OK {
t.Fatalf("expected OK JSON response, got %#v", result.Response)
}

items, ok := result.Response.Data.([]any)
if !ok {
t.Fatalf("expected command catalog slice, got %#v", result.Response.Data)
}
if len(items) == 0 {
t.Fatal("expected command catalog entries")
}

found := false
for _, item := range items {
entry, ok := item.(map[string]any)
if !ok {
continue
}
if entry["name"] == "fizzy commands" {
found = true
break
}
}
if !found {
t.Fatalf("expected command catalog to include fizzy commands, got %#v", items)
}
}
Loading
Loading