diff --git a/internal/output/formatter.go b/internal/output/formatter.go index dd0b59c..2379d08 100644 --- a/internal/output/formatter.go +++ b/internal/output/formatter.go @@ -66,22 +66,44 @@ func printTable(w io.Writer, envelope *Envelope) error { return nil } - // Try to render as table if data is a slice of maps switch data := envelope.Data.(type) { case []interface{}: return printSliceTable(w, data) case map[string]interface{}: - // For maps with nested structures, prefer JSON + if slice := findSliceInMap(data); slice != nil { + return printSliceTable(w, slice) + } if hasComplexValues(data) { return printJSON(w, envelope) } return printMapTable(w, data) default: - // Fallback to JSON return printJSON(w, envelope) } } +func findSliceInMap(m map[string]interface{}) []interface{} { + for _, key := range []string{ + "issues", "pull_requests", "milestones", "webhooks", "issue_tags", + "commits", "files", "members", "collaborators", "users", "branches", + "releases", "entries", "tags", "watchers", + } { + if v, ok := m[key]; ok { + if slice, ok := v.([]interface{}); ok && len(slice) > 0 { + return slice + } + } + } + for _, v := range m { + if slice, ok := v.([]interface{}); ok && len(slice) > 0 { + if _, isMap := slice[0].(map[string]interface{}); isMap { + return slice + } + } + } + return nil +} + func hasComplexValues(m map[string]interface{}) bool { for _, v := range m { switch v.(type) { @@ -98,7 +120,6 @@ func printSliceTable(w io.Writer, items []interface{}) error { return nil } - // Collect headers from first item first, ok := items[0].(map[string]interface{}) if !ok { data, _ := json.MarshalIndent(items, "", " ") @@ -109,7 +130,6 @@ func printSliceTable(w io.Writer, items []interface{}) error { headers := collectKeys(first) tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) - // Print headers fmt.Fprintln(tw, strings.Join(headers, "\t")) dashes := make([]string, len(headers)) for i, h := range headers { @@ -117,7 +137,6 @@ func printSliceTable(w io.Writer, items []interface{}) error { } fmt.Fprintln(tw, strings.Join(dashes, "\t")) - // Print rows for _, item := range items { m, ok := item.(map[string]interface{}) if !ok { @@ -144,7 +163,6 @@ func printMapTable(w io.Writer, m map[string]interface{}) error { func collectKeys(m map[string]interface{}) []string { keys := make([]string, 0, len(m)) - // Prefer common keys first priority := []string{"id", "name", "login", "title", "status", "state", "created_at", "updated_at"} seen := map[string]bool{} for _, k := range priority { @@ -170,10 +188,21 @@ func formatValue(v interface{}) string { case reflect.Map, reflect.Slice: data, _ := json.Marshal(v) s := string(data) - if len(s) > 60 { - return s[:57] + "..." + if len(s) > 50 { + return s[:47] + "..." } return s + case reflect.Bool: + if v.(bool) { + return "yes" + } + return "no" + case reflect.Float64: + f := v.(float64) + if f == float64(int64(f)) { + return fmt.Sprintf("%d", int64(f)) + } + return fmt.Sprintf("%v", v) default: return fmt.Sprintf("%v", v) } diff --git a/shortcuts/issue/batch.go b/shortcuts/issue/batch.go index 1ec9eb7..154afef 100644 --- a/shortcuts/issue/batch.go +++ b/shortcuts/issue/batch.go @@ -6,6 +6,7 @@ import ( "os" "strconv" "strings" + "time" "github.com/gitlink-org/gitlink-cli/shortcuts/common" ) @@ -25,6 +26,7 @@ type batchSummary struct { Total int `json:"total" yaml:"total"` Succeeded int `json:"succeeded" yaml:"succeeded"` Failed int `json:"failed" yaml:"failed"` + Duration string `json:"duration" yaml:"duration"` Results []batchResult `json:"results" yaml:"results"` } @@ -46,6 +48,8 @@ func runBatchClose(ctx *common.RuntimeContext) error { return err } + start := time.Now() + numbers, err := collectIssueNumbers(ctx.Arg("numbers"), ctx.Arg("from")) if err != nil { return err @@ -82,6 +86,8 @@ func runBatchClose(ctx *common.RuntimeContext) error { summary.Results = append(summary.Results, result) } + summary.Duration = time.Since(start).String() + if err := ctx.OutputData(summary); err != nil { return err }