Created
March 24, 2026 18:01
-
-
Save Wumpf/97534424d73e7da2931d3ce3ed5841d7 to your computer and use it in GitHub Desktop.
Vibe coded script for building `nono` invocations that make the git directory either read-only or read+write
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 python3 | |
| """ | |
| Launch a command under nono with fine-grained git permissions. | |
| Instead of --allow-cwd (blanket read-write to the entire working directory), | |
| this resolves the git directory (including worktree indirection) and passes | |
| explicit --read / --write flags. | |
| Usage: | |
| nono-git.py [--git-read-only] [--] <command> [args...] | |
| Examples: | |
| nono-git.py -- claude | |
| nono-git.py --git-read-only -- cargo build | |
| nono-git.py --allow-proxy api.anthropic.com -- claude | |
| """ | |
| import os | |
| import shlex | |
| import subprocess | |
| import sys | |
| def git(*args: str) -> str: | |
| """Run a git command and return its stripped stdout, or None on failure.""" | |
| result = subprocess.run( | |
| ["git", *args], | |
| capture_output=True, | |
| text=True, | |
| ) | |
| if result.returncode != 0: | |
| return None | |
| return result.stdout.strip() | |
| def resolve_git_paths() -> dict: | |
| """Figure out the worktree root, git dir, and (for worktrees) the shared git dir.""" | |
| worktree = git("rev-parse", "--show-toplevel") | |
| if not worktree: | |
| sys.exit("Error: not inside a git repository.") | |
| git_dir = git("-C", worktree, "rev-parse", "--absolute-git-dir") | |
| git_common_dir = git("-C", worktree, "rev-parse", "--path-format=absolute", "--git-common-dir") | |
| # Normalise so comparisons work | |
| git_dir = os.path.realpath(git_dir) if git_dir else None | |
| git_common_dir = os.path.realpath(git_common_dir) if git_common_dir else None | |
| return { | |
| "worktree": worktree, | |
| "git_dir": git_dir, | |
| "git_common_dir": git_common_dir, | |
| } | |
| def parse_args(argv: list[str]) -> tuple[bool, list[str], list[str]]: | |
| """ | |
| Split argv into: | |
| - git_read_only flag | |
| - extra nono flags (anything before --) | |
| - the command to run (everything after --) | |
| """ | |
| git_read_only = False | |
| nono_args = [] | |
| command = [] | |
| it = iter(argv) | |
| for arg in it: | |
| if arg == "--": | |
| command = list(it) | |
| break | |
| elif arg == "--git-read-only": | |
| git_read_only = True | |
| else: | |
| nono_args.append(arg) | |
| return git_read_only, nono_args, command | |
| def worktree_entries_except_git(worktree: str) -> list[tuple[str, bool]]: | |
| """List top-level entries in the worktree (excluding .git) as (path, is_dir) tuples.""" | |
| entries = [] | |
| for name in sorted(os.listdir(worktree)): | |
| if name == ".git": | |
| continue | |
| full = os.path.join(worktree, name) | |
| entries.append((full, os.path.isdir(full))) | |
| return entries | |
| def build_nono_command(paths: dict, git_read_only: bool, nono_args: list[str], command: list[str]) -> list[str]: | |
| """Assemble the full nono invocation.""" | |
| git_flag = "--read" if git_read_only else "--write" | |
| cmd = ["nono", "run"] | |
| # nono distinguishes files (--read-file/--write-file) from dirs (--read/--write) | |
| git_dir_inside_worktree = os.path.isdir(os.path.join(paths["worktree"], ".git")) | |
| if git_read_only and git_dir_inside_worktree: | |
| # Can't use --write on the whole worktree because .git is a subdirectory | |
| # and that would grant write access to it too. | |
| # Instead, list every top-level entry except .git individually. | |
| for path, is_dir in worktree_entries_except_git(paths["worktree"]): | |
| flag = "--write" if is_dir else "--write-file" | |
| cmd += [flag, path] | |
| else: | |
| # Either full access, or .git is external (worktree) so --write on the | |
| # whole tree is safe. | |
| cmd += ["--write", paths["worktree"]] | |
| # Git directory — in a worktree .git is a file, in a normal repo it's a dir | |
| git_dir_flag = git_flag if os.path.isdir(paths["git_dir"]) else f"{git_flag}-file" | |
| cmd += [git_dir_flag, paths["git_dir"]] | |
| # For worktrees the common dir is different from git_dir — grant access too | |
| if paths["git_common_dir"] and paths["git_common_dir"] != paths["git_dir"]: | |
| common_flag = git_flag if os.path.isdir(paths["git_common_dir"]) else f"{git_flag}-file" | |
| cmd += [common_flag, paths["git_common_dir"]] | |
| cmd += nono_args | |
| cmd += ["--"] | |
| cmd += command | |
| return cmd | |
| def confirm(cmd: list[str]) -> bool: | |
| """Show the command and ask the user to confirm.""" | |
| # Group flag+value pairs onto one line, show each on its own indented line | |
| print("Will execute:\n") | |
| print(f" nono run \\") | |
| i = 1 # skip "nono" | |
| i = 2 # skip "nono run" | |
| while i < len(cmd): | |
| arg = cmd[i] | |
| if arg == "--": | |
| # Everything after -- is the command | |
| print(f" -- {shlex.join(cmd[i + 1:])}") | |
| break | |
| elif arg.startswith("--") and i + 1 < len(cmd) and not cmd[i + 1].startswith("-"): | |
| # Flag with a value: print together | |
| print(f" {arg} {shlex.quote(cmd[i + 1])} \\") | |
| i += 2 | |
| else: | |
| # Standalone flag | |
| print(f" {arg} \\") | |
| i += 1 | |
| print() | |
| try: | |
| answer = input("Proceed? [Y/n] ").strip().lower() | |
| except (EOFError, KeyboardInterrupt): | |
| print() | |
| return False | |
| return answer in ("", "y", "yes") | |
| def main(): | |
| git_read_only, nono_args, command = parse_args(sys.argv[1:]) | |
| if not command: | |
| print(__doc__.strip()) | |
| sys.exit(1) | |
| paths = resolve_git_paths() | |
| cmd = build_nono_command(paths, git_read_only, nono_args, command) | |
| if confirm(cmd): | |
| os.execvp(cmd[0], cmd) | |
| else: | |
| sys.exit("Aborted.") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment