Last active
March 20, 2026 15:28
-
-
Save bedwards/62b01df5e733ead96f83c9c26ef99196 to your computer and use it in GitHub Desktop.
Wrapper that launches `claude` after ensuring the current project has "claude-in-chrome" disabled in ~/.claude.json.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # /// script | |
| # requires-python = ">=3.10" | |
| # /// | |
| """ | |
| Wrapper that launches `claude` after ensuring the current project | |
| has "claude-in-chrome" disabled in ~/.claude.json. | |
| Usage: | |
| cl [cl-options] [-- claude-args...] | |
| Options: | |
| -h, --help Show this help message | |
| -n, --dry-run Show what would change without writing | |
| Everything after `--` is passed through to `claude` as-is. | |
| If no `--` is present, all arguments are passed to `claude`. | |
| """ | |
| import json | |
| import os | |
| import shutil | |
| import sys | |
| from datetime import datetime | |
| from pathlib import Path | |
| CLAUDE_JSON = Path.home() / ".claude.json" | |
| BACKUP_DIR = Path.home() / "Documents" | |
| MCP_SERVER = "claude-in-chrome" | |
| HELP = """\ | |
| cl — claude wrapper that disables chrome MCP | |
| Usage: | |
| cl [options] [-- claude-args...] | |
| Options: | |
| -h, --help Show this help message and exit | |
| -n, --dry-run Show what would change without writing | |
| Everything after -- is forwarded to claude. | |
| If no -- is present, all arguments go to claude. | |
| Examples: | |
| cl # launch claude in cwd | |
| cl -- --resume # launch claude --resume | |
| cl -n # dry-run: see what cl would do | |
| cl -- -p "hello" # pass -p "hello" to claude | |
| """ | |
| def resolve_project_key() -> str: | |
| """Return the canonical project path that claude uses as a key.""" | |
| return os.path.realpath(os.getcwd()) | |
| def backup(path: Path) -> None: | |
| """Copy claude.json to ~/Documents/ with a timestamp suffix.""" | |
| if not path.exists(): | |
| return | |
| ts = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| dest = BACKUP_DIR / f"claude.json.backup.{ts}" | |
| shutil.copy2(path, dest) | |
| def ensure_disabled(data: dict, project_key: str) -> bool: | |
| """ | |
| Ensure data["projects"][project_key]["disabledMcpServers"] | |
| contains MCP_SERVER. Returns True if data was modified. | |
| """ | |
| projects = data.setdefault("projects", {}) | |
| project = projects.setdefault(project_key, {}) | |
| disabled = project.setdefault("disabledMcpServers", []) | |
| if MCP_SERVER not in disabled: | |
| disabled.append(MCP_SERVER) | |
| return True | |
| return False | |
| def parse_args(argv: list[str]) -> tuple[dict, list[str]]: | |
| """ | |
| Split argv into cl options (before --) and claude args (after --). | |
| If no -- is present, everything goes to claude. | |
| """ | |
| cl_opts = {"help": False, "dry_run": False} | |
| claude_args: list[str] = [] | |
| if "--" in argv: | |
| sep = argv.index("--") | |
| cl_part = argv[:sep] | |
| claude_args = argv[sep + 1 :] | |
| else: | |
| # No separator: if any arg looks like a cl flag, parse it; | |
| # otherwise pass everything to claude | |
| cl_flags = {"-h", "--help", "-n", "--dry-run"} | |
| if any(a in cl_flags for a in argv): | |
| cl_part = argv | |
| else: | |
| return cl_opts, argv | |
| for arg in cl_part: | |
| if arg in ("-h", "--help"): | |
| cl_opts["help"] = True | |
| elif arg in ("-n", "--dry-run"): | |
| cl_opts["dry_run"] = True | |
| else: | |
| print(f"cl: unknown option: {arg}", file=sys.stderr) | |
| print("Try 'cl --help' for usage.", file=sys.stderr) | |
| sys.exit(1) | |
| return cl_opts, claude_args | |
| def main() -> None: | |
| opts, claude_args = parse_args(sys.argv[1:]) | |
| if opts["help"]: | |
| print(HELP) | |
| sys.exit(0) | |
| # 1. Read existing config (or start fresh) | |
| if CLAUDE_JSON.exists(): | |
| text = CLAUDE_JSON.read_text(encoding="utf-8") | |
| data = json.loads(text) | |
| else: | |
| data = {} | |
| project_key = resolve_project_key() | |
| modified = ensure_disabled(data, project_key) | |
| if opts["dry_run"]: | |
| if modified: | |
| print(f"Would add '{MCP_SERVER}' to disabledMcpServers for {project_key}") | |
| else: | |
| print(f"'{MCP_SERVER}' already disabled for {project_key} — no changes") | |
| print(f"Would exec: claude {' '.join(claude_args)}" if claude_args else "Would exec: claude") | |
| sys.exit(0) | |
| if modified: | |
| # 2. Backup before writing | |
| backup(CLAUDE_JSON) | |
| # 3. Write atomically via temp file in same directory | |
| tmp = CLAUDE_JSON.with_suffix(".tmp") | |
| tmp.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") | |
| tmp.replace(CLAUDE_JSON) | |
| # 4. Exec claude, forwarding claude args | |
| os.execvp("claude", ["claude"] + claude_args) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment