Why
Skills and fix workflows need to read file contents across all hub.toml repos; going over the network on every scan is wasteful, and depending on the user's local checkouts risks dirty working trees or feature branches. Agent-driven fixes need an isolated working directory per task. And none of this should require the user to remember to run a command — the TUI should keep everything current automatically.
Current state
hub.toml stores a GitHub slug (repo = "ooloth/hub") and a name per project but no local path. Skills that read repo files must call gh api on every invocation. No hub-managed checkout exists, no hub fetch command exists, and there is no canonical local path convention for any project.
Ideal state
Bare clones
hub fetch creates a bare clone at ~/.hub/repos/<name>/ for each project in hub.toml (where <name> is the TOML name field)
- Running
hub fetch again on an existing clone fetches latest without re-cloning
- File contents are readable via
git -C ~/.hub/repos/<name> show HEAD:<path>
- All tracked paths are listable via
git -C ~/.hub/repos/<name> ls-tree -r --name-only HEAD
- Adding a project to hub.toml and running
hub fetch creates its bare clone automatically
- Removing a project from hub.toml leaves its clone intact, with a logged warning that it is orphaned
Worktrees
- Agent-created worktrees live nested inside the bare clone dir:
~/.hub/repos/<name>/<branch-slug>/
- This makes clone and its worktrees co-located —
ls ~/.hub/repos/<name>/ shows the full picture
hub fetch detects worktrees whose branches have merged on GitHub and removes them automatically
- Worktrees for unmerged branches are left intact so the agent's in-progress work can be inspected
TUI integration
- The TUI refresh loop runs fetch automatically on startup and on each refresh cycle
- No manual
hub fetch is needed for interactive use — opening the TUI is sufficient
- The
hub fetch CLI command remains available for debugging and unattended automation (agents, scripts)
Out of scope
- Scheduling
hub fetch via launchd or a daemon (TUI handles the interactive case; scripts handle automation)
- Auto-pruning orphaned clones (projects removed from hub.toml)
Starting points
config/src/toml.rs — Project struct with name and repo fields that hub fetch would iterate
ui/cli/src/main.rs — CLI command dispatch where the fetch subcommand would be added
ui/tui/src/main.rs — background refresh loop where fetch should be integrated
docs/architecture/secrets.md — credential injection pattern; hub fetch will need GITHUB_TOKEN for private repos
QA plan
- Run
hub fetch with hub.toml listing one project — expect ~/.hub/repos/<name>/ to exist as a bare clone with a valid HEAD ref
- Run
git -C ~/.hub/repos/<name> show HEAD:README.md — expect the file contents to print
- Run
git -C ~/.hub/repos/<name> ls-tree -r --name-only HEAD — expect a flat list of all tracked file paths
- Run
hub fetch again — expect it to complete without re-cloning; expect any commits pushed since step 1 to now appear in git log HEAD
- Add a second project to hub.toml, run
hub fetch — expect a new bare clone at ~/.hub/repos/<second-name>/
- Remove a project from hub.toml, run
hub fetch — expect its clone to remain untouched, with a warning that it is orphaned
- Create a worktree manually:
git -C ~/.hub/repos/<name> worktree add <name>/<branch> -b <branch> — expect the working directory to appear at ~/.hub/repos/<name>/<branch>/
- Merge the branch on GitHub, run
hub fetch — expect the worktree directory to be removed and a log line confirming cleanup
- Start the TUI — expect bare clones to be created or updated as part of startup without any manual fetch invocation
- Push a commit to a tracked repo while the TUI is running — expect the clone to reflect that commit after the next TUI refresh cycle
Done when
hub fetch creates and keeps bare clones current for every project in hub.toml, and file contents are readable offline via git show HEAD:<path>
- Agent-created worktrees live at
~/.hub/repos/<name>/<branch-slug>/ and are removed automatically by hub fetch when their branches merge
- The TUI refresh loop runs fetch automatically — no manual command needed for interactive use
Why
Skills and fix workflows need to read file contents across all hub.toml repos; going over the network on every scan is wasteful, and depending on the user's local checkouts risks dirty working trees or feature branches. Agent-driven fixes need an isolated working directory per task. And none of this should require the user to remember to run a command — the TUI should keep everything current automatically.
Current state
hub.toml stores a GitHub slug (
repo = "ooloth/hub") and anameper project but no local path. Skills that read repo files must callgh apion every invocation. No hub-managed checkout exists, nohub fetchcommand exists, and there is no canonical local path convention for any project.Ideal state
Bare clones
hub fetchcreates a bare clone at~/.hub/repos/<name>/for each project in hub.toml (where<name>is the TOMLnamefield)hub fetchagain on an existing clone fetches latest without re-cloninggit -C ~/.hub/repos/<name> show HEAD:<path>git -C ~/.hub/repos/<name> ls-tree -r --name-only HEADhub fetchcreates its bare clone automaticallyWorktrees
~/.hub/repos/<name>/<branch-slug>/ls ~/.hub/repos/<name>/shows the full picturehub fetchdetects worktrees whose branches have merged on GitHub and removes them automaticallyTUI integration
hub fetchis needed for interactive use — opening the TUI is sufficienthub fetchCLI command remains available for debugging and unattended automation (agents, scripts)Out of scope
hub fetchvia launchd or a daemon (TUI handles the interactive case; scripts handle automation)Starting points
config/src/toml.rs—Projectstruct withnameandrepofields thathub fetchwould iterateui/cli/src/main.rs— CLI command dispatch where thefetchsubcommand would be addedui/tui/src/main.rs— background refresh loop where fetch should be integrateddocs/architecture/secrets.md— credential injection pattern;hub fetchwill needGITHUB_TOKENfor private reposQA plan
hub fetchwith hub.toml listing one project — expect~/.hub/repos/<name>/to exist as a bare clone with a validHEADrefgit -C ~/.hub/repos/<name> show HEAD:README.md— expect the file contents to printgit -C ~/.hub/repos/<name> ls-tree -r --name-only HEAD— expect a flat list of all tracked file pathshub fetchagain — expect it to complete without re-cloning; expect any commits pushed since step 1 to now appear ingit log HEADhub fetch— expect a new bare clone at~/.hub/repos/<second-name>/hub fetch— expect its clone to remain untouched, with a warning that it is orphanedgit -C ~/.hub/repos/<name> worktree add <name>/<branch> -b <branch>— expect the working directory to appear at~/.hub/repos/<name>/<branch>/hub fetch— expect the worktree directory to be removed and a log line confirming cleanupDone when
hub fetchcreates and keeps bare clones current for every project in hub.toml, and file contents are readable offline viagit show HEAD:<path>~/.hub/repos/<name>/<branch-slug>/and are removed automatically byhub fetchwhen their branches merge