Repository Mirror List Command Enhancement

I'm working on entire repo mirror list command. Right now, it just prints a list with all the visible mirrors by the loged in user. I do need to make that list more usable in terms of making easier to find what you need to find or just explore. Those are the 2 use cases I reckon an user will need to perform over this command. Before starting a proposal, I need to know if we have in code any other command that manages a long list of elements with sort/filter actions on it.
Yes. Codebase has strong precedents. Two tiers:
Best reference — entire search (also checkpoint search)
search_cmd.go + search_tui.go + search/search.go. Richest list UX in repo:
- Filter: flags (
--author,--date,--branch,--repo,--all-repos) AND inline query filters (author:/date:/branch:/repo:) parsed bysearch.ParseSearchInput. Live type-filter tabs in TUI (filteredResults()). - Paging:
--limit/--page, client-side paging in TUI (pageResults,totalPages). - Three-way output branch (search_cmd.go:151-201) — the agent-safe template:
--jsonor non-TTY →writeSearchJSONACCESSIBLEmode →renderSearchStatic- interactive → bubbletea TUI
This is the model if you want browse + live filter.
The scaffold mirror list already sits on — runCoreList[T] (corecmd.go)
repo mirror list (newRepoMirrorListCmd, repo_mirror.go:345) delegates to generic runCoreList[T] → table or --json. Current state:
- Sort: none — server order only.
- Filter: server-side flags only (
--cluster,--provider,--owner,--show-available). - JSON: yes (inherited
--json). - TUI: none.
Natural insertion point = a client-side sort/filter step before printTable in renderCoreList (corecmd.go:168-186), so every control-plane list gets it uniformly. Pairs with runCoreObject (show-by-ID detail).
Other sort/filter examples (weaker)
session list— fixedsort.Sliceby StartedAt desc,--json, no sort flag.activity—sort.Slice+ day-grouping, TUI + static fallback.sortAuthSessionsByRecency(auth.go:578) — clean multi-keysort.Sliceexample to copy.experts --tui— exemplary opt-in TUI + plain-text fallback (cited in CLAUDE.md).
Two divergent directions for your two use-cases
Your use-cases — find (targeted) vs explore (browse) — map onto the two references:
- Find → extend
runCoreListwith client-side--sort+--filter/--grepflags. Minimal, uniform, agent-safe by default. Low effort. - Explore → add a bubbletea TUI like
search, with live filtering/paging + the three-way fallback branch. More work, richer browse.
Before I brainstorm a proposal — want me to design against runCoreList (flags-only, lands in every control-plane list), a search-style TUI (mirror-list-specific browse), or both?
Control-plane lists all sit on runCoreList[T] / runCoreListForCluster. Beyond repo mirror list:
| Command | File | Struct | Empty msg |
|---|---|---|---|
repo mirror list | repo_mirror.go:373 | coreapi.Mirror | "No mirrors found." |
repo mirror list --show-available | repo_mirror.go:354 | coreapi.AvailableMirror | "No repos available to mirror." |
repo mirror collaborators list | repo_mirror_collaborators.go:72 | coreapi.MirrorCollaborator | (per-cluster variant) |
org list | org.go:67 | coreapi.Org | "No organizations found." |
project list | project.go:102 | coreapi.Project | "No projects found." |
repo list | repo.go:166 | coreapi.Repo | "No repositories found in this project." |
grant org members | grant.go:158 | coreapi.Membership | "No members found." |
grant project grants | grant.go:268 | coreapi.ProjectGrant | "No grants found." |
grant repo grants | grant.go:418 | coreapi.RepoGrant | "No grants found." |
9 lists total across 6 command families. All identical shape: table or --json, no sort, filter server-side only.
Detail (show-by-ID) views on runCoreObject: org, project, repo, mirror, repo visibility.
Implication for your work: if you add sort/filter at the renderCoreList layer (corecmd.go:168-186), all 9 lists inherit it for free — one change, uniform behavior. If you build a mirror-specific TUI, only mirror list benefits. That trade-off is the core design fork.
Read it. Here's exactly how entire search filters — and it's not a generic mechanism.
search has two filter paths, both narrow
1. Known-key filters → server-side (search.go)
ParseSearchInput scans the query for hardcoded prefixes — author:, date:, branch:, repo: — pulls them into typed Config fields (Author, Date, Branch, Repos), and the server does the filtering. HasFilters() just checks those fixed fields. Also exposed as cobra flags (--author, --date…). The keys are enumerated in code, one branch per key — not derived from data.
2. Type-tab facet → client-side (search_tui.go:145 filteredResults())
In the TUI only, filters already-loaded results by a single fixed dimension — r.Type equals checkpoint/commit/session. Equality on one hardcoded field. That's the entire client-side filter.
What this means for your 9 lists
search's model is "enumerate the filterable keys per command, filter server-side." Your control-plane lists already do exactly that — repo mirror list has --cluster, --provider, --owner, all server-side. So copying search = adding more typed server flags to each command individually. That is not a one-change-hits-all-9 win; it's per-command work, and it depends on the server supporting each filter.
The uniform win — filter all 9 lists with one edit — only exists at the renderCoreList layer, filtering the rendered row cells client-side. search does not do this (its client-side filter is just the coarse type facet).
So the real fork is:
| Approach | Coverage | Server dependency | Models after |
|---|---|---|---|
A. Client-side cell filter in renderCoreList | all 9 lists, one edit | none — filters what's fetched | nothing existing (new, but tiny) |
B. search-style typed server flags | per-command, incremental | needs server support per key | search, existing mirror flags |
"Filter all those commands at once" points at A. Want A, or per-command server-side flags like search, or a hybrid (A now, add typed server flags where the API supports it later)?
quick question, does the backend that list the repo mirror list allows server filtering ?
Yes — but narrowly, and already fully wired.
ListMirrorsParams (oas_parameters_gen.go:136) supports exactly three server-side filters:
Cluster— exact, case-sensitiveProvider— case-insensitiveOwner— case-insensitive (upstream owner login)
Plus paging (PageSize, PageToken). No free-text search, no sort. All three are already exposed as --cluster/--provider/--owner (repo_mirror.go:410-412). So there's nothing left to add server-side for mirrors — the backend's filter surface is maxed out.
Two things worth noting for "improve filter across all 9":
- Server filters are per-endpoint and inconsistent. Right below,
ListOIDCProvidersParamsandListOrgMembersParamshave onlyPageSize/PageToken— zero filters. So there is no uniform server-side filter to lean on across the 9 lists; each endpoint differs. - Mirror's server filters are exact-match, single-value (e.g.
--owner acme= only that owner). No substring, no "contains", no multi-value.
So the gap a client-side cell filter fills, even for mirrors: substring/fuzzy matching, matching across columns at once (e.g. one term hitting name OR owner OR cluster), and — critically — a consistent filter that works identically on all 9 lists regardless of what each backend supports. Server-side can't give you that uniformity.
Want me to proceed planning the client-side cell filter at renderCoreList (approach A)?
lets be first focus on improving repo mirror list as this list might get quite long soon, so, it's best to filter on server side than having filter options on all the commands.
What would you like to clarify? Ask away.
For context while you think: the exploration confirmed the CLI is client-only by design (ogen.yml disables the server half) — the listMirrors filtering lives in the entiredb backend repo. So any server-side filter change is a backend ticket first, then a small CLI wire-up here. My two pending questions were (1) what filter to add (free-text q over name+owner, a repo-name filter, or both) and (2) how to handle the backend/CLI split in the plan. Tell me what's unclear or what you'd rather decide differently.
Done. Summary:
Branch: feat/mirror-list-filter-sort (off main).
What shipped — client-side --filter + --sort on entire repo mirror list:
--filter cli→ any column containscli;--filter repo=cli→ scoped to REPO column. Case-insensitive substring.--sort private→ by column;-repo→ descending; default = repo-name ascending (list now deterministic).- Both apply to table,
--json, and--show-available. Unknown column errors, naming valid columns.
Files:
corecmd.go— new generic helpersfilterRows[T],sortRows[T],headerIndex,rowMatches,cellAt. Reusable across all 9 control-plane lists; wired only to mirror list per KISS.repo_mirror.go— two flags + filter-then-sort in both list closures.corecmd_test.go+repo_mirror_test.go— unit + command-level tests (TDD-first).
Verification:
go test ./cmd/entire/cli/→ ok (29s, full package).- golangci-lint v2.11.3 (mise-pinned) → 0 issues;
go vetclean. - Help renders both flags.
KISS applied: dropped the planned repeatable/ANDed filters, rowFilter slice, matchesAll, and index-permutation sort — single-clause filter + plain sort.SliceStable.
Two caveats:
mise run lintfails locally — a stray golangci-lint v1 in the go bindir shadows the pinned v2 (mise whichresolves the v1). Env issue, not code; CI's pinned v2 passes. Want me to look at fixing the shadowing?- Client-side = still fetches all pages via
fetchAllPages, then filters. Fine for realistic mirror counts; true server-side scaling needs the entiredb backend change (out of this repo), as noted in the plan.
Not committed yet — say the word to commit/push/PR.
Re-added. Both KISS-dropped pieces are back:
1. Repeatable, ANDed --filter (corecmd.go)
--filteris nowStringArrayVar— repeatable, all clauses must match (AND).rowFilterstruct (col index + term) +matchesAllhelper; column index resolved once at parse, not per row.- Example:
--filter repo=acme --filter private=yes.
2. Precomputed-key permutation sort (sortRows)
- Sort key computed once per item (
row()called n times, not O(n log n)); a stable index permutation is sorted against the keys, then items reordered.
Tests added: multiple clauses AND together (unit) + repeatable --filter clauses AND together (command-level).
Verification:
- Filter/sort tests → ok; full package → ok (28s).
gofmtclean; golangci-lint v2.11.3 → 0 issues.
Still on branch feat/mirror-list-filter-sort, uncommitted. Ready to commit/push/PR on your go.
Script ready and executable:
Run it:
What it does — builds a throwaway binary, checks you're logged in, then walks 11 steps, each printing the command + an EXPECT line:
- Baseline (default repo-name sort)
- Bare
--filter(any column) - Scoped
--filter repo=… - Repeatable
--filter(ANDed) --sort private--sort -repo(descending)--filter … --json- No-match
--json→[]notnull - Filter + sort under
--show-available - Error: unknown filter column
- Error: unknown sort column
Two things to tweak before running: it uses placeholder substrings (a, e, private=no) — swap them for strings that actually appear in your mirrors so steps 2–9 return rows. Requires an active login (! entire login) and ≥1 mirror; steps 10–11 (error cases) run after a successful fetch, so they still need the backend reachable.
Want me to also add a --cluster/--provider combined-with-filter step, or is this enough?
Published — entire in ~/go/bin now has the filter/sort changes. Test directly:
No need for the temp-binary script now; run the real command against your mirrors.
great, while sorting, we might have multiple entries for the same repo, If that happens I do also want to filter by cluster host
Done. CLUSTER is now a first-class column.
Change: mirrorColumns → REPO, CLUSTER, CLONE URL, PRIVATE (cluster placed right after repo to group duplicates visually). mirrorRow emits m.ClusterHost. This flows to both the list and mirror get views (shared columns).
What you get:
- Duplicate repos across clusters are now visually distinguishable (CLUSTER column).
--filter cluster=aws— substring, case-insensitive (softer than the exact-match server--cluster).--sort cluster— order the same-repo rows by cluster.- Combine:
--filter repo=web --sort cluster.
Tests: updated TestMirrorRow for the 4-column row; added a duplicate-repo subtest (filter + sort by cluster).
Verified: affected + full package tests pass (27s); gofmt clean; golangci-lint v2.11.3 → 0 issues.
Note: --show-available is unchanged — available repos are cluster-agnostic (GitHub-only), so no CLUSTER column there.
Re-publish to try it: ! mise run dev:publish, then entire repo mirror list --filter cluster=<substr> --sort cluster.
Published. Try it:
You'll now see the CLUSTER column disambiguating any repo mirrored on multiple clusters.
Do not display host as column, it is implicit into clone url. it is orderer des? whereas the repo is asc ?
Done — both changes in.
CLUSTER column removed. Back to REPO, CLONE URL, PRIVATE. Cluster still filterable via bare --filter aws (it's in the clone URL); --filter cluster= / --sort cluster are gone (would error as unknown column, as intended).
Default sort now: repo asc, then cluster asc. New sortMirrorsDefault breaks duplicate-repo ties by cluster host, so a repo mirrored on several clusters is deterministic instead of arbitrary server order — that fixes the "desc/random" you saw. Explicit --sort <col> still uses the generic single-key stable sort.
Tests: reverted TestMirrorRow to 3 columns; replaced the cluster subtests with (1) cluster-filterable-via-clone-URL and (2) duplicate-repo tiebreak ordering.
Verified: affected + full package pass (29s); gofmt clean; golangci-lint v2.11.3 → 0 issues.
Re-publish to try: ! mise run dev:publish, then entire repo mirror list (watch duplicate repos order by cluster) and entire repo mirror list --filter aws.
after playing with it, I notice we have server side filters like, cluster, owner and provider. I would replace out new --filter with just --repo which is what is missing, and do that on client side as server does not implement it. And also, display the cluster/owner./provider columns into the table.
Done. Reworked per your usage findings.
Filter: dropped the generic --filter; added --repo — client-side case-insensitive substring on the repo name, the one dimension the server lacks. Server keeps handling --cluster/--owner/--provider. Applies to existing + available mirrors, and to --json.
Columns: OWNER, REPO, PROVIDER, CLUSTER, CLONE URL, PRIVATE — the server-side filter fields (owner/provider/cluster) are now visible, so what you filtered on shows up, and cluster is sortable.
Sort: kept --sort (now covers owner/provider/cluster too); default stays repo-asc with cluster-asc tiebreak for duplicates.
Cleanup (KISS): removed the generic filterRows/rowFilter/matchesAll/rowMatches machinery and its tests; new filterByRepo[T] is a one-field helper shared by both mirror paths.
Verified: affected + full package pass (28s); gofmt clean; golangci-lint v2.11.3 → 0 issues.
Re-publish and try:
Want me to commit this now? (Branch feat/mirror-list-filter-sort, still uncommitted.)
Published. Try:
Ready to commit when you are — branch feat/mirror-list-filter-sort, still uncommitted.
I do like more the old way to print the repo which is, owner/repo. Lets delete the owner,provider and cluster columns as they all are infered from repo and clone url columns
Done. Back to the original 3 columns.
Table: REPO (owner/repo), CLONE URL, PRIVATE. Dropped OWNER/PROVIDER/CLUSTER — all inferable from the owner/repo pair and the clone URL.
Kept: --repo (client-side repo-name substring), --sort, and the duplicate tiebreak. Fixed an inconsistency along the way — default sort now orders by owner/repo (matching the REPO column and --sort repo) with cluster as the tiebreaker, instead of repo-name-only.
Note: --sort cluster no longer works (cluster isn't a column); cluster still shows in the clone URL and duplicates still order by it under the default sort.
Verified: affected + full package pass (28s); gofmt clean; golangci-lint v2.11.3 → 0 issues.
Re-publish: ! mise run dev:publish, then entire repo mirror list --repo cli.
Ready to commit whenever — branch feat/mirror-list-filter-sort, still uncommitted.
Could we use bubbletea to make the repo name copiable? I want to, if the user clicks on it copy into the clipboard the entire repo clone <url>
Done — interactive click-to-copy shipped.
Behavior: on an interactive terminal, entire repo mirror list now opens a bubbletea list. Click a row (or ↑/↓ + Enter/Space/c) to copy that mirror's entire repo clone entire://… command to the clipboard; q/Esc quits. Piped, --json, ACCESSIBLE, and --show-available all still render the plain table (agent-safe fallback preserved).
Files:
repo_mirror_tui.go—mirrorCopyModel(v2View+MouseMode, mouse-Y→row mapping, scroll windowing),cloneCommandForMirror, injectablecopyFnseam overatotto/clipboard, graceful copy-failure status.repo_mirror.go— extracted sharedlistExisting(fetch +--repofilter + sort), branches to the TUI whenIsTerminalWriter && !json && !accessible.repo_mirror_tui_test.go— model tests via injected clipboard + constructed mouse/window messages (no real clipboard, no TTY).atotto/clipboardpromoted to a direct dep.
Verified: full package tests pass (28s); gofmt clean; golangci-lint v2.11.3 → 0 issues.
Caveats worth knowing:
- While the TUI is open the terminal's native drag-to-select is captured by mouse mode — expected for interactive TUIs.
- Clipboard needs
pbcopy(present on macOS); over bare SSH it may fail, in which case you'll seecopy failed: …in the status line instead of a crash. - Mouse-click can't be unit-tested end-to-end without a TTY — the model logic is tested, but please smoke-test the actual click after publishing.
Re-publish and try:
Ready to commit whenever — branch feat/mirror-list-filter-sort, uncommitted.
Published. Smoke-test the click in a real terminal:
Click a row (or ↑/↓ + Enter) → should copy entire repo clone entire://… and show ✓ Copied: … in the status line. q to quit. Then paste to confirm.
Let me know how the click behaves — ready to commit once you're happy.
TUI/clipboard feature fully reverted — files deleted, go.mod restored (clipboard back to indirect), no leftover imports.
This ticket's diff (4 files):
corecmd.go—sortRows,headerIndex,cellAthelpers.repo_mirror.go—--repo(client-side repo-name filter),--sort,sortMirrorsDefault(owner/repo asc, cluster tiebreak),filterByRepo.- test files — coverage for the above.
Columns stayed at REPO, CLONE URL, PRIVATE.
Verified after revert: gofmt clean; golangci-lint v2.11.3 → 0 issues; full package tests pass (28s).
Click-to-copy is parked for a future ticket. Want me to jot a quick note somewhere (issue/TODO) so it's not lost, or just leave it? And ready to commit this branch whenever.
Published — back to the plain table with --repo + --sort, no TUI.
Ready to commit feat/mirror-list-filter-sort whenever. Want me to capture the click-to-copy idea as a GitHub issue before we move on, or leave it?
Base directory for this skill: /Users/gtrrz-victor/.claude/skills/ship
Ship Changes
Commit current changes, push to remote, and update the PR.
Step 1: Analyze changes
- Run
git diff --statandgit diffto understand what changed - Run
git statusto see untracked files - Run
git log --oneline -3to match the repo's commit message style
Step 2: Commit
- Stage only the relevant changed/untracked files by name (never
git add -Aorgit add .) - Write a concise commit message that focuses on the "why" not the "what"
- If `` is provided, use it as the commit message
- Otherwise, draft one from the diff
- Commit. If hooks fail, diagnose and fix — do NOT use
--no-verify
Step 3: Push
- Check if the branch tracks a remote:
git rev-parse --abbrev-ref --symbolic-full-name @{u} - If not tracking, push with
-u origin <branch> - If tracking, just
git push - If push is rejected (remote has new commits), pull with rebase first then push
Step 4: Update PR
- Find the open PR for the current branch:
gh pr view --json number,body,title - If no PR exists, skip this step and inform the user
- Compare the current PR body against the new changes:
- If the changes are within the existing PR scope, update the description to reflect the current state
- If the changes are additive or a follow-up, add a PR comment summarizing what was just pushed instead
- When updating the PR body, preserve the overall structure but refresh the summary bullets and test plan
- When adding a comment, keep it concise — bullet points of what changed and why
- Before making any PR update (description edit or comment), show the user the exact text you plan to post and ask for their approval. Do NOT post until they confirm.
Rules
- Never commit files that look like secrets (.env, credentials, tokens)
- Never force push
- Always show the user what was committed and the PR URL when done
Committed and pushed. No PR exists yet — per the skill I'll create one, but here's the exact title/body I'd post first. Approve or edit:
Title: repo mirror list: add --repo filter and --sort
Body:
Want me to create the PR with this (against main), tweak the text first, or hold off?
Shipped. 🚀
- Commit:
088121703 repo mirror list: add --repo filter and --sort - Pushed:
feat/mirror-list-filter-sort→ origin - PR: https://github.com/entireio/cli/pull/1665 (against
main)
Click-to-copy is parked. Want me to open a follow-up issue for it, or leave that to you?
Shipped.
- Commit:
c40e894d9 repo mirror list: simplify --repo flag help text - Pushed to
feat/mirror-list-filter-sort→ PR #1665 updated automatically.
Change is within the existing PR scope (help-text wording), so the description stays accurate — no PR edit needed. Lint clean.
Checked usage. Verdict: needed, but misplaced.
sortRows— only caller isrepo mirror list(both branches). It powers--sort.headerIndex+cellAt— used only insidesortRows. Nothing else.
So they're not dead — --sort breaks without them. But they're generic helpers in corecmd.go with a single command using them. That's speculative reuse (YAGNI): no other control-plane list sorts today. Per DRY-before-second-call-site, they'd be better co-located in repo_mirror.go next to filterByRepo/sortMirrorsDefault until a second list needs sorting.
Two clean options:
- Move
sortRows/headerIndex/cellAt→repo_mirror.go. Locality;corecmd.gostays lean. Recommended. - Keep in
corecmd.go— only if you expect to add--sorttoorg list/project list/repo list/grantsoon (then they're the shared base).
Third angle if you want it leaner still: --sort is only on mirror list, and sortMirrorsDefault already sorts mirrors — a mirror-specific sort could replace sortRows entirely and drop all three generics. But that loses arbitrary-column sort (--sort private).
Which way — move to repo_mirror.go, keep generic, or drop --sort to mirror-specific?
Shipped.
- Commit:
110a063fd repo mirror list: co-locate and simplify sortRows - Pushed → PR #1665 updated.
Net result: corecmd.go untouched vs main — the whole ticket now lives in repo_mirror.go + its test. sortRows simplified to a plain stable sort (no permutation opt, no headerIndex/cellAt helpers).
Verified: lint (v2) 0 issues; full package tests pass (28s).
PR description still accurate (no user-facing behavior change), so no edit needed. https://github.com/entireio/cli/pull/1665
Pulled state is green — lint 0 issues, mirror sort/filter tests pass. The autofix is sound: --sort repo/-repo now share the default's cluster tiebreak (consistent), and filterByRepo is nil-safe for JSON.
One gap worth noting: the autofix added no test for the behavior it changed — nothing proves --sort repo now breaks duplicate-repo ties by cluster (the consistency it fixed). If a future refactor reverts --sort repo to sortRows, tests stay green but the finding regresses.
Want me to add a small regression test (a duplicate-repo fixture asserting --sort repo orders by cluster within the tie, and --sort -repo reverses it), then ship? Or leave it as-is since the PR finding is resolved.
Shipped.
- Commit:
82c22d5a9 repo mirror list: regression-test --sort repo cluster tiebreak - Pushed → PR #1665.
The autofix's behavior is now locked in by a test: a repo mirrored on two clusters orders aws before eu under --sort repo, and reverses under -repo. If anyone reverts --sort repo to a plain single-key sort, this fails.
Verified: gofmt clean; golangci-lint v2 → 0 issues; full package tests pass (30s).
(Also noticed a second 0e3f8aad9 "Potential fix for pull request finding" in the log — there were two autofix commits. Both are in and green.)
Published. PR #1665 is green and up to date — --repo filter + --sort with the cluster-tiebreak consistency now covered by tests.
Anything else, or are we good to merge?
Before I branch — two things to pin down:
Base: you want the new branch off the current feat/mirror-list-filter-sort HEAD (so it includes the --repo/--sort work), not off main. Correct? Note: if this PR gets squash-merged later, the new branch will carry these commits until you rebase onto the merged main — usually fine, just flagging.
What's the feature? Is it the parked click-to-copy TUI, or something new? Tell me what it does so I can name the branch and start.
I do want to merge entire repo mirror and entire repo mirror --show-available into just entire repo mirror with more filters, so we display same data
Branch feat/mirror-list-unified created off the current work. Now let me brainstorm the design properly.
Base directory for this skill: /Users/gtrrz-victor/.claude/plugins/cache/claude-plugins-official/superpowers/6.1.1/skills/brainstorming
Brainstorming Ideas Into Designs
Help turn ideas into fully formed designs and specs through natural collaborative dialogue.
Start by understanding the current project context, then ask questions one at a time to refine the idea. Once you understand what you're building, present the design and get user approval.
<HARD-GATE> Do NOT invoke any implementation skill, write any code, scaffold any project, or take any implementation action until you have presented a design and the user has approved it. This applies to EVERY project regardless of perceived simplicity. </HARD-GATE>Anti-Pattern: "This Is Too Simple To Need A Design"
Every project goes through this process. A todo list, a single-function utility, a config change — all of them. "Simple" projects are where unexamined assumptions cause the most wasted work. The design can be short (a few sentences for truly simple projects), but you MUST present it and get approval.
Checklist
You MUST create a task for each of these items and complete them in order:
- Explore project context — check files, docs, recent commits
- Offer the visual companion just-in-time — NOT upfront. The first time a question would genuinely be clearer shown than described, offer it then (its own message); on approval its browser tab opens for you. If no visual question ever arises, never offer it. See the Visual Companion section below.
- Ask clarifying questions — one at a time, understand purpose/constraints/success criteria
- Propose 2-3 approaches — with trade-offs and your recommendation
- Present design — in sections scaled to their complexity, get user approval after each section
- Write design doc — save to
docs/superpowers/specs/YYYY-MM-DD-<topic>-design.mdand commit - Spec self-review — quick inline check for placeholders, contradictions, ambiguity, scope (see below)
- User reviews written spec — ask user to review the spec file before proceeding
- Transition to implementation — invoke writing-plans skill to create implementation plan
Process Flow
The terminal state is invoking writing-plans. Do NOT invoke frontend-design, mcp-builder, or any other implementation skill. The ONLY skill you invoke after brainstorming is writing-plans.
The Process
Understanding the idea:
- Check out the current project state first (files, docs, recent commits)
- Before asking detailed questions, assess scope: if the request describes multiple independent subsystems (e.g., "build a platform with chat, file storage, billing, and analytics"), flag this immediately. Don't spend questions refining details of a project that needs to be decomposed first.
- If the project is too large for a single spec, help the user decompose into sub-projects: what are the independent pieces, how do they relate, what order should they be built? Then brainstorm the first sub-project through the normal design flow. Each sub-project gets its own spec → plan → implementation cycle.
- For appropriately-scoped projects, ask questions one at a time to refine the idea
- Prefer multiple choice questions when possible, but open-ended is fine too
- Only one question per message - if a topic needs more exploration, break it into multiple questions
- Focus on understanding: purpose, constraints, success criteria
Exploring approaches:
- Propose 2-3 different approaches with trade-offs
- Present options conversationally with your recommendation and reasoning
- Lead with your recommended option and explain why
Presenting the design:
- Once you believe you understand what you're building, present the design
- Scale each section to its complexity: a few sentences if straightforward, up to 200-300 words if nuanced
- Ask after each section whether it looks right so far
- Cover: architecture, components, data flow, error handling, testing
- Be ready to go back and clarify if something doesn't make sense
Design for isolation and clarity:
- Break the system into smaller units that each have one clear purpose, communicate through well-defined interfaces, and can be understood and tested independently
- For each unit, you should be able to answer: what does it do, how do you use it, and what does it depend on?
- Can someone understand what a unit does without reading its internals? Can you change the internals without breaking consumers? If not, the boundaries need work.
- Smaller, well-bounded units are also easier for you to work with - you reason better about code you can hold in context at once, and your edits are more reliable when files are focused. When a file grows large, that's often a signal that it's doing too much.
Working in existing codebases:
- Explore the current structure before proposing changes. Follow existing patterns.
- Where existing code has problems that affect the work (e.g., a file that's grown too large, unclear boundaries, tangled responsibilities), include targeted improvements as part of the design - the way a good developer improves code they're working in.
- Don't propose unrelated refactoring. Stay focused on what serves the current goal.
After the Design
Documentation:
- Write the validated design (spec) to
docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md- (User preferences for spec location override this default)
- Use elements-of-style:writing-clearly-and-concisely skill if available
- Commit the design document to git
Spec Self-Review: After writing the spec document, look at it with fresh eyes:
- Placeholder scan: Any "TBD", "TODO", incomplete sections, or vague requirements? Fix them.
- Internal consistency: Do any sections contradict each other? Does the architecture match the feature descriptions?
- Scope check: Is this focused enough for a single implementation plan, or does it need decomposition?
- Ambiguity check: Could any requirement be interpreted two different ways? If so, pick one and make it explicit.
Fix any issues inline. No need to re-review — just fix and move on.
User Review Gate: After the spec review loop passes, ask the user to review the written spec before proceeding:
"Spec written and committed to
<path>. Please review it and let me know if you want to make any changes before we start writing out the implementation plan."
Wait for the user's response. If they request changes, make them and re-run the spec review loop. Only proceed once the user approves.
Implementation:
- Invoke the writing-plans skill to create a detailed implementation plan
- Do NOT invoke any other skill. writing-plans is the next step.
Key Principles
- One question at a time - Don't overwhelm with multiple questions
- Multiple choice preferred - Easier to answer than open-ended when possible
- YAGNI ruthlessly - Remove unnecessary features from all designs
- Explore alternatives - Always propose 2-3 approaches before settling
- Incremental validation - Present design, get approval before moving on
- Be flexible - Go back and clarify when something doesn't make sense
Visual Companion
A browser-based companion for showing mockups, diagrams, and visual options during brainstorming. Available as a tool — not a mode. Accepting the companion means it's available for questions that benefit from visual treatment; it does NOT mean every question goes through the browser.
Offering the companion (just-in-time): Do NOT offer it upfront. Wait until a question would genuinely be clearer shown than told — a real mockup / layout / diagram question, not merely a UI topic. The first time that happens, offer it then, as its own message:
"This next part might be easier if I show you — I can put together mockups, diagrams, and comparisons in a browser tab as we go. It's still new and can be token-intensive. Want me to? I'll open it for you."
This offer MUST be its own message. Only the offer — no clarifying question, summary, or other content. Wait for the user's response. If they accept, start the server with --open so their browser opens to the first screen automatically. If they decline, continue text-only and don't offer again unless they raise it.
Per-question decision: Even after the user accepts, decide FOR EACH QUESTION whether to use the browser or the terminal. The test: would the user understand this better by seeing it than reading it?
- Use the browser for content that IS visual — mockups, wireframes, layout comparisons, architecture diagrams, side-by-side visual designs
- Use the terminal for content that is text — requirements questions, conceptual choices, tradeoff lists, A/B/C/D text options, scope decisions
A question about a UI topic is not automatically a visual question. "What does personality mean in this context?" is a conceptual question — use the terminal. "Which wizard layout works better?" is a visual question — use the browser.
If they agree to the companion, read the detailed guide before proceeding:
skills/brainstorming/visual-companion.md
What would you like to clarify about the STATUS column? (Or tell me directly what you want it to show — e.g. the mirror health for placements, or a coarse mirrored/available/owner-only.)
owner-only comes from the /mirrors/available status enum. It means: a repo in another user's personal account that you can see but only its owner may mirror.
From the code's own description (availableMirrorColumns comment + the OpenAPI enum doc):
available— you can onboard it (entire repo mirror create)mirrored— already mirroredowner-only— a personal repo of another user; only that owner can mirror it, so you can't onboard it even though it's visible
Practically: it's a "visible but not actionable by you" repo — GitHub personal-account repos where mirroring is restricted to the account owner (org repos with sufficient access are available instead).
Back to the question: should STATUS show the mirror health (ready/processing/failed/suspended) for mirrored placements and available/owner-only for the rest — or a coarse mirrored/available/owner-only?
That's enough to design it. Here's the proposed design — review section by section.
1. Command & scope
entire repo mirror list becomes a single unified view. --show-available is removed. (The AvailableMirror model and /available calls stay — the create wizard still uses them; only the list command's separate branch goes.) Assumption: this is the list subcommand, not bare entire repo mirror (which stays the command group). Correct me if you meant bare mirror.
2. Columns (fixed order)
REPO · PRIVATE · ACCESS · STATUS · URL — one row per placement; un-mirrored repos are one row with empty URL (and STATUS = available/owner-only). ACCESS is blank for a placement you can see but have no GitHub access to.
3. Data flow (every invocation calls both endpoints)
GET /mirrors/available→ per-repoaccess,private, availability (available/mirrored/owner-only).GET /mirrors(with server filters--owner/--cluster/--provider) → per-(repo,cluster) clone URL,provider, lifecyclestatus.- Full outer join on (owner, repo) into unified rows:
- repo with placements → one row per cluster:
STATUS=lifecycle,URL=clone URL,ACCESSfrom/available(blank if absent). - repo with no placement → one row:
STATUS=available/owner-only,URLempty.
- repo with placements → one row per cluster:
4. Filters (all AND)
- Carried over:
--repo(client substring),--owner/--cluster/--provider(server-side on/mirrors; when--cluster/--providerset, un-mirrored rows are dropped since they have no placement). - New (client-side on joined rows):
--status <available|owner-only|mirrored|ready|processing|failed|suspended>(mirrored= any placement),--access <read|write|admin>,--private/--public.
5. Sort, JSON, fallback
- Default sort: repo asc, tiebreak by URL/cluster asc (deterministic, like today).
--sortworks over the 5 columns via existingsortRows. --json: array of unified row objects (owner,repo,private,access,status,clusterHost,url). Non-TTY/--json/ACCESSIBLE= plain output (agent-safe), same as now.
Does this match what you have in mind? Flag anything to change and I'll adjust, then write the spec.
The spec is written and self-reviewed (I made the availableMirror* removal definitive). Note: docs/superpowers is gitignored in this repo, so the spec stays a local working doc rather than a commit — that's the repo's intent, not an error.
Spec: docs/superpowers/specs/2026-07-07-unified-mirror-list-design.md
Please review it and tell me if you want any changes before I turn it into an implementation plan. Key decisions captured:
listunified;--show-availableremoved (unknown-flag error)- Columns
REPO · PRIVATE · ACCESS · STATUS · URL, one row per placement - Full outer join of
/mirrors+/mirrors/availableon(owner, repo) - Filters: existing
--repo/--owner/--cluster/--provider+ new--status/--access/--private|--public - Always calls live-GitHub
/available(slower, accepted) ListAvailableMirrors/AvailableMirrorstay (create wizard + the join); only list-specificavailableMirrorColumns/availableMirrorRowdeleted
Approve as-is, or want edits?
Base directory for this skill: /Users/gtrrz-victor/.claude/plugins/cache/claude-plugins-official/superpowers/6.1.1/skills/writing-plans
Writing Plans
Overview
Write comprehensive implementation plans assuming the engineer has zero context for our codebase and questionable taste. Document everything they need to know: which files to touch for each task, code, testing, docs they might need to check, how to test it. Give them the whole plan as bite-sized tasks. DRY. YAGNI. TDD. Frequent commits.
Assume they are a skilled developer, but know almost nothing about our toolset or problem domain. Assume they don't know good test design very well.
Announce at start: "I'm using the writing-plans skill to create the implementation plan."
Context: If working in an isolated worktree, it should have been created via the superpowers:using-git-worktrees skill at execution time.
Save plans to: docs/superpowers/plans/YYYY-MM-DD-<feature-name>.md
- (User preferences for plan location override this default)
Scope Check
If the spec covers multiple independent subsystems, it should have been broken into sub-project specs during brainstorming. If it wasn't, suggest breaking this into separate plans — one per subsystem. Each plan should produce working, testable software on its own.
File Structure
Before defining tasks, map out which files will be created or modified and what each one is responsible for. This is where decomposition decisions get locked in.
- Design units with clear boundaries and well-defined interfaces. Each file should have one clear responsibility.
- You reason best about code you can hold in context at once, and your edits are more reliable when files are focused. Prefer smaller, focused files over large ones that do too much.
- Files that change together should live together. Split by responsibility, not by technical layer.
- In existing codebases, follow established patterns. If the codebase uses large files, don't unilaterally restructure - but if a file you're modifying has grown unwieldy, including a split in the plan is reasonable.
This structure informs the task decomposition. Each task should produce self-contained changes that make sense independently.
Task Right-Sizing
A task is the smallest unit that carries its own test cycle and is worth a fresh reviewer's gate. When drawing task boundaries: fold setup, configuration, scaffolding, and documentation steps into the task whose deliverable needs them; split only where a reviewer could meaningfully reject one task while approving its neighbor. Each task ends with an independently testable deliverable.
Bite-Sized Task Granularity
Each step is one action (2-5 minutes):
- "Write the failing test" - step
- "Run it to make sure it fails" - step
- "Implement the minimal code to make the test pass" - step
- "Run the tests and make sure they pass" - step
- "Commit" - step
Plan Document Header
Every plan MUST start with this header:
Task Structure
No Placeholders
Every step must contain the actual content an engineer needs. These are plan failures — never write them:
- "TBD", "TODO", "implement later", "fill in details"
- "Add appropriate error handling" / "add validation" / "handle edge cases"
- "Write tests for the above" (without actual test code)
- "Similar to Task N" (repeat the code — the engineer may be reading tasks out of order)
- Steps that describe what to do without showing how (code blocks required for code steps)
- References to types, functions, or methods not defined in any task
Remember
- Exact file paths always
- Complete code in every step — if a step changes code, show the code
- Exact commands with expected output
- DRY, YAGNI, TDD, frequent commits
Self-Review
After writing the complete plan, look at the spec with fresh eyes and check the plan against it. This is a checklist you run yourself — not a subagent dispatch.
1. Spec coverage: Skim each section/requirement in the spec. Can you point to a task that implements it? List any gaps.
2. Placeholder scan: Search your plan for red flags — any of the patterns from the "No Placeholders" section above. Fix them.
3. Type consistency: Do the types, method signatures, and property names you used in later tasks match what you defined in earlier tasks? A function called clearLayers() in Task 3 but clearFullLayers() in Task 7 is a bug.
If you find issues, fix them inline. No need to re-review — just fix and move on. If you find a spec requirement with no task, add the task.
Execution Handoff
After saving the plan, offer execution choice:
"Plan complete and saved to docs/superpowers/plans/<filename>.md. Two execution options:
1. Subagent-Driven (recommended) - I dispatch a fresh subagent per task, review between tasks, fast iteration
2. Inline Execution - Execute tasks in this session using executing-plans, batch execution with checkpoints
Which approach?"
If Subagent-Driven chosen:
- REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development
- Fresh subagent per task + two-stage review
If Inline Execution chosen:
- REQUIRED SUB-SKILL: Use superpowers:executing-plans
- Batch execution with checkpoints for review
Plan written and self-reviewed — docs/superpowers/plans/2026-07-07-unified-mirror-list.md (gitignored, so it's a local working doc, same as the spec).
Three TDD tasks, each with failing test → implement → verify → commit:
- Unified model +
joinMirrors(full-outer-join, pure, unit-tested) filterUnified+sortUnifiedDefault(status/access/private filters, default sort — unit-tested)- Rewire the
listcommand — fetch both endpoints, join, filter, sort, render; remove--show-availableand the deadavailableMirror*/sortMirrorsDefaulthelpers; full command-level tests
Two execution options:
- Subagent-Driven (recommended) — I dispatch a fresh subagent per task, review between tasks, fast iteration.
- Inline Execution — I execute the tasks in this session with checkpoints for your review.
Which approach?