#!/usr/bin/env bash # Shared helpers for the git-manager skill. Sourced by git_cmd.sh, not # meant to be executed directly. CONFIG_FILE="$HOME/.agent-skills/git-manager/config.json" # Subcommands this skill is allowed to run. Anything not in this list is # rejected outright by git_cmd.sh, regardless of what the caller asks for. ALLOWED_SUBCOMMANDS=(status log diff show fetch remote branch checkout add commit push pull) is_allowed_subcommand() { local sub="$1" local allowed for allowed in "${ALLOWED_SUBCOMMANDS[@]}"; do [ "$sub" = "$allowed" ] && return 0 done return 1 } # Resolve a repo's absolute path. # - If a name is given, look it up in the config file's "repos" array. # - If no name is given, default to $PWD, but only if it's actually inside # a git work tree. # Never accepts a raw path from the caller directly — named repos always go # through the config file, so the whitelisted script can't be pointed at an # arbitrary directory outside what's configured (or the current project # directory Claude Code itself already scoped the session to). resolve_repo_path() { local repo_name="${1:-}" if [ -n "$repo_name" ]; then if [ ! -f "$CONFIG_FILE" ]; then echo "Error: no configuration found at $CONFIG_FILE. Run setup first, or omit the repo name to use the current directory." >&2 exit 1 fi if ! command -v python3 >/dev/null 2>&1; then echo "Error: python3 is required to parse the config file." >&2 exit 1 fi python3 - "$CONFIG_FILE" "$repo_name" << 'PYEOF' import json, sys config_file, repo_name = sys.argv[1], sys.argv[2] with open(config_file) as f: cfg = json.load(f) repos = cfg.get("repos", []) match = [r for r in repos if r.get("name") == repo_name] if not match: sys.stderr.write(f"Error: no repo named '{repo_name}' in config.\n") sys.exit(1) print(match[0]["path"]) PYEOF else echo "$PWD" fi } # Validate that a resolved path is a real, existing directory that is # actually inside a git work tree. Prints the canonical repo root. canonicalize_and_check_repo() { local path="$1" local real real=$(realpath -e "$path" 2>/dev/null) || { echo "Error: path '$path' does not exist." >&2 exit 1 } if [ ! -d "$real" ]; then echo "Error: path '$real' is not a directory." >&2 exit 1 fi if ! git -C "$real" rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "Error: '$real' is not inside a git work tree." >&2 exit 1 fi git -C "$real" rev-parse --show-toplevel }