From 8836b3a113f8b8781a1935217f008fe67ae8e09f Mon Sep 17 00:00:00 2001 From: teknium1 <127238744+teknium1@users.noreply.github.com> Date: Fri, 29 May 2026 12:14:09 -0700 Subject: [PATCH] fix(cli): widen Windows .bat wrapper fix to custom-name alias path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The profile alias --name path in main.py rewrote the wrapper with a hardcoded #!/bin/sh script right after create_wrapper_script(), clobbering the .bat on Windows and reintroducing the exact bug for custom aliases. create_wrapper_script() now takes an optional target so the alias file is named after the alias while the -p content references the profile — one platform-aware code path, no post-hoc rewrite. --- hermes_cli/main.py | 7 +++---- hermes_cli/profiles.py | 11 ++++++++--- tests/hermes_cli/test_profiles.py | 26 ++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/hermes_cli/main.py b/hermes_cli/main.py index 904e47f8a..8540048aa 100644 --- a/hermes_cli/main.py +++ b/hermes_cli/main.py @@ -10562,11 +10562,10 @@ def cmd_profile(args): if collision: print(f"Error: {collision}") sys.exit(1) - wrapper_path = create_wrapper_script(alias_name) + wrapper_path = create_wrapper_script( + alias_name, target=name if custom_name else None + ) if wrapper_path: - # If custom name, write the profile name into the wrapper - if custom_name: - wrapper_path.write_text(f'#!/bin/sh\nexec hermes -p {name} "$@"\n') print(f"✓ Alias created: {wrapper_path}") if not _is_wrapper_dir_in_path(): print(f"⚠ {_get_wrapper_dir()} is not in your PATH.") diff --git a/hermes_cli/profiles.py b/hermes_cli/profiles.py index 38df5de80..f490cbbfb 100644 --- a/hermes_cli/profiles.py +++ b/hermes_cli/profiles.py @@ -359,13 +359,18 @@ def _is_wrapper_dir_in_path() -> bool: return wrapper_dir in os.environ.get("PATH", "").split(os.pathsep) -def create_wrapper_script(name: str) -> Optional[Path]: +def create_wrapper_script(name: str, target: Optional[str] = None) -> Optional[Path]: """Create a shell wrapper script at ~/.local/bin/. + The wrapper file is named after ``name`` (the alias). The profile it + activates is ``target`` if given, otherwise ``name`` — this lets a custom + alias name point at a differently-named profile without a post-hoc rewrite. + On Windows, creates a ``.bat`` file instead of a POSIX shell script. Returns the path to the created wrapper, or None if creation failed. """ canon = normalize_profile_name(name) + profile = normalize_profile_name(target) if target else canon wrapper_dir = _get_wrapper_dir() try: wrapper_dir.mkdir(parents=True, exist_ok=True) @@ -377,7 +382,7 @@ def create_wrapper_script(name: str) -> Optional[Path]: if is_windows: wrapper_path = wrapper_dir / f"{canon}.bat" try: - wrapper_path.write_text(f"@echo off\r\nhermes -p {canon} %*\r\n") + wrapper_path.write_text(f"@echo off\r\nhermes -p {profile} %*\r\n") return wrapper_path except OSError as e: print(f"⚠ Could not create wrapper at {wrapper_path}: {e}") @@ -385,7 +390,7 @@ def create_wrapper_script(name: str) -> Optional[Path]: else: wrapper_path = wrapper_dir / canon try: - wrapper_path.write_text(f'#!/bin/sh\nexec hermes -p {canon} "$@"\n') + wrapper_path.write_text(f'#!/bin/sh\nexec hermes -p {profile} "$@"\n') wrapper_path.chmod(wrapper_path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH) return wrapper_path except OSError as e: diff --git a/tests/hermes_cli/test_profiles.py b/tests/hermes_cli/test_profiles.py index 49241f310..22e36d421 100644 --- a/tests/hermes_cli/test_profiles.py +++ b/tests/hermes_cli/test_profiles.py @@ -682,6 +682,32 @@ class TestWrapperScript: from hermes_cli.profiles import remove_wrapper_script assert remove_wrapper_script("nonexistent") is False + def test_custom_alias_target_on_posix(self, profile_env, monkeypatch): + # Custom alias name pointing at a differently-named profile: the file + # is named after the alias, the -p content references the profile. + monkeypatch.setattr("sys.platform", "darwin") + from hermes_cli.profiles import create_wrapper_script + wrapper = create_wrapper_script("rq", target="redqueen") + assert wrapper is not None + assert wrapper.name == "rq" + content = wrapper.read_text() + assert content.startswith("#!/bin/sh") + assert "hermes -p redqueen" in content + + def test_custom_alias_target_on_windows(self, profile_env, monkeypatch): + # Regression: custom-name aliases must still produce an executable + # .bat (not a clobbered #!/bin/sh) on Windows. + monkeypatch.setattr("sys.platform", "win32") + from hermes_cli.profiles import create_wrapper_script + wrapper = create_wrapper_script("rq", target="redqueen") + assert wrapper is not None + assert wrapper.name == "rq.bat" + content = wrapper.read_text() + assert "@echo off" in content + assert "hermes -p redqueen" in content + assert "%*" in content + assert "#!/bin/sh" not in content + # =================================================================== # TestRenameProfile