Corral

io.github.sebastienrousseau由 GitHub 帳號 sebastienrousseau 發布v0.0.36更新於 Oct 27, 2026

Search code and symbols across every clone in your Corral workspace. Zero network calls.

已驗證STDIO僅桌面檔案與儲存開發者工具

概覽

AI 產生的概覽

把本機 Corral 工作區中已複製的 Git 儲存庫提供給 AI 助理,用於跨所有複本的唯讀程式碼與符號搜尋。

可以做什麼
  • 依可見性、語言、名稱或同步狀態列出、尋找並檢視本機複本,取得中繼資料以及工作區摘要或完整索引
  • 跨工作區每個複本搜尋程式碼文字並定位符號宣告
  • 透過有大小上限、防路徑穿越的資源讀取單一儲存庫的概觀、目錄樹與檔案內容
  • 發掘本機磁碟上未被追蹤或未納管的儲存庫
  • 啟用變更功能後,可選擇複製、同步、納管或刪除儲存庫,並附稽核日誌與逐次刪除確認
適用情境
適合把大量儲存庫集中放在同一個本機目錄,並希望助理回答某符號在哪裡宣告、哪些複本已過期等問題,同時不把程式碼送到遠端服務。預設為唯讀索引,適合程式碼探索與工作區盤點。
執行需求
需要一個本機 Corral 工作區(通常是家目錄下的程式碼目錄),根路徑由參數指定。以本機 stdio 程序執行,可用已發布的容器映像或已安裝的執行檔;容器方式需以唯讀方式掛載工作區並對應使用者 ID。底層工具需要 Git;此 MCP 伺服器本身未宣告任何 API 金鑰或環境變數。
安裝前請注意
伺服器會暴露其根目錄下的所有儲存庫,應把根目錄限定得越窄越好並以唯讀方式掛載。寫入工具需明確開啟,刪除還需第二個開關並逐次人工確認;變更會寫入稽核日誌。非回送位址的遠端 HTTP 傳輸必須提供 bearer 權杖,且伺服器只使用明文 HTTP、沒有 TLS。在命令列上傳權杖會被其他使用者看到。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Corral,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[corralctl logo]

corralctl

Automatically clone and organise repositories from GitHub, GitLab, Gitea, Forgejo, Codeberg and Bitbucket using Finder-friendly collections, ecosystems, and metadata.

[Build Status] [Go Reference] [golangci-lint] [Code Coverage] [Glama MCP server score] [OpenSSF Scorecard] [OpenSSF Best Practices] [Documentation] [Release Version] [License]

[corralctl Demo]


Contents

Getting started

  • Install — mise, Homebrew, Arch, Nix, Go, or from source
  • Quick Start — clone and organise in one command

Features & Capabilities

Reference & Operational

Project


Install

mise (macOS / Linux)

bash
mise use -g github:sebastienrousseau/corralctl

This installs the latest released corralctl binary and keeps it managed with the rest of your mise tools.

Homebrew (macOS)

bash
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)

bash
yay -S corralctl-bin    # or: paru -S corralctl-bin

Nix (any platform)

sh
nix run github:sebastienrousseau/corralctl -- --help   # run without installingnix profile install github:sebastienrousseau/corralctl # install

The flake ships the binary with its manpages and shell completions, and nix develop gives a shell with every tool the project's CI gates need, pinned by flake.lock.

Go toolchain

bash
go install github.com/sebastienrousseau/corralctl/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 Git and Go at or above the go directive in go.mod, but not Go 1.27.0 to 1.27.2, which carry a reachable crypto/x509 advisory (see the minimum toolchain policy):

bash
git clone https://github.com/sebastienrousseau/corralctl.gitcd corralmake install            # installs ~/.local/bin/corralctl

Platform Prerequisites

macOS
bash
brew install go git gh
Ubuntu / Debian / WSL2
bash
sudo apt install golang git

Install gh separately following the GitHub CLI installation guide.

Fedora / RHEL
bash
sudo dnf install golang git gh

Quick Start

Run corralctl clone with an owner name (GitHub username or organization) to clone and automatically sort all repositories into a clean local directory hierarchy:

bash
# Log in to GitHub CLI first (or set GITHUB_TOKEN)gh auth login
# Clone and organise every repository for your profilecorralctl clone my-username

The bare form, corralctl my-username, does the same thing. One binary, one base command, and every operation is a subcommand of it:

CommandDoes
corralctl clone <owner>Clone what is missing, pull what is stale, into the organised layout
corralctl sync --to <forge>Mirror the organised tree out to another forge — see Mirror to other forges
corralctl statusInventory local clones and their state
corralctl plan <owner>Preview a reconciliation without touching disk
corralctl prune <owner>Remove clones no longer upstream, refusing any with unpublished work
corralctl exec <cmd>Run a command across every clone — see Exec Mode
corralctl mcpServe the workspace to AI agents — see MCP Server

This converges your local directory structure into a structured mirror:

text
~/Code/├── Public/│   ├── Go/│   │   └── corral/│   ├── Rust/│   │   └── my-crate/│   └── Web/│       └── project.github.io/├── Private/│   └── Python/│       └── internal-tool/├── Forks/│   └── Rust/│       └── upstream-project/└── Work/

On macOS, corralctl 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

FeatureDescription
Apple-style LayoutSorts source repositories into Public/, Private/, and Forks/, using Finder-friendly ecosystem names such as Go, Rust, Python, and Web.
Finder TagsApplies native macOS lifecycle colors and searchable visibility, ecosystem, owner, fork, archive, template, and mirror metadata without replacing personal tags.
Smart SyncingCompares remote pushed_at metadata to skip redundant network calls, speeding up syncs by 10x-50x.
Interactive SelectionA fully featured Terminal UI (TUI) selector dashboard to search, preview, and select repositories to clone.
Legacy MigrationAutomatically moves existing flat directory layouts into the new structure and cleans up empty folders.
ConcurrencyProcesses clones and pulls concurrently with configurable worker limits (--concurrency).
Batch CommandsBatch execute Git commands concurrently across all cloned repositories using exec.
Zero ConfigurationNo configuration files required — simple, sensible defaults that work out of the box.

Architecture

A single run resolves git, fetches every repository concurrently from the forge (GitHub, GitLab, Gitea, Forgejo, Codeberg or Bitbucket), 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.

mermaid
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[Forge 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)" --> LG{"Operation in progress,<br/>local commits ahead,<br/>or no upstream?"}    LG -- Yes --> LR[ERROR: pull refused]    LG -- No --> N[git pull --rebase --autostash]    N --> N1["+ optional submodule update<br/>(--ignore-submodule-failures)"]    J & N1 --> O[Stamp .corral-state.json]    O & K & M & LR --> 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:

bash
./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, corralctl 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:

bash
./corralctl --layout "{{.Owner}}/{{.Name}}" my-org

Supported placeholders:

  • {{.Owner}} — GitHub owner name.
  • {{.Name}} — Repository name.
  • {{.Collection}} — Canonical root (Public, Private, or Forks).
  • {{.Bucket}} — Finder-facing ecosystem bucket (Go, Rust, Web, etc.).
  • {{.Language}} — Primary language normalized to lowercase.
  • {{.Visibility}} — Repository visibility normalized to lowercase.

macOS Finder Color Labeling & Tags

Finder tagging is enabled by default on macOS and can be disabled with --finder-tags=false. corralctl automatically categorises every repository folder using native macOS extended attributes (com.apple.metadata:_kMDItemUserTags and com.apple.FinderInfo), applying visual color dots and folder color labels:

High-Signal Operational Taxonomy

ColorTag NameMeaning & Trigger Signal
🔴 RedNeeds FixSync error or merge conflict. Applied on merge conflicts, diverged tracking branches, or sync failures requiring manual intervention.
🟢 GreenIn-FlightActive modifications in progress. Applied when uncommitted changes, unpushed commits, or stashes exist.
🟣 PurpleWorktree / ExperimentLinked worktrees & experimental clones. Applied to active linked worktrees, upstream open-source forks, template repositories, and mirrors.
🔵 BluePristineClean, synced ground truth. Applied when the repository is clean, pushed, and in sync with upstream.
🟡 YellowOn HoldArchived repository. Applied when the remote repository is archived and read-only.
⚪ GreyDormantUntouched reference repository. Applied when clean and has received no commits for over 60 days.
🟠 OrangeNew RepoUntracked local candidate. Applied by corralctl adopt for unmanaged local repositories.

Finder Folder Color Synchronization

corralctl synchronizes both Spotlight metadata (_kMDItemUserTags) and native macOS Finder directory labels (com.apple.FinderInfo byte 9 bits 1-3). This ensures Finder window list views, icon badges, and the Finder sidebar color filters reflect the exact operational state of each repository in real time.

You can audit and re-synchronize all workspace folder tags anytime with:

bash
corralctl doctor ~/Code --refresh-tags

Searchable Metadata Tags

In addition to color dots, corralctl writes plain text tags that macOS Finder and Spotlight index automatically:

  • Ecosystem: <Language>: Searchable by language bucket (e.g., Ecosystem: Go, Ecosystem: Rust, Ecosystem: Python, Ecosystem: Web).
  • Visibility: <Visibility>: Visibility: Public or Visibility: Private.
  • Collection: <Collection>: Collection: Public, Collection: Private, Collection: Forks, or Collection: Work.
  • Owner: <Owner>: Repository owner or organization namespace (e.g., Owner: sebastienrousseau).
  • Forge identifier: GitHub, GitLab, Gitea, Forgejo, Codeberg, or Bitbucket.
  • Repository characteristics: Fork, Template, Mirror, Archived.

Finder Searches & Smart Folders

You can combine these tags in macOS Finder to organize and navigate your code:

  • Finder Sidebar: Click any color in Finder's sidebar (e.g., click Green to see in-flight projects, or click Blue to see pristine reference clones).
  • Spotlight & Finder Search: Press Cmd+F in Finder and search:
    • tag:"In-Flight" - view active projects with uncommitted or unpushed work
    • tag:Pristine - view verified clean clones
    • tag:"Needs Fix" - find broken or conflicted repositories
    • tag:Go or tag:"Ecosystem: Go" - filter across collections by language
  • Smart Folders: Save searches as persistent Smart Folders in your Finder sidebar.

Preservation of Personal Tags

corralctl never deletes or overwrites user-defined tags (such as Client, Urgent, or Review). It reads existing file tags, updates only the managed taxonomy, and merges your custom tags cleanly without data loss.


Smart Syncing

corralctl 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 pull is skipped completely.
  • Overrides: To bypass smart checks and force corralctl to perform a full git pull, pass the --force-sync flag.
  • Skip Syncing entirely: Pass --no-sync to skip updates on all cloned repositories.

Exec Mode

Execute arbitrary shell commands concurrently across your organized repositories:

bash
# Check git status for all Go/Rust private repositories./corralctl exec "git status -s" --languages go,rust --visibility private
  • Shell: the command runs through /bin/sh -c on Unix and cmd.exe /C on Windows, whatever $SHELL names. To use another shell, name it in the command.
  • Deadline: --timeout 2m gives each repository its own deadline (default 0, none). A command that overruns is killed with its children and counted as failed.
  • Collections: --visibility takes all or any top-level collection: public, private, work or forks.
  • Machine output: --output json or --output ndjson writes one record per repository (repo, exit_code, duration_ms, stdout, stderr, timed_out).

Mirror to other forges

corralctl sync is the outbound half of the workflow: what clone pulls in from one forge, sync pushes out to others, so the organised tree becomes the source of truth for a mirror on every host you care about.

bash
export GITLAB_TOKEN=glpat-…            # scope: apiexport GITEA_TOKEN=…                   # scope: write:repository
corralctl sync --to gitlab --to gitea@https://git.example.com --dry-runcorralctl sync --to gitlab --to gitea@https://git.example.com

For every repository under the base directory, on every destination, sync creates the destination repository if it does not exist, points a remote named after the forge at it, and pushes in one round trip:

text
git push --prune --no-verify <forge> refs/heads/*:refs/heads/* +refs/tags/*:refs/tags/*

Every local branch and tag exists on the destination afterwards, and nothing else does. Branches are never forced: a destination that has moved on is refused and reported, not overwritten. Tags follow the local namespace.

"Local" means the clone's own branches (refs/heads/*), so a destination mirrors your clone, not its origin. A clone made by corralctl clone or git clone holds only the default branch locally; every other upstream branch exists only as a remote-tracking ref (origin/<name>) and is not pushed. Because the push prunes, such a branch is also deleted from a destination that already has it, as is any branch created directly on the destination. To carry a branch, check it out locally (git switch <name>) before running sync.

A new destination is created public only when two things agree: the source forge reported the repository as public the last time corralctl clone listed it (the answer is recorded in the clone's state sidecar), and the clone sits under a Public/ directory. Anything else is created private, including a clone corral did not make and one cloned before 0.0.54. The directory name alone is a guess, and a repository made private upstream can still sit under Public/. Run corralctl clone for the owner before sync so the record is current; --dry-run shows the visibility each destination would get.

A destination is <forge>[:<owner>][@<url>]:

--toMirrors to
gitlabgitlab.com, under the token's own account
gitlab:my-groupgitlab.com, under a group
github:my-orggithub.com, under an organisation
codeberg · bitbucket:workspacethe public instance
gitea@https://git.example.coma self-hosted Gitea or Forgejo

Credentials are the same environment variables clone uses to list from a forge: GITHUB_TOKEN (or the gh CLI), GITLAB_TOKEN, BITBUCKET_TOKEN, and GITEA_TOKEN, FORGEJO_TOKEN or CODEBERG_TOKEN for the Gitea family, each also accepted with a CORRAL_ prefix. Over HTTPS, the default, the same token authenticates the push, scoped to that forge's origin and never written to .git/config; --protocol ssh uses your keys instead.

Three refusals are built in. Two local repositories with the same name — a fork kept beside its original — would land on one destination path, so both are reported and neither is pushed while the rest of the tree proceeds. A repository is never pushed to the forge its origin lives on — corral clones from six forges, so the tree can hold a clone whose origin is a destination, and pruning against your own upstream is not a mirror. And a destination that already holds a same-named repository with the other visibility is an error, never a silent reuse.

Results go to stdout in --output text, json or ndjson; the exit code is non-zero if any repository failed, so a cron job can alert on it. This replaces the standalone corral-sync tool, whose gitlab and gitea remote names are kept so existing clones carry over unchanged.


MCP Server (for AI agents)

corralctl ships a Model Context Protocol server that exposes your local, corralctl-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 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_find_symbol — Where a symbol is declared, across every clone
  • corral_search_code — Where text appears, across every clone
  • corral_repo_overview — One repository's shape in a single call
  • corral_discover_repos — Discover untracked or unmanaged repositories on local disk

Write tools (v0.0.12, opt-in via --enable-mutations):

  • corral_sync_repo — Runs git pull --rebase --autostash against one clone, with the checks corralctl clone makes before a pull: it refuses a clone with no origin or whose origin changed since the index was read, one with no commits, and one that is not on the default branch origin/HEAD names, so an agent cannot rebase a feature branch. It holds the same per-clone lock as corral_delete_repo. Like sync, it also refuses when a rebase, merge, cherry-pick, revert or bisect is in progress, when the branch has commits its upstream lacks, or when it has no upstream.
  • corral_clone_repo — Clones a URL into a sandboxed target path
  • corral_adopt_repo — Adopts a local repository with optional relocation and remote creation. A self-hosted forge_url must be one the operator passed to corralctl mcp --forge-url
  • corral_delete_repo — Removes a clone. Requires --enable-destructive-mutations. Refuses on uncommitted/unpushed changes, and asks a person to approve each deletion

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 it
  • identify_stale_repos — Ask the agent to find clones whose .corral-state.json says they haven't been synced recently (default: 30 days)

Resources

  • corral://workspace/index
  • corral://repo/{owner}/{name}/state
  • corral://repo/{owner}/{name}/tree
  • corral://repo/{owner}/{name}/file/{path} (bounded at 1 MiB; path-traversal protected)

Install

Claude Code:

bash
claude mcp add corral -- corralctl mcp

Cursor / Cline (mcp.json):

json
{  "mcpServers": {    "corral": {      "command": "corralctl",      "args": ["mcp"]    }  }}

Docker (no local install required) — the same binary the MCP Registry advertises, mounted against your workspace:

json
{  "mcpServers": {    "corral": {      "command": "docker",      "args": [        "run", "--rm", "-i",        "--user", "1000:1000",        "-v", "${HOME}/Code:/workspace:ro",        "ghcr.io/sebastienrousseau/corralctl: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 hits permission denied on any directory your workspace makes group- or user-private.
  • -v … :ro — read-only mount. The v0 tools are read-only anyway; mounting :ro documents 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):

bash
corralctl mcp --root /custom/workspace

Transports

stdio is the default: the client launches corralctl mcp and owns the pipe. The other two listen on --host and --port, for a client that connects to a server somebody else started.

CommandTransportEndpointProtocol revisions
corralctl mcpstdio—negotiated on initialize
corralctl mcp --transport streamable-http --host 127.0.0.1 --port 8000Streamable HTTPhttp://127.0.0.1:8000/mcp2026-07-28 (stateless, server/discover) and 2025-11-25 (initialize, Mcp-Session-Id), on the one endpoint
corralctl mcp --transport sse --port 8001HTTP+SSE (legacy)http://127.0.0.1:8001/sse2024-11-05

--http 127.0.0.1:7777 is the older spelling of --transport streamable-http and still works.

Every listening transport binds loopback by default: --host 0.0.0.0, or --http :7777, which binds every interface, is refused because this server exposes every repository under its root. --allow-remote lifts that refusal only together with a bearer token, read from a file or the environment:

bash
export CORRAL_MCP_TOKEN="$(cat ~/.config/corral/mcp-token)"corralctl mcp --transport streamable-http --host 0.0.0.0 --allow-remote# or: corralctl mcp ... --allow-remote --token-file ~/.config/corral/mcp-token

A request without Authorization: Bearer <token>, or with the wrong token, is answered 401 with a WWW-Authenticate challenge. --token still works but is deprecated: anything on the command line is visible to every user on the machine through ps. The server speaks plain HTTP and has no TLS listener, so the token and every response cross the network unencrypted unless a TLS-terminating reverse proxy sits in front of it; it says so on stderr when it starts on a routable address. There is no OAuth resource-server mode.

A browser sends an Origin header with every request, and the server refuses one that is not listed in --allowed-origins. Loopback origins are not trusted by default, because every local dev server is one; list the page a browser client runs on, for example --allowed-origins http://localhost:6274. A client that is not a browser sends no Origin and is unaffected.

Safety

  • Read-only by default. --enable-mutations unlocks 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 (.env files, local databases, caches — the least recoverable thing in a clone, and invisible to a plain git 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. Search, the symbol index and the file resource never follow a symlink committed in a repository.
  • Per-call approval for deletion. With --enable-destructive-mutations, each individual deletion is put to a person over MCP elicitation before it runs. The refusal cascade above stops mistakes; this is what stops a persuaded agent choosing the one clone that passes every check, which no amount of prompt text can. Pass --no-confirm-deletes only for an unattended workspace you are willing to lose.
  • stdio by default; loopback when not. Without --transport the server has no endpoint and no listening port, and only ever speaks to the parent process that launched it. --transport streamable-http and --transport sse listen on --host/--port, and because the server exposes every repository under its root, a non-loopback address is refused unless you also pass --allow-remote and a bearer token (--token-file or CORRAL_MCP_TOKEN). The accidental --host 0.0.0.0, or --http :7777, which binds every interface, does not start. The server speaks plain HTTP; a remote listener needs a TLS-terminating proxy in front of it.

Forges

Cloning works against six hosting services:

bash
corralctl <owner>                                    # GitHub (default)corralctl <owner> --forge github --forge-url https://ghe.example.com  # GitHub Enterprise Servercorralctl <group> --forge gitlab                     # GitLabcorralctl <owner> --forge codeberg                   # Codebergcorralctl <owner> --forge gitea --forge-url https://git.example.comcorralctl <owner> --forge forgejo --forge-url https://forgejo.example.comcorralctl <workspace> --forge bitbucket                # Bitbucket Cloud

Gitea and Forgejo have no single public instance, so they need --forge-url. GitLab and Codeberg default to theirs. --forge-url alone is enough when the host is recognisable — --forge-url https://codeberg.org implies Codeberg.

For GitHub, --forge-url names a GitHub Enterprise Server instance: the listing goes to its /api/v3 API, and a URL that is not http or https with a host is refused rather than replaced with github.com. Its token comes from GH_ENTERPRISE_TOKEN (or GITHUB_ENTERPRISE_TOKEN), then gh auth token --hostname <host>, the same names the gh CLI uses. A github.com token is never sent to an Enterprise Server.

Credentials come from the environment, under the names each forge's own tooling already uses: GITLAB_TOKEN (or CI_JOB_TOKEN), and GITEA_TOKEN / FORGEJO_TOKEN / CODEBERG_TOKEN, and BITBUCKET_TOKEN. A corral-specific CORRAL_GITLAB_TOKEN or CORRAL_FORGE_TOKEN wins where both are set. GitHub keeps its existing ladder — explicit token, then the environment, then the gh CLI.

The same token authenticates HTTPS clones and pulls of private repositories, on every forge, presented the way each forge expects it (oauth2 for GitLab, x-token-auth for Bitbucket, the token as the password for the Gitea family). It is sent only to the forge's own HTTPS origin, never over plain http://.

--forge applies to everything that talks to a hosting service: corralctl <owner>, plan, profile and prune. Orphan detection and prune compare against the forge you listed from, and scope by host — a GitLab clone under the same owner name is never a GitHub orphan.

A profile can pin its own forge, so one config can cover owners on several services:

json
{  "profiles": {    "work":     { "owners": ["acme"],   "settings": { "forge": "gitlab" } },    "personal": { "owners": ["me"] },    "upstream": { "owners": ["forgejo"], "settings": { "forge": "codeberg" } }  }}

Reading was never host-specific. The index, the MCP server, symbol lookup and content search work on clones, so a repository you cloned by hand from anywhere has always been a first-class citizen.


Coming from another tool

Migration guides live in docs/migrating/: from ghq, from a hand-written clone script, from a single-repository code index, or from an unsorted ~/src.

Each says what carries over, what is genuinely different, and what corral will not do — nothing there requires re-cloning, and corralctl plan shows you the outcome before anything changes.


Cross-repository symbol lookup

Every code-context server can tell an agent where a symbol is defined in the repository it has open. corralctl is the only one that can answer across every clone on the machine, because it is the only one that knows they are all there.

jsonc
// corral_find_symbol { "name": "CanonicalRemote" }{  "query": "CanonicalRemote",  "repositories_search": 2,  "total_matched": 1,  "symbols": [    {      "repo": "Public/go/corral",      "symbol": "CanonicalRemote",      "kind": "func",      "file": "internal/git/git.go",      "line": 266,      "exported": true,      "language": "go"    }  ]}

Filter by kind (func, method, type, interface, const, var), scope to one repo, match by substring, or restrict to the exported_only surface. Methods are found by their bare name or as Receiver.Name. Test declarations are excluded by default — on a well-tested repository they outnumber everything else — and include_tests brings them back.

corral_repo_overview summarises one repository in a single call: its origin, file count, declaration counts by kind, and its most significant exported types and functions. Reach for it before reading files.

corral_search_code is the counterpart to corral_find_symbol: find_symbol answers where something is declared, search_code answers where it is written — call sites, configuration keys, the error string from a ticket. Literal by default, regex for RE2, and narrowable by repo, language or path_glob.

It searches only the files the file resource would serve, so a credential file can never match — otherwise search would be a way to read a refused file one line at a time. Test files are excluded unless include_tests is set, and the response says plainly when a bound was reached rather than presenting a partial answer as complete.

Indexed languages: Go, Python, TypeScript, JavaScript, Rust.

Go is parsed with go/ast — the compiler's own parser, so the index agrees with the language by construction. The rest are read by a line scanner, in the tradition of ctags: it recognises declaration syntax rather than building a syntax tree, because every mature parser for those languages is either CGO (tree-sitter), a port that lags the language, or larger than corral itself. ADR-0006 records why CGO is not available here.

The scanner runs over source that has had comment and string contents blanked out, so a class inside a docstring or a function inside a template literal is invisible to it.

Extracted symbols are cached under $XDG_CACHE_HOME/corral/symbols so the first lookup of a session is not the slow one. A cache hit still walks the repository — the walk is what produces the fingerprint the entry is keyed on — so an edited clone is never served stale. --symbol-cache off disables it; --symbol-cache <dir> moves it.

On a real 187-repository workspace a cross-repository lookup went from 6.9 s to 1.3 s: most of that from searching repositories concurrently rather than one after another, the rest from the cache. What it cannot do is resolve types, see through macros, or follow a declaration split across lines unusually — and it is wrong cheaply: a missed symbol falls back to reading files, and a spurious one is a wrong line in the right file. What it will not do is invent a symbol that does not exist.


Usage & Flags

Positional Arguments

bash
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

OptionShortDefaultDescription
--base-dir—$HOME/CodeRoot directory for cloned repos
--limit-l1000Maximum repositories to fetch
--concurrency-c1Number of concurrent worker threads
--dry-run-noffPreview actions without making changes
--orphans-ooffDetect local repositories no longer on GitHub
--protocol-phttpsProtocol to clone: ssh or https
--no-sync—offSkip pulling latest changes for existing clones
--force-sync—offForce 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-ioffLaunch the interactive selector TUI dashboard
--recurse-submodules—offInitialise submodules on clone and sync
--output—textOutput format: text, json, or ndjson
--auth—autoAuth mode: auto, token, or gh
--visibility—allFilter by visibility: all, public, private
--include-forks—onInclude forked repositories under Forks/
--include-archived—onInclude 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—0Shallow clone depth (0 disables shallow clone)
--api-request-timeout—30sDeadline for a single GitHub API request
--api-total-timeout—10mDeadline for the whole paginated fetch, including retries and backoff
--api-timeout——Deprecated. Was applied to both of the above at once, capping a whole listing at 30s. Sets both when given; warns on stderr
--log-level—infoDiagnostic 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.

bash
# 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.

Sync Options

corralctl sync [base_dir] takes the persistent --base-dir, --dry-run and --log-level flags, plus:

OptionShortDefaultDescription
--to——Destination as <forge>[:<owner>][@<url>]; repeatable, at least one required
--protocol-phttpsPush transport: https (the token authenticates) or ssh (your keys do)
--concurrency-c4–8Repositories mirrored at once, sized from the host
--timeout—5mDeadline for one repository on one destination
--output—textOutput format: text, json, or ndjson
--auth—autoGitHub authentication mode: auto, token, or gh
--api-request-timeout—30sDeadline for a single forge API request

Operational Commands

bash
corralctl status --base-dir ~/Codecorralctl doctor ~/Codecorralctl doctor ~/Code --refresh-tagscorralctl doctor ~/Code --prune-worktreescorralctl doctor ~/Code --exclude winget-pkgs --timeout 30scorralctl plan sebastienrousseau --base-dir ~/Codecorralctl prune sebastienrousseau --base-dir ~/Code --dry-runcorralctl prune sebastienrousseau --base-dir ~/Code --yescorralctl adopt ~/Projects --forge github --owner sebastienrousseau

status inventories local clones, doctor audits workspace hygiene, worktrees, disk budget, exclusions, and Finder tags, and lists any .corral-deleting-* directory an interrupted corral_delete_repo left behind (it never removes one; inspect it, then delete it or rename it back), plan emits a non-mutating reconciliation, prune removes only upstream-orphaned clones that pass the unpublished-work checks, and adopt discovers untracked local repositories to relocate and link to remote forges. 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>:

json
{  "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 corralctl modules, see the self-contained, copy-pasteable Go code examples in the examples directory:

  1. Interactive Selector — Programmatically configure and launch the selection checklist TUI in AltScreen mode.
  2. GitHub Repository Fetcher — Query the GitHub REST API using github.FetchReposWithOptions with stars sorting and language constraints.
  3. Git Syncing — Call the git helper package to perform clones, query branches, and resolve origin URLs.
  4. Engine Orchestrator — Integrate the core engine engine.Run to run repository syncing with custom filters, layout structures, and dry-run pre-flights.

Troubleshooting

Error MessageCauseSolution
ERROR: git not found on PATHGit 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/repoAuthentication 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?
    Yes. corralctl integrates with six major git forges: GitHub, GitLab, Gitea, Forgejo, Codeberg, and Bitbucket.
  • What happens to repositories deleted on remote forges?
    Normal reconciliation never deletes them. --orphans reports them; the explicit prune command can remove verified-safe orphans after --yes confirmation.
  • 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 the Other/ ecosystem category (e.g. Public/Other/my-repo).

THE ARCHITECT ᛫ Sebastien Rousseau
THE ENGINE ᛞ EUXIS ᛫ Enterprise Unified Execution Intelligence System


Documentation

ResourceWhere
User manualhttps://doc.corrallib.com
API referencehttps://pkg.go.dev/github.com/sebastienrousseau/corralctl
Developer docsDEVELOPMENT.md — toolchain and every CI gate reproduced locally
Architecturedocs/ARCHITECTURE.md
Decision recordsdocs/adr/
Security modeldocs/security-model.md
Packagingdocs/packaging.md — for distribution maintainers
SupportSUPPORT.md

Once installed, man corralctl works offline, and every subcommand has its own page (man corralctl-mcp).


When not to use corralctl

corralctl is opinionated, and the opinions do not suit everyone.

  • You want a full mirror or backup. corralctl clones working copies to be read and edited. For archival mirroring use git clone --mirror or a purpose-built tool; corralctl will not preserve every ref or hold a guaranteed-complete copy.
  • You need a forge corral does not list from. GitHub, GitLab, Gitea, Forgejo, Codeberg and Bitbucket are supported; anything else is not. Reading is forge-neutral — a clone from any host is a first-class citizen in the index, the MCP server and symbol lookup — but corralctl <owner> only knows those six.
  • Your repositories must stay where they are. corralctl's value is a consistent layout, and the default reorganises clones into Collection/Bucket/Name. If a fixed path matters, use --layout to match your existing tree — or a different tool.
  • You want code intelligence. The MCP server does index symbols and search file contents across every clone, but shallowly: a declaration is a name, a kind, a file and a line. It does not resolve types, find references, or rename — an LSP does those, one project at a time, and corral is the layer that tells an agent which project to open.
  • You need Windows without WSL. Binaries are published for Windows, but macOS Finder tags are a no-op there and the experience is less tested than on macOS and Linux.

Requirements & toolchain policy

GoThe go directive in go.mod — currently 1.26.10
git2.31 or newer, on PATH (credentials reach git through GIT_CONFIG_COUNT, which 2.31 introduced)
ghOptional; only for --auth gh

The Go floor is stated in exactly one place, go.mod, and CI sets GOTOOLCHAIN=auto so it cannot disagree with a workflow input.

Policy for raising it. The floor may rise in any release when a standard-library fix or language feature justifies it, and the reason is recorded in that release's CHANGELOG entry. corralctl makes no distro-LTS compatibility promise — an aspirational claim without a table mapping distro toolchains to the floor would be worse than none. Packagers should check go.mod on every version bump rather than assume the floor held.


Stability guarantees

corralctl is pre-1.0 and follows SemVer, with the patch digit moving for everything until 1.0.

The breaking axis is behaviour, not signatures. For a tool that moves and deletes directories, a change to what it does to a workspace is breaking even when no flag or function signature moves. Specifically, these are treated as breaking:

  • A change to the default layout, or to how a repository maps onto a path
  • A change to what --output json / ndjson emits, beyond added fields
  • A change to an exit code
  • A refusal becoming permissive: any case where corralctl used to decline to delete, prune or migrate and now proceeds
  • A change to the MCP tool or resource surface that an existing client would notice

Added fields, new flags with inert defaults, and new refusals are not breaking.

Deprecation window. A deprecated flag or tool keeps working for at least one minor release after the release that announces it, and warns on stderr — never on stdout, which carries the selected output format.


Security & hardening

Reporting. Do not open a public issue. Follow the private process in SECURITY.md; the response SLA is stated there.

Posture. corralctl runs with the user's own credentials against the user's own machine, so the threat model is about limiting blast radius, not crossing a privilege boundary. Full detail in docs/security-model.md.

  • Credentials never reach argv or .git/config. The forge's token is handed to git as an http.extraheader scoped to that forge's HTTPS origin (https://github.com/, https://gitlab.com/, your instance), so a submodule on another host cannot see it. Clone errors deliberately omit their arguments so a URL-embedded credential cannot reach a log.
  • Git runs non-interactively, always. GIT_TERMINAL_PROMPT=0 and friends, so an unattended run fails rather than hanging on a prompt.
  • Destructive operations refuse without evidence. prune and corral_delete_repo decline when a clone holds uncommitted, unpushed, stashed, submodule or gitignored work — the last because git status hides exactly the files no remote has a copy of.
  • The MCP server is sandboxed and gated. Path resolution canonicalises symlinks on both sides and compares path segments, not string prefixes, and no repository file is read through a symlink. Write tools require --enable-mutations; deletion requires a second flag. Every mutation is written to an audit log before it is attempted.
  • Memory safety comes from Go; there is no CGO anywhere (CGO_ENABLED=0), so released binaries are static and free of libc coupling.

Fuzzing. Fuzz targets cover the parsing and path-resolution boundaries — layout evaluation, language normalisation, remote-URL parsing, and the sandbox check that must never let a path escape its root. They run on every push for a fixed duration. corralctl is not enrolled in OSS-Fuzz.

Supply chain. Releases are signed with keyless cosign, carry SLSA build provenance and a CycloneDX SBOM, and are built with -trimpath and a commit-pinned timestamp so two builds of a commit are byte-identical. Every GitHub Action is pinned by commit SHA and the container base by digest. govulncheck runs on every push.


License

Licensed under the GNU General Public License v3.0.

Back to Top

來源:README.md,提交 880dd20

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.0.36最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分