feat(clone): add wt clone with category-based placement - #122
Conversation
Introduce `wt clone <category> <owner/repo|url> [dest]`: acquire a repo's canonical checkout under a category's repo_root in a host/owner/repo layout, left on its default branch and ready to inspect. Fills wt's missing front half — every other command assumes the repo already exists locally. Adds the `categories` config concept (repo_root + gh_auth / git_protocol / glab_host) that drives placement and auth. owner/repo is resolved to a clone URL via gh/glab honoring git_protocol; a full URL is used as-is. - cmd/clone.go: command, URL resolution, gh auth switch, placement, hooks - cmd/category.go: Category type, builtins, resolve/merge, repoPlacementPath - cmd/config.go: [categories.*], default_category, repo_pattern, clone hooks - README/llms.txt/examples + unit tests + e2e scenarios Default repo_pattern now places clones at owner/repo/<branch> where <branch> is the remote's default branch, resolved via git ls-remote --symref before cloning. This makes the clone directory a valid main-worktree slot for sibling worktree strategies. Falls back to "main" when the remote is unreachable.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #122 +/- ##
==========================================
+ Coverage 37.62% 37.85% +0.23%
==========================================
Files 28 31 +3
Lines 3216 3545 +329
==========================================
+ Hits 1210 1342 +132
- Misses 1917 2101 +184
- Partials 89 102 +13
🚀 New features to boost your workflow:
|
|
Let me think about this (Original thinking was to focus on worktrees (only)... replicating git clone/init was intentionally not considered...) |
|
Sure, take your time; I would see workspace management as a future enhancement as well. |
|
Thanks for this @brosu — the core idea was right and the implementation was solid. What I kept: the whole shape of the command — acquiring the main repo, What I changed: the category tier is gone. Placement is now two flat settings: repo_root = "~/dev/repos"
repo_pattern = "{.repoRoot}/{.repo.Host}/{.repo.Owner}/{.repo.Name}/{.branch}"Two reasons. First, repo_pattern = "{.repoRoot}/{.env.WT_CATEGORY}/{.repo.Owner}/{.repo.Name}/{.branch}"Second, A few bugs got fixed along the way, mostly in code your PR didn't introduce: a On categories — you were solving a real problem and I don't want that to get lost. That's now #132, which tracks the real requirement: user-owned config that spans a group of repos and is never committed. Directory-scoped |
…127) Adds `wt clone <owner/repo|url> [dest]`, which acquires the main repository so that `wt create`/`checkout` have something to hang worktrees off. Placement is driven by two flat top-level settings: repo_root = "~/dev/repos" repo_pattern = "{.repoRoot}/{.repo.Host}/{.repo.Owner}/{.repo.Name}/{.branch}" The trailing branch segment is the remote's default branch, resolved with `git ls-remote --symref` before the clone, so the clone directory is a valid main-worktree slot rather than a bare repo folder. `owner/repo` resolves through `gh` or `glab`, each asked for its own `git_protocol`. Account selection stays with those tools (GH_CONFIG_DIR, GH_TOKEN, GLAB_HOST); wt never mutates their global auth state. Extra grouping levels compose through the pattern rather than being named by wt: `{.env.WT_CATEGORY}` gives back the "category" layer without wt owning a vocabulary. Durable, non-ephemeral grouping is tracked separately in #132. Based on #122 by @brosu, whose commits are carried forward here. Co-authored-by: brosu <bogdanrosu24@gmail.com>
|
@brosu — following up now that both halves of what this PR was reaching for are on 1. wt clone timvw/wt # owner/repo resolved via gh/glab
wt clone git@github.com:me/dotfiles.git # full URL used as-is
wt clone acme/api ~/src/api # explicit destination2. Categories — the repo_pattern = "{.repoRoot}/{.env.WT_CATEGORY:-personal}/{.repo.Owner}/{.repo.Name}/{.branch}"WT_CATEGORY=work wt clone acme/api # ~/dev/repos/work/acme/api/main
wt clone timvw/wt # ~/dev/repos/personal/timvw/wt/mainThe Two things worth being straight about:
Thanks for the original push here; the shape of |
|
Correction to the last point above: #138 is no longer parked — it shipped in #145, so the category can now be set per directory without direnv: # ~/.config/wt/config.toml
[[context]]
when_path = "~/dev/repos/work"
env = { WT_CATEGORY = "work" }wt clone acme/api # ~/dev/repos/work/acme/api/main, no export neededMatching is a path prefix, all matching rules apply (later wins per variable), and an actual exported Rules are read from |
Motivation
Every wt command assumes you are already inside a repo. There is no way to acquire the main repository in the first place.
wt clone fills that gap.
What this adds
wt clone <owner/repo|url> [dest] — clones a repository into its canonical location for a category, left on its
default branch, ready to inspect. A worktree can be added later with the usual wt commands.
wt clone oss timvw/wt # -> ~/dev/repos/oss/timvw/wt/main
wt clone work owner/repo # -> ~/dev/repos/work/owner/repo/main
wt clone personal git@…/x.git # full URL, same layout
wt clone personal git@…/x.git ~/src/x # explicit dest, bypasses layout
Categories
A category is an organizational context: where repos live (repo_root) plus the auth profile used to reach them (gh_auth,
git_protocol, glab_host). Three builtins ship: work, personal, oss.
repo_root = "~/dev/repos" # base; category root = <repo_root>/
[categories.work]
gh_auth = "work"
git_protocol = "ssh"
[categories.personal]
gh_auth = "personal"
[categories.client]
repo_root = "~/clients/acme/src" # explicit override
glab_host = "gitlab.acme.com"
Placement pattern
Default: {.category.RepoRoot}/{.repo.Owner}/{.repo.Name}/{.branch}
{.branch} is the remote's default branch, resolved via git ls-remote --symref before cloning (fallback "main"). This makes
the clone directory a valid main-worktree slot for sibling-worktree strategies. Override with repo_pattern.
Other details
Tests