Skip to content

fix(windows): harden native CLI/TUI dependency bootstrap - #21631

Closed
OutThisLife wants to merge 2 commits into
mainfrom
bb/windows-cli-tui-native-main
Closed

fix(windows): harden native CLI/TUI dependency bootstrap#21631
OutThisLife wants to merge 2 commits into
mainfrom
bb/windows-cli-tui-native-main

Conversation

@OutThisLife

@OutThisLife OutThisLife commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • make scripts/install.ps1 execution-policy-safe for npm by resolving npm.cmd first, converting npm.ps1 to .cmd when needed, and falling back to node npm-cli.js
  • add a Windows-native Python dependency fallback chain for bootstrap installs: .[all] -> .[pty,mcp,honcho,acp] -> .
  • persist managed Node discovery across sessions by writing both user PATH and HERMES_NODE when Hermes installs Node under %LOCALAPPDATA%\\hermes\\node
  • make LocalEnvironment PATH repair Windows-native (; separator, case-insensitive dedupe via ntpath, no POSIX /usr/bin injection)
  • add a Windows UTF-8 bootstrap helper that re-execs Hermes entrypoints with -X utf8 (marker-gated, single-pass) so implicit text-mode open() calls use UTF-8 defaults on legacy Windows locales
  • add regression coverage for installer branch logic, UTF-8 bootstrap behavior, and Windows PATH behavior

Scope

  • in scope: native Windows CLI + --tui install/bootstrap reliability, plus Windows default text encoding hardening at Hermes entrypoints
  • out of scope: dashboard/desktop runtime boot flow changes (kept separate)

Test plan

  • scripts/run_tests.sh /Users/brooklyn/www/hermes-agent-win-cli-tui/tests/test_utf8_bootstrap.py /Users/brooklyn/www/hermes-agent-win-cli-tui/tests/test_install_ps1_windows_dependency_paths.py /Users/brooklyn/www/hermes-agent-win-cli-tui/tests/tools/test_local_env_blocklist.py

Native Windows manual matrix

  • winget present + restrictive execution policy (npm.ps1 blocked): run scripts/install.ps1 and confirm both repo-root and ui-tui npm installs succeed
  • no winget: confirm Node ZIP fallback installs to %LOCALAPPDATA%\\hermes\\node and install completes
  • open a fresh terminal after install: hermes --tui starts without needing manual Node path edits
  • rerun installer: PATH entries for Hermes/Node stay deduplicated (no repeated entries)
  • on a legacy non-UTF-8 Windows locale, run CLI + execute_code with non-ASCII file contents and verify no decode/encode crash from default text mode I/O

Handle native Windows dependency edge cases by avoiding npm.ps1 execution-policy failures, persisting managed Node resolution, and validating runtime imports per platform.
@github-actions

github-actions Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: bb/windows-cli-tui-native-main vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 7687 on HEAD, 7683 on base (🆕 +4)

🆕 New issues (3):

Rule Count
invalid-argument-type 3
First entries
tests/test_utf8_bootstrap.py:103: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `bound method Top[dict[Unknown, Unknown]].__getitem__(key: Never, /) -> object` cannot be called with key of type `Literal["PYTHONIOENCODING"]` on object of type `Top[dict[Unknown, Unknown]]`
tests/test_utf8_bootstrap.py:102: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `bound method Top[dict[Unknown, Unknown]].__getitem__(key: Never, /) -> object` cannot be called with key of type `Literal["PYTHONUTF8"]` on object of type `Top[dict[Unknown, Unknown]]`
tests/test_utf8_bootstrap.py:145: [invalid-argument-type] invalid-argument-type: Method `__getitem__` of type `bound method Top[dict[Unknown, Unknown]].__getitem__(key: Never, /) -> object` cannot be called with key of type `Literal["_HERMES_UTF8_REEXEC"]` on object of type `Top[dict[Unknown, Unknown]]`

✅ Fixed issues: none

Unchanged: 4033 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels May 8, 2026
Force UTF-8 defaults on legacy Windows by re-execing Hermes entrypoints with -X utf8, preventing locale codec crashes from implicit text encoding in file and stdio paths.
@OutThisLife
OutThisLife requested review from Copilot and teknium1 May 8, 2026 02:58
@OutThisLife

Copy link
Copy Markdown
Collaborator Author

@teknium1 maybe salvage whats useful from this into your PR?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens native Windows bootstrap for Hermes’ CLI/TUI by improving PowerShell install reliability (Node/npm + Python extras), making LocalEnvironment PATH repair Windows-native, and introducing a Windows UTF-8 re-exec bootstrap to avoid legacy locale encoding failures in entrypoints.

Changes:

  • Add utf8_bootstrap.ensure_windows_utf8_mode() and wire it into key entrypoints to re-exec with -X utf8 when needed.
  • Improve scripts/install.ps1 Windows robustness: safer npm invocation, Python extras fallback chain, and persistence of managed Node via PATH + HERMES_NODE.
  • Make LocalEnvironment PATH repair Windows-native (separator + case-insensitive dedupe) and add regression tests for PATH + installer branch logic.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
utf8_bootstrap.py Adds Windows UTF-8 bootstrap helper with optional marker-gated re-exec.
tools/environments/local.py Makes PATH “saning” Windows-native (; separator, case-insensitive dedupe, avoids POSIX injection).
tests/tools/test_local_env_blocklist.py Adds tests covering Windows PATH behavior and dedupe.
tests/test_utf8_bootstrap.py Adds unit tests for UTF-8 bootstrap re-exec / marker / guard behaviors.
tests/test_install_ps1_windows_dependency_paths.py Adds regression assertions to lock in critical Windows installer branches.
scripts/install.ps1 Hardened installer: npm resolution workaround, Python extras fallback chain, managed Node persistence.
run_agent.py Calls UTF-8 bootstrap in the agent entrypoint.
hermes_cli/main.py Calls UTF-8 bootstrap early to protect CLI startup defaults.
gateway/run.py Adds UTF-8 bootstrap call for direct gateway invocation.
cli.py Adds UTF-8 bootstrap call in CLI main.
acp_adapter/entry.py Calls UTF-8 bootstrap at ACP adapter import-time for legacy Windows defaults.
pyproject.toml Ensures utf8_bootstrap is shipped via py-modules.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread utf8_bootstrap.py
Comment on lines +45 to +49
return False
if not reexec:
return False
if os.environ.get(_UTF8_REEXEC_GUARD) == "1":
return False
Comment thread utf8_bootstrap.py
Comment on lines +23 to +43
"""Ensure UTF-8 defaults on Windows.

Behavior:
- Always sets ``PYTHONUTF8=1`` and ``PYTHONIOENCODING=utf-8`` on Windows.
- If Python is already in UTF-8 mode, returns immediately.
- Otherwise re-execs the current interpreter with ``-X utf8`` (once),
unless marker-gated or explicitly disabled via ``reexec=False``.
- When ``module=...`` is supplied, re-execs as ``python -m <module>`` and
forwards original user args (excluding argv0), which avoids Windows
console-script ``.exe`` wrappers being treated as Python scripts.

Returns ``True`` only when a re-exec is attempted and the exec call
unexpectedly returns (e.g. under a patched test double). In normal
operation ``os.execvpe`` never returns on success.
"""
if sys.platform != "win32":
return False

os.environ.setdefault("PYTHONUTF8", "1")
os.environ.setdefault("PYTHONIOENCODING", "utf-8")

Comment thread scripts/install.ps1
Comment on lines +83 to +86
$normalizedEntry = $Entry.Trim().TrimEnd("\")
foreach ($part in $parts) {
if ($part.Trim().TrimEnd("\") -ieq $normalizedEntry) {
return $CurrentPath
Comment on lines +18 to +20
def test_node_install_keeps_winget_and_zip_fallback_paths() -> None:
text = INSTALL_PS1.read_text()

@teknium1

Copy link
Copy Markdown
Contributor

Closing as superseded by #21561.

Triage notes (medium confidence):
PR #21561 (merged 2026-05-08) added native Windows support including hermes_bootstrap.py (registered in pyproject.toml:215) which provides the same PYTHONUTF8/stdio reconfiguration this PR's utf8_bootstrap.py module attempts.

Thanks for the contribution — the underlying problem this PR addresses has been resolved by the linked PR on current main. If you believe this was closed in error, please comment and we'll reopen.

(Bulk-closed during a CLI PR triage sweep.)

@teknium1 teknium1 closed this May 24, 2026
@alt-glitch alt-glitch added comp/acp Agent Communication Protocol adapter comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery backend/local Local shell execution platform/windows Native Windows-specific behavior or breakage duplicate This issue or pull request already exists sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows labels Aug 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend/local Local shell execution comp/acp Agent Communication Protocol adapter comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/cli CLI entry point, hermes_cli/, setup wizard comp/gateway Gateway runner, session dispatch, delivery comp/tui Terminal UI (ui-tui/ + tui_gateway/) duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists platform/windows Native Windows-specific behavior or breakage sweeper:risk-platform-windows Sweeper risk: may break or behave differently on native Windows type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants