diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 849cbf5096e..8c2657b0da4 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -26,16 +26,18 @@ HERMES_DESKTOP_HERMES_ROOT=/path/to/your/clone npm run dev Hermes Desktop needs: -- **Python 3.11+** — for the agent runtime, dashboard backend, and tool execution. -- **Git for Windows** (Windows only) — provides Git Bash, which Hermes' terminal tool calls directly. Linux and macOS already ship a system bash. +- **Python 3.11+** — for the agent runtime, dashboard backend, and tool execution. (required) +- **Git for Windows** (Windows only) — provides Git Bash, which Hermes' terminal tool calls directly. Linux and macOS already ship a system bash. (required) +- **ripgrep** — used by Hermes' `search_files` tool for fast `.gitignore`-aware file/content search. Recommended on all platforms; Hermes falls back to `grep`/`find` if missing (works but slower and noisier). -The packaged Windows installer (`Hermes-*.exe`) detects both at install time. If either is missing it offers to install them via `winget install -e --id Python.Python.3.11` and `winget install -e --id Git.Git`. If `winget` isn't available the installer shows manual download URLs and lets you continue. The MSI installer (`Hermes-*.msi`) doesn't run the prereq page — enterprise deploys are expected to handle prereqs out-of-band. +The packaged Windows installer (`Hermes-*.exe`) detects all three at install time. Required items missing are auto-installed via `winget install -e --id Python.Python.3.11 --scope user` and `winget install -e --id Git.Git`. The recommended ripgrep is offered as `winget install -e --id BurntSushi.ripgrep.MSVC --scope user`. If `winget` isn't available the installer shows manual download URLs and lets you continue. The MSI installer (`Hermes-*.msi`) doesn't run the prereq page — enterprise deploys are expected to handle prereqs out-of-band. -For dev (`npm run dev`) the same checks happen at first launch via the Electron bootstrapper, which throws a clear error if either prereq is missing. Manual install commands you can run yourself: +For dev (`npm run dev`) the Python and Git Bash checks happen at first launch via the Electron bootstrapper, which throws a clear error if either prereq is missing. Manual install commands you can run yourself: ```powershell -winget install -e --id Python.Python.3.11 +winget install -e --id Python.Python.3.11 --scope user winget install -e --id Git.Git +winget install -e --id BurntSushi.ripgrep.MSVC --scope user ``` ## Development diff --git a/apps/desktop/installer/prereq-check.nsh b/apps/desktop/installer/prereq-check.nsh index fe0a80b05d9..f5d611f923b 100644 --- a/apps/desktop/installer/prereq-check.nsh +++ b/apps/desktop/installer/prereq-check.nsh @@ -3,8 +3,8 @@ ; ============================================================================ ; ; A native NSIS Wizard page (using nsDialogs) inserted between the directory -; selection page and the install-files page. Detects Python 3.11+ and Git -; for Windows; offers to install missing prereqs via winget. +; selection page and the install-files page. Detects Python 3.11+, Git for +; Windows, and ripgrep; offers to install missing items via winget. ; ; Page sequence: ; Welcome → Directory → [PrereqPage] → InstFiles → Finish @@ -17,31 +17,38 @@ ; ; The Function declarations live at top-level in this file so they're parsed ; at include time; the customPageAfterChangeDir macro references them via -; the Page directive so the optimizer doesn't strip them. customInstall has -; a defensive runtime reference too, in case the customPageAfterChangeDir -; hook isn't expanded by some future electron-builder version. +; the Page directive so the optimizer doesn't strip them. ; ; UAC behavior: -; Python is installed with --scope user (no UAC prompt). -; Git for Windows always installs per-machine and triggers a UAC prompt. -; We pre-warn the user via the page footer; the UAC dialog may appear -; behind the installer, so BringToFront is called after each winget run. +; Python: --scope user, no UAC. +; ripgrep: --scope user, no UAC. +; Git for Windows: always per-machine, triggers UAC prompt. +; Footer warns the user about Git's UAC; ExecShellWait preserves the +; foreground focus chain so the prompt comes to front. ; ; Detection: -; Python: try `py -3.11`, `py -3.12`, `py -3.13`, `py -3.14` in order. -; The Python launcher returns exit 0 only when that specific version is -; installed. The Microsoft Store "Python stub" doesn't install py.exe, -; so users with only the stub get correctly classified as not-installed. -; +; Python: try `py -3.11`/`-3.12`/`-3.13`/`-3.14`. The Python launcher +; returns exit 0 only when that specific version is installed. The +; Microsoft Store "Python stub" doesn't install py.exe, so users with +; only the stub get correctly classified as not-installed. ; Git: `where git` returns exit 0 if git is on PATH. -; +; ripgrep: `where rg` returns exit 0 if rg is on PATH. ; winget: `where winget` returns exit 0 on Win11 / Win10 1809+ with App -; Installer. If unavailable, the page shows manual download URLs. +; Installer. If unavailable, the page shows manual download URLs. +; +; Required vs. recommended: +; Python and Git are REQUIRED — without them the agent's runtime + terminal +; tool fail. The page emphasizes "required" wording and the bootstrapper +; throws if either is missing at first launch. +; ripgrep is RECOMMENDED — Hermes' search_files tool uses it for fast +; .gitignore-aware search, and falls back to grep/find from Git Bash when +; missing (works but slower, less filtering). Page wording is softer for +; ripgrep so users understand they CAN skip it. ; ; Skip behaviors: -; - Both prereqs already installed → page is auto-skipped via Abort +; - All three already detected → page is auto-skipped via Abort ; - Silent install (/S) → customInstall winget block skips -; - User unchecks both checkboxes → page advances without running winget +; - User unchecks all checkboxes → page advances without running winget ; ============================================================================ !include "LogicLib.nsh" @@ -53,16 +60,21 @@ Var HermesPyStatusLabel Var HermesPyCheckbox Var HermesGitStatusLabel Var HermesGitCheckbox +Var HermesRgStatusLabel +Var HermesRgCheckbox Var HermesFooterLabel Var HermesHasWinget Var HermesHasPython Var HermesHasGit +Var HermesHasRipgrep Var HermesInstallPython Var HermesInstallGit +Var HermesInstallRipgrep ; ---------------------------------------------------------------------------- ; HermesDetectPrereqs — populates $HermesHasWinget / $HermesHasPython / -; $HermesHasGit with "0" or "1". Called from the page-create function. +; $HermesHasGit / $HermesHasRipgrep with "0" or "1". Called from the +; page-create function. ; ---------------------------------------------------------------------------- Function HermesDetectPrereqs ; --- winget --- @@ -110,10 +122,19 @@ Function HermesDetectPrereqs ${Else} StrCpy $HermesHasGit "0" ${EndIf} + + ; --- ripgrep --- + nsExec::Exec 'cmd.exe /c where rg >nul 2>&1' + Pop $0 + ${If} $0 == 0 + StrCpy $HermesHasRipgrep "1" + ${Else} + StrCpy $HermesHasRipgrep "0" + ${EndIf} FunctionEnd ; ---------------------------------------------------------------------------- -; HermesPrereqPageCreate — builds the prereq page UI. If both prereqs are +; HermesPrereqPageCreate — builds the prereq page UI. If all three items are ; already installed we Abort, which causes NSIS to skip directly to the next ; page in the sequence (InstFiles). ; ---------------------------------------------------------------------------- @@ -122,9 +143,18 @@ Function HermesPrereqPageCreate ${If} $HermesHasPython == "1" ${AndIf} $HermesHasGit == "1" + ${AndIf} $HermesHasRipgrep == "1" Abort ${EndIf} + ; Set the wizard's standard header (top blue/gradient bar). 1037 is the + ; title control, 1038 is the subtitle. Without this, the header still + ; reads "Choose Install Location" left over from the Directory page. + GetDlgItem $0 $HWNDPARENT 1037 + SendMessage $0 ${WM_SETTEXT} 0 "STR:System Requirements" + GetDlgItem $0 $HWNDPARENT 1038 + SendMessage $0 ${WM_SETTEXT} 0 "STR:Hermes needs Python 3.11+ and Git for Windows. ripgrep is recommended." + nsDialogs::Create 1018 Pop $HermesDialog ${If} $HermesDialog == error @@ -133,61 +163,75 @@ Function HermesPrereqPageCreate StrCpy $HermesInstallPython "0" StrCpy $HermesInstallGit "0" + StrCpy $HermesInstallRipgrep "0" - ; Page title (bold) and subtitle. We can't use MUI_HEADER_TEXT here — - ; electron-builder's NSIS template configures MUI internally but doesn't - ; expose the header-text macros to user includes. So we render our own - ; title in the page body using a label with bold font. - ${NSD_CreateLabel} 0u 0u 100% 10u "System Requirements" - Pop $0 - CreateFont $1 "$(^Font)" "10" "700" - SendMessage $0 ${WM_SETFONT} $1 0 - - ${NSD_CreateLabel} 0u 12u 100% 18u "Hermes Agent needs Python 3.11+ and Git for Windows to run. Items already installed are listed as detected; missing items can be installed automatically." + ; Page body intro. The wizard's header (set above) shows the title + ; "System Requirements" and subtitle, so we don't repeat them here — + ; just one short explanatory line. + ${NSD_CreateLabel} 0u 0u 100% 16u "Items already installed are listed as detected. Missing items can be installed automatically via winget." Pop $0 - ; --- Python panel --- - ${NSD_CreateGroupBox} 0u 34u 100% 32u "Python 3.11+" + ; --- Python panel (REQUIRED) --- + ${NSD_CreateGroupBox} 0u 18u 100% 26u "Python 3.11+ (required)" Pop $0 ${If} $HermesHasPython == "1" - ${NSD_CreateLabel} 8u 46u 95% 12u "Detected on your system." + ${NSD_CreateLabel} 8u 28u 95% 10u "Detected on your system." Pop $HermesPyStatusLabel ${Else} ${If} $HermesHasWinget == "1" - ${NSD_CreateLabel} 8u 44u 95% 9u "Not detected." + ${NSD_CreateLabel} 8u 27u 95% 9u "Not detected." Pop $HermesPyStatusLabel - ${NSD_CreateCheckbox} 8u 54u 95% 10u "Install Python 3.11 (per-user install, no admin prompt)" + ${NSD_CreateCheckbox} 8u 36u 95% 9u "Install Python 3.11" Pop $HermesPyCheckbox ${NSD_Check} $HermesPyCheckbox ${Else} - ${NSD_CreateLabel} 8u 44u 95% 20u "Not detected. Install manually from https://www.python.org/downloads/ and re-run this installer." + ${NSD_CreateLabel} 8u 27u 95% 14u "Not detected. Install manually from https://www.python.org/downloads/ and re-run this installer." Pop $HermesPyStatusLabel ${EndIf} ${EndIf} - ; --- Git panel --- - ${NSD_CreateGroupBox} 0u 70u 100% 32u "Git for Windows (provides Git Bash)" + ; --- Git panel (REQUIRED) --- + ${NSD_CreateGroupBox} 0u 48u 100% 26u "Git for Windows (required, provides Git Bash)" Pop $0 ${If} $HermesHasGit == "1" - ${NSD_CreateLabel} 8u 82u 95% 12u "Detected on your system." + ${NSD_CreateLabel} 8u 58u 95% 10u "Detected on your system." Pop $HermesGitStatusLabel ${Else} ${If} $HermesHasWinget == "1" - ${NSD_CreateLabel} 8u 80u 95% 9u "Not detected. Required by Hermes' terminal tool." + ${NSD_CreateLabel} 8u 57u 95% 9u "Not detected. Required by Hermes' terminal tool." Pop $HermesGitStatusLabel - ${NSD_CreateCheckbox} 8u 90u 95% 10u "Install Git for Windows (administrator approval required)" + ${NSD_CreateCheckbox} 8u 66u 95% 9u "Install Git for Windows" Pop $HermesGitCheckbox ${NSD_Check} $HermesGitCheckbox ${Else} - ${NSD_CreateLabel} 8u 80u 95% 20u "Not detected. Install manually from https://git-scm.com/download/win and re-run this installer." + ${NSD_CreateLabel} 8u 57u 95% 14u "Not detected. Install manually from https://git-scm.com/download/win and re-run this installer." Pop $HermesGitStatusLabel ${EndIf} ${EndIf} + ; --- ripgrep panel (RECOMMENDED) --- + ${NSD_CreateGroupBox} 0u 78u 100% 26u "ripgrep (recommended for fast file search)" + Pop $0 + ${If} $HermesHasRipgrep == "1" + ${NSD_CreateLabel} 8u 88u 95% 10u "Detected on your system." + Pop $HermesRgStatusLabel + ${Else} + ${If} $HermesHasWinget == "1" + ${NSD_CreateLabel} 8u 87u 95% 9u "Not detected. Hermes will fall back to slower grep/find." + Pop $HermesRgStatusLabel + ${NSD_CreateCheckbox} 8u 96u 95% 9u "Install ripgrep" + Pop $HermesRgCheckbox + ${NSD_Check} $HermesRgCheckbox + ${Else} + ${NSD_CreateLabel} 8u 87u 95% 14u "Not detected. Install manually from https://github.com/BurntSushi/ripgrep#installation if you want fast .gitignore-aware search." + Pop $HermesRgStatusLabel + ${EndIf} + ${EndIf} + ; --- Footer (UAC notice when Git install will run) --- ${If} $HermesHasGit == "0" ${AndIf} $HermesHasWinget == "1" - ${NSD_CreateLabel} 0u 108u 100% 30u "Note: installing Git for Windows requires administrator approval. The User Account Control prompt may appear behind this window — use the taskbar to find it if needed." + ${NSD_CreateLabel} 0u 108u 100% 18u "Note: Git for Windows requires administrator approval. The UAC prompt may appear behind this window — check your taskbar." Pop $HermesFooterLabel ${EndIf} @@ -208,6 +252,10 @@ Function HermesPrereqPageLeave ${AndIf} $HermesHasWinget == "1" ${NSD_GetState} $HermesGitCheckbox $HermesInstallGit ${EndIf} + ${If} $HermesHasRipgrep == "0" + ${AndIf} $HermesHasWinget == "1" + ${NSD_GetState} $HermesRgCheckbox $HermesInstallRipgrep + ${EndIf} FunctionEnd ; ---------------------------------------------------------------------------- @@ -250,6 +298,20 @@ FunctionEnd ${EndIf} ${EndIf} + ${If} $HermesInstallRipgrep == "1" + ; ripgrep with --scope user — ~5MB, no UAC needed. Failure is non-fatal: + ; Hermes' search_files tool falls back to grep/find from Git Bash. + ; nsExec::ExecToLog streams output to the install log. + DetailPrint "Installing ripgrep via winget (silent per-user install, no admin prompt)..." + nsExec::ExecToLog 'winget install -e --id BurntSushi.ripgrep.MSVC --scope user --silent --disable-interactivity --accept-package-agreements --accept-source-agreements' + Pop $0 + ${If} $0 != 0 + DetailPrint "ripgrep install via winget exited with code $0 (non-fatal — Hermes will fall back to grep/find)." + ${Else} + DetailPrint "ripgrep installed successfully." + ${EndIf} + ${EndIf} + ${If} $HermesInstallGit == "1" ; Git for Windows always installs per-machine and triggers UAC. We use ; ExecShellWait (NSIS's wrapper around Windows ShellExecute) instead of