From 2d6696226400ee85981571941e5c889352440f6d Mon Sep 17 00:00:00 2001 From: "Matthew (Pilot Protocol Bot)" Date: Wed, 8 Jul 2026 07:43:58 +0000 Subject: [PATCH] fix(appstore/catalogue): show headline instead of full description, per-entry view pointer (PILOT-405) The catalogue list previously printed the full description for every entry, which makes long descriptions hard to scan. Now the list shows the display_name (headline) when available, falling back to description. The view pointer is now per-entry (`view: pilotctl appstore view `) so a user can immediately see how to get details for each app without remembering the bottom hint. --json output is unchanged. Closes PILOT-405 --- cmd/pilotctl/appstore_catalogue.go | 7 +++++-- cmd/pilotctl/zz_appstore_cmds_test.go | 27 +++++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/cmd/pilotctl/appstore_catalogue.go b/cmd/pilotctl/appstore_catalogue.go index 174a5f87..b04a98e9 100644 --- a/cmd/pilotctl/appstore_catalogue.go +++ b/cmd/pilotctl/appstore_catalogue.go @@ -316,9 +316,12 @@ func cmdAppStoreCatalogue(_ []string) { return } for _, e := range c.Apps { - fmt.Printf("%-40s %s\n", e.ID, e.Description) + headline := e.Description + if e.DisplayName != "" { + headline = e.DisplayName + } + fmt.Printf("%-40s %-30s view: pilotctl appstore view %s\n", e.ID, headline, e.ID) } - fmt.Println("\nRun 'pilotctl appstore view ' for full details.") } // installSource tags how a bundle reached the install command. diff --git a/cmd/pilotctl/zz_appstore_cmds_test.go b/cmd/pilotctl/zz_appstore_cmds_test.go index e59b314a..2b6a19bf 100644 --- a/cmd/pilotctl/zz_appstore_cmds_test.go +++ b/cmd/pilotctl/zz_appstore_cmds_test.go @@ -882,10 +882,11 @@ func TestCmdAppStoreDispatcher(t *testing.T) { } // TestCmdAppStoreCatalogueTextOneLinePerApp asserts that text-mode output -// prints exactly one line per app in the form " " (PILOT-405). +// prints one line per app: " view: pilotctl appstore view ". +// When display_name is present, it is used as headline; otherwise description. func TestCmdAppStoreCatalogueTextOneLinePerApp(t *testing.T) { stageCatalogue(t, `{"version":2,"updated_at":"2026-06-17","apps":[ - {"id":"io.pilot.wallet","version":"1.0.0","description":"Manages x402 payment credentials","bundle_url":"https://x/a.tgz","bundle_sha256":"abc"}, + {"id":"io.pilot.wallet","version":"1.0.0","display_name":"Wallet","description":"Manages x402 payment credentials","bundle_url":"https://x/a.tgz","bundle_sha256":"abc"}, {"id":"io.pilot.cosift","version":"0.2.0","description":"Web search and answer agent","bundle_url":"https://x/b.tgz","bundle_sha256":"def"} ]}`) @@ -895,12 +896,14 @@ func TestCmdAppStoreCatalogueTextOneLinePerApp(t *testing.T) { out := captureStdout(t, func() { cmdAppStoreCatalogue(nil) }) - // Each app must appear as a single line containing both id and description. + // Each app must have id, headline (display_name or description), and view pointer. for _, want := range []string{ "io.pilot.wallet", - "Manages x402 payment credentials", + "Wallet", + "view: pilotctl appstore view io.pilot.wallet", "io.pilot.cosift", "Web search and answer agent", + "view: pilotctl appstore view io.pilot.cosift", } { if !strings.Contains(out, want) { t.Errorf("missing %q in catalogue output:\n%s", want, out) @@ -918,8 +921,8 @@ func TestCmdAppStoreCatalogueTextOneLinePerApp(t *testing.T) { if len(appLines) != 2 { t.Errorf("expected 2 app lines, got %d:\n%s", len(appLines), out) } - // Each app line must contain the id and the description on the same line. - if !strings.Contains(appLines[0], "io.pilot.wallet") || !strings.Contains(appLines[0], "Manages x402") { + // First app uses display_name as headline; second falls back to description. + if !strings.Contains(appLines[0], "io.pilot.wallet") || !strings.Contains(appLines[0], "Wallet") { t.Errorf("first app line wrong: %q", appLines[0]) } if !strings.Contains(appLines[1], "io.pilot.cosift") || !strings.Contains(appLines[1], "Web search") { @@ -927,9 +930,9 @@ func TestCmdAppStoreCatalogueTextOneLinePerApp(t *testing.T) { } } -// TestCmdAppStoreCatalogueTextViewPointerHint asserts the view-pointer hint -// appears at the end of text-mode catalogue output (PILOT-405). -func TestCmdAppStoreCatalogueTextViewPointerHint(t *testing.T) { +// TestCmdAppStoreCatalogueTextViewPointer asserts the view pointer appears +// on each app line in text-mode catalogue output (PILOT-405). +func TestCmdAppStoreCatalogueTextViewPointer(t *testing.T) { stageCatalogue(t, `{"version":1,"updated_at":"2026-06-17","apps":[ {"id":"io.pilot.wallet","version":"1.0.0","description":"Manages x402 payment credentials","bundle_url":"https://x/a.tgz","bundle_sha256":"abc"} ]}`) @@ -940,9 +943,9 @@ func TestCmdAppStoreCatalogueTextViewPointerHint(t *testing.T) { out := captureStdout(t, func() { cmdAppStoreCatalogue(nil) }) - const hint = "Run 'pilotctl appstore view ' for full details." - if !strings.Contains(out, hint) { - t.Errorf("view-pointer hint missing from catalogue output:\n%s", out) + const pointer = "view: pilotctl appstore view io.pilot.wallet" + if !strings.Contains(out, pointer) { + t.Errorf("view pointer missing from catalogue output:\n%s", out) } }