From 32087e4bc962744b1496da05c8d6e8b770068c12 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Sun, 28 Jun 2026 17:41:47 -0500 Subject: [PATCH] fix(windows): hide console flash on checkpoint git + skills_hub gh probes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The #54236/#54417 backend git/gh sweep routed git_probe, the repo-file picker, coding_context, context_references, copilot_auth, and the gateway process scans through CREATE_NO_WINDOW, but two sibling spawn legs that also run inside the console-less desktop/gateway backend were missed: - tools/checkpoint_manager.py `_run_git` (and the one-shot `git init --bare` in `_init_store`) — when checkpoints are enabled, every file-mutating turn fires multiple bare `git` calls (status, add, write-tree/commit-tree, update-ref). Spawned from a parent with no console (Electron spawns the backend with windowsHide → CREATE_NO_WINDOW), each one allocates its own conhost window → a flurry of terminal popups. - tools/skills_hub.py `GitHubAuth._try_gh_cli` — `gh auth token`, the same bug class as the already-fixed copilot_auth gh probe. Route both through `windows_hide_flags()` (no-op on POSIX), matching the established per-site pattern. Tests added to tests/test_windows_subprocess_no_window_flags.py. --- ...test_windows_subprocess_no_window_flags.py | 38 +++++++++++++++++++ tools/checkpoint_manager.py | 7 ++++ tools/skills_hub.py | 2 + 3 files changed, 47 insertions(+) diff --git a/tests/test_windows_subprocess_no_window_flags.py b/tests/test_windows_subprocess_no_window_flags.py index 7768f5fec08..3777182167e 100644 --- a/tests/test_windows_subprocess_no_window_flags.py +++ b/tests/test_windows_subprocess_no_window_flags.py @@ -300,6 +300,44 @@ def test_local_stt_audio_prep_hides_ffmpeg_window(monkeypatch, tmp_path): assert captured[0][0][0] == "ffmpeg" assert captured[0][1]["creationflags"] == _CREATE_NO_WINDOW +def test_checkpoint_manager_git_hides_windows(monkeypatch): + from tools import checkpoint_manager + + captured = [] + + def fake_run(cmd, **kwargs): + captured.append((cmd, kwargs)) + return _Completed(stdout="clean\n") + + monkeypatch.setattr(checkpoint_manager, "IS_WINDOWS", True) + monkeypatch.setattr(checkpoint_manager, "windows_hide_flags", lambda: _CREATE_NO_WINDOW) + monkeypatch.setattr(checkpoint_manager.subprocess, "run", fake_run) + + ok, _, _ = checkpoint_manager._run_git(["status", "--short"], Path("C:/store"), ".") + assert ok + assert captured[0][0][0] == "git" + assert captured[0][1]["creationflags"] == _CREATE_NO_WINDOW + + +def test_skills_hub_gh_token_hides_windows(monkeypatch): + from tools import skills_hub + + captured = [] + + def fake_run(cmd, **kwargs): + captured.append((cmd, kwargs)) + return _Completed(stdout="gho_from_cli\n") + + monkeypatch.setattr(skills_hub, "IS_WINDOWS", True) + monkeypatch.setattr(skills_hub, "windows_hide_flags", lambda: _CREATE_NO_WINDOW) + monkeypatch.setattr(skills_hub.subprocess, "run", fake_run) + + auth = skills_hub.GitHubAuth.__new__(skills_hub.GitHubAuth) + assert auth._try_gh_cli() == "gho_from_cli" + assert captured[0][0] == ["gh", "auth", "token"] + assert captured[0][1]["creationflags"] == _CREATE_NO_WINDOW + + def test_tui_slash_worker_hides_python_window(monkeypatch): from tui_gateway import server diff --git a/tools/checkpoint_manager.py b/tools/checkpoint_manager.py index 720973b67e0..589c993ba6b 100644 --- a/tools/checkpoint_manager.py +++ b/tools/checkpoint_manager.py @@ -58,6 +58,7 @@ import subprocess import time from pathlib import Path from hermes_constants import get_hermes_home +from hermes_cli._subprocess_compat import IS_WINDOWS, windows_hide_flags from typing import Dict, List, Optional, Set, Tuple from utils import env_int @@ -321,6 +322,10 @@ def _run_git( env = _git_env(store, str(normalized_working_dir), index_file=index_file) cmd = ["git"] + list(args) allowed_returncodes = allowed_returncodes or set() + # Checkpoints run inside the console-less desktop/gateway backend; a bare + # git spawn there pops a fresh conhost window per call (status, add, + # commit, …) on Windows. No-op on POSIX. + _popen_kwargs = {"creationflags": windows_hide_flags()} if IS_WINDOWS else {} try: result = subprocess.run( cmd, @@ -330,6 +335,7 @@ def _run_git( env=env, cwd=str(normalized_working_dir), stdin=subprocess.DEVNULL, + **_popen_kwargs, ) ok = result.returncode == 0 stdout = result.stdout.strip() @@ -450,6 +456,7 @@ def _init_store(store: Path, working_dir: str) -> Optional[str]: capture_output=True, text=True, env=init_env, timeout=_GIT_TIMEOUT, stdin=subprocess.DEVNULL, + **({"creationflags": windows_hide_flags()} if IS_WINDOWS else {}), ) if result.returncode != 0: return f"Shadow store init failed: {result.stderr.strip()}" diff --git a/tools/skills_hub.py b/tools/skills_hub.py index 76969fb8d8c..676b6c81f76 100644 --- a/tools/skills_hub.py +++ b/tools/skills_hub.py @@ -26,6 +26,7 @@ from dataclasses import dataclass, field from datetime import datetime, timezone from pathlib import Path, PurePosixPath from hermes_constants import get_hermes_home +from hermes_cli._subprocess_compat import IS_WINDOWS, windows_hide_flags from agent.skill_utils import is_excluded_skill_path from typing import Any, Dict, List, Optional, Tuple, Union from urllib.parse import urljoin, urlparse, urlunparse @@ -302,6 +303,7 @@ class GitHubAuth: ["gh", "auth", "token"], capture_output=True, text=True, timeout=5, stdin=subprocess.DEVNULL, + **({"creationflags": windows_hide_flags()} if IS_WINDOWS else {}), ) if result.returncode == 0 and result.stdout.strip(): return result.stdout.strip()