oci ghcr.io/sebastienrousseau/corral:0.0.27stdioGPL-3.0updated 9d ago
Automatically clone and organise GitHub repositories using Finder-friendly collections, ecosystems, and metadata.
What can you do with Corral?
Contents
Getting started
- Install — mise, Homebrew, Arch, Go, or from source
- Quick Start — clone and organise in one command
Features & Capabilities
- Features — structured layout, concurrency, and security
- Architecture — end-to-end flow from API fetch to per-repo dispatch
- Interactive TUI Mode — keybindings, commands, and autocomplete
- Layout Customization — Apple-style collections, ecosystems, and custom templates
- Smart Syncing — network-optimised incremental updates
- Exec Mode — concurrent batch execution of Git commands
- MCP Server — expose your local workspace to AI coding agents
Reference & Operational
- Usage & Flags — complete CLI parameter reference
- Examples — index of runnable programmatic examples
- Troubleshooting — quick solutions to common errors
- Frequently Asked Questions — design decisions and Windows/WSL support
- License
Install
mise (macOS / Linux)
mise use -g github:sebastienrousseau/corral
This installs the latest released corralctl binary and keeps it managed with
the rest of your mise tools.
Homebrew (macOS)
brew install sebastienrousseau/tap/corralctl
Homebrew here is a cask, which is a macOS-only mechanism — brew install on
Linux will refuse it. On Linux use the .deb/.rpm packages or the tarballs
attached to each release,
or install with mise or the
Go toolchain.
Arch Linux (AUR)
yay -S corralctl-bin # or: paru -S corralctl-bin
Go toolchain
go install github.com/sebastienrousseau/corral/cmd/corralctl@latest
Installs into $(go env GOPATH)/bin (or $GOBIN when set). Note that a
binary built this way reports corralctl version dev: the real version is
stamped by the release pipeline through -ldflags, which go install does
not apply. Use a release artefact if you need --version to be meaningful.
Build from source
Requires Go 1.26+ and Git:
git clone https://github.com/sebastienrousseau/corral.git
cd corral
make install # installs ~/.local/bin/corralctl
Platform Prerequisites
brew install go git gh
sudo apt install golang git
Install gh separately following the GitHub CLI installation guide.
sudo dnf install golang git gh
Quick Start
Run Corral with an owner name (GitHub username or organization) to clone and automatically sort all repositories into a clean local directory hierarchy:
# Log in to GitHub CLI first (or set GITHUB_TOKEN)
gh auth login
# Run Corral for your profile
./corralctl my-username
This converges your local directory structure into a structured mirror:
~/Code/
├── Public/
│ ├── Go/
│ │ └── corral/
│ ├── Rust/
│ │ └── my-crate/
│ └── Web/
│ └── project.github.io/
├── Private/
│ └── Python/
│ └── internal-tool/
├── Forks/
│ └── Rust/
│ └── upstream-project/
└── Work/
On macOS, Corral also writes native Finder Tags to repository folders while preserving tags you added yourself. This keeps the physical hierarchy shallow and makes Finder searches and Smart Folders useful across ecosystems.
Features
| Feature | Description |
|---|---|
| Apple-style Layout | Sorts source repositories into Public/, Private/, and Forks/, using Finder-friendly ecosystem names such as Go, Rust, Python, and Web. |
| Finder Tags | Applies native macOS lifecycle colors and searchable visibility, ecosystem, owner, fork, archive, template, and mirror metadata without replacing personal tags. |
| Smart Syncing | Compares remote pushed_at metadata to skip redundant network calls, speeding up syncs by 10x-50x. |
| Interactive Selection | A fully featured Terminal UI (TUI) selector dashboard to search, preview, and select repositories to clone. |
| Legacy Migration | Automatically moves existing flat directory layouts into the new structure and cleans up empty folders. |
| Concurrency | Processes clones and pulls concurrently with configurable worker limits (--concurrency). |
| Batch Commands | Batch execute Git commands concurrently across all cloned repositories using exec. |
| Zero Configuration | No configuration files required — simple, sensible defaults that work out of the box. |
Architecture
A single run resolves git, fetches every repository concurrently from GitHub, optionally lets you pick a subset interactively, then dispatches clone / smart-sync / skip decisions across a worker pool. Smart sync consults a per-repository .corral-state.json sidecar to skip a git pull when the upstream pushed_at is unchanged.
graph TD
A[User Shell] --> B{corralctl}
B --> C[Pre-flight: exec.LookPath git]
C -- Missing --> Z1[Exit: git not found on PATH]
C -- OK --> D[Resolve auto/token/gh auth]
D --> E[GitHub API: list repos]
E --> E1["First page<br/>+ resp.LastPage"]
E1 --> E2{LastPage > 1?}
E2 -- Yes --> E3["Concurrent fetch<br/>pages 2..N (max 5)"]
E2 -- No --> F
E3 --> F[Filtered repository set]
F --> F1{TUI selector?}
F1 -- "--select" --> F2[Interactive TUI<br/>/sort, /all, /none, search]
F1 -- No --> G
F2 --> G[Layout template render<br/>Collection/Bucket/Name]
G --> H["Worker pool<br/>(--concurrency)"]
H --> I{Already cloned?}
I -- No --> J["git clone (+ blobless/<br/>depth/single-branch)"]
I -- "Yes (--no-sync)" --> K[SKIP]
I -- Yes --> L{Smart sync:<br/>pushed_at advanced?}
L -- No --> M[SKIP up-to-date]
L -- "Yes (or --force-sync)" --> N[git pull --rebase --autostash]
N --> N1["+ optional submodule update<br/>(--ignore-submodule-failures)"]
J & N1 --> O[Stamp .corral-state.json]
O & K & M --> P{All workers done?}
P -- No --> H
P -- Yes --> Q[Cleanup empty legacy dirs]
Q --> R{--orphans?}
R -- Yes --> S[Walk baseDir<br/>parse .git/config]
R -- No --> T[Print summary]
S --> T
Interactive TUI Mode
By passing the -i or --interactive flag, you can launch the selection dashboard:
./corralctl -i my-username
Keybindings
[space]— Toggle selection of the current repository.[ctrl+a]— Select all currently filtered repositories.[ctrl+n]— Deselect all currently filtered repositories.[/]— Enter command / filter mode.[enter]— Confirm selection and begin cloning/syncing.[esc]— Exit the application silently.
In-Session Commands
Press / inside the TUI to enter Command Mode. Commands support prefix-based autocompletion (press [tab] or [right-arrow] to autocomplete):
/sort <field>— Sort repositories. Fields:name— Alphabetical sort by repository name.language/lang— Alphabetical sort by language.visibility/vis— Alphabetical sort by visibility (Private/Public).public— Prioritize public repositories at the top.private— Prioritize private repositories at the top.
/all— Select all filtered repositories./none— Deselect all filtered repositories./exit//quit— Cancel and exit silently./help— Display the in-session help panel overlay.
Layout Customization
By default, Corral uses the Apple-style layout {{.Collection}}/{{.Bucket}}/{{.Name}}. Forks use the Forks collection, while .github.io repositories use the Web bucket regardless of their detected language. You can override this using the --layout flag:
./corralctl --layout "{{.Owner}}/{{.Name}}" my-org
Supported placeholders:
{{.Owner}}— GitHub owner name.{{.Name}}— Repository name.{{.Collection}}— Canonical root (Public,Private, orForks).{{.Bucket}}— Finder-facing ecosystem bucket (Go,Rust,Web, etc.).{{.Language}}— Primary language normalized to lowercase.{{.Visibility}}— Repository visibility normalized to lowercase.
Finder Tags on macOS
Finder tagging is enabled by default on macOS and can be disabled with
--finder-tags=false. Corral manages the following lifecycle taxonomy:
| Color | Tag | Automatic signal |
|---|---|---|
| Green | Active |
Pushed within seven days or checked out on a non-default branch |
| Yellow | On Hold |
Archived on GitHub |
| Red | Needs Fix |
Corral encountered a sync failure for the matching clone |
| Purple | Experiment |
Fork, template, or mirror repository |
Uncolored metadata tags include Visibility: Public, Collection: Forks,
Ecosystem: Rust, Owner: example, GitHub, Fork, Archived, Template, and Mirror.
Finder can combine these tags in searches or saved Smart Folders.
Smart Syncing
Corral stores synchronization metadata next to each repository's .git/ folder inside a .corral-state.json sidecar file:
- No Redundant Pulls: If the remote repository has not received new pushes since the last sync,
git pullis skipped completely. - Overrides: To bypass smart checks and force Corral to perform a full
git pull, pass the--force-syncflag. - Skip Syncing entirely: Pass
--no-syncto skip updates on all cloned repositories.
Exec Mode
Execute arbitrary shell commands concurrently across your organized repositories:
# Check git status for all Go/Rust private repositories
./corralctl exec "git status -s" --languages go,rust --visibility private
MCP Server (for AI agents)
Corral ships a Model Context Protocol server that exposes your local, Corral-organised workspace to AI coding agents — Claude Code, Cursor, Cline, Codex CLI, Aider, and anything else that speaks MCP. In its default read-only mode no network calls are made and the GitHub API is never contacted; the server is a window into the clones already on disk. (--enable-mutations adds corral_clone_repo and corral_sync_repo, which do reach the network — they shell out to git. The GitHub API is still never contacted.)
Where GitHub's own MCP server covers the remote API surface (issues, PRs, search), corral-mcp covers the dimension only it can — your local mirror, organised by visibility and language, queryable without a round-trip.
Tools
corral_list_repos— Filter local clones by visibility / language / name / sync statecorral_find_repo— Resolve a fuzzy name to one clone (returns candidates on ambiguity)corral_get_repo_metadata— Full metadata for one clone, including current branchcorral_status_summary— Workspace summary: counts by visibility and languagecorral_workspace_index— Full structured index in a single call
Write tools (v0.0.12, opt-in via --enable-mutations):
corral_sync_repo— Runsgit pull --rebase --autostashagainst one clonecorral_clone_repo— Clones a URL into a sandboxed target pathcorral_delete_repo— Removes a clone. Requires--enable-destructive-mutations. Refuses on uncommitted/unpushed changes
Every mutation writes a JSONL audit record to
$XDG_STATE_HOME/corral/mutations.log (or ~/.local/state/corral/mutations.log),
capturing tool, target, args, result and timestamp. Two records per mutation: a
durable intent before anything happens, and a completion linked by operation
ID. A mutation whose intent cannot be recorded does not run.
Override the location with --audit-log <path>. The file rotates at 8 MiB and
keeps three previous generations (mutations.log.1 … .3), so a long-running
server cannot fill the disk.
Prompts (v0.0.12)
explain_workspace— Ask the agent to survey the workspace via read-only tools and summarise itidentify_stale_repos— Ask the agent to find clones whose.corral-state.jsonsays they haven't been synced recently (default: 30 days)
Resources
corral://workspace/indexcorral://repo/{owner}/{name}/statecorral://repo/{owner}/{name}/treecorral://repo/{owner}/{name}/file/{path}(bounded at 1 MiB; path-traversal protected)
Install
Claude Code:
claude mcp add corral -- corralctl mcp
Cursor / Cline (mcp.json):
{
"mcpServers": {
"corral": {
"command": "corralctl",
"args": ["mcp"]
}
}
}
Docker (no local install required) — the same binary the MCP Registry advertises, mounted against your workspace:
{
"mcpServers": {
"corral": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"--user", "1000:1000",
"-v", "${HOME}/Code:/workspace:ro",
"ghcr.io/sebastienrousseau/corral:latest",
"mcp", "--root", "/workspace"
]
}
}
}
Notes on the args:
--user 1000:1000— replace with your host UID:GID (id -u:id -g) so the containerised scanner reads the mounted workspace with the same permissions your host user has. Without this the image runs as a system UID inside the container and hitspermission deniedon any directory your workspace makes group- or user-private.-v … :ro— read-only mount. The v0 tools are read-only anyway; mounting:rodocuments that and defends against a hostile agent asking the server for a write it doesn't have.--root /workspace— sandbox root inside the container. Every tool and resource path check is scoped to this prefix; requests outside it are rejected regardless of what the agent asks for.
Sandbox a different root (defaults to --base-dir, then $HOME/Code):
corralctl mcp --root /custom/workspace
Safety
- Read-only by default.
--enable-mutationsunlocks clone and sync. Deletion additionally requires--enable-destructive-mutations; every mutation writes intent and completion records to the audit log. - Deletion fails closed. MCP and CLI pruning refuse repositories with
working-tree changes; commits on any local branch not reachable from a remote;
stashes; local-only or divergent tags; gitignored content (
.envfiles, local databases, caches — the least recoverable thing in a clone, and invisible to a plaingit status); submodules holding unpublished commits; a target that is not a git repository at all; or any state Git cannot verify. Each refusal names its specific reason and is written to the audit log. - Path-traversal protected. File-resource lookups canonicalise the selected repository root and candidate path, blocking
..and symlink escapes into sibling repositories or outside the workspace. - stdio-only. No HTTP endpoint, no listening port — the server only ever speaks to the parent process that launched it.
Usage & Flags
Positional Arguments
corralctl <owner> [base_dir] [limit]
<owner>— GitHub username or organization (Required).[base_dir]— Root directory to save repositories (Default:$HOME/Code).[limit]— Maximum repositories to fetch (Default:1000).
Command Options
| Option | Short | Default | Description |
|---|---|---|---|
--base-dir |
— | $HOME/Code |
Root directory for cloned repos |
--limit |
-l |
1000 |
Maximum repositories to fetch |
--concurrency |
-c |
1 |
Number of concurrent worker threads |
--dry-run |
-n |
off | Preview actions without making changes |
--orphans |
-o |
off | Detect local repositories no longer on GitHub |
--protocol |
-p |
https |
Protocol to clone: ssh or https |
--no-sync |
— | off | Skip pulling latest changes for existing clones |
--force-sync |
— | off | Force git pull regardless of cached state |
--layout |
— | ... |
Templated path layout for repositories |
--finder-tags |
— | on (macOS) | Apply managed native Finder Tags to repository folders |
--interactive |
-i |
off | Launch the interactive selector TUI dashboard |
--recurse-submodules |
— | off | Initialise submodules on clone and sync |
--output |
— | text |
Output format: text, json, or ndjson |
--auth |
— | auto |
Auth mode: auto, token, or gh |
--visibility |
— | all |
Filter by visibility: all, public, private |
--include-forks |
— | on | Include forked repositories under Forks/ |
--include-archived |
— | on | Include archived repositories and tag them On Hold |
--languages |
— | — | Comma-separated language filter (e.g. go,rust) |
--exclude-languages |
— | — | Comma-separated language exclude list |
--clone-depth |
— | 0 |
Shallow clone depth (0 disables shallow clone) |
--api-timeout |
— | 30s |
Deadline for GitHub API operations |
--log-level |
— | info |
Diagnostic verbosity on stderr: error, warn, info, debug |
Diagnostics
Results go to stdout in the format --output selects. Diagnostics — what was
skipped, what was worked around, why — go to stderr, so --output json stays
pipeable no matter how noisy the run is.
--log-level controls how much of that stderr you get. CORRAL_LOG_LEVEL
sets the same thing for a whole shell session.
# Why did that repository not migrate? Turn the detail up.
corralctl sebastienrousseau --log-level debug
# Machine-readable results, quiet stderr, both at once.
corralctl sebastienrousseau --output json --log-level error > repos.json
# For a bug report: full detail, everything captured.
CORRAL_LOG_LEVEL=debug corralctl sebastienrousseau > out.json 2> diagnostics.log
The default, info, is what corral has always printed.
Operational Commands
corralctl status --base-dir ~/Code
corralctl plan sebastienrousseau --base-dir ~/Code
corralctl prune sebastienrousseau --base-dir ~/Code --dry-run
corralctl prune sebastienrousseau --base-dir ~/Code --yes
status inventories local clones, plan emits a non-mutating reconciliation,
and prune removes only upstream-orphaned clones that pass the unpublished-work
checks. JSON output is available on each command.
Multi-owner profiles use a strict JSON config (default
~/.config/corral/config.json) and run with corralctl profile <name>:
{
"profiles": {
"work": {
"owners": ["org-one", "org-two"],
"base_dir": "/home/me/Code",
"layout": "{{.Owner}}/{{.Visibility}}/{{.Language}}/{{.Name}}",
"concurrency": 4
}
}
}
Examples
To inspect the package layout and programmatically run Corral modules, see the self-contained, copy-pasteable Go code examples in the examples directory:
- Interactive Selector — Programmatically configure and launch the selection checklist TUI in AltScreen mode.
- GitHub Repository Fetcher — Query the GitHub REST API using
github.FetchReposWithOptionswith stars sorting and language constraints. - Git Syncing — Call the
githelper package to perform clones, query branches, and resolve origin URLs. - Engine Orchestrator — Integrate the core engine
engine.Runto run repository syncing with custom filters, layout structures, and dry-run pre-flights.
Troubleshooting
| Error Message | Cause | Solution |
|---|---|---|
ERROR: git not found on PATH |
Git is not installed or missing from the current PATH environment. | Install git via your package manager. |
ERROR: GITHUB_TOKEN environment variable not set |
--auth token was specified but no environment variable is present. |
Run export GITHUB_TOKEN=$(gh auth token) or switch to --auth auto. |
FAILED: owner/repo |
Authentication error or network failure during clone/pull. | Check connectivity and confirm gh auth status displays a valid session. |
Frequently Asked Questions
- Does it work with GitLab or other hosts?
No. Corral is specifically built to integrate with the GitHub API and GitHub CLI (gh). - What happens to repositories deleted on GitHub?
Normal reconciliation never deletes them.--orphansreports them; the explicitprunecommand can remove verified-safe orphans after--yesconfirmation. - Can I run it inside Cron or systemd timers?
Yes. The command runs non-interactively by default. All Git command credential prompts are bypassed to ensure automated jobs never hang. - How are repositories with no primary language stored?
They default to theOther/ecosystem category (e.g.Public/Other/my-repo).
THE ARCHITECT ᛫ Sebastien Rousseau
THE ENGINE ᛞ EUXIS ᛫ Enterprise Unified Execution Intelligence System
License
Licensed under the GNU General Public License v3.0.
Install
Add Corral to your client. Pick the one you use.
claude mcp add ghcr-io-sebastienrousseau-corral-0-0-27 -- docker run -i --rm ghcr.io/sebastienrousseau/corral:0.0.27codex mcp add ghcr-io-sebastienrousseau-corral-0-0-27 -- docker run -i --rm ghcr.io/sebastienrousseau/corral:0.0.27amp mcp add ghcr-io-sebastienrousseau-corral-0-0-27 -- docker run -i --rm ghcr.io/sebastienrousseau/corral:0.0.27{
"mcpServers": {
"ghcr-io-sebastienrousseau-corral-0-0-27": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/sebastienrousseau/corral:0.0.27"
]
}
}
}Add to `claude_desktop_config.json`, then restart Claude Desktop.
{
"mcpServers": {
"ghcr-io-sebastienrousseau-corral-0-0-27": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/sebastienrousseau/corral:0.0.27"
]
}
}
}Add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` for a single project.
code --add-mcp '{"name":"ghcr-io-sebastienrousseau-corral-0-0-27","command":"docker","args":["run","-i","--rm","ghcr.io/sebastienrousseau/corral:0.0.27"]}'Or add the block manually to `.vscode/mcp.json` under `servers`.
{
"mcpServers": {
"ghcr-io-sebastienrousseau-corral-0-0-27": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/sebastienrousseau/corral:0.0.27"
]
}
}
}Add to `~/.codeium/windsurf/mcp_config.json`.
{
"mcpServers": {
"ghcr-io-sebastienrousseau-corral-0-0-27": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/sebastienrousseau/corral:0.0.27"
]
}
}
}Add to `cline_mcp_settings.json` via the MCP Servers panel.
{
"mcpServers": {
"ghcr-io-sebastienrousseau-corral-0-0-27": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/sebastienrousseau/corral:0.0.27"
]
}
}
}Add to `~/.gemini/settings.json`.
{
"mcpServers": {
"ghcr-io-sebastienrousseau-corral-0-0-27": {
"type": "local",
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/sebastienrousseau/corral:0.0.27"
],
"tools": [
"*"
]
}
}
}Add to `~/.copilot/mcp-config.json`, or run `/mcp add` inside the CLI.
{
"context_servers": {
"ghcr-io-sebastienrousseau-corral-0-0-27": {
"command": {
"path": "docker",
"args": [
"run",
"-i",
"--rm",
"ghcr.io/sebastienrousseau/corral:0.0.27"
]
}
}
}
}Add to your Zed `settings.json`.
docker run -i --rm ghcr.io/sebastienrousseau/corral:0.0.27Run `goose configure`, choose **Add Extension → Command-line Extension**, and paste this command.
8 tools
Corral exposes 8 tools to a connected agent.
- corral_list_repos
- Filter local clones by visibility / language / name / sync state
- corral_find_repo
- Resolve a fuzzy name to one clone (returns candidates on ambiguity)
- corral_get_repo_metadata
- Full metadata for one clone, including current branch
- corral_status_summary
- Workspace summary: counts by visibility and language
- corral_workspace_index
- Full structured index in a single call
- corral_sync_repo
- Runs `git pull --rebase --autostash` against one clone
- corral_clone_repo
- Clones a URL into a sandboxed target path
- corral_delete_repo
- Removes a clone. Requires `--enable-destructive-mutations`. Refuses on uncommitted/unpushed changes
Score
88 / 100
Excellent
- Documentation25/25
- Maintenance25/25
- Trust13/20
- Capability13/15
- Install experience12/15
- Documents what it does and how to connect
- Has a resolvable package or endpoint
- Exposes at least one tool, prompt or resource
- README has substantive content
- Includes a code example
- Documents its configuration
- Mentions credentials or security posture
- Last commit 1 days ago
- Has a release history
- Repository is not archived
- Licensed GPL-3.0
- Namespace verified in the official MCP registry
- Claimed by its owner
- Published under an organisation
- 8 tool(s) documented
- Provides prompt templates
- Provides resources
- 12 documented install method(s)
- Published to a package registry
- Offers a hosted endpoint — no local install
Version history
| Versions | Published |
|---|---|
| 0.0.27Latest | Aug 29, 2026 |
| 0.0.26 | Aug 29, 2026 |
| 0.0.25 | Aug 20, 2026 |
| 0.0.24 | Aug 19, 2026 |
| 0.0.23 | Aug 18, 2026 |
| 0.0.22 | Aug 18, 2026 |
| 0.0.13 | Jul 1, 2026 |
| 0.0.10 | Jul 1, 2026 |
| 0.0.9 | Jul 1, 2026 |