repository-manager exposes the same capability three ways: as MCP tools an
agent calls, as a Python API (Git) the operator import, and as a command-line
interface.
Once deployed, the server registers consolidated, action-routed tool modules. Each module groups related methods behind one tool to keep the LLM context small, and each can be toggled independently with its environment variable.
| Module | Toggle | Default | Action-routed methods |
|---|---|---|---|
| Misc | MISCTOOL |
True |
health check and miscellaneous helpers |
| Git Operations | GIT_OPERATIONSTOOL |
True |
clone, pull, push (legacy raw is permanently retired) |
| Workspace Management | WORKSPACE_MANAGEMENTTOOL |
True |
list, list_branches, maintain, remediate, save, setup, template |
| Project Management | PROJECT_MANAGEMENT_TOOL |
True |
build, install, validate, validate_status |
Example agent prompts that map onto these tools:
- "List every project in the workspace." →
workspace_management list - "Pull the latest changes for all repositories." →
git_operations pull - "Roll scion-sdk 0.6.4 to every connector." →
rm_rollout(plan,apply,status,release)
Git (repository_manager.repository_manager) is a workspace-aware client for bulk
Git operations and workspace introspection.
import os
from repository_manager.repository_manager import Git
git = Git(path=os.environ["REPOSITORY_MANAGER_WORKSPACE"])
# Reads
projects = git.get_workspace_projects() # list of managed project names
project_map = git.get_project_map() # name -> absolute path
branches = git.list_branches() # name -> current branch
# Bulk operations
git.pull_projects() # pull every managed repository
git.clone_projects(["scion-sdk"]) # clone selected projects
# Validation
result = git.validate_single_project(project_map["scion-sdk"])Load a workspace from its declarative workspace.yml:
import os
git = Git(path=os.environ["REPOSITORY_MANAGER_WORKSPACE_ROOT"])
git.setup_from_yaml(os.environ["WORKSPACE_YML"]) # use the XDG-managed manifestThe packaged manifest contains only environment references for the workspace root, private Git origin, and deployment DNS suffix. Inject those values at runtime; the manifest never persists a user name, machine path, or private endpoint.
The repository-manager console script drives the full maintenance lifecycle from
the command line.
# Set up the workspace from its declared configuration
repository-manager --setup
# Enumerate branches across every managed repository
repository-manager --branches
# Clone and pull in bulk
repository-manager --clone
repository-manager --pull--validate runs a full pre-release validation across the workspace.
To roll a dependency floor across the fleet (one pull request per repository,
auto-merge, resumable ledger, tag after merge), use the rollout command:
repository-manager rollout --dependency scion-sdk --floor 0.6.4 --dry-run
repository-manager rollout --dependency scion-sdk --floor 0.6.4
repository-manager rollout status --dependency scion-sdk --floor 0.6.4
repository-manager rollout release --dependency scion-sdk --floor 0.6.4See Dependency rollout.
When multiple agents and humans develop the same repositories at once, each unit of
work runs as an isolated lane. The --lane verbs make that lifecycle
executable rather than a convention:
# Open a lane: a worktree PLUS a partitioned cargo target dir, pytest basetemp,
# TMPDIR and PRE_COMMIT_HOME -- and a preflight that proves the isolation.
repository-manager --lane start --lane-repo scion-sdk --lane-branch lane/my-change
# Adopt the environment in your shell.
eval "$(repository-manager --lane env --lane-path . --lane-shell)"
# Diagnose a lane that is behaving impossibly. Mutates nothing, answers in <1s.
repository-manager --lane doctor --lane-path .
# Close it out: blocking preflight, then hand the branch to the merge queue.
repository-manager --lane finish --lane-path . --lane-base maindoctor is the one to reach for when a test fails in a way that cannot be true,
a build will not go green, a merge keeps being refused, or work has gone missing.
Each check reports its verdict, its evidence, and a literal remedy command;
fail blocks finish, warn never does. What it checks, and why each check
exists:
| Check | Failure it exists to catch |
|---|---|
not-canonical |
editing a canonical checkout a background git reset can discard |
no-worktree-venv |
a foreign or stale worktree-local .venv shadowing the workspace environment; the exact all-extras environment managed by scripts/uv_workspace.py is accepted |
cargo-partition |
a shared CARGO_TARGET_DIR, which corrupts concurrent builds rather than merely serializing them |
precommit-home |
the shared pre-commit store, where a crash inside staged_files_only()'s window loses unstaged work to an orphaned patch |
pytest-basetemp |
concurrent lanes contending on one pytest temp root |
shared-stash-ref |
refs/stash — one ref shared by every worktree of the repository |
test-runner |
uv run pytest silently resolving the system interpreter |
canonical-clean |
a dirty canonical tree, which blocks every lane's landing |
merge-queue-config |
a repository declaring no gates, which the queue refuses rather than defaults |
base-drift |
reasoning about the branch tip when the merged tree is what lands |
committed-work |
uncommitted work, the only kind a tree reset can take |
The same action core backs the rm_lane MCP tool and
python -m repository_manager.lane_doctor, so the surfaces cannot drift.
Working-tree mutation safety (CONCEPT:RM-SAFE-COMMIT, CONCEPT:RM-DESTRUCTIVE-GUARD, CONCEPT:RM-TREE-REPAIR)
The pure safety primitives are available to lane and job implementations without changing MCP registration:
from repository_manager.destructive_guard import guard, issue_override_token
from repository_manager.safe_commit import safe_commit
from repository_manager.tree_repair import diagnose, repair
commit = safe_commit("/path/to/lane", "implement the reviewed change")
finding = diagnose("/path/to/lane")
if finding["finding"] != "clean":
repair("/path/to/lane", finding=finding)
# Destructive argv is refused unless this explicit token is consumed once.
decision = guard(["git", "clean", "-fd"], path="/path/to/lane")
override = guard(
["git", "clean", "-fd"],
path="/path/to/lane",
lane="lane-id",
override=issue_override_token(
authorization=operator_authorization_callback,
audit_context={
"actor": "operator",
"lane": "lane-id",
"repository": "/path/to/lane",
"argv": "git clean -fd",
"operation": "git clean -fd",
},
),
)Override context is mandatory and exact: the repository is resolved before
consumption and argv must be normalized fixed-argument text. Unknown commands,
alternate --git-dir/--work-tree targets, aliases/config injection, and
non-git executables are refused. The mutation lease is cooperative; a raw
external Git process that ignores it remains outside this boundary, and a
post-execution tree invariant is returned when such a process races it.
safe_commit stages deletions and untracked files before invoking the configured
gate and returns an explicit nothing_left_unstaged assertion plus the staged
path set, commit SHA, and refreshed tree baseline evidence. destructive_guard refuses reset --hard, broad
checkout/clean/stash operations, forced branch deletion, and force-push by
default. A token cannot be minted from configuration or an environment flag;
the caller must supply a live authorization callback and
actor/lane/repository/argv/operation audit context. An override first creates
refs/lane-backup/pre-destructive/<lane>-<uuid> and parks dirty WIP at the
lane-private ref; a missing snapshot is a refusal. Use stash_guard.park and
stash_guard.unpark for a temporary clean tree instead of the shared
refs/stash stack.
flowchart LR
TREE[Dirty worktree] --> STAGE[git add -A]
STAGE --> PROVE{nothing left unstaged?}
PROVE -->|yes| GATE[Configured gate]
GATE --> RESTAGE[Restage formatter output]
RESTAGE --> COMMIT[Commit + SHA]
DANGER[Destructive argv] --> REFUSE[Refuse + safer alternative]
DANGER -->|single-use override| SNAP[Backup ref + private WIP park]
SNAP --> EXEC[Execute fixed argv]
flowchart LR
V{Worktree has .venv} -->|No| OK[Isolation check passes]
V -->|Yes| M{Launcher marker config and interpreter are valid}
M -->|Yes| MANAGED[Managed all-extras environment passes]
M -->|No| FAIL[Foreign or stale environment blocks finish]
For a development bootstrap, the root workspace.yml is the only authority.
The Arbora XDG copy retains its canonical bytes, including private runtime
values. This package's workspace.yml is a separate portable projection: the
workspace root becomes ${REPOSITORY_MANAGER_WORKSPACE_ROOT}, private URL origins
become ${ARBORA_REPO_ORIGIN}, and private service suffixes become
${AGENT_UTILITIES_SERVICE_DOMAIN_SUFFIX}. The gate rejects embedded URL
credentials, secret fields, and absolute paths outside the declared workspace
before either destination can be written.
Validate all three before cloning. --manifest-check never writes and exits 1
when either mirror has drifted. --manifest-sync --manifest-dry-run previews
the two updates. A real --manifest-sync stages both files first, replaces them
atomically, and rolls back the first replacement if the second one fails.
repository-manager --manifest-check \
--manifest-source <workspace-root>/workspace.yml \
--manifest-profile development
repository-manager --manifest-sync --manifest-dry-run \
--manifest-source <workspace-root>/workspace.ymlEvery destination is overridable for an isolated bootstrap or test. The command does not search for a source manifest and never treats the packaged seed as authority. The runtime mirror must match the canonical source byte-for-byte; the portable seed is compared semantically, so seed-only formatting changes do not trigger a replacement. Its JSON result reports only roles, SHA-256 digests, declared profiles/selectors, and selected workspace-relative repository identifiers; it omits local paths.
flowchart LR
ROOT[Canonical root workspace.yml] --> GATE[repository-manager manifest gate]
GATE -->|canonical projection / atomic replace| XDG[Arbora runtime copy]
GATE -->|portable projection / atomic replace| SEED[Packaged distribution seed]
GATE -->|profile + selector resolution| BOOT[Bounded development bootstrap]
Profiles make a development subset explicit without changing the repository
tree. A profile names one or more selectors. Selectors use stable,
workspace-relative identifiers, unambiguous basenames, or *:
profiles:
development:
selectors: [core]
selectors:
core:
include:
- agent-packages/scion-sdk
- agent-packages/agents/repository-managerThe rules are fail-closed:
- a missing
includestarts with every repository, whileinclude: []starts empty; excludeis applied within each selector, then multiple selectors are unioned;*cannot be combined with another value in the same list;- every profile reference and every selector member is validated, even when that profile was not requested;
- a basename that occurs in more than one directory is rejected as ambiguous; use its workspace-relative identifier instead.
A manifest with no requested profile or selector exposes every declared
repository. Automation should consume the reported selected_repositories
identifiers directly; do not collapse them to basenames when the manifest
contains duplicate names.
With no -f and no WORKSPACE_YML, DEFAULT_WORKSPACE_YML is the packaged seed
repository_manager/workspace.yml; --setup and --clone provision from it. The runtime
copy at ~/.config/arbora/workspace.yml is used only by manifest synchronisation
(workspace_manifest), never as an implicit override of the packaged default. The seed
reads ${REPOSITORY_MANAGER_WORKSPACE_ROOT} (workspace root) and ${ARBORA_REPO_ORIGIN}
(private git origin).