# repo mirror list: split column key from display header

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

gtrrz-victor·1w ago·2 files·+50 added/-48 removed

Replace the parseSortColumn parenthetical-stripping (regexp + sortKeyOf)
with a column{key, header} struct: key is the canonical --sort/match
name, header is the table text. They differ only where the header adds a
display hint the key shouldn't (NAME -> "NAME (owner/repo)"), so --sort
matching is a plain equality on key with no header parsing.

--sort now takes the key ("name"); the full display form is no longer a
second accepted spelling (it never needed to be — it was only a
workaround for overloading one string for both jobs).

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

## Sessions

01KX0MWF385YSK197X1BWAJ6CJView transcript

[?\
Rename Repo Column to Name for ConsistencyClaude Code·1 step](/content/gh/entireio/cli/session/dec0d061-ea6a-4326-b5dd-6ba25b48eed6#timeline-01KX0MWF385YSK197X1BWAJ6CJ/index.html)

## Changes

2

- cmd/entire/cli

- Mrepo_mirror.go+47/-45

- Mrepo_mirror_test.go+3/-3

```go
17 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. The NAME header carries an inline
// "(owner/repo)" hint spelling out what the cell holds; parseSortColumn accepts
// the header with that trailing parenthetical stripped, so `--sort name` works.
const (
	colName     = "NAME (owner/repo)"
	colCloneURL = "CLONE URL"
	colPrivate  = "PRIVATE"
	colAccess   = "ACCESS"
	colStatus   = "STATUS"
)

// column is a table column with two separable identities: key is the canonical
// name a caller types for --sort (and the value parseSortColumn returns, so the
// sort switches compare against these constants directly); header is the text
// shown in the table. They differ only where the header carries a display hint
// the sort key shouldn't — e.g. NAME's inline "(owner/repo)" — which keeps
// --sort matching a simple equality on key with no header parsing.
type column struct {
	key    string
	header string
}

var (
	colName     = column{key: "NAME", header: "NAME (owner/repo)"}
	colCloneURL = column{key: "CLONE URL", header: "CLONE URL"}
	colPrivate  = column{key: "PRIVATE", header: "PRIVATE"}
	colAccess   = column{key: "ACCESS", header: "ACCESS"}
	colStatus   = column{key: "STATUS", header: "STATUS"}
)

// columnHeaders is the display-header view of a column set, for the table/field
// renderers (runCoreList/runCoreObject) which take plain header strings.
func columnHeaders(cols []column) []string {
	h := make([]string, len(cols))
	for i, c := range cols {
		h[i] = c.header
	}
	return h
}

// mirrorColumns is the human table/field view of a mirror: the scannable
// 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
// model's internal ids are dropped. The clone URL is synthesized from the
// mirror's coords (the form `git clone` accepts), since the list API doesn't
// return it.
var mirrorColumns = []column{colName, 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.
func availableMirrorRow(m coreapi.AvailableMirror) []string {
	return []string{m.Owner + "/" + m.Repo, string(m.Access), string(m.Status)}
}

// 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. A spec matches a column by its full header or
// by the header with its trailing parenthetical hint stripped, so both
// `--sort "name (owner/repo)"` and the friendly `--sort name` resolve. An
// unknown name errors naming the valid (short) columns. Returning the matched
// header lets callers switch on the col* constants directly.
func parseSortColumn(spec string, columns []column) (col column, desc bool, err error) {
	spec = strings.TrimSpace(spec)
	desc = strings.HasPrefix(spec, "-")
	name := strings.TrimSpace(strings.TrimPrefix(spec, "-"))
	if name == "" {
		return columns[0], desc, nil
	}
	for _, h := range columns {
		if strings.EqualFold(h.key, name) {
			return h, desc, nil
		}
	}
	valid := make([]string, len(columns))
	for i, h := range columns {
		valid[i] = strings.ToLower(h.key)
	}
	return column{}, false, fmt.Errorf("unknown sort column %q; valid columns: %s", name, strings.Join(valid, ", "))
}
```
