shoka 0.15.0

A repository workspace manager — jj-aware, TUI-first successor to ghq / rhq.
Documentation

shoka (書架, "bookshelf") is a repository workspace manager written in Rust — a modern, jj-aware successor to ghq and rhq. It clones repos into a flat <root>/<host>/<owner>/<name> layout, lets you fuzzy-cd between them, runs commands in parallel across the whole shelf, and surfaces every repo's working state at a glance via a TUI dashboard.

Why another one

ghq and rhq nailed "where do I clone things" — but they're git-only and stop at list / look. shoka picks up from there:

  • jj as a first-class VCS alongside git — shoka clone / exec work for both, no ghq-jj shim needed.
  • TUI dashboard (Phase 2) for the whole shelf at a glance.
  • Profiles to keep work / personal / oss separated.
  • Routes for per-org clone destinations + per-route VCS / protocol.
  • AGENTS.md aware for AI-heavy workflows (shoka list --has-agents).
  • ghq layout compatible — drop-in for existing ~/ghq/... trees via shoka import.
  • In-process git via gix — no git subprocess fan-out, so import over a large shelf stays fast even on Windows where CreateProcess dominates the cost.

Install

cargo install shoka

Pre-built binaries for Linux / macOS / Windows are attached to each GitHub release.

Quick start

# 1. (optional) adopt an existing ghq tree
shoka import ~/ghq

# 2. or clone fresh
shoka clone yukimemi/shoka          # → <root>/github.com/yukimemi/shoka

# 3. see what's on the shelf
shoka list

# 4. jump into one (with the shell wrapper installed — see below)
s shoka

# 5. run something across everything (or a tag-filtered subset)
shoka exec --tag rust -- cargo check

Commands

Command What it does
shoka clone <spec> Clone a repo via gix (or jj git clone when vcs = jj). Accepts full URLs, owner/name, or host/owner/name.
shoka import <dir> Walk a directory, find existing .git/ repos, and adopt them onto the shelf.
shoka list Print the shelf with optional --tag / --has-agents filters.
shoka cd [hint] Resolve a repo to its on-disk path (use the shell wrapper to actually cd).
shoka exec -- <cmd> Run <cmd> in each matching repo in parallel; output is captured + banner-headed per repo.
shoka prune Drop shelf entries whose clone path is missing on disk. --dry-run to rehearse; --yes to skip the prompt.
shoka cache {refresh,show,clear} Per-repo volatile cache. Auto-refreshed in the background after most commands.
shoka doctor Diagnose environment + config.
shoka init-shell <shell> Print a shell wrapper that claims the shoka name and chdirs the parent shell on cd / tui (see below).
shoka completion <shell> Print a shell-completion script.
shoka tui TUI dashboard — branch / ↑↓ / dirty / PR / CI columns from the cached snapshot; j/k navigation, / for nucleo filter, Enter exits and emits the chosen repo's path so the shell wrapper can cd to it.

shoka exec is the transparent escape hatch: shoka never interprets the command. shoka exec -- git fetch, shoka exec -- jj git fetch, shoka exec -- cargo check all work the same way. Output from each repo is captured and printed as a banner-headed block when the process exits, so parallel runs don't interleave into nonsense.

Shell integration

A child process can't chdir its parent shell — kernel rule, no escape on any OS shoka cares about. So shoka init-shell emits a shell function that claims the shoka name itself, intercepts the cd and tui subcommands (which need to chdir the parent), and transparently passes every other subcommand through to the binary. The user sees one shoka they can run any subcommand against; the wrapper is invisible until they touch cd or tui.

Behavior of the installed function:

  • shoka (no args) — opens the TUI dashboard, Enter selects and cds into the highlighted repo.
  • shoka cd [hint] — fuzzy picker (or direct hint), cds on selection. The path travels out-of-band via a SHOKA_CD_OUT sidechannel file so the interactive prompt UI can render normally.
  • shoka tui [--tag …] — same as bare shoka, with explicit args.
  • shoka <anything-else> — passes through to the binary unchanged (shoka clone <url>, shoka list, shoka exec …, shoka --version, …).

PowerShell

# one-shot (current session):
shoka init-shell powershell | Out-String | Invoke-Expression

# persistent (append to $PROFILE — create it first if missing,
# which it is on a fresh PowerShell install):
if (!(Test-Path $PROFILE)) { New-Item -Type File -Path $PROFILE -Force | Out-Null }
shoka init-shell powershell | Add-Content $PROFILE
. $PROFILE

bash / zsh

# bash — add to ~/.bashrc:
eval "$(shoka init-shell bash)"

# zsh — add to ~/.zshrc:
eval "$(shoka init-shell zsh)"

fish

# ~/.config/fish/config.fish:
shoka init-shell fish | source

Pass --name s (or any other identifier) to install the function under a shorter alias instead of shadowing shoka. The alias behaves the same — s opens the TUI, s cd <hint> picks, s clone <url> passes through.

Configuration

config.toml lives at the OS-standard config dir (Linux $XDG_CONFIG_HOME/shoka/, macOS ~/Library/Application Support/yukimemi.shoka/, Windows %APPDATA%\yukimemi\shoka\config\). A starter is auto-written on first run.

Minimal example:

[global]
root = "~/src"
default_host = "github.com"
default_protocol = "https"
default_vcs = "auto"

[global.cache]
background_refresh = true
refresh_threshold_secs = 60

[[routes]]
pattern = "host:github.com/mycompany"
root = "~/src/work"
default_protocol = "ssh"

[profiles.work]
default_host = "github.mycompany.com"

Route patterns support host:<host>, host:<host>/<owner>, and /<regex>/. The first matching route wins. Profile beats routes when profile.root is set; otherwise routes evaluate against the raw [global] baseline.

Files in the same dir matching config.*.toml are layered on top (alphabetical order; config.local.toml last wins). All values flow through teravars so [vars] self-references resolve before deserialization.

Roadmap

  • Phase 1 — CLI MVPclone / import / list / cd / exec / prune / cache, shell integration, completion. Done.
  • Phase 2 — TUI dashboardratatui + crossterm + nucleo fuzzy. Per-repo cached status (branch / ahead-behind / dirty) from gix, open PR count + CI status from octocrab. Done.
  • Phase 3 — Polish. Per-profile route overrides, scaffolding via shoka new, bulk org-move follow, OSC 7 cwd hint, contribution-graph column.

Development

cargo make check        # fmt + clippy + locked check + tests
cargo make pre-release  # the whole check suite, exactly as CI runs it

PRs go through Gemini Code Assist + CodeRabbit reviewers on top of standard CI. The yukimemi/* convention is: renri worktrees + kata apply for the shared template, see AGENTS.md for the details.

License

MIT — see LICENSE.