diff --git a/skills/fix-plan/scripts/hook_integrity_check.py b/skills/fix-plan/scripts/hook_integrity_check.py index 5d737771..cf556e45 100644 --- a/skills/fix-plan/scripts/hook_integrity_check.py +++ b/skills/fix-plan/scripts/hook_integrity_check.py @@ -64,7 +64,12 @@ def resolve_script_operand(command): resolves to `/path/hook.sh`, not to the interpreter. """ try: - tokens = shlex.split(command) + # posix=True (the default) treats backslash as an escape character, + # so a Windows path like C:\Users\... loses every backslash + # (\U -> U, \A -> A, ...) and the resolved path silently stops + # existing. posix=False keeps backslashes literal; the manual + # strip('"')/strip("'") calls below still handle quoting. + tokens = shlex.split(command, posix=(sys.platform != "win32")) except ValueError: tokens = command.split() for tok in tokens: diff --git a/tests/test_hook_integrity_check.py b/tests/test_hook_integrity_check.py index 39eb2416..c821ad6f 100644 --- a/tests/test_hook_integrity_check.py +++ b/tests/test_hook_integrity_check.py @@ -88,6 +88,24 @@ def test_resolve_plain_path_unchanged(): assert mod.resolve_script_operand('"/p/with space/hook.sh"') == "/p/with space/hook.sh" +def test_resolve_preserves_windows_backslashes(monkeypatch): + # posix=True shlex.split treats backslash as an escape character, so a + # Windows path silently loses every backslash (\U -> U, \A -> A, ...) + # and the resolved path stops existing. Guards the fix for that. + # Force the win32 branch explicitly so this test is deterministic + # regardless of the platform actually running it (CI runs on Linux). + monkeypatch.setattr(mod.sys, "platform", "win32") + assert mod.resolve_script_operand(r"python3 C:\Users\me\hook.sh") == r"C:\Users\me\hook.sh" + + +def test_resolve_posix_path_unaffected_by_win32_branch(monkeypatch): + # On win32, posix=False is used -- confirm ordinary POSIX paths and + # interpreter/flag skipping still resolve correctly under that mode. + monkeypatch.setattr(mod.sys, "platform", "win32") + assert mod.resolve_script_operand("python3 /p/hook.sh") == "/p/hook.sh" + assert mod.resolve_script_operand('"/p/with space/hook.sh"') == "/p/with space/hook.sh" + + # --- check_hook_integrity: end-to-end on the installed schema --- def test_installed_schema_is_audited(tmp_path, monkeypatch):