repoverlay is a CLI tool that overlays config files into git repositories without committing them. It supports local overlays, GitHub repository overlays, and shared overlay repositories.
src/
├── main.rs # CLI entry point (minimal - delegates to lib)
├── cli/
│ ├── mod.rs # CLI command definitions, argument parsing, and dispatch (clap)
│ └── commands/ # Handlers: browse, cache, claude, copilot, create, edit,
│ # library, marketplace, move, profile, source, sync
├── lib.rs # Core library: apply_overlay, composition resolution, glue
├── resolve.rs # Source resolution (source string → local path)
├── reference.rs # Input reference parsing
├── status.rs # show_status / show_status_json
├── remove.rs # remove_overlay
├── create.rs # create_overlay and update_overlays
├── update.rs # CLI version string and self-update check
├── state.rs # State persistence (in-repo and external backup)
├── github.rs # GitHub URL parsing
├── cache.rs # GitHub repository cache management
├── config.rs # Global and per-repo configuration (CCL format)
├── sources.rs # Multi-source overlay resolution with priority ordering
├── overlay_repo.rs # Shared overlay repository integration
├── upstream.rs # Upstream repository detection for fork inheritance
├── library.rs # In-repo overlay library management
├── git.rs # Git subprocess helpers and repository inspection
├── git_exclude.rs # .git/info/exclude section management
├── json_merge.rs # JSON deep merge for --merge
├── fuzzy.rs # Fuzzy matching for overlay name suggestions
├── fs_util.rs # Filesystem utilities (atomic writes, copies)
├── path_safety.rs # Path safety validation (traversal protection)
├── overlay_name.rs # Normalized overlay name newtype
├── detection.rs # File discovery for overlay creation
├── selection.rs # Interactive file selection UI
├── widgets/ # Reusable ratatui UI components
│ └── multi_select_tree.rs # Tree widget with tri-state checkboxes
├── profile.rs # Profile configuration, merge rules, state metadata
├── plugin.rs # Plugin reference model and marketplace resolution
├── profile_plan.rs # Profile apply planning and transactional execution
├── profile_applicators/ # Per-harness placement (claude.rs, copilot.rs)
├── harness_process.rs # Managed harness process execution (ephemeral mode)
├── snapshots/ # insta snapshot files for unit tests
└── testutil.rs # Test utilities (create_test_repo, create_test_overlay)
tests/
├── cli.rs # CLI integration tests using assert_cmd
└── common/mod.rs # Shared test utilities and fixtures
-
main.rs - Minimal CLI entry point. Initializes logging and delegates to
lib::run(). -
cli/ - CLI command definitions using clap derive macros.
mod.rsdefines top-level commands, arguments, flags, and dispatch;commands/contains command-specific handlers. -
lib.rs - Core apply machinery:
apply_overlay, overlay composition resolution (extends/includes), and shared helpers. Status, remove, create, and update operations live in their own modules (status.rs,remove.rs,create.rs). -
resolve.rs / reference.rs - Turn user input into something applyable:
reference.rsparses the input string (path, URL,org/repo/name, bare name),resolve.rsresolves it to a local overlay directory, consulting the library, configured sources, and the GitHub cache. -
selection.rs - Interactive file selection UI. Handles checkbox-style multi-select for overlay creation.
-
widgets/ - Reusable ratatui UI components.
- multi_select_tree.rs -
MultiSelectTreestateful widget: renders a tree with tri-state checkboxes (checked/unchecked/partial) based on descendant selection state.
- multi_select_tree.rs -
-
state.rs - State persistence layer. Manages overlay state in two locations:
- In-repo:
.repoverlay/overlays/<name>.ccl- tracks applied overlays - External:
~/.local/share/repoverlay/applied/- backup for recovery aftergit clean
- In-repo:
-
github.rs - GitHub URL parsing. Handles URL formats like
https://github.com/owner/repo/tree/branch/subpathand extracts owner, repo, ref, and subpath components. -
cache.rs - GitHub repository caching. Manages cloned repos in
~/.cache/repoverlay/github/owner/repo/. Supports shallow clones and update checking. -
config.rs - Configuration management using CCL format. Handles global config (
~/.config/repoverlay/config.ccl), per-repo config (.repoverlay/config.ccl), and per-overlay config (repoverlay.ccl). -
sources.rs - Multi-source overlay resolution. Manages a priority-ordered list of overlay sources (configured via
repoverlay source add/remove/list). ProvidesSourceManagerfor resolving overlay references across multiple sources with first-match-wins semantics. Configured git sources are cloned to~/.cache/repoverlay/sources/<name>/. -
overlay_repo.rs - Shared overlay repository support. Allows overlays to be referenced as
org/repo/namefrom a centrally managed repository. Supports fallback resolution for fork inheritance. -
upstream.rs - Upstream repository detection. Scans git remotes to identify parent repositories (forks), enabling automatic overlay inheritance from upstream.
-
library.rs - In-repo overlay library management. Handles the
.repoverlay/library/directory for storing shareable overlays within a repository. Provides path resolution (configurable via per-repo config), overlay listing, import/export/remove operations, and gitignore detection. Library overlays are auto-discovered and resolved with highest priority. -
git.rs / git_exclude.rs - Git subprocess helpers (with Ctrl+C handling) and management of named overlay sections in
.git/info/exclude. -
json_merge.rs / fuzzy.rs / fs_util.rs / path_safety.rs / overlay_name.rs - Support modules: JSON deep merge for
--merge, fuzzy name suggestions, atomic filesystem operations, path traversal protection, and the normalized overlay name newtype. -
detection.rs - File discovery for the
createcommand. Identifies AI configs, gitignored files, and untracked files that might be candidates for overlay creation. -
profile.rs / plugin.rs / profile_plan.rs / profile_applicators/ / harness_process.rs - The profile subsystem.
profile.rsdefinesProfileConfig(description, overlays, instructions, plugins) and profile state;plugin.rsmodels plugin references and resolves them from marketplaces or local paths;profile_plan.rsplans and executes a profile apply transactionally;profile_applicators/owns per-harness placement (Claude vs Copilot paths);harness_process.rsruns a harness process with ephemeral profiles applied for its lifetime. See DEV.md "Profiles Feature Status" for the capability matrix. -
testutil.rs - Test utilities including
create_test_repo()andcreate_test_overlay()helpers for setting up temporary git repositories in tests.
Source string → resolve_source() → local path
↓
Load repoverlay.ccl config
↓
If extends/includes present:
Recursively resolve composition → merged file list
Else:
Walk files in overlay directory → file list
↓
For each file:
- Check for conflicts with existing overlays
- Check for conflicts with existing files
- Create symlink or copy
↓
Update .git/info/exclude with overlay section
↓
Save state to .repoverlay/overlays/<name>.ccl
↓
Save external backup to ~/.local/share/repoverlay/applied/
Load state from .repoverlay/overlays/<name>.ccl
↓
For each file entry:
- Remove file/symlink
- Clean empty parent directories
↓
Remove overlay section from .git/info/exclude
↓
Delete state file
↓
Remove external backup
Load external state backup from ~/.local/share/repoverlay/applied/
↓
For each saved overlay:
- Re-apply using original source (path or GitHub URL)
For each applied GitHub overlay:
- Check remote for new commits
- If updates available:
- Remove old overlay
- Re-apply with updated cache
Discover files in repository (AI configs, gitignored, untracked)
↓
Interactive selection or --include flags
↓
Copy selected files to output directory
↓
Generate repoverlay.ccl config
Remove all existing overlays
↓
Apply new overlay (atomic replacement)
repoverlay apm install runs APM in a temporary project. repoverlay apm sync
replays the target repository's pinned manifest with APM's --root and --frozen
options, so local APM inputs remain readable while all generated output stays outside
the repository. Sync then compiles context and atomically replaces the apm-project
profile, restoring the previous snapshot if application fails.
Sync checks that the repository's manifest and lockfile remain unchanged, but
allows APM to update deployment metadata in the temporary lockfile. APM's frozen
install enforces the dependency pins.
Both paths capture allowlisted target-native output (commands or prompts,
instructions, hooks, LSP configuration, and compiled context) into
<artifact>/.repoverlay-native/. Package installs pack skills, agents, and MCP
servers as a plugin bundle; project sync captures their deployed native output
directly. Captured output reaches the plan through runtime-only resolved placements,
JSON merges, and managed regions. These reuse the ordinary profile action,
conflict, removal, and restore mechanisms. Hooks need --allow-hooks.
Apply and restore check placement ownership before executing actions, including
directory overlaps. Claude hook-script paths use the durable artifact, including
APM's ${CLAUDE_PROJECT_DIR}/.claude/hooks/ references.
Look up profile in config (repo-local .repoverlay/config.ccl over global config)
↓
Resolve plugins (PluginRef → marketplace clone or local bundle dir)
↓
Build ProfilePlan for the chosen harness (claude | copilot):
- overlays → regular overlay apply machinery
- instructions → managed region in CLAUDE.md / AGENTS.md
- plugin skills/agents → harness-specific directories
- plugin MCP servers → merged into .mcp.json
- delegate plugins (Claude only) → .claude/settings[.local].json
- resolved placements/JSON merges/managed regions (APM) → commands, prompts,
instructions, hooks, LSP configuration, and compiled context
↓
Execute plan transactionally (rollback on failure)
↓
Record state under .repoverlay/profiles/ + external snapshot
Ephemeral mode (repoverlay claude|copilot --profile X) runs the same plan, holds a
PID lock while the harness process runs, and rolls back all placements when it exits.
See DEV.md "Profiles Feature Status" for the full per-harness capability matrix.
Overlay state is stored in CCL format (a human-readable configuration language). Example:
name = my-overlay
applied_at = 2024-01-15T10:30:00Z
source =
type = Local
path = /path/to/overlay
files =
=
source = .envrc
target = .envrc
link_type = symlink
The source is an internally tagged enum (OverlaySource in src/state.rs, #[serde(tag = "type")]): a nested block whose type field selects the variant, with the variant's fields alongside it. The variants and their fields:
Local:path, optionalsource_nameLibrary:name(in-repo.repoverlay/library/overlay)GitHub:url,owner,repo,git_ref,commit, optionalsubpath,cached_atOverlayRepo:org,repo,name,commit, optionalresolved_via, optionalsource_name
(repoverlay status --json exposes the same shape with lowercase type values; that JSON schema is the stable contract for scripting.)
Overlay files are excluded from git tracking via .git/info/exclude using named sections:
# repoverlay:my-overlay start
.envrc
.claude/
# repoverlay:my-overlay end
This approach:
- Keeps overlay files out of version control
- Doesn't modify
.gitignore(which is tracked) - Allows multiple overlays with distinct sections
- Enables clean removal of individual overlays
The resolve_source() function determines the overlay source type:
- GitHub URL (
https://github.com/...) - Downloads to cache, returns cached path - Local path (
./pathor/path) - Returns path directly after validation - Library overlay (bare name) - Checks
.repoverlay/library/first (highest priority) - Configured source reference (
org/repo/name) - Resolves from configured sources in priority order
When applying overlays from a shared repository to a forked repo, repoverlay automatically inherits overlays from the upstream (parent) repository.
- Direct match - Look for
fork-org/fork-repo/overlay-name - Upstream fallback - If not found and upstream exists, look for
upstream-org/upstream-repo/overlay-name
The upstream repository is detected by scanning git remotes:
- Check for a remote named
upstream(standard fork convention) - Parse the remote URL (supports both HTTPS and SSH formats)
- Extract org/repo for fallback resolution
Example:
# Fork setup
git remote -v
# origin git@github.com:tylerbutler/FluidFramework.git (fetch)
# upstream git@github.com:microsoft/FluidFramework.git (fetch)
# Apply overlay - falls back to microsoft/FluidFramework if needed
repoverlay apply microsoft/FluidFramework/claude-configThe ResolvedVia enum tracks how an overlay was resolved:
Direct- Exact match in overlay repositoryUpstream- Resolved via upstream fallback
This is stored in the overlay state and displayed in repoverlay status:
Overlay: claude-config
Source: microsoft/FluidFramework/claude-config (via upstream) (overlay repo)
Commit: abc123def456
Overlays can inherit files from other library overlays via extends and includes in repoverlay.ccl.
Full inheritance from a single parent overlay. The parent's files, mappings, and directories are inherited. Child files win on conflict.
extends =
overlay = parent-name
Multi-level chains are supported (child extends parent extends grandparent). Cycle detection prevents infinite recursion.
Cherry-pick specific files from other overlays without inheriting everything.
includes =
=
overlay = tools
files =
= .editorconfig
= scripts/lint.sh
Multiple includes are allowed. Included overlays are recursively resolved (they may themselves use extends/includes).
When the same target path appears in multiple sources, the highest-precedence version wins:
- Child's own files (highest)
- extends parent files
- includes files (in listed order, later overrides earlier)
Referenced overlays must be library overlays (.repoverlay/library/). Other source types (GitHub, local path) are not supported for composition references.
GitHub repositories are cached in ~/.cache/repoverlay/github/owner/repo/:
- Uses shallow clones to minimize disk usage
- Caches are updated on
repoverlay updateor when--refchanges - Cache metadata tracks commit hash and last update time
repoverlay cachesubcommands manage the cache
Configured sources are cloned separately to ~/.cache/repoverlay/sources/<name>/ and refreshed when commands resolve from them. The cache subcommands only operate on the github/ directory.
See docs/adr/ for architectural decision records.