mcp server
repokeeper
Repository automation and policy tooling.
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 21, 2026RepoKeeper
A cross-platform multi-repo hygiene tool for developers who work across multiple machines and directory layouts.
RepoKeeper inventories your repositories, reports drift and broken tracking, and performs safe sync actions (remote refresh by default, optional local update when explicitly enabled) — without hidden working-tree mutation or submodule recursion.
Features
- Discover repositories across configured root directories (Git by default, optional experimental Hg)
- Report per-repo health: dirty/clean, branch, tracking status, ahead/behind, stale upstreams
- Sync safely with fetch/prune-first behavior; optional
--update-localusespull --rebaseunder explicit conditions - Registry is stored in
.repokeeper.yamlwith staleness detection - Repo-local metadata can be read from
.repokeeper-repo.yamlorrepokeeper.yamland surfaced in JSON, describe output, and MCP repository context - Remote-ref hygiene reports stale remote-tracking refs in status, sync plans, and JSON/MCP output without pruning them during inspection
- CLI-first with tabular (
table/wide) and JSON output formats - Cross-platform — macOS, Windows, Linux (incl. WSL)
Multi-VCS (Experimental)
RepoKeeper is Git-first. The Mercurial (hg) adapter is available as an experimental backend.
- Default backend remains
git - Opt in per command with
--vcs git,hg - Mixed roots are auto-detected per repo path when multiple backends are selected
Current experimental limits:
hg: discovery/status and safepull-based fetch are supportedhg:reconcile --update-local(rebase/push/stash flows) is intentionally unsupported and is skipped with a reason- Repair and remote mismatch reconciliation flows remain Git-oriented
Install
See INSTALL.md for full install and upgrade instructions.
Homebrew (cask)
brew tap skaphos/tools
brew install --cask skaphos/tools/repokeeper
Linux packages
# Debian/Ubuntu
sudo dpkg -i repokeeper_<version>_amd64.deb
# Fedora/RHEL
sudo rpm -i repokeeper-<version>-1.x86_64.rpm
Download from the Releases page. There is no
hosted apt or yum repository — a .deb on a release page is not an implied apt repo. Upgrading
means downloading the next release's package.
From release binaries
Download the latest release from the Releases page.
From source
go install github.com/skaphos/repokeeper/v2@latest
The /v2 suffix is required from 2.0.0 onward — Go encodes the major version in the module path.
Staying on v1 needs no change: the unsuffixed path keeps resolving the newest v1 release, so
go install github.com/skaphos/repokeeper@latest continues to install the latest v1.
Container (MCP server)
docker pull ghcr.io/skaphos/repokeeper:latest
See MCP Server for the client configuration and its workspace contract.
Upgrading
RepoKeeper ships no update subcommand. Upgrade through the channel you installed from — the
table below is the complete list. The reasoning is recorded in
ADR-0016: a self-updater that cannot be skipped must
verify in-process, and the dependency that makes that possible more than doubles the binary for
every user on every channel to serve one install path.
| Installed via | Upgrade with |
|---|---|
| Homebrew cask | brew upgrade --cask skaphos/tools/repokeeper |
.deb | Download the next release's .deb, then sudo dpkg -i repokeeper_<version>_amd64.deb |
.rpm | Download the next release's .rpm, then sudo rpm -U repokeeper-<version>-1.x86_64.rpm |
go install | go install github.com/skaphos/repokeeper/v2@latest — note the /v2; the unsuffixed path stays on v1 |
| Release archive | Download the next archive and replace the binary on your PATH |
| Container image | docker pull ghcr.io/skaphos/repokeeper:latest |
Not sure which you have? repokeeper version reports whether the binary was produced by the release
pipeline or installed from source, which narrows it.
Quick Start
# Bootstrap config and run initial scan for this directory
repokeeper init
# Scan your roots for git repos
repokeeper scan
# Scan/get across mixed git + hg roots
repokeeper scan --vcs git,hg
repokeeper get --vcs git,hg
# Check the health of all repos
repokeeper get
# Preview repo-local metadata for one tracked repo
repokeeper index github.com/org/repo
# Write repo-local metadata after preview
repokeeper index github.com/org/repo --write
# Fetch/prune all repos safely
repokeeper reconcile
Expected User Flow
- From the directory you want to manage, run
repokeeper init. initcreates.repokeeper.yaml, sets that directory as the default root, and performs an initial scan.- Run
repokeeper getto review repo health and identify issues (dirty worktrees, gone upstreams, missing repos). - Run
repokeeper reconcileto safely fetch/prune across registered repos. - Re-run
repokeeper scanwhenever clones are added, moved, or removed so the embedded registry stays current. - If needed, widen scope for a specific run with
repokeeper scan --roots <dir1,dir2,...>.
Scans preserve each known checkout's identity and local labels, including when another checkout of the same repository is temporarily unavailable. Missing checkouts remain registered. A new path gets a distinct checkout_id; a matching directory name and an absent old path do not establish a move. After an unrecorded move, scan retains the old entry as missing and registers the new path separately.
Scan treats each discovered repository root as a boundary and does not descend into it. Nested repositories or linked worktrees stored inside an already discovered working tree are therefore not registered separately and do not require extra exclude patterns.
Commands
Detailed command breakdown moved to docs:
- docs/commands.md - full command reference, flags, and behavior notes
- docs/adapter-contract.md - stable machine-readable surfaces, adapter support declarations, and runtime compatibility checks
- docs/skills/README.md - installable agent skill for OpenCode/compatible runtimes
- docs/man/README.md - manpage generation and release integration plan
Quick highlights:
repokeeper getandrepokeeper reconcileare direct command forms (... reposaliases still supported).repokeeper edit <selector>opens a single repo entry YAML in your editor ($VISUAL/$EDITOR), validates, then saves.describe,label,edit,delete,move, andindexaccept an absolute path, barecheckout_id,repo_id, or qualifiedrepo_id@checkout_id. Relative paths work from the current directory or config root; symlink paths are resolved when available. Ambiguous IDs list matching qualified IDs and absolute paths.repokeeper label <selector>manages machine-local labels via--set key=valueand--remove key.repokeeper index <selector>interactively proposes repo-local metadata and writes it only when--writeis passed.repokeeper index repos --local-selector ... --promote-local-labels --writeexplicitly bulk-promotes machine-local labels into repo-local metadata for selected repos.- Running
repokeeperwith no subcommand prints help. There is no interactive dashboard; see ADR-0017. Uselabelfor repo labels andindexfor repo-local metadata. repokeeper installregistersrepokeeper mcpwith your agent runtime (Claude Code, Codex, OpenCode, or Grok);repokeeper install listshows registration state;repokeeper uninstallremoves the entry.- MCP
plan_syncmatches CLIreconcile --dry-runfor equivalent health filters and update options, including skip/failure reasons and repositories after a failed entry. Sync does not accept label filters; unsupported MCPlabel_selectorrequests are rejected. - Adapter JSON uses a versioned object with named payloads; see the adapter contract for fields and migration notes. MCP config and registry resources use snake_case fields and omit unknown observation timestamps.
describe -o jsonreads live metadata without updating the on-disk cache. Batch commands may return valid JSON alongside a nonzero exit when individual repositories fail. getreports repository errors on stderr, includes their count in the completion summary, and addsERROR_CLASSto the default table when errors are present. Error diagnostics remain visible with--quiet; JSON stays on stdout.getsupports shared label filtering with-l/--selectorand machine-local label filtering with--local-selector(keyandkey=value, comma-separated AND).addsupports metadata on create with--labeland--annotation(repeatablekey=value).
MCP Server (Agent Integration)
RepoKeeper includes a built-in MCP server for agent runtimes that support the Model Context Protocol (Claude Code, Codex, OpenCode, Grok, Cursor, Windsurf, etc.). MCP is the preferred integration path for inspection and planning workflows — it provides typed tool schemas, structured JSON responses, and automatic tool discovery.
The fastest setup is repokeeper install, which auto-detects Claude Code, Codex, OpenCode, and Grok and writes the server entry into the runtime's config:
repokeeper install # register with every detected runtime
repokeeper install list # inspect registration state
repokeeper uninstall # remove entries (prompts unless --yes)
For runtimes without a flat-file MCP config RepoKeeper can adapter (Cursor, Windsurf), repokeeper install --manual prints the snippet you paste into their settings UI. See docs/mcp-setup.md for per-runtime paths, the full install/uninstall flag reference, and the MCP tool catalog.
The MCP server is primarily intended for inspection and planning workflows with browsable resources, but the current tool surface also includes some explicit state-changing operations. Those mutation-capable tools follow the same safety gates and opt-in behavior as their CLI counterparts, so agents and users should treat them as execution surfaces when enabled. The server reloads workspace configuration and registry state from disk for every request, keeping long-running sessions current without silently reverting manual configuration edits.
RepoKeeper is published to the MCP registry as io.skaphos/repokeeper.
Running the MCP server from a container
{
"mcpServers": {
"repokeeper": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "/home/me/work/skaphos:/home/me/work/skaphos:ro",
"ghcr.io/skaphos/repokeeper:latest",
"mcp", "--config", "/home/me/work/skaphos/.repokeeper.yaml"
]
}
}
}
Four things about this configuration are deliberate, and each is a constraint rather than a preference:
- The workspace is mounted at its identical host path.
.repokeeper.yamlrecords absolute paths, so mounting anywhere else makes every registry entry miss and the inventory reports every repository as missing. There is no path remapping — the registry means the same thing inside and outside the container. - The mount is read-only by default. All nine inspection tools work normally. The five mutating
tools (
scan_workspace,execute_sync,set_labels,add_repository,remove_repository) refuse and name the read-only mount as the reason. They stay advertised rather than disappearing, so a refusal is explainable rather than a silently missing capability. For read-write use, drop the:rosuffix, add--user "$(id -u):$(id -g)"so files land with your ownership, and supply git credentials. - One configured entry serves exactly one workspace root. The CLI finds its registry by walking upward from the current directory, which is what lets several purpose-specific roots self-select by where you are. A container's working directory and mounts are fixed before any tool call, so that cannot be reproduced. If you keep several roots, add one server entry per root — and for a position-sensitive multi-root workflow, the native binary remains the better path.
- The container supports Git only. Mercurial is not installed in the image.
Repo-local metadata
RepoKeeper can read optional repo-root metadata from either .repokeeper-repo.yaml or repokeeper.yaml.
-
.repokeeper.yamlremains the machine-local workspace config and registry. -
.repokeeper-repo.yamlis the source-controlled repo metadata surface. -
Reads are automatic and read-only in
scan,get,describe, and the MCP repository resources. -
labelremains machine-local only. -
index --promote-local-labels --writeexplicitly promotes machine-local labels into shared repo metadata. -
index repos --selector .../--local-selector ...previews or writes that promotion across multiple selected repos. -
Inspection refreshes repo-metadata snapshots in memory when on-disk metadata changes. Plain
get/statusreads do not persist those refreshes; explicit scan and reconciliation operations save the registry. -
--yesskips the final write confirmation, but does not change the requirement to pass--write. -
Existing
repo_metadata.labelswin on key conflicts; promoted local labels only fill missing keys.
RepoKeeper keeps two identity layers:
repo_id: stable upstream identity, used as the cross-machine join key.checkout_id: machine-local checkout identity, used to distinguish multiple local clones of the samerepo_id.
Example:
apiVersion: repokeeper/v1
kind: RepoMetadata
repo_id: ops-runbooks
name: Ops Runbooks
labels:
role: runbooks
domain: ops
entrypoints:
readme: README.md
paths:
authoritative:
- runbooks/
- templates/
low_value:
- generated/
provides:
- runbook-templates
related_repos:
- repo_id: internal-docs
relationship: references
Global flags
--verbose/-v— increase verbosity (repeatable:-vvfor debug)--quiet/-q— suppress non-essential output--config <path>— override config file location--no-color— disable colored output (also respectsNO_COLORenv var)--yes— accept mutating actions without interactive confirmation
Configuration
By default, repokeeper init writes .repokeeper.yaml in your current directory.
Runtime commands (scan, get, reconcile) resolve config in this order:
--config <path>REPOKEEPER_CONFIGenvironment variable- Nearest
.repokeeper.yamlfound by walking upward from the current directory - Global fallback in platform config dir:
- Linux:
$XDG_CONFIG_HOME/repokeeper/config.yaml(default~/.config/repokeeper/config.yaml) - macOS:
~/Library/Application Support/repokeeper/config.yaml - Windows:
%APPDATA%\\repokeeper\\config.yaml
Flag precedence (highest to lowest):
- Explicit command flags (
--config,--only,--field-selector,--selector, etc.) - Environment variables where supported (
REPOKEEPER_CONFIG,NO_COLOR) - Values loaded from the resolved config file
- Built-in command defaults
Selector precedence:
--field-selectorwhen set--onlywhen--field-selectoris not set- Providing both in one command is rejected
-l/--selectoris applied as an additional shared-label filter on the resulting repo set--local-selectoris applied as an additional machine-local label filter on the resulting repo set
Example config:
apiVersion: "skaphos.io/repokeeper/v1beta1"
kind: "RepoKeeperConfig"
exclude:
- "**/node_modules/**"
- "**/.terraform/**"
- "**/vendor/**"
registry:
updated_at: "2026-02-14T10:00:00Z"
repos: []
defaults:
concurrency: 8
timeout_seconds: 60
The default scan/display root is inferred from the directory containing the active config file.
Safety
RepoKeeper is designed to be safe to run on repos with dirty working trees:
- By default, sync does not run checkout, reset, merge, or other hidden branch-navigation behavior
- Never updates or recurses into submodules
- Fetch uses
--no-recurse-submodulesand-c fetch.recurseSubmodules=falseas belt-and-suspenders - All mutating commands support
--dry-run scan,get,describe,add, andimportdo not create or rewrite repo-local metadata fileslabelandeditdo not write repo-local metadata filesindex --writeis the explicit repo-local metadata write path
Optional local checkout update:
repokeeper reconcile --update-localaddspull --rebaseafter fetch, but only when all of these are true:- working tree is clean (or
--rebase-dirtyis set) - branch is not detached
- branch tracks an upstream
- branch is not ahead
- branch is not diverged unless
--forceis set - branch is not matched by
--protected-branches(default: none) unless--allow-protected-rebaseis set --rebase-dirtystashes changes, rebases, then pops the stash--push-localpushes local commits when a branch is ahead (instead of skipping with "local commits to push")--continue-on-errorkeeps processing all repos after per-repo failures (default true)- In dry-run/preflight mode, these checks are evaluated up front so the plan calls out which repos are candidates for
fetch + rebaseversusskip local update (...).
Branch switching and prune execution are separate workflow areas rather than hidden sync side effects.
Documentation
- docs/commands.md - command reference
- docs/adapter-contract.md - stable machine-readable contract for external adapters
- docs/mcp-setup.md - MCP server setup for agent runtimes
- docs/skills/README.md - user-scope agent skill installation and usage
- docs/man/README.md - manpage generation plan
- DESIGN.md — full design specification and architecture
- TASKS.md — implementation milestones and task tracking
- CONTRIBUTING.md — contributor workflow and PR expectations
- RELEASE.md — release and tagging process
- INSTALL.md — installation and upgrade paths
Development
Spec Kit includes repo-local Codex skills in .agents/skills/speckit-*/SKILL.md.
Open Codex in this repository and invoke $speckit-specify, $speckit-plan,
$speckit-tasks, or $speckit-implement to use the specification workflow.
The skills share the constitution and templates in .specify/ and feature
artifacts in specs/. Claude remains the default Spec Kit integration; Codex
skills can be invoked directly without changing that default. If the skills
do not appear in an existing Codex session, restart the session.
With the Specify CLI installed, run specify integration status to check the
setup and specify integration upgrade codex to refresh the generated skills.
The fast unit tier remains go test ./.... Process-level coverage is integration-tagged and excluded from the ordinary package graph:
go -C tools tool task test-integration
go -C tools tool task test-integration-race
go -C tools tool task test-default-excludes-e2e
See test/e2e/README.md for recipe authoring, focused CLI/MCP runs, isolation guarantees, compatibility matrix commands, and failure diagnosis.
The Ginkgo unit command (also used by task test) explicitly excludes E2E packages because Ginkgo discovers test files without evaluating their integration build tags. Use test-integration above to run those packages.
# List all task targets without installing task globally
go -C tools tool task --list
# Run unit tests
go run github.com/onsi/ginkgo/v2/ginkgo@v2.33.1 --skip-package=e2e ./...
# Run with coverage
go test -coverprofile=coverage.out ./...
# Run with coverage and enforce per-package thresholds
go -C tools tool task test-cover-check
# Run coverage and print lowest-covered packages/functions
go -C tools tool task coverage-report
# Run performance benchmarks and append historical record
go -C tools tool task perf-bench
# Lint
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.14.0 run ./...
# Build locally
go build -o repokeeper .
# Build locally (task runner)
go -C tools tool task build
# CI-style full platform build (task runner)
go -C tools tool task build-ci
# Run standard CI pipeline locally (lint/test/staticcheck/vuln/build-ci)
go -C tools tool task ci
# Format imports + code
go -C tools tool task fmt
Security
Report suspected vulnerabilities privately using the security policy. Please do not use public issues for security reports.
License
Third-Party Notices
RepoKeeper includes third-party open source components.
- Runtime dependency notices for distributed binaries live in
third_party_licenses/runtime/with an inventory inthird_party_licenses/runtime-report.csv. - The maintenance workflow and attribution expectations live in
THIRD_PARTY_NOTICES.md.
When dependencies change, regenerate the notice artifacts before opening a PR:
go -C tools tool task notices
Skaphos is a project of Rillan AI LLC, a Missouri limited liability company. © 2026 Rillan AI LLC. Released under the MIT License.