Help us improve
Share bugs, ideas, or general feedback.
From virtual-workspace
Creates and manages multi-repo workspaces for AI coding assistants (Claude Code, Codex) by unifying configs from sibling git repos and supporting worktree-based feature branches.
npx claudepluginhub filipealva/virtual-workspace --plugin virtual-workspaceHow this skill is triggered — by the user, by Claude, or both
Slash command
/virtual-workspace:virtual-workspaceThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill creates workspaces compatible with both **Claude Code** (`.claude/`) and **Codex** (`.codex/`).
assets/assistant-support.shassets/bootstrap.shassets/pull-all.shassets/templates/AGENTS.md.tmplassets/templates/CLAUDE.md.tmplassets/templates/README.md.tmplassets/templates/code-workspace.tmplassets/templates/gitignore.tmplassets/templates/workspace.conf.tmplassets/worktree-create.shassets/worktree-remove.shassets/worktree-status.shCreates, lists, and cleans up Git worktrees for parallel Claude Code sessions on separate branches, enabling conflict-free multi-feature development.
Manages git worktrees for parallel feature development: create, list, switch between, and merge worktrees with isolated task boards.
Interactive wizard configures repositories for Claude Code best practices by creating CLAUDE.md, slash commands, agents, hooks, and permissions. Activates on 'setup claude', 'init claude', or repo setup requests.
Share bugs, ideas, or general feedback.
This skill creates workspaces compatible with both Claude Code (.claude/) and Codex (.codex/).
This skill has two modes:
workspace.conf exists in the working directory).workspace.conf exists).When this skill is invoked, check whether workspace.conf exists in the current working directory (or any parent up to 3 levels):
workspace.conf is NOT found: run the Create flow (section below).workspace.conf IS found: run the Worktree management flow (section below). The workspace root is the directory containing workspace.conf.This flow creates a workspace shell: a parent folder that hosts multiple sibling git repos and exposes the union of their AI coding assistant configs to a single session via symlinks:
.claude/{agents,commands,skills,hooks}/.codex/{agents,skills}/The workspace shell never tracks changes inside the sub-repos — they are gitignored. The shell only owns the cross-repo glue.
The workspace supports worktrees for multi-repo feature development: create synchronized feature branches across multiple repos, work on them in one session, commit/push independently, and open PRs per repo.
This skill ships canonical scripts and templates in ${CLAUDE_PLUGIN_ROOT}/skills/virtual-workspace/assets/:
bootstrap.sh — copy verbatim into every new workspaceassistant-support.sh — copy verbatim into every new workspacepull-all.sh — copy verbatim into every new workspaceworktree-create.sh — copy verbatim into every new workspaceworktree-remove.sh — copy verbatim into every new workspaceworktree-status.sh — copy verbatim into every new workspacetemplates/workspace.conf.tmpltemplates/CLAUDE.md.tmpltemplates/AGENTS.md.tmpltemplates/README.md.tmpltemplates/gitignore.tmpltemplates/code-workspace.tmplRead the asset files using their absolute paths under ${CLAUDE_PLUGIN_ROOT}. Never inline the script bodies into this SKILL.md — always source them from disk so the skill stays a thin orchestrator.
Follow these steps in order. Do not skip the confirmation step.
Tell the user, in 2-3 sentences, what you're about to build and that the shell will not track sub-repo contents. Example:
I'll create a workspace shell — a thin parent folder that holds your repos side-by-side and surfaces all their AI coding assistant configs (agents/commands/skills/hooks) in one session via symlinks. It works with both Claude Code (
.claude/) and Codex (.codex/). The shell itself never tracks the sub-repos' contents; it only owns the cross-repo glue (bootstrap script, workspace.conf, etc.). It also includes worktree scripts for multi-repo feature branches.
Collect these via plain conversation (free-text) or AskUserQuestion (choices). Use the tool that fits each input type.
a) Workspace name — free text.
Ask: "What should I call the workspace? (e.g. AcmeWorkspace, acme-monorepo-shell)". Use the answer verbatim as the directory name; do NOT lowercase or sluggify unless the user uses spaces (in which case suggest a kebab or PascalCase replacement and confirm).
b) Parent directory — AskUserQuestion.
Default options: ~/Developer/ (recommended), ~/Code/, ~/Projects/, Other (free-text path). Resolve ~ and any env vars. The workspace will land at <parent>/<name>/.
c) Sub-repo list — free text, multi-line. Ask: "Paste the git URLs of the repos you want in this workspace, one per line. SSH or HTTPS, both work. I'll auto-derive each repo's folder name from the URL — you can override after."
For each URL the user provides:
.git. E.g. git@github.com:acme/web-app.git → web-app; https://github.com/torvalds/linux → linux.name → url and ask whether to rename any. Only rename if they explicitly say so.d) Pre-existence check — only ask if needed.
For each <parent>/<name>/<sub-repo-name> path that already exists, run git -C <path> remote get-url origin 2>/dev/null to check if it's already the right repo. If yes, silently keep it. If it exists but is unrelated (different remote, or no .git), use AskUserQuestion:
rm -rf <path> then bootstrap clones fresh. Confirm again with a yes/no before deleting; this is destructive.Print a summary block and ask for explicit confirmation:
Workspace: <Name>
Location: <parent>/<Name>/
Sub-repos:
- <name1> → <url1>
- <name2> → <url2>
...
Files I will create at <parent>/<Name>/:
workspace.conf
bootstrap.sh
pull-all.sh
assistant-support.sh
worktree-create.sh
worktree-remove.sh
worktree-status.sh
CLAUDE.md
AGENTS.md
README.md
.gitignore
<Name>.code-workspace
I will then run ./bootstrap.sh to clone any missing sub-repos and create symlinks.
Sound good? (yes / no / changes)
Wait for "yes" (or equivalent). If the user wants changes, loop back to step 2.
Use absolute paths throughout. Let WS=<parent>/<Name> and ASSETS=${CLAUDE_PLUGIN_ROOT}/skills/virtual-workspace/assets.
mkdir -p "$WS".
Copy (do not edit) $ASSETS/bootstrap.sh, $ASSETS/assistant-support.sh, $ASSETS/pull-all.sh, $ASSETS/worktree-create.sh, $ASSETS/worktree-remove.sh, and $ASSETS/worktree-status.sh into $WS/. Use cp via Bash. Then chmod +x "$WS/bootstrap.sh" "$WS/assistant-support.sh" "$WS/pull-all.sh" "$WS/worktree-create.sh" "$WS/worktree-remove.sh" "$WS/worktree-status.sh".
Read each *.tmpl file with the Read tool, perform substitutions, and Write the result to $WS/. Substitutions:
| Template | Output filename | Substitutions |
|---|---|---|
workspace.conf.tmpl | workspace.conf | {{NAME}} → workspace name. {{REPOS}} → newline-joined ` "name |
CLAUDE.md.tmpl | CLAUDE.md | {{NAME}} → workspace name. {{REPO_LIST}} → newline-joined bullets in the form - \/` — <one-line description, leave blank if unknown>`. |
AGENTS.md.tmpl | AGENTS.md | {{NAME}} → workspace name. {{REPO_LIST}} → same bullets as above. |
README.md.tmpl | README.md | {{NAME}} → workspace name. {{REPO_LIST}} → same bullets as above. {{CLONE_INSTRUCTION}} → see step 6 below; until the workspace is published, write # (workspace not yet pushed to a remote — clone command will be added when published). |
gitignore.tmpl | .gitignore | {{REPO_DIRS}} → newline-joined <name>/ lines. |
code-workspace.tmpl | <Name>.code-workspace | {{FOLDERS}} → for each sub-repo, prepend ,\n { "name": "<name>", "path": "<name>" }. (The leading comma + 4-space indent matches the existing Workspace entry in the template.) |
After writing, sanity-check the JSON in <Name>.code-workspace by running python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$WS/<Name>.code-workspace". If it errors, re-render and retry.
cd "$WS" && ./bootstrap.sh
Stream the output. After it completes, verify the result:
find "$WS/.claude" -type l | head — list Claude Code symlinks.find "$WS/.codex" -type l | head — list Codex symlinks.readlink -f resolves to a real file.<repo-1>: 2 agents, 0 commands; <repo-2>: 1 agent, 0 commands; .... Use find <repo>/.claude/<kind> -maxdepth 1 -type f -name "*.md" | wc -l for each.If bootstrap fails (clone failure, auth issue), surface the exact error to the user and stop. Do not auto-retry.
AskUserQuestion:
Want to git-init this workspace shell and push it to a new GitHub repo so coworkers can clone it?
- No, leave it local (recommended)
- Yes, create a private GitHub repo and push
- Yes, but I'll give you an existing repo URL to push to
If yes:
gh api user --jq .login if gh is available).cd "$WS", then git init -b main.git add .gitignore CLAUDE.md AGENTS.md README.md bootstrap.sh assistant-support.sh pull-all.sh worktree-create.sh worktree-remove.sh worktree-status.sh workspace.conf <Name>.code-workspace
Co-Authored-By: Claude trailer.gh repo create <owner>/<name> --private --source=. --push. For existing-repo path: git remote add origin <url> && git push -u origin main.README.md with {{CLONE_INSTRUCTION}} → git clone <repo-url> <Name>, commit the README update with message Update README with clone instructions, and push.If anything fails (e.g., gh not installed, auth error), report the error and stop — do not invent fallback paths.
Print a concise, copy-pasteable next-step block:
Workspace ready at: <parent>/<Name>/
Next steps:
cd <parent>/<Name>
claude # start a Claude Code session
codex # or start a Codex session
Maintenance:
./pull-all.sh # fast-forward pull every sub-repo and worktree
./bootstrap.sh # rerun whenever a sub-repo adds/renames/removes an agent or skill
./assistant-support.sh codex # add Codex config from existing Claude Code config
Multi-repo features:
./worktree-create.sh feature/x # create worktrees for all repos on branch feature/x
./worktree-status.sh # check branch + dirty state across all repos/worktrees
./worktree-remove.sh feature/x # clean up when the feature is merged
To add a new sub-repo later:
1. Append "name|git-url" to workspace.conf
2. Add "name/" to .gitignore
3. (optional) add it to <Name>.code-workspace
4. ./bootstrap.sh
This flow runs when workspace.conf is found. It handles creating, inspecting, and removing worktrees for multi-repo feature development.
Determine which action the user wants. If unclear, use AskUserQuestion:
| Intent | Triggers | Action |
|---|---|---|
| Create | "create worktrees for...", "start feature/x", "work on feature/x across repos", "set up branch..." | → Run Worktree Create |
| Status | "show worktree status", "what branches are active", "status" | → Run Worktree Status |
| Remove | "remove worktrees for...", "clean up feature/x", "done with feature/x" | → Run Worktree Remove |
| Pull | "pull all repos", "update everything", "sync" | → Run Pull All |
| Assistant support | "add Codex support", "add Claude Code support", "make this workspace work with codex", "make this workspace work with claude", "upgrade this old workspace" | → Run Assistant Support |
a) Branch name — free text.
Ask: "What branch name for this feature? (e.g. feature/payments, fix/auth-bug)"
If the user already stated the branch in their message (e.g. "create worktrees for feature/payments"), use that directly — don't re-ask.
b) Detect existing worktrees for this branch.
Read workspace.conf and check for any existing worktree entries matching the branch. Partition the standard repos into two lists:
worktree:<repo>|<branch> entry.If all standard repos already have worktrees for this branch, tell the user and stop — there's nothing to create.
c) Route: fresh vs. incremental.
d) Which repos — AskUserQuestion (multi-select) or free text (fresh path only).
Present the full list of standard repos:
Which repos should get a worktree on
<branch>?
- All repos (recommended) — creates worktrees for every standard repo
- Select specific repos — let me pick
If "specific", show a multi-select with the repo names. If the user already specified repos in their message, use those directly.
Print a summary:
I'll create worktrees for branch '<branch>' in:
- <repo1> → <repo1>--<sanitized-branch>/
- <repo2> → <repo2>--<sanitized-branch>/
This will:
1. Create the branch in each repo (if it doesn't exist)
2. Create worktree directories
3. Update workspace.conf and .gitignore
4. Run bootstrap.sh to wire up .claude/ and .codex/ symlinks
Proceed? (yes / no / changes)
Wait for confirmation.
cd "<workspace-root>"
./worktree-create.sh "<branch>" <repo1> <repo2> ...
./bootstrap.sh
Stream output from both commands.
Run ./worktree-status.sh and show the user the result. Highlight the newly created worktrees. Then print:
Worktrees ready. You can now work in:
<repo1>--<sanitized-branch>/
<repo2>--<sanitized-branch>/
These share git history with the source repos — commits are lightweight.
When done, commit in each worktree independently and open PRs per repo.
To clean up later: ask me to "remove worktrees for <branch>"
This path runs when worktrees for <branch> already exist for some repos and the user (or an agent) wants to add more. This commonly happens when agents discover mid-flight that another repo needs changes for the same feature.
Print what already exists:
Branch '<branch>' already has worktrees in this workspace:
- <repo1>--<sanitized-branch>/ (exists)
- <repo2>--<sanitized-branch>/ (exists)
Repos without a worktree for this branch:
- <repo3>
- <repo4>
If the user already specified which repos to add (e.g. "also create worktree for backend"), use those directly — don't re-ask. Otherwise, use AskUserQuestion (multi-select) with the available repos only:
Which additional repos should get a worktree on
<branch>?
Show only repos that don't already have a worktree for this branch.
I'll add worktrees for branch '<branch>' to:
- <repo3> → <repo3>--<sanitized-branch>/
Existing worktrees are not affected.
Proceed? (yes / no)
Wait for confirmation.
Same as the fresh path:
cd "<workspace-root>"
./worktree-create.sh "<branch>" <repo3> ...
./bootstrap.sh
The script skips repos that already have worktrees, so passing the full list is safe — but prefer passing only the new repos for clearer output.
Run ./worktree-status.sh and show the result. Then print:
Added worktree for '<branch>':
- <repo3>--<sanitized-branch>/ (new)
All worktrees for this branch:
- <repo1>--<sanitized-branch>/ (already existed)
- <repo2>--<sanitized-branch>/ (already existed)
- <repo3>--<sanitized-branch>/ (new)
Run:
cd "<workspace-root>" && ./worktree-status.sh
Present the output to the user. If any worktrees are dirty, highlight them. If any are ahead of their remote, suggest pushing.
a) Branch name — from the user's message or ask.
b) Which repos — if the user didn't specify, default to ALL worktrees matching that branch.
Run ./worktree-status.sh and check if any of the target worktrees are dirty. If so, warn:
WARNING: These worktrees have uncommitted changes:
- <repo>--<branch>/ (3 files modified)
Options:
- Commit changes first (I can help with that)
- Stash changes
- Force remove (changes will be lost!)
Use AskUserQuestion to let the user decide. Do NOT force-remove without explicit confirmation.
For each worktree, check if there are commits ahead of the remote:
git -C "<worktree-dir>" log --oneline @{upstream}..HEAD 2>/dev/null
If there are unpushed commits, warn the user and ask whether to push first.
cd "<workspace-root>"
./worktree-remove.sh "<branch>" <repo1> <repo2> ...
./bootstrap.sh
Stream output.
Removed worktrees for '<branch>':
- <repo1>--<branch>/ ✓
- <repo2>--<branch>/ ✓
workspace.conf and .gitignore updated. Stale .claude/ and .codex/ symlinks pruned.
To also delete the local branches:
git -C <repo1> branch -d <branch>
git -C <repo2> branch -d <branch>
Want me to delete the local branches too? (yes / no)
If yes, run the branch delete commands.
Run:
cd "<workspace-root>" && ./pull-all.sh
Stream the output. If any pull fails (diverged history, not fast-forwardable), report which repos failed and suggest next steps (rebase, merge, etc.).
After pulling, ask if the user wants to run ./bootstrap.sh in case any pulled changes added/removed agents.
Use this when an existing generated workspace was created for only one assistant CLI, or when the user wants to add the other CLI after setup.
Infer the target from the user request:
If unclear, ask whether to run codex, claude, or both.
If assistant-support.sh is missing in the workspace root, copy it from $ASSETS/assistant-support.sh and make it executable:
cp "$ASSETS/assistant-support.sh" "<workspace-root>/assistant-support.sh"
chmod +x "<workspace-root>/assistant-support.sh"
If bootstrap.sh is older and does not contain CODEX_DIRS=(agents skills), replace it with $ASSETS/bootstrap.sh and make it executable. Do not hand-edit the workspace copy.
Run the helper with the selected mode:
cd "<workspace-root>"
./assistant-support.sh codex # or claude / both
The helper mirrors repo-local configs before rerunning ./bootstrap.sh:
codex: copies .claude/agents/*.md to .codex/agents/ and converts .claude/skills/*.md to .codex/skills/<name>/SKILL.md.claude: copies .codex/agents/*.md to .claude/agents/ and copies .codex/skills/<name>/ to .claude/skills/<name>/.both: runs both directions.Run:
find "<workspace-root>/.claude" -type l | head
find "<workspace-root>/.codex" -type l | head
find "<workspace-root>/.codex" -type l -exec test -e {} \; -print
Report the linked agents and skills. If duplicate names exist across repos, explain that later entries in workspace.conf win because bootstrap.sh uses ln -sfn.
The extended pipe format supports three entry types:
REPOS=(
# Standard clone (default branch)
"web-app|git@github.com:acme/web-app.git"
# Clone with specific branch
"shared-lib|git@github.com:acme/shared-lib.git|v2"
# Worktree: "dirname|worktree:<source-repo>|<branch>"
"web-app--feature-auth|worktree:web-app|feature/auth"
"api--feature-auth|worktree:api|feature/auth"
)
When invoked with ./worktree-create.sh <branch> [repo1 repo2 ...]:
origin/<branch> if available, else from HEAD).<repo>--<sanitized-branch>/.workspace.conf..gitignore../bootstrap.sh.When invoked with ./worktree-remove.sh <branch> [repo1 repo2 ...]:
git worktree remove.workspace.conf and .gitignore../bootstrap.sh and optionally delete local branches.Prints a table of all repos and worktrees showing: directory name, type (clone/wt), current branch, and status (clean/dirty + ahead/behind).
bootstrap.sh, assistant-support.sh, pull-all.sh, or worktree-*.sh per workspace. They are identical across every workspace; everything workspace-specific lives in workspace.conf..git directories or files. If a sub-repo path exists with a different remote, ask before any destructive action. A .git file (not directory) indicates a worktree — treat it the same as a .git directory for detection purposes..claude/agents/ etc. to the workspace shell — they are symlinks regenerated by bootstrap.sh and are gitignored by the template. Same for .codex/agents/ and .codex/skills/.[ -d "$name/.git" ] || [ -f "$name/.git" ] or git -C "$name" rev-parse --git-dir to detect both clones and worktrees.bash (not POSIX sh) and use find -exec test -e, shopt -s nullglob, ln -sfn. These all work on macOS BSD tools and GNU/Linux. Do not switch to symlink commands that require GNU-only flags.${CLAUDE_PLUGIN_ROOT} is set by Claude Code when this skill runs; use it to read assets. If it isn't set, fall back to the path where this SKILL.md lives.git@host:owner/repo.git and https://host/owner/repo[.git]. Strip query strings and fragments. If parsing yields an empty name, ask the user explicitly.bootstrap.sh processes entries in order. Standard clones must appear before any worktree entries that reference them. worktree-create.sh enforces this by checking that the source repo exists before creating worktrees.workspace.conf and reruns ./bootstrap.sh..cursor/rules/ aggregation. Cursor multi-root workspaces handle per-root rules natively; the generated <Name>.code-workspace is enough.<repo>/.claude/agents/, <repo>/.codex/agents/, or <repo>/.codex/skills/. The assistant-support.sh helper can mirror existing config between supported layouts.git clone fails for SSH or HTTPS, surface the error and stop.gh pr create in each worktree).