Ctrl K
ComunidadGuías…

butler/tools/command_canonicalize.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Canonicalize shell commands for stable approval fingerprints (Codex subset)."""

from __future__ import annotations

import re
import shlex
from typing import Sequence

_BASH_NAMES = frozenset({"bash", "sh", "zsh", "/bin/bash", "/bin/sh", "/usr/bin/bash"})
_LC_FLAGS = frozenset({"-lc", "-c", "/c"})


def _split_command(command: str) -> list[str]:
    text = (command or "").strip()
    if not text:
        return []
    try:
        return shlex.split(text, posix=True)
    except ValueError:
        return text.split()


def canonicalize_command_for_approval(command: str) -> str:
    """
    Normalize wrapper paths so `/bin/bash -lc 'git status'` and `bash -lc git status`
    share the same approval fingerprint string.
    """
    argv = _split_command(command)
    if not argv:
        return ""

    prog = argv[0].lower()
    base = prog.rsplit("/", 1)[-1]
    if base in _BASH_NAMES and len(argv) >= 2:
        flag = argv[1]
        if flag in _LC_FLAGS and len(argv) >= 3:
            script = " ".join(argv[2:])
            mode = flag
            return f"__butler_shell__:{mode}:{_normalize_script(script)}"
        if flag in _LC_FLAGS and len(argv) == 2:
            return f"__butler_shell__:{flag}:"

    return " ".join(_normalize_token(t) for t in argv)


def _normalize_token(token: str) -> str:
    t = token.strip()
    if t.startswith("/") and "/" in t[1:]:
        return t.rsplit("/", 1)[-1]
    return t


def _normalize_script(script: str) -> str:
    body = script.strip()
    if (body.startswith("'") and body.endswith("'")) or (
        body.startswith('"') and body.endswith('"')
    ):
        body = body[1:-1].strip()
    body = re.sub(r"\s+", " ", body)
    return body


def canonical_argv_list(argv: Sequence[str]) -> str:
    if not argv:
        return ""
    return canonicalize_command_for_approval(" ".join(shlex.quote(str(a)) for a in argv))


__all__ = ["canonical_argv_list", "canonicalize_command_for_approval"]