Virtual monorepo
Skill PrecisionUtilityGuild/virtual-monorepo/skills/virtual-monorepo
Claude Code skill: symlink-based multi-repo workspace with a generated system map (virtual monorepo)
npx -y skills add PrecisionUtilityGuild/virtual-monorepo --skill virtual-monorepoAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 23 days oldThe repository was created 23 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Invoke when the user wants Claude Code to work across MULTIPLE related repos at once — mentions of a "virtual monorepo", "multi-repo workspace", "give Claude my whole system", "wire up my microservices/frontend+backend together", stale workspace symlinks, or "regenerate the workspace CLAUDE.md". Sets up a symlink-based workspace directory with a system-level CLAUDE.md written by reading each repo once. Strictly one-shot Bash (mkdir, ln -s, write file); never builds an index, database, daemon, or per-file catalog.
SKILL.md
7.0 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
Virtual Monorepo
A "virtual monorepo" is a parent directory holding symlinks to several real repos plus a
workspace-level CLAUDE.md that explains how the system fits together. Claude Code works one
repo at a time; this convention gives it the whole system in one working tree. You (the model)
do all setup and maintenance with plain Bash. There is no CLI binary, no index, no daemon.
Refuse permanently
Never, under any framing, create: a background process/watcher, a database or SQLite file, a
JSON/YAML index of the repos, or a per-file catalog. A repo summary is one paragraph of prose,
not a file listing. If asked to "keep it in sync automatically" or "index the code," decline and
explain: the workspace is regenerated on demand by re-reading, one shot. This constraint is the
product. Prior in-house attempts at an index measured no better than plain grep — do not rebuild it.
1. Discover
Given a starting repo or directory, find candidate sibling repos and propose the set before creating anything.
Algorithm: scan the parent dir one level down for entries containing a .git (dir or file —
a .git file means a worktree, still counts). Enumerate all candidates first, then prune
any candidate whose realpath is contained inside another candidate's realpath (drop the nested
one). Apply the skip-list by directory name at that single level.
- Skip (by dir name at this level):
node_modules,dist,build,target,.next, plus.gitinternals and any dir the user excludes. - Optionally ask the user for extra roots beyond the parent dir.
- List each candidate with its path and (if present) its
package.json/pyproject.toml/go.modname. Ask the user to confirm/trim the set. Do not proceed silently.
2. Set up
- Pick a workspace dir: default
~/workspaces/<name>, or a user-chosen path. Ask for<name>. - Containment check — do this BEFORE
mkdir. Resolve the workspace path and every candidate repo withrealpath, then refuse if the resolved workspace path is prefix-contained in any resolved repo path (symlink-loop / repo-pollution hazard). Only proceed to relocate outside every candidate. - Create the workspace:
mkdir -p "<workspace>". - For each confirmed repo:
ln -s "<absolute-repo-path>" "<workspace>/<repo-name>". Quote every path — a space in any path silently breaks or mislinks otherwise.- Name collisions: if two repos share a basename, disambiguate (
api-web,api-mobile, or<org>-<name>) and tell the user the mapping. - Different disks: symlinks work across volumes; note that git operations still run per-repo (no cross-disk cross-repo git). This is fine — each symlink is just a door.
- Windows: symlinks require Developer Mode enabled (or admin);
ln -smaps tomklink /D. If on Windows, tell the user to enable Developer Mode first.
- Name collisions: if two repos share a basename, disambiguate (
- Generate
"<workspace>/CLAUDE.md"(see section 4).
3. Wire into Claude Code
- Launch from the workspace dir (
cd "<workspace>" && claude) for ongoing multi-repo work: the workspaceCLAUDE.mdloads as context and every repo is reachable through its symlink. This is the default for a system you return to. --add-dirfor ad-hoc sessions:claude --add-dir "<other-repo>"adds one extra root to a session already running in a single repo, without building a workspace. Use it for a one-off "also look at that repo" — no symlinks, no CLAUDE.md, nothing to clean up.- Rule of thumb: recurring system → workspace; one-time glance →
--add-dir.
Headless / permission-restricted sessions
In non-interactive sessions (e.g. claude -p) the permission system may deny ln -s. When a
command you need is denied, present the exact commands for the user to run themselves and stop —
never bypass a permission gate through another mechanism (e.g. scripting the symlink through
an interpreter). A denied gate is a stop, not an obstacle to route around.
4. Generate the workspace CLAUDE.md
Read each repo once and write prose. The file (see spec/output-samples/workspace-CLAUDE.md
for the golden shape) contains, in order:
- System overview — one sentence naming what the whole system does.
- Per-repo summary — one paragraph per repo. Derive it by reading that repo's
README,CLAUDE.md, and package manifest (package.json/pyproject.toml/go.mod/Cargo.toml): what it is, its stack, how it's run. Prose, not a file tree. - Cross-repo relationships — who calls whom and shared packages, derived one-shot by
reading:
dependencies/devDependenciesnaming sibling repos,file:/workspace:deps, and obvious API-client code (base URLs, generated clients, shared types). State it as a snapshot ("as of <date>"), not a live index. If unsure, say so rather than guess. - How to run — per-repo start/test commands (pulled from
scripts/ Makefile / README), and any known startup order.
Keep it tight — a page or two. It is a map, not a mirror of the code.
5. Maintain (all one-shot)
- Stale symlinks:
find "<workspace>" -maxdepth 1 -type l '!' -exec test -e {} ';' -printlists only broken links, on both BSD/macOS and GNU find (ls -lcan't — it prints the target whether or not it exists). Offer to remove or re-point each broken one. - Add a repo: confirm it's a git dir,
ln -s "<abs-path>" "<workspace>/<name>"(watch for name collision), then append a summary paragraph and update the relationships section. - Remove a repo:
rm "<workspace>/<name>"(removes the symlink only, never the real repo — confirm the target is a symlink first withtest -L "<workspace>/<name>"), then prune its paragraph. - Regenerate the summary: on request, re-read the repos and rewrite
CLAUDE.mdfresh. This is the "keep it current" answer — a manual regenerate, never a watcher.
Edge cases checklist
- Workspace dir inside a repo → refuse, relocate.
- Repos on different disks → symlinks fine; git stays per-repo.
- Windows → needs Developer Mode for symlinks.
- Name collisions → disambiguate and report the mapping.
- A
.gitfile (worktree) → still a valid candidate; treat as a repo.
What "done" looks like
A workspace dir of symlinks + one hand-written CLAUDE.md, launchable with cd <workspace> && claude. No process running, no file the user has to keep fed. If you created anything that must be
kept in sync automatically, you did it wrong — delete it and regenerate on demand instead.
What ships with it: 1 file
205 B alongside SKILL.md
agents/
- openai.yaml205 B