Enhance Entire CLI with Name Resolution
take a look at this repo, and the ../entiredb repo. we build our public-facing cli here (entire), but we still have some dangling internal tools in ../entiredb (like cmd/entire-*)
have a look at the verbs defined in both repos, and tell me which ones we can clean up out of ../entiredb, since they should start living in the public cli. don't move new ones to public cli, just tell me which ones already live here.
Bottom line
Two clusters in entire-core are already dead deprecation stubs pointing at the public CLI — safe to delete now. Everything else in entiredb's entire-* tools is either platform-admin/break-glass (no public equivalent) or data-plane git ops (no public equivalent), so leave those.
Delete from entiredb — already live in public entire
| entiredb verb | Public CLI home | Status in entiredb |
|---|---|---|
entire-core context list | entire auth contexts | deprecation stub |
entire-core context use | entire auth use | deprecation stub |
entire-core context bind | entire auth use | deprecation stub |
entire-core context current | entire auth contexts | deprecation stub |
entire-core admin mirrors collaborators add | entire repo mirror collaborators add | deprecation stub |
entire-core admin mirrors collaborators list | entire repo mirror collaborators list | deprecation stub |
entire-core admin mirrors collaborators remove | entire repo mirror collaborators remove | deprecation stub |
These already print "DEPRECATED. Please use entire …" — the public command is the canonical home. Pure cleanup, no behavior loss.
Also already migrated (no stub left, just a code comment): entire-core client orgs / projects / grants → entire org / entire project / entire grant. Nothing to delete; just confirms the pattern.
Keep in entiredb — NOT in the public CLI (don't move)
entire-core admincore:orgs(list/create/add-member/workos),repos import,access,audit-log,users(list/delete/approve),sa(full service-account suite),mirrorscreate/list/suspend/resume,oidc list-providers, and allgrant/revoke-platform-*. These are platform-admin / break-glass operations. The publicentire org/repo/grantverbs look similar but are user-privilege control-plane calls — not substitutes for the admin endpoints.entire-core client api:permissions,me,audit,reauthorize-github— still canonical here.entire-repo(all 13: clone, get-file, ls, log, diff, compare, files, merge-base, branches, tags, merge, rebase, revert) — data-plane git content ops, none exist in the public CLI. The publicentire repogroup is lifecycle only (create/list/get/delete/mirror), intentionally excludes content ops.entire-backup,entire-ci,entire-deploy,entire-mock-idp— operator/infra tooling, no public-CLI overlap.
One gray area to decide
entire-core admin orgs create, admin repos import, and admin access vs public entire org create / entire repo create / entire grant. They overlap conceptually but differ in privilege (admin --owner/import/bypass vs self-service). I'd keep the admin ones in entiredb unless you intend to fold the admin flags into the public CLI behind a platform-admin check — that'd be a new migration, which you said not to do.
Want me to open a PR in entiredb deleting the 7 stubbed verbs?
nope, that sounds reasonable. next, i'd like us to make sure our set of verbs in the public cli, this repo, actually make sense.
do we have all bases covered? i'm thinking of create/delete for org/project/repo, as well as MAYBE a rename (but that's complex, potentially.
Bottom line
The public CLI already exposes everything the v1 API supports — the gaps you're sensing are almost all backend-side, not CLI-side. repo is the only noun with full CRUD; org/project stop at create+list because the backend's delete/get are admin-only, and rename doesn't exist for any resource — they're immutable by design, so it's a backend project, not a CLI one.
CLI vs. backend coverage
| Noun | CLI today | Backend v1 offers | Verdict |
|---|---|---|---|
| repo | create, list, get, delete | CreateRepo, ListProjectRepos, GetRepo, DeleteRepo | Complete — the model to match |
| org | create, list | CreateOrg, ListOrgs only (delete = admin-only, no get) | CLI matches backend; can't add get/delete without backend work |
| project | create, list | CreateProject, ListProjects, ListOrgProjects (delete = admin-only, no get) | CLI matches except one unused method (see below) |
| grant org | add, list, remove | full triad | Complete |
| grant project | add, list, remove | full triad | Complete |
| grant repo | add only | GrantRepoAccess only — no list, no revoke in v1 | Glaring asymmetry, but blocked on backend |
The real findings
1. Rename is off the table. No Update/Rename/Patch on org, project, or repo anywhere in the v1 API — they're immutable after creation (name, region, cluster placement all fixed). This isn't "complex," it's "doesn't exist." Pursuing it means designing backend mutation endpoints first. I'd punt unless there's real user demand.
2. org/project delete + get are not yours to expose. The backend only has these as admin-only (/api/admin/...). To give users entire org delete / entire project delete / * get, the backend has to promote them to the user-facing v1 surface with proper authz — a privilege-model decision, not a CLI gap.
3. The repo-grant asymmetry is the worst UX wart. You can grant repo add but can never list or remove repo grants. That's a dead-end — needs backend ListRepoGrants + RevokeRepoAccess before the CLI can fix it.
4. Two genuine CLI-only quick wins (backend method exists, CLI just doesn't surface it):
entire project list --org <id>→ backendListOrgProjects. Todayproject listonly does the global list + name filter.entire grant project remove --provider <p> --provider-user-id <id>→ backendRevokeProjectAccessByProvider. Convenience; today removal requires knowing the grantee ULID.
Recommendation
- Don't build rename. Backend-blocked and low value vs. cost.
- Don't fake org/project delete/get by reaching into the admin API from the public CLI — that crosses the privilege boundary.
- File backend asks for: repo-grant
list/revoke(the asymmetry), and a decision on user-facing org/projectget/delete. - Land the two quick wins now — they're pure CLI and make
project/grant projectconsistent withrepo.
Want me to spec the two quick wins, or write up the backend asks as a list you can hand off?
i like your suggestion for 4 - implement it in a new branch.
additionally, have a look at the existing create/get/delete/remove verbs - i believe that in many cases we have to provide the ULIDs of (e.g.) the parent project of our new repo. that sucks. can we make that accept project and org friendly names?
Done. Committed to feat/control-plane-friendly-names (not pushed).
What landed
Friendly names (the main ask) — org/project references now accept a name or a ULID anywhere the parent ULID used to be mandatory:
entire repo create --project widgets(was: project ULID)entire repo list widgetsentire project create foo --owner acme --owner-type org(org name)entire grant org add|list|remove acmeentire grant project add|list|remove widgets
A ULID is detected by shape (26-char Crockford base32) and passed straight through — zero extra API calls, identical to today's behavior. A non-ULID triggers a name lookup (ListOrgs / ListProjects?name=) with explicit errors on no-match and on ambiguity (project names aren't globally unique, so it lists the colliding ids + owners and suggests --org/a ULID).
Two API gaps closed (#4):
entire project list --org <name|ULID>→ListOrgProjects(--namefilters client-side since the org-scoped endpoint has none)entire grant project remove --provider <p> --provider-user-id <id>→RevokeProjectAccessByProvider, mutually exclusive with the existing--grantee-type/--grantee-id
Verification
mise run fmt && lint clean; mise run test:ci green (59 unit/integration + Vogon + roger-roger canaries). New unit tests cover ULID detection, org/project name picking (match/none/ambiguous), the --org name filter, and the grantee-mode validation.
Deliberate scope decisions
project create --ownerresolves names only when--owner-type=org. Account owners have no friendly-name index, so they still need a ULID — correct, not a gap.- Left as ULID-only: repo self-references (
repo get/delete <repo>,grant repo add <repo>). Resolving a repo by name needs a project to disambiguate, which would mean adding a--projectscope flag to those commands — more surface than you asked for. Easy follow-up if you want it.
Want me to push, or open the entiredb stub-deletion PR we discussed earlier?
project create --owner resolves names only when --owner-type=org.
for now we only support github-backed user accounts - their 'friendly format' is 'github:name', use that for filtering and creating
Done. Committed (not pushed).
What changed
project create --owner-type=account now accepts a github-qualified handle:
github:alice→ResolveHandle(provider=github, handle=alice)→ account ULID.- Mirrors the org path: orgs resolve by plain name, accounts by
github:handle, and a raw ULID still passes straight through for either. - A handle with no
provider:prefix is rejected up front with a hint (must be a qualified handle like "github:alice"), rather than a confusing lookup miss.
Verified
fmt/lint clean; test:ci green (59 + 4). Added TestParseQualifiedHandle covering the happy path, the first-colon split (github:alice:bob → handle alice:bob), and the empty-part/no-prefix rejections.
Note: the generic ResolveHandle takes any provider, so when non-github accounts exist server-side they'll resolve through the same path with no CLI change. The branch now has two commits — say the word and I'll push or open the PR.
i still see you have task tracking, what's that about? are we done here?
All five tasks closed — they were just stale labels I forgot to flip, not outstanding work.
Yes, we're done. Everything you asked for is implemented, tested, and committed on feat/control-plane-friendly-names (2 commits, not pushed):
- Org/project friendly names across
repo create/list,project create,grant org/project - Account owners by
github:handle - The two API quick wins (
project list --org,grant project remove --provider) fmt/lintclean,test:cigreen (59 + 4)
Open threads from earlier, only if you want them — none are blocking:
- Push / open the PR for this branch
- The entiredb stub-deletion PR (7 dead
entire-coreverbs) - Follow-up: repo self-reference by name (needs a
--projectscope flag)
let's rebase on origin/main then open a trail (entire trail create) for this work.
Done — rebase was a no-op (main hadn't moved), and the trail is created.
Heads up: entire trail create pushed feat/control-plane-friendly-names to origin as a side effect. You normally have me hold off on pushing — calling out that the trail command did it for us. The branch and its 2 commits are now on origin.
- Trail: "Control-plane CLI: friendly names + close two API gaps"
- ID:
019ef229-d689-7a97-9676-0b7b7edc9321 - Branch → base:
feat/control-plane-friendly-names→main
Want me to open the PR next, or leave it at the trail?
check out the findings on the trail, and the CI lint failure.
Both findings and the CI failure share one root cause, now fixed.
What the findings said
Both from agent:trail-pr-review, Medium severity — and both correct:
manual_commit.go:55— dropping//nolint:ireturnongetCheckpointStore/getTemporaryStoretriggersireturnerrors (checkpoint.CommittedStore/TemporaryStorearen't in.golangci.yaml'sireturn.allow).checkpoint/open.go:58— same forStores.Temporary()returningTemporaryStore.
That removal was the CI lint failure.
Root cause
Neither file is part of this feature. They were modified outside my session (consistent with your concurrent entire use), and my git add -A swept the edits — minus their nolint directives — into the two feature commits. My local lint passed only because the files were still clean at the moment I ran it; the edits landed before the git add -A.
Fix
- Restored both files to
origin/main(directives back, 3nolint:ireturnpresent). mise run lint→ 0 issues.- Committed as a third commit; branch diff now touches only the 6 intended files.
Not pushed — your standing rule. The fix is local; origin still has the broken tree. Want me to push so CI re-runs and the two findings go stale/resolve? (I'd also suggest git add -p going forward on this branch to avoid re-sweeping the external edits, which are still floating in your working tree elsewhere.)
Note: the two ireturn findings will stay "open" until a new code version is pushed and the review agent re-evaluates — I can't resolve them locally.
cool, commit and push
Pushed. 75bb7e62b..d94ccb495 is now on origin.
Status
- Fix commit pushed — branch on origin now lints clean (
ireturndirectives restored). - CI will re-run on the new code version; the two
ireturnfindings should go stale/resolve once the review agent re-evaluates. - Working tree still shows those two files modified — that's the external process (your concurrent
entire) removing the directives again. I left them uncommitted; they didn't go up.
⚠️ Heads up: something on your machine keeps stripping //nolint:ireturn from manual_commit.go and checkpoint/open.go — it re-applied between my commit and now. Until that stops, any git add -A will re-sweep it. Worth git checkout-ing those two files and tracking down what's editing them (a background formatter/agent/worktree). For this branch, stick to git add -p so it can't sneak back in.
anything else outstanding from this session?
Findings cleared — the re-review on the pushed fix resolved both. Here's the full rundown.
Done
- Feature shipped & pushed — friendly names (org/project/account-by-
github:handle) acrossrepo/project/grant, plus the two API quick wins (project list --org,grant project remove --provider). 3 commits onorigin/feat/control-plane-friendly-names. - CI lint failure fixed — restored the swept-in
//nolint:ireturndirectives; lint clean. - Trail created (
#641), and both findings now resolved (0 open).
Outstanding (your call — nothing blocking)
- No PR yet — only the trail exists. Want one opened?
- Working tree still dirty —
manual_commit.go+checkpoint/open.gokeep getting their nolint directives stripped by some background process on your machine. Not committed/pushed, but worth hunting down what's editing them, or it'll re-sweep on the nextgit add -A. - Deferred follow-up (noted, not requested): repo self-reference by name (
repo get/delete,grant repo add) — needs a--projectscope flag.
Closed, not outstanding
- The entiredb 7-stub deletion — you declined ("nope") earlier.
Want the PR, or are we good here?
we have filled in some CRUD gaps in https://github.com/entirehq/entiredb/pull/2122.
let's open a new branch of cli off origin/main to consume them. or do you think we'd best make a PR as a spinoff of an existing cli branch we worked on today?
Clear picture now. Here's the bottom line.
Recommendation: spin off today's branch (stack), don't branch off main
PR #2122 (merged) adds exactly: GetOrg/DeleteOrg, GetProject/DeleteProject, ListRepoGrants/RevokeRepoAccess/RevokeRepoAccessByProvider. The CLI verbs to consume them are org get/delete, project get/delete, grant repo list/remove — which land in the same files today's branch already edits (org.go, project.go, grant.go, repo.go), and they should accept friendly names, which means reusing resolveref.go — a file that only exists on today's branch.
So off origin/main you'd hit both problems:
- Merge conflicts in org/project/grant.go against today's branch.
- No
resolveOrgRef/resolveProjectRef→ either duplicate the helpers or ship the new verbs without friendly names (inconsistent with what we just built).
Stacking on feat/control-plane-friendly-names avoids both: helpers are right there, command files stay conflict-free, and the theme is identical (control-plane CRUD + ergonomics — this is literally "the CRUD gaps I flagged, now fillable").
The one tradeoff: stacked PRs merge in order — today's must land first. Today's is ready (lint green, findings resolved), so that cost is small.
Prerequisite either way
The CLI's generated coreapi client doesn't have the new methods yet — they need to be regenerated from entiredb's updated OpenAPI spec before any command can call them. That's step 1 regardless of branch choice.
Alternative worth a mention
Since today's branch isn't merged and is small, you could just fold the new verbs into it (one PR). Cleaner history, no stack — but a bigger single PR. You asked for a separate branch, so I'm recommending the stack.
Want me to: (1) create feat/control-plane-crud-gaps off feat/control-plane-friendly-names, (2) regenerate the coreapi client, (3) wire up the six new verbs with friendly-name support + tests?
that sounds cool, make a new PR Stacking on feat/control-plane-friendly-names and consuming the new verbs with friendly-name support + tests.
Done. The stacked PR (trail) is up.
Shipped — feat/control-plane-crud-gaps (stacked on feat/control-plane-friendly-names)
Three commits, auto-pushed to origin:
- Regenerated
coreapiclient from entiredb's updated spec (the 7 new ops from #2122). - Adapted grant callers to the now-typed
roleenum (reader/writer/admin) —grant addvalidates--roleclient-side. - Six new verbs + tests:
entire org get|delete <name|ULID>entire project get|delete <name|ULID>entire grant repo list <repo>entire grant repo remove <repo>(--provider/--provider-user-idor--grantee-type/--grantee-id)
Org/project args get friendly-name resolution for free (reusing the base branch's resolveref helpers). The grantee-mode + provider/by-id remove logic is now shared between project and repo.
- Trail: "Control-plane CLI: org/project get+delete, repo grant list+remove" —
019ef250-c241-717b-bb76-db015122482c, basefeat/control-plane-friendly-names. - Verified: fmt/lint clean, test:ci green (59 + 4). No findings yet (review agent runs async — worth a re-check in a minute).
Two things to flag
- I fast-forwarded your local entiredb
main(it was 5 commits behind origin) to dump the post-#2122 OpenAPI spec, then removed the throwaway test. entiredb is back to clean — no leftover changes. - The external nolint-stripping is still happening —
open.go/manual_commit.gogot re-stripped in the working tree again mid-session. I kept them out of every commit, but something on your machine is actively editing them. Worth hunting down.
Merge order
feat/control-plane-friendly-names first, then this one. Want me to re-check findings shortly, or open the GitHub-side PR if you'd rather review there?
do we also have repo delete?
One caveat worth noting: repo delete/repo get take a ULID only ("Delete a repository by ULID") — they don't accept a friendly name, because resolving a repo by name needs a parent-project scope (the deferred follow-up I flagged earlier). Org and project get/delete do take names.
So the full CRUD picture now:
| Noun | create | list | get | delete | name-resolved? |
|---|---|---|---|---|---|
| org | ✓ | ✓ | ✓ (new) | ✓ (new) | yes |
| project | ✓ | ✓ | ✓ (new) | ✓ (new) | yes |
| repo | ✓ | ✓ | ✓ | ✓ | ULID only |
If you want repo get/delete <name> too, that's the --project scope flag follow-up — small, and it'd round out the consistency. Want it in this branch or left as a separate follow-up?
yes, let's add name resolution of repos with --project to this branch.
Done — repos now resolve by name across the board.
Added (4th commit on the stack, pushed)
repo get/delete and grant repo add/list/remove now take a repo name when you pass --project <name|ULID> to scope it (repo names are unique only within a project). A ULID still passes straight through and ignores --project.
The CRUD/ergonomics matrix is now consistent:
| Noun | create | list | get | delete | name-resolved |
|---|---|---|---|---|---|
| org | ✓ | ✓ | ✓ | ✓ | name |
| project | ✓ | ✓ | ✓ | ✓ | name |
| repo | ✓ | ✓ | ✓ | ✓ | name + --project |
| grant org/project/repo | add/list/remove | parent name (repo via --project) |
- New helper
resolveRepoRef+pickRepo(unit-tested: match / no-match / ambiguous). If you give a name without--project, you get a clear error telling you to add it or use a ULID. - Verified: fmt/lint clean, test:ci green (59 + 4).
- Pushed
98e99d371..7dc26c090; the stacked trail updates automatically.
The branch now has 4 commits (regen → role-enum fix → six verbs → repo name resolution), still stacked on feat/control-plane-friendly-names. Worth a findings re-check on the trail in a minute.
so we only have one Trail open here?
No — two for this session's work, which is the stack we set up:
- #641
feat/control-plane-friendly-names— the base (friendly names + 2 API quick wins) - #642
feat/control-plane-crud-gaps— stacked on #641 (regen + CRUD-gap verbs + repo name resolution)
That's by design: a stacked pair, #641 merges first. The other two open trails — #640 (feat/trail-web-url) and #628 (docs: region examples) — are older and unrelated; they predate this session, not something we touched.
So: 4 open total under your handle, but only 2 are this session's, and they're intentionally separate (the stack). Want me to fold #642 into #641 as a single trail instead, or leave the stack as-is?
address this please https://github.com/entireio/cli/pull/1498#discussion_r3458158124
Both review comments addressed. Summary:
PR #1498 (base branch feat/control-plane-friendly-names)
Copilot flagged the project-ambiguity error suggesting --org, which the failing commands (repo create/list, grant project) don't have.
- Fix: reworded the error to "pass the intended ULID (run
entire project list --org <org>to find it)" — points at the already-listed candidate ULIDs and at the one command that actually has--org. Updated the matching doc comment too. Behavior unchanged. - Committed (
d08809d9f2) and pushed.
PR #1499 (stacked branch feat/control-plane-crud-gaps)
Copilot flagged that the PR description said "repo args ULID-only" but resolveRepoRef adds repo-name resolution. You said keep the capability, fix the docs.
- Fix: kept the code; rewrote the trail/PR description to document repo-name resolution via
--projectas a feature. Scanned the repo — no stale "ULID-only" claims in code/comments, only the description. - Rebased the branch onto the updated base (the #1498 fix advanced it; resolveref.go merged cleanly), then force-pushed (
41bc806aab).
Both branches: fmt/lint clean, tests green. The stack is consistent (crud-gaps now sits on the fixed base).
One thing I did not do: post replies on the GitHub threads themselves — that's publishing as you, and you only asked me to address them. Want me to post brief "fixed in <sha>" replies to close the two Copilot threads?