
Git Worktree Manager
- 560 installs
- 23.5k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
git-worktree-manager is an agent skill with Python CLI scripts that create isolated git worktrees with deterministic ports, synced .env files, and safe stale-worktree cleanup for parallel feature development.
About
git-worktree-manager is a production workflow skill from alirezarezvani/claude-skills for parallel branch development using git worktrees. It ships two Python scripts—worktree_manager.py for create/list-prep with deterministic ports, .env* sync, and optional dependency install, and worktree_cleanup.py for stale worktree review with a configurable stale-days threshold (default example: 14 days). Developers run commands like `python scripts/worktree_manager.py --repo . --branch feature/api-hardening --install-deps` to spin up isolated environments without port collisions. Reach for this skill when juggling multiple features, agent sessions, or PR branches that each need their own checkout, env, and dev server port.
- worktree_manager.py:create, list-prep, deterministic port assignment, .env* sync, optional dependency install
- worktree_cleanup.py: stale, dirty, and merged analysis with optional safe removal after configurable stale days
- JSON stdin/--input automation interfaces on both scripts for agent pipelines
- Reference docs for port-allocation strategy and per-worktree Docker Compose override patterns
- Install paths documented for Claude Code, Codex, and OpenClaw skill directories
Git Worktree Manager by the numbers
- 560 all-time installs (skills.sh)
- Ranked #102 of 735 Git & Pull Requests skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill git-worktree-managerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 560 |
|---|---|
| repo stars | ★ 23.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
How do you run parallel git worktrees with isolated ports?
Spin up isolated git worktrees with deterministic ports, synced env files, and safe stale-worktree cleanup while juggling parallel features.
Who is it for?
Developers running multiple feature branches or agent sessions who need isolated worktrees, env sync, and port assignment without manual git plumbing.
Skip if: Teams that only use a single branch checkout or rely on container-based isolation instead of native git worktrees.
When should I use this skill?
The developer needs parallel branch checkouts with env sync, port assignment, or cleanup of stale git worktrees.
What you get
Prepared git worktree, synced .env files, assigned dev port, stale-worktree cleanup report, and branch-isolated checkout.
- isolated worktree
- synced env files
- stale worktree report
By the numbers
- Bundles 2 Python scripts: worktree_manager.py and worktree_cleanup.py
- Documents --stale-days 14 as example stale-worktree threshold
Files
Git Worktree Manager
Tier: POWERFUL Category: Engineering Domain: Parallel Development & Branch Isolation
Overview
Use this skill to run parallel feature work safely with Git worktrees. It standardizes branch isolation, port allocation, environment sync, and cleanup so each worktree behaves like an independent local app without stepping on another branch.
This skill is optimized for multi-agent workflows where each agent or terminal session owns one worktree.
Core Capabilities
- Create worktrees from new or existing branches with deterministic naming
- Auto-allocate non-conflicting ports per worktree and persist assignments
- Copy local environment files (
.env*) from main repo to new worktree - Optionally install dependencies based on lockfile detection
- Detect stale worktrees and uncommitted changes before cleanup
- Identify merged branches and safely remove outdated worktrees
When to Use
- You need 2+ concurrent branches open locally
- You want isolated dev servers for feature, hotfix, and PR validation
- You are working with multiple agents that must not share a branch
- Your current branch is blocked but you need to ship a quick fix now
- You want repeatable cleanup instead of ad-hoc
rm -rfoperations
Key Workflows
1. Create a Fully-Prepared Worktree
1. Pick a branch name and worktree name. 2. Run the manager script (creates branch if missing). 3. Review generated port map. 4. Start app using allocated ports.
python scripts/worktree_manager.py \
--repo . \
--branch feature/new-auth \
--name wt-auth \
--base-branch main \
--install-deps \
--format textIf you use JSON automation input:
cat config.json | python scripts/worktree_manager.py --format json
# or
python scripts/worktree_manager.py --input config.json --format json2. Run Parallel Sessions
Recommended convention:
- Main repo: integration branch (
main/develop) on default port - Worktree A: feature branch + offset ports
- Worktree B: hotfix branch + next offset
Each worktree contains .worktree-ports.json with assigned ports.
3. Cleanup with Safety Checks
1. Scan all worktrees and stale age. 2. Inspect dirty trees and branch merge status. 3. Remove only merged + clean worktrees, or force explicitly.
python scripts/worktree_cleanup.py --repo . --stale-days 14 --format text
python scripts/worktree_cleanup.py --repo . --remove-merged --format text4. Docker Compose Pattern
Use per-worktree override files mapped from allocated ports. The script outputs a deterministic port map; apply it to docker-compose.worktree.yml.
See docker-compose-patterns.md for concrete templates.
5. Port Allocation Strategy
Default strategy is base + (index * stride) with collision checks:
- App:
3000 - Postgres:
5432 - Redis:
6379 - Stride:
10
See port-allocation-strategy.md for the full strategy and edge cases.
Script Interfaces
python scripts/worktree_manager.py --help- Create/list worktrees
- Allocate/persist ports
- Copy
.env*files - Optional dependency installation
python scripts/worktree_cleanup.py --help- Stale detection by age
- Dirty-state detection
- Merged-branch detection
- Optional safe removal
Both tools support stdin JSON and --input file mode for automation pipelines.
Common Pitfalls
1. Creating worktrees inside the main repo directory 2. Reusing localhost:3000 across all branches 3. Sharing one database URL across isolated feature branches 4. Removing a worktree with uncommitted changes 5. Forgetting to prune old metadata after branch deletion 6. Assuming merged status without checking against the target branch
Best Practices
1. One branch per worktree, one agent per worktree. 2. Keep worktrees short-lived; remove after merge. 3. Use a deterministic naming pattern (wt-<topic>). 4. Persist port mappings in file, not memory or terminal notes. 5. Run cleanup scan weekly in active repos. 6. Use --format json for machine flows and --format text for human review. 7. Never force-remove dirty worktrees unless changes are intentionally discarded.
Validation Checklist
Before claiming setup complete:
1. git worktree list shows expected path + branch. 2. .worktree-ports.json exists and contains unique ports. 3. .env files copied successfully (if present in source repo). 4. Dependency install command exits with code 0 (if enabled). 5. Cleanup scan reports no unintended stale dirty trees.
References
- port-allocation-strategy.md
- docker-compose-patterns.md
- README.md for quick start and installation details
Decision Matrix
Use this quick selector before creating a new worktree:
- Need isolated dependencies and server ports -> create a new worktree
- Need only a quick local diff review -> stay on current tree
- Need hotfix while feature branch is dirty -> create dedicated hotfix worktree
- Need ephemeral reproduction branch for bug triage -> create temporary worktree and cleanup same day
Operational Checklist
Before Creation
1. Confirm main repo has clean baseline or intentional WIP commits. 2. Confirm target branch naming convention. 3. Confirm required base branch exists (main/develop). 4. Confirm no reserved local ports are already occupied by non-repo services.
After Creation
1. Verify git status branch matches expected branch. 2. Verify .worktree-ports.json exists. 3. Verify app boots on allocated app port. 4. Verify DB and cache endpoints target isolated ports.
Before Removal
1. Verify branch has upstream and is merged when intended. 2. Verify no uncommitted files remain. 3. Verify no running containers/processes depend on this worktree path.
CI and Team Integration
- Use worktree path naming that maps to task ID (
wt-1234-auth). - Include the worktree path in terminal title to avoid wrong-window commits.
- In automated setups, persist creation metadata in CI artifacts/logs.
- Trigger cleanup report in scheduled jobs and post summary to team channel.
Failure Recovery
- If
git worktree addfails due to existing path: inspect path, do not overwrite. - If dependency install fails: keep worktree created, mark status and continue manual recovery.
- If env copy fails: continue with warning and explicit missing file list.
- If port allocation collides with external service: rerun with adjusted base ports.
Git Worktree Manager
Production workflow for parallel branch development with isolated ports, env sync, and cleanup safety checks. This skill packages practical CLI tooling and operating guidance for multi-worktree teams.
Quick Start
# Create + prepare a worktree
python scripts/worktree_manager.py \
--repo . \
--branch feature/api-hardening \
--name wt-api-hardening \
--base-branch main \
--install-deps \
--format text
# Review stale worktrees
python scripts/worktree_cleanup.py --repo . --stale-days 14 --format textIncluded Tools
scripts/worktree_manager.py: create/list-prep workflow, deterministic ports,.env*sync, optional dependency installscripts/worktree_cleanup.py: stale/dirty/merged analysis with optional safe removal
Both support --input <json-file> and stdin JSON for automation.
References
references/port-allocation-strategy.mdreferences/docker-compose-patterns.md
Installation
Claude Code
cp -R engineering/git-worktree-manager ~/.claude/skills/git-worktree-managerOpenAI Codex
cp -R engineering/git-worktree-manager ~/.codex/skills/git-worktree-managerOpenClaw
cp -R engineering/git-worktree-manager ~/.openclaw/skills/git-worktree-managerDocker Compose Patterns For Worktrees
Pattern 1: Override File Per Worktree
Base compose file remains shared; each worktree has a local override.
docker-compose.worktree.yml:
services:
app:
ports:
- "3010:3000"
db:
ports:
- "5442:5432"
redis:
ports:
- "6389:6379"Run:
docker compose -f docker-compose.yml -f docker-compose.worktree.yml up -dPattern 2: .env Driven Ports
Use compose variable substitution and write worktree-specific values into .env.local.
docker-compose.yml excerpt:
services:
app:
ports: ["${APP_PORT:-3000}:3000"]
db:
ports: ["${DB_PORT:-5432}:5432"]Worktree .env.local:
APP_PORT=3010
DB_PORT=5442
REDIS_PORT=6389Pattern 3: Project Name Isolation
Use unique compose project name so container, network, and volume names do not collide.
docker compose -p myapp_wt_auth up -dCommon Mistakes
- Reusing default
5432from multiple worktrees simultaneously - Sharing one database volume across incompatible migration branches
- Forgetting to scope compose project name per worktree
Port Allocation Strategy
Objective
Allocate deterministic, non-overlapping local ports for each worktree to avoid collisions across concurrent development sessions.
Default Mapping
- App HTTP:
3000 - Postgres:
5432 - Redis:
6379 - Stride per worktree:
10
Formula by slot index n:
app = 3000 + (10 * n)db = 5432 + (10 * n)redis = 6379 + (10 * n)
Examples:
- Slot 0:
3000/5432/6379 - Slot 1:
3010/5442/6389 - Slot 2:
3020/5452/6399
Collision Avoidance
1. Read .worktree-ports.json from existing worktrees. 2. Skip any slot where one or more ports are already assigned. 3. Persist selected mapping in the new worktree.
Operational Notes
- Keep stride >= number of services to avoid accidental overlaps when adding ports later.
- For custom service sets, reserve a contiguous block per worktree.
- If you also run local infra outside worktrees, offset bases to avoid global collisions.
Recommended File Format
{
"app": 3010,
"db": 5442,
"redis": 6389
}#!/usr/bin/env python3
"""Inspect and clean stale git worktrees with safety checks.
Supports:
- JSON input from stdin or --input file
- Stale age detection
- Dirty working tree detection
- Merged branch detection
- Optional removal of merged, clean stale worktrees
"""
import argparse
import json
import subprocess
import sys
import time
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Any, Dict, List, Optional
class CLIError(Exception):
"""Raised for expected CLI errors."""
@dataclass
class WorktreeInfo:
path: str
branch: str
is_main: bool
age_days: int
stale: bool
dirty: bool
merged_into_base: bool
def run(cmd: List[str], cwd: Optional[Path] = None, check: bool = True) -> subprocess.CompletedProcess[str]:
return subprocess.run(cmd, cwd=cwd, text=True, capture_output=True, check=check)
def load_json_input(input_file: Optional[str]) -> Dict[str, Any]:
if input_file:
try:
return json.loads(Path(input_file).read_text(encoding="utf-8"))
except Exception as exc:
raise CLIError(f"Failed reading --input file: {exc}") from exc
if not sys.stdin.isatty():
raw = sys.stdin.read().strip()
if raw:
try:
return json.loads(raw)
except json.JSONDecodeError as exc:
raise CLIError(f"Invalid JSON from stdin: {exc}") from exc
return {}
def parse_worktrees(repo: Path) -> List[Dict[str, str]]:
proc = run(["git", "worktree", "list", "--porcelain"], cwd=repo)
entries: List[Dict[str, str]] = []
current: Dict[str, str] = {}
for line in proc.stdout.splitlines():
if not line.strip():
if current:
entries.append(current)
current = {}
continue
key, _, value = line.partition(" ")
current[key] = value
if current:
entries.append(current)
return entries
def get_branch(path: Path) -> str:
proc = run(["git", "rev-parse", "--abbrev-ref", "HEAD"], cwd=path)
return proc.stdout.strip()
def get_last_commit_age_days(path: Path) -> int:
proc = run(["git", "log", "-1", "--format=%ct"], cwd=path)
timestamp = int(proc.stdout.strip() or "0")
age_seconds = int(time.time()) - timestamp
return max(0, age_seconds // 86400)
def is_dirty(path: Path) -> bool:
proc = run(["git", "status", "--porcelain"], cwd=path)
return bool(proc.stdout.strip())
def is_merged(repo: Path, branch: str, base_branch: str) -> bool:
if branch in ("HEAD", base_branch):
return False
try:
run(["git", "merge-base", "--is-ancestor", branch, base_branch], cwd=repo)
return True
except subprocess.CalledProcessError:
return False
def format_text(items: List[WorktreeInfo], removed: List[str]) -> str:
lines = ["Worktree cleanup report"]
for item in items:
lines.append(
f"- {item.path} | branch={item.branch} | age={item.age_days}d | "
f"stale={item.stale} dirty={item.dirty} merged={item.merged_into_base}"
)
if removed:
lines.append("Removed:")
for path in removed:
lines.append(f"- {path}")
return "\n".join(lines)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Analyze and optionally cleanup stale git worktrees.")
parser.add_argument("--input", help="Path to JSON input file. If omitted, reads JSON from stdin when piped.")
parser.add_argument("--repo", default=".", help="Repository root path.")
parser.add_argument("--base-branch", default="main", help="Base branch to evaluate merged branches.")
parser.add_argument("--stale-days", type=int, default=14, help="Threshold for stale worktrees.")
parser.add_argument("--remove-merged", action="store_true", help="Remove worktrees that are stale, clean, and merged.")
parser.add_argument("--force", action="store_true", help="Allow removal even if dirty (use carefully).")
parser.add_argument("--format", choices=["text", "json"], default="text", help="Output format.")
return parser.parse_args()
def main() -> int:
args = parse_args()
payload = load_json_input(args.input)
repo = Path(str(payload.get("repo", args.repo))).resolve()
stale_days = int(payload.get("stale_days", args.stale_days))
base_branch = str(payload.get("base_branch", args.base_branch))
remove_merged = bool(payload.get("remove_merged", args.remove_merged))
force = bool(payload.get("force", args.force))
try:
run(["git", "rev-parse", "--is-inside-work-tree"], cwd=repo)
except subprocess.CalledProcessError as exc:
raise CLIError(f"Not a git repository: {repo}") from exc
try:
run(["git", "rev-parse", "--verify", base_branch], cwd=repo)
except subprocess.CalledProcessError as exc:
raise CLIError(f"Base branch not found: {base_branch}") from exc
entries = parse_worktrees(repo)
if not entries:
raise CLIError("No worktrees found.")
main_path = Path(entries[0].get("worktree", "")).resolve()
infos: List[WorktreeInfo] = []
removed: List[str] = []
for entry in entries:
path = Path(entry.get("worktree", "")).resolve()
branch = get_branch(path)
age = get_last_commit_age_days(path)
dirty = is_dirty(path)
stale = age >= stale_days
merged = is_merged(repo, branch, base_branch)
info = WorktreeInfo(
path=str(path),
branch=branch,
is_main=path == main_path,
age_days=age,
stale=stale,
dirty=dirty,
merged_into_base=merged,
)
infos.append(info)
if remove_merged and not info.is_main and info.stale and info.merged_into_base and (force or not info.dirty):
try:
cmd = ["git", "worktree", "remove", str(path)]
if force:
cmd.append("--force")
run(cmd, cwd=repo)
removed.append(str(path))
except subprocess.CalledProcessError as exc:
raise CLIError(f"Failed removing worktree {path}: {exc.stderr}") from exc
if args.format == "json":
print(json.dumps({"worktrees": [asdict(i) for i in infos], "removed": removed}, indent=2))
else:
print(format_text(infos, removed))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except CLIError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
raise SystemExit(2)
#!/usr/bin/env python3
"""Create and prepare git worktrees with deterministic port allocation.
Supports:
- JSON input from stdin or --input file
- Worktree creation from existing/new branch
- .env file sync from main repo
- Optional dependency installation
- JSON or text output
"""
import argparse
import json
import os
import shutil
import subprocess
import sys
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import Any, Dict, List, Optional
ENV_FILES = [".env", ".env.local", ".env.development", ".envrc"]
LOCKFILE_COMMANDS = [
("pnpm-lock.yaml", ["pnpm", "install"]),
("yarn.lock", ["yarn", "install"]),
("package-lock.json", ["npm", "install"]),
("bun.lockb", ["bun", "install"]),
("requirements.txt", [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"]),
]
@dataclass
class WorktreeResult:
repo: str
worktree_path: str
branch: str
created: bool
ports: Dict[str, int]
copied_env_files: List[str]
dependency_install: str
class CLIError(Exception):
"""Raised for expected CLI errors."""
def run(cmd: List[str], cwd: Optional[Path] = None, check: bool = True) -> subprocess.CompletedProcess[str]:
return subprocess.run(cmd, cwd=cwd, text=True, capture_output=True, check=check)
def load_json_input(input_file: Optional[str]) -> Dict[str, Any]:
if input_file:
try:
return json.loads(Path(input_file).read_text(encoding="utf-8"))
except Exception as exc:
raise CLIError(f"Failed reading --input file: {exc}") from exc
if not sys.stdin.isatty():
data = sys.stdin.read().strip()
if data:
try:
return json.loads(data)
except json.JSONDecodeError as exc:
raise CLIError(f"Invalid JSON from stdin: {exc}") from exc
return {}
def parse_worktree_list(repo: Path) -> List[Dict[str, str]]:
proc = run(["git", "worktree", "list", "--porcelain"], cwd=repo)
entries: List[Dict[str, str]] = []
current: Dict[str, str] = {}
for line in proc.stdout.splitlines():
if not line.strip():
if current:
entries.append(current)
current = {}
continue
key, _, value = line.partition(" ")
current[key] = value
if current:
entries.append(current)
return entries
def find_next_ports(repo: Path, app_base: int, db_base: int, redis_base: int, stride: int) -> Dict[str, int]:
used_ports = set()
for entry in parse_worktree_list(repo):
wt_path = Path(entry.get("worktree", ""))
ports_file = wt_path / ".worktree-ports.json"
if ports_file.exists():
try:
payload = json.loads(ports_file.read_text(encoding="utf-8"))
used_ports.update(int(v) for v in payload.values() if isinstance(v, int))
except Exception:
continue
index = 0
while True:
ports = {
"app": app_base + (index * stride),
"db": db_base + (index * stride),
"redis": redis_base + (index * stride),
}
if all(p not in used_ports for p in ports.values()):
return ports
index += 1
def sync_env_files(src_repo: Path, dest_repo: Path) -> List[str]:
copied = []
for name in ENV_FILES:
src = src_repo / name
if src.exists() and src.is_file():
dst = dest_repo / name
shutil.copy2(src, dst)
copied.append(name)
return copied
def install_dependencies_if_requested(worktree_path: Path, install: bool) -> str:
if not install:
return "skipped"
for lockfile, command in LOCKFILE_COMMANDS:
if (worktree_path / lockfile).exists():
try:
run(command, cwd=worktree_path, check=True)
return f"installed via {' '.join(command)}"
except subprocess.CalledProcessError as exc:
raise CLIError(f"Dependency install failed: {' '.join(command)}\n{exc.stderr}") from exc
return "no known lockfile found"
def ensure_worktree(repo: Path, branch: str, name: str, base_branch: str) -> Path:
wt_parent = repo.parent
wt_path = wt_parent / name
existing_paths = {Path(e.get("worktree", "")) for e in parse_worktree_list(repo)}
if wt_path in existing_paths:
return wt_path
try:
run(["git", "show-ref", "--verify", f"refs/heads/{branch}"], cwd=repo)
run(["git", "worktree", "add", str(wt_path), branch], cwd=repo)
except subprocess.CalledProcessError:
try:
run(["git", "worktree", "add", "-b", branch, str(wt_path), base_branch], cwd=repo)
except subprocess.CalledProcessError as exc:
raise CLIError(f"Failed to create worktree: {exc.stderr}") from exc
return wt_path
def format_text(result: WorktreeResult) -> str:
lines = [
"Worktree prepared",
f"- repo: {result.repo}",
f"- path: {result.worktree_path}",
f"- branch: {result.branch}",
f"- created: {result.created}",
f"- ports: app={result.ports['app']} db={result.ports['db']} redis={result.ports['redis']}",
f"- copied env files: {', '.join(result.copied_env_files) if result.copied_env_files else 'none'}",
f"- dependency install: {result.dependency_install}",
]
return "\n".join(lines)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Create and prepare a git worktree.")
parser.add_argument("--input", help="Path to JSON input file. If omitted, reads JSON from stdin when piped.")
parser.add_argument("--repo", default=".", help="Path to repository root (default: current directory).")
parser.add_argument("--branch", help="Branch name for the worktree.")
parser.add_argument("--name", help="Worktree directory name (created adjacent to repo).")
parser.add_argument("--base-branch", default="main", help="Base branch when creating a new branch.")
parser.add_argument("--app-base", type=int, default=3000, help="Base app port.")
parser.add_argument("--db-base", type=int, default=5432, help="Base DB port.")
parser.add_argument("--redis-base", type=int, default=6379, help="Base Redis port.")
parser.add_argument("--stride", type=int, default=10, help="Port stride between worktrees.")
parser.add_argument("--install-deps", action="store_true", help="Install dependencies in the new worktree.")
parser.add_argument("--format", choices=["text", "json"], default="text", help="Output format.")
return parser.parse_args()
def main() -> int:
args = parse_args()
payload = load_json_input(args.input)
repo = Path(str(payload.get("repo", args.repo))).resolve()
branch = payload.get("branch", args.branch)
name = payload.get("name", args.name)
base_branch = str(payload.get("base_branch", args.base_branch))
app_base = int(payload.get("app_base", args.app_base))
db_base = int(payload.get("db_base", args.db_base))
redis_base = int(payload.get("redis_base", args.redis_base))
stride = int(payload.get("stride", args.stride))
install_deps = bool(payload.get("install_deps", args.install_deps))
if not branch or not name:
raise CLIError("Missing required values: --branch and --name (or provide via JSON input).")
try:
run(["git", "rev-parse", "--is-inside-work-tree"], cwd=repo)
except subprocess.CalledProcessError as exc:
raise CLIError(f"Not a git repository: {repo}") from exc
wt_path = ensure_worktree(repo, branch, name, base_branch)
created = (wt_path / ".worktree-ports.json").exists() is False
ports = find_next_ports(repo, app_base, db_base, redis_base, stride)
(wt_path / ".worktree-ports.json").write_text(json.dumps(ports, indent=2), encoding="utf-8")
copied = sync_env_files(repo, wt_path)
install_status = install_dependencies_if_requested(wt_path, install_deps)
result = WorktreeResult(
repo=str(repo),
worktree_path=str(wt_path),
branch=branch,
created=created,
ports=ports,
copied_env_files=copied,
dependency_install=install_status,
)
if args.format == "json":
print(json.dumps(asdict(result), indent=2))
else:
print(format_text(result))
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except CLIError as exc:
print(f"ERROR: {exc}", file=sys.stderr)
raise SystemExit(2)
Related skills
How it compares
Pick git-worktree-manager over generic git skills when you need scripted port assignment, env sync, and stale cleanup—not just basic worktree commands.
FAQ
What scripts does git-worktree-manager include?
git-worktree-manager bundles worktree_manager.py for create/list-prep with deterministic ports and .env* sync, plus worktree_cleanup.py for reviewing and removing stale worktrees by age threshold.
How does git-worktree-manager avoid port conflicts?
git-worktree-manager assigns deterministic ports during worktree creation via worktree_manager.py, so each parallel branch checkout can run its own dev server without manual port hunting.
Is Git Worktree Manager safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.