# repo mirror list: rename REPO column and --repo flag to NAME

`570854e`→[main](/content/gh/entireio/cli/commits/main/index.html)·

gtrrz-victor·1w ago·2 files·+83 added/-53 removed

Under the `entire repo mirror` group, the column header REPO and the
--repo filter stutter against the group noun. Rename the column to
`NAME (owner/repo)` (inline hint spells out the composite value) and the
filter to --name. --name still matches the whole owner/repo string.

parseSortColumn now accepts a column header with its trailing
parenthetical hint stripped, so the friendly `--sort name` resolves the
NAME column alongside the full `--sort "name (owner/repo)"`.

--json output is unchanged: it serializes the raw coreapi model, not the
column headers, so the wire field stays "repo" and scripts don't break.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

## Sessions

01KX0M0EKRCW08N4FDCHTCDPR8View transcript

## Changes

2

- cmd/entire/cli

- Mrepo_mirror.go+49/-29

- Mrepo_mirror_test.go+34/-24

``` 
20 unmodified lines

// Column header names, the single source of truth for both the table headers
// (mirrorColumns/availableMirrorColumns) and the --sort key switches. parseSort-
// Column returns the canonical header it matched, so the sort switches compare
// against these constants directly.
const (
	colRepo     = "REPO"
	colName     = "NAME (owner/repo)"
	colCloneURL = "CLONE URL"
	colPrivate  = "PRIVATE"
	colAccess   = "ACCESS"
)

// owner/repo name, the clone URL you'd copy, and whether the upstream is
// private. Owner, provider, and cluster aren't columns of their own — they're
// inferable from the owner/repo pair and the clone URL
// (entire://<cluster>/gh/<owner>/<repo>). `--repo` filters on the repo name
// only; owner/provider/cluster stay server-side filters, and the wire model's
// internal ids are dropped. The clone URL is synthesised from the mirror's
// coords (the form `git clone` accepts), since the list API doesn't return it.
var mirrorColumns = []string{colRepo, colCloneURL, colPrivate}

// mirrorPrivate renders the PRIVATE column ("yes"/"no"), shared by the table
// row and the --sort private key so both agree on the cell value.

// parenHint matches a trailing " (...)", qualifier on a column header, e.g. the
// "(owner/repo)" in "NAME (owner/repo)". Stripping it yields the friendly short
// name a caller can type for --sort.
var parenHint = regexp.MustCompile(`\s*\([^)]*\)$`)

// sortKeyOf returns the header with its trailing parenthetical hint removed,
// i.e. the short name accepted by --sort ("NAME (owner/repo)" -> "NAME").
func sortKeyOf(header string) string {
	return parenHint.ReplaceAllString(header, "")
}

// parseSortColumn resolves a --sort spec to the canonical column header it
// names (one of the columns entries) and a direction. It trims first, then
// reads the '-' prefix, so leading/trailing whitespace is handled identically
// on every path (the direction and the column name never disagree). An empty
// spec selects the first column. An unknown name errors naming the valid
// columns. Returning the matched header lets callers switch on the col*
// constants directly.
func parseSortColumn(spec string, columns []string) (col string, desc bool, err error) {
	spec = strings.TrimSpace(spec)
	desc = strings.HasPrefix(spec, "-" )
	return columns[0], desc, nil
}
for _, h := range columns {
	if strings.EqualFold(h, name) {
		if strings.EqualFold(h, name) || strings.EqualFold(sortKeyOf(h), name) {
			return h, desc, nil
		}
	}
}

// sortMirrors orders mirrors in place by the --sort spec: by the named column's
// value ascending (case-insensitive), always breaking ties by owner/repo then
// cluster host so a repo mirrored across clusters (or rows equal on any other
// column) has a stable, deterministic order rather than arbitrary server order.
// A '-' prefix reverses the whole ordering.
func sortMirrors(mirrors []coreapi.Mirror, spec string) error {
	col, desc, err := parseSortColumn(spec, mirrorColumns)
	return nil
}
// filterByRepo keeps items whose repo identifier contains substr (case-//
filterByName keeps items whose owner/repo name contains substr (case-
// insensitive). The control plane already filters by owner/provider/cluster
// server-side but not by repo name, so `repo mirror list --repo` narrows that
// last dimension client-side. repoOf returns the item's displayed identifier —
// the callers pass the owner/repo form shown in the REPO column, so a value
// copied from the table (e.g. acme/web) matches the row it came from.
// An empty substr returns items unchanged.
func filterByRepo[T any](items []T, repoOf func(T) string, substr string) []T {
// server-side but not by name, so `repo mirror list --name` narrows that last
// dimension client-side. nameOf returns the item's displayed identifier —
// the callers pass the owner/repo form shown in the NAME column, so a value
// copied from the table (e.g. acme/web) matches the row it came from.
// An empty substr returns items unchanged.
func filterByName[T any](items []T, nameOf func(T) string, substr string) []T {
	substr = strings.TrimSpace(substr)
	if substr == "" {
		return items
	}
	substr = strings.ToLower(substr)
	out := make([]T, 0, len(items))
	for _, it := range items {
		if strings.Contains(strings.ToLower(repoOf(it)), substr) {
			if strings.Contains(strings.ToLower(nameOf(it)), substr) {
				out = append(out, it)
			}
		}
	}

return out
}
// clone URL), or "owner-only" (a personal repo of another user; only its
// owner may mirror it). No clone URL column: an un-onboarded repo doesn't
// have one yet.
var availableMirrorColumns = []string{colRepo, colAccess, colStatus}
var availableMirrorColumns = []string{colName, colAccess, colStatus}

func availableMirrorRow(m coreapi.AvailableMirror) []string {
	return []string{m.Owner + "/" + m.Repo, string(m.Access), string(m.Status)}
}
