From 0595bf7e0fa7afba504fafe0048d7372fb14888a Mon Sep 17 00:00:00 2001 From: Virgil Date: Tue, 31 Mar 2026 19:50:59 +0000 Subject: [PATCH] feat(pkg): show search repo metadata Co-Authored-By: Virgil --- cmd/core/pkgcmd/cmd_manage_test.go | 24 +++++++++++ cmd/core/pkgcmd/cmd_search.go | 69 ++++++++++++++++++++++++------ 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/cmd/core/pkgcmd/cmd_manage_test.go b/cmd/core/pkgcmd/cmd_manage_test.go index 7908121..f555036 100644 --- a/cmd/core/pkgcmd/cmd_manage_test.go +++ b/cmd/core/pkgcmd/cmd_manage_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -114,3 +115,26 @@ func TestRunPkgList_UnsupportedFormat(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "unsupported format") } + +func TestRenderPkgSearchResults_ShowsMetadata(t *testing.T) { + out := capturePkgOutput(t, func() { + renderPkgSearchResults([]ghRepo{ + { + Name: "core-alpha", + Description: "Alpha package", + Visibility: "private", + StargazerCount: 42, + PrimaryLanguage: ghLanguage{ + Name: "Go", + }, + UpdatedAt: time.Now().Add(-2 * time.Hour).Format(time.RFC3339), + }, + }) + }) + + assert.Contains(t, out, "core-alpha") + assert.Contains(t, out, "Alpha package") + assert.Contains(t, out, "42 stars") + assert.Contains(t, out, "Go") + assert.Contains(t, out, "updated 2h ago") +} diff --git a/cmd/core/pkgcmd/cmd_search.go b/cmd/core/pkgcmd/cmd_search.go index 615a2d6..f9a6ad7 100644 --- a/cmd/core/pkgcmd/cmd_search.go +++ b/cmd/core/pkgcmd/cmd_search.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "forge.lthn.ai/core/cli/pkg/cli" "forge.lthn.ai/core/go-cache" "forge.lthn.ai/core/go-i18n" coreio "forge.lthn.ai/core/go-io" @@ -60,12 +61,17 @@ func addPkgSearchCommand(parent *cobra.Command) { } type ghRepo struct { - Name string `json:"name"` - FullName string `json:"full_name"` - Description string `json:"description"` - Visibility string `json:"visibility"` - UpdatedAt string `json:"updated_at"` - Language string `json:"language"` + Name string `json:"name"` + FullName string `json:"fullName"` + Description string `json:"description"` + Visibility string `json:"visibility"` + UpdatedAt string `json:"updatedAt"` + StargazerCount int `json:"stargazerCount"` + PrimaryLanguage ghLanguage `json:"primaryLanguage"` +} + +type ghLanguage struct { + Name string `json:"name"` } func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error { @@ -107,7 +113,7 @@ func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error fmt.Printf("%s %s... ", dimStyle.Render(i18n.T("cmd.pkg.search.fetching_label")), org) cmd := exec.Command("gh", "repo", "list", org, - "--json", "name,description,visibility,updatedAt,primaryLanguage", + "--json", "name,description,visibility,updatedAt,stargazerCount,primaryLanguage", "--limit", fmt.Sprintf("%d", limit)) output, err := cmd.CombinedOutput() @@ -152,9 +158,18 @@ func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error return cmp.Compare(a.Name, b.Name) }) - fmt.Print(i18n.T("cmd.pkg.search.found_repos", map[string]int{"Count": len(filtered)}) + "\n\n") + renderPkgSearchResults(filtered) - for _, r := range filtered { + fmt.Println() + fmt.Printf("%s %s\n", i18n.T("common.hint.install_with"), dimStyle.Render(fmt.Sprintf("core pkg install %s/", org))) + + return nil +} + +func renderPkgSearchResults(repos []ghRepo) { + fmt.Print(i18n.T("cmd.pkg.search.found_repos", map[string]int{"Count": len(repos)}) + "\n\n") + + for _, r := range repos { visibility := "" if r.Visibility == "private" { visibility = dimStyle.Render(" " + i18n.T("cmd.pkg.search.private_label")) @@ -170,12 +185,42 @@ func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error fmt.Printf(" %s%s\n", repoNameStyle.Render(r.Name), visibility) fmt.Printf(" %s\n", desc) + + if meta := formatPkgSearchMetadata(r); meta != "" { + fmt.Printf(" %s\n", dimStyle.Render(meta)) + } + } +} + +func formatPkgSearchMetadata(r ghRepo) string { + var parts []string + + if r.StargazerCount > 0 { + parts = append(parts, fmt.Sprintf("%d stars", r.StargazerCount)) } - fmt.Println() - fmt.Printf("%s %s\n", i18n.T("common.hint.install_with"), dimStyle.Render(fmt.Sprintf("core pkg install %s/", org))) + if lang := strings.TrimSpace(r.PrimaryLanguage.Name); lang != "" { + parts = append(parts, lang) + } - return nil + if updated := formatPkgSearchUpdatedAt(r.UpdatedAt); updated != "" { + parts = append(parts, "updated "+updated) + } + + return strings.Join(parts, " ") +} + +func formatPkgSearchUpdatedAt(raw string) string { + if raw == "" { + return "" + } + + updatedAt, err := time.Parse(time.RFC3339, raw) + if err != nil { + return raw + } + + return cli.FormatAge(updatedAt) } // matchGlob does simple glob matching with * wildcards