# NousResearch/hermes-agent · Windows install.ps1 script bugs and fixes

## Open now

- Nothing open on GitHub.

Multiple bugs and fixes related to the Windows PowerShell installer script, including a malformed param() block, UTF-8 BOM characters, and syntax errors that break the one-line installation command. Several fixes address the same or closely related root causes.

## TL;DR

- 8 member(s): 0 open issue(s), 4 closed issue(s), 0 open PR(s), 1 merged PR(s), 3 PR(s) closed without merging.
- A fix was merged to main in PR #28169. Verify it addresses your case.
- **Recommended:** read PR #28169 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `unresolved`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.26`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #27397: \[Bug\]: One Line Install not working
- #27622: \[Bug\]: Windows Install Isn't Working
- #28071: Fix critical syntax error in install.ps1 that breaks Windows installation
- #28141: install.ps1: malformed param() block breaks Windows one-line installer

## Prior & competing PRs

- **#28169: feat(install.ps1): strip BOM, add -Commit/-Tag pin params, harden git ops**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `scripts/install.ps1`
  - fix hunks:
  - `scripts/install.ps1`

    ```diff
    @@ -1,4 +1,4 @@
    -﻿# ============================================================================
    +# ============================================================================
     # Hermes Agent Installer for Windows
     # ============================================================================
     # Installation script for Windows (PowerShell).
    ```

    ```diff
    @@ -16,6 +16,13 @@ param(
         [switch]$NoVenv,
         [switch]$SkipSetup,
         [string]$Branch = "main",
    +    # -Commit and -Tag are higher-precedence variants of -Branch for users
    +    # who need reproducible installs (desktop installer pinning, CI, release
    +    # bundles).  When set, the repository stage clones $Branch (faster than
    +    # cloning the full default-branch history) and then `git checkout`s the
    +    # exact ref.  Precedence: Commit > Tag > Branch.
    +    [string]$Commit = "",
    +    [string]$Tag = "",
         [string]$HermesHome = "$env:LOCALAPPDATA\hermes",
         [string]$InstallDir = "$env:LOCALAPPDATA\hermes\hermes-agent",
     
    ```

    ```diff
    @@ -955,14 +962,36 @@ function Install-Repository {
             if ($repoValid) {
                 Write-Info "Existing installation found, updating..."
                 Push-Location $InstallDir
    +            # Wrap the entire fetch+checkout block in EAP=Continue so git's
    +            # routine stderr output (e.g. 'From <url>' info lines emitted by
    +            # `git fetch`) doesn't terminate the script under the global
    +            # EAP=Stop.  We rely on $LASTEXITCODE for actual failures.
    +            $prevEAP = $ErrorActionPreference
    +            $ErrorActionPreference = "Continue"
                 try {
                     git -c windows.appendAtomically=false fetch origin
                     if ($LASTEXITCODE -ne 0) { throw "git fetch failed (exit $LASTEXITCODE)" }
    -                git -c windows.appendAtomically=false checkout $Branch
    -                if ($LASTEXITCODE -ne 0) { throw "git checkout $Branch failed (exit $LASTEXITCODE)" }
    -                git -c windows.appendAtomically=false pull origin $Branch
    -                if ($LASTEXITCODE -ne 0) { throw "git pull failed (exit $LASTEXITCODE)" }
    +                # Precedence: Commit > Tag > Branch.  Commit and Tag check
    +                # out as detached HEAD intentionally -- they're meant to be
    +                # reproducible pins, not branches the user pulls into.
    +                if ($Commit) {
    +                    # Make sure we have the commit locally (a tag-less commit
    +                    # SHA isn't always reachable from any one branch fetch).
    +                    git -c windows.appendAtomically=false fetch origin $Commit
    +                    git -c windows.appendAtomically=false checkout --detach $Commit
    +                    if ($LASTEXITCODE -ne 0) { throw "git checkout $Commit failed (exit $LASTEXITCODE)" }
    +                } elseif ($Tag) {
    +                    git -c windows.appendAtomically=false fetch origin "refs/tags/${Tag}:refs/tags/${Tag}"
    +                    git -c windows.appendAtomically=false checkout --detach "refs/tags/$Tag"
    +                    if ($LASTEXITCODE -ne 0) { throw "git checkout tag $Tag failed (exit $LASTEXITCODE)" }
    +                } else {
    +                    git -c windows.appendAtomically=false checkout $Branch
    +                    if ($LASTEXITCODE -ne 0) { throw "git checkout $Branch failed (exit $LASTEXITCODE)" }
    +                    git -c windows.appendAtomically=false pull origin $Branch
    +                    if ($LASTEXITCODE -ne 0) { throw "git pull failed (exit $LASTEXITCODE)" }
    +                }
                 } finally {
    +                $ErrorActionPreference = $prevEAP
                     Pop-Location
                 }
                 $didUpdate = $true
    ```

    ```diff
    @@ -1020,8 +1049,20 @@ function Install-Repository {
                 if (Test-Path $InstallDir) { Remove-Item -Recurse -Force $InstallDir -ErrorAction SilentlyContinue }
                 Write-Warn "Git clone failed -- downloading ZIP archive instead..."
                 try {
    -                $zipUrl = "https://github.com/NousResearch/hermes-agent/archive/refs/heads/$Branch.zip"
    -                $zipPath = "$env:TEMP\hermes-agent-$Branch.zip"
    +                # Pick the ZIP URL for the most-specific ref the caller asked
    +                # for.  GitHub supports archive URLs for commits, tags, and
    +                # branches; we honour Commit > Tag > Branch.
    +                if ($Commit) {
    +                    $zipUrl = "https://github.com/NousResearch/hermes-agent/archive/$Commit.zip"
    +                    $zipLabel = $Commit
    +                } elseif ($Tag) {
    +                    $zipUrl = "https://github.com/NousResearch/hermes-agent/archive/refs/tags/$Tag.zip"
    +                    $zipLabel = $Tag
    +                } else {
    +                    $zipUrl = "https://github.com/NousResearch/hermes-agent/archive/refs/heads/$Branch.zip"
    +                    $zipLabel = $Branch
    +                }
    +                $zipPath = "$env:TEMP\hermes-agent-$zipLabel.zip"
                     $extractPath = "$env:TEMP\hermes-agent-extract"
     
                     Invoke-WebRequest -Uri $zipUrl -OutFile $zipPath -UseBasicParsing
    ```

    ```diff
    @@ -1063,6 +1104,37 @@ function Install-Repository {
         Push-Location $InstallDir
         git -c windows.appendAtomically=false config windows.appendAtomically false 2>$null
     
    +    # Post-clone pin: when a clone (or ZIP-fallback init) just landed us on
    +    # $Branch's tip, honour the higher-precedence $Commit / $Tag by checking
    +    # the exact ref out as a detached HEAD.  Skipped for the in-place update
    +    # path (above) since that already routed via the same precedence.
    +    if (-not $didUpdate) {
    +        # Same EAP=Continue wrap as the update path -- git fetch's 'From <url>'
    +        # info line goes to stderr and would terminate the script under the
    +        # global EAP=Stop otherwise.  We check $LASTEXITCODE for real errors.
    +        $prevEAP = $ErrorActionPreference
    +        $ErrorActionPreference = "Continue"
    +        try {
    +            if ($Commit) {
    +                Write-Info "Pinning to commit $Commit..."
    +                git -c windows.appendAtomically=false fetch origin $Commit
    +                git -c windows.appendAtomically=false checkout --detach $Commit
    +                if ($LASTEXITCODE -ne 0) {
    +                    throw "git checkout $Commit failed (exit $LASTEXITCODE)"
    +                }
    +            } elseif ($Tag) {
    +                Write-Info "Pinning to tag $Tag..."
    +                git -c windows.appendAtomically=false fetch origin "refs/tags/${Tag}:refs/tags/${Tag}"
    +                git -c windows.appendAtomically=false checkout --detach "refs/tags/$Tag"
    +                if ($LASTEXITCODE -ne 0) {
    +                    throw "git checkout tag $Tag failed (exit $LASTEXITCODE)"
    +                }
    +            }
    +        } finally {
    +            $ErrorActionPreference = $prevEAP
    +        }
    +    }
    +
         # Ensure submodules are initialized and updated
         Write-Info "Initializing submodules..."
         git -c windows.appendAtomically=false submodule update --init --recursive 2>$null
    ```
- **#27419: fix(install.ps1): drop UTF-8 BOM that breaks the irm | iex one-liner (#27397)**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`; refs `#28169`, `#28169`
- **#28732: fix(install.ps1): remove UTF-8 BOM that breaks irm | iex path**. closed without merge
  - maintainer direction: category `uncategorized`; implication `needs_llm`; refs `#28169`
- **#29206: fix(install): correct param() block placement in install.ps1 (#28141)**. closed without merge
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `a53e8ca73327da1b4788ef4b913c59155cfbc662`, `#28141`, `#27397`

## Fix status

- Merged to main in PR #28169 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-05-19`.
- Subsystem: `hermes_cli`
