# ============================================================================ # Hermes Agent Setup Script (Windows) — THE dev-environment entry point. # ============================================================================ # Sets up the pm-managed development environment from a fresh clone: # 1. Stage the pinned uv from pm/lock.json (sha256-verified, into the pm # store slot) - pm needs uv to bootstrap, so it cannot stage uv itself. # 2. Use uv to install and locate bootstrap Python, then let uv exit. # Run `python -m pm.cli install` directly so PM can safely replace uv. # PM owns the final interpreter, tool store, and dependency generation. # 3. Point you at `.\activate.ps1` - the venv-style way to put the pm env # (PATH + tool vars) into your current session. # ============================================================================ # Setup and activation both prepare the isolated test interpreter; installers # invoke pm.cli directly and do not select it. -TestExtras overrides coverage. param([switch]$RuntimeOnly, [string]$TestExtras = '') $ErrorActionPreference = 'Stop' Write-Host '' Write-Host 'Hermes Agent Setup' -ForegroundColor Cyan Write-Host '' $repo = $PSScriptRoot $lockPath = Join-Path $repo 'pm/lock.json' if (-not (Test-Path $lockPath)) { throw 'pm/lock.json not found' } $lock = Get-Content -Raw $lockPath | ConvertFrom-Json $machineArch = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment').PROCESSOR_ARCHITECTURE $arch = if ($machineArch -eq 'ARM64') { 'arm64' } else { 'x64' } $target = "win32-$arch" # --------------------------------------------------------------------------- # Stage the pinned uv from pm/lock.json into the pm store slot # --------------------------------------------------------------------------- $uvPin = $lock.packages.uv if (-not $uvPin) { throw 'no uv pin in pm/lock.json' } $artifact = $uvPin.artifacts.$target if (-not $artifact) { $artifact = $uvPin.artifacts.any } if (-not $artifact) { throw "no uv artifact for $target" } $pyPin = $lock.packages.python $pyVersion = if ($pyPin) { ($pyPin.version -split '\+')[0] -replace '^(\d+\.\d+).*', '$1' } else { '3.11' } $store = if ($env:HERMES_RUNTIME_DIR) { $env:HERMES_RUNTIME_DIR } else { Join-Path $HOME '.hermes/tools' } $entry = Join-Path $store "uv-$($uvPin.version)-$target" $uv = Join-Path $entry 'uv.exe' if (Test-Path $uv) { Write-Host ("pinned uv found: " + (& $uv --version)) -ForegroundColor Green } else { Write-Host "Staging pinned uv $($uvPin.version) ($target) into the pm store..." -ForegroundColor Cyan $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("hermes-setup-" + [guid]::NewGuid().ToString('n')) New-Item -ItemType Directory -Path $tmp | Out-Null try { $archive = Join-Path $tmp ([uri]$artifact.url).Segments[-1] Invoke-WebRequest -Uri $artifact.url -OutFile $archive $got = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant() if ($got -ne $artifact.sha256) { throw "sha256 mismatch for uv (got $got, pinned $($artifact.sha256))" } $tree = Join-Path $tmp 'tree' Expand-Archive -Path $archive -DestinationPath $tree # flatten a single wrapping dir $inner = @(Get-ChildItem $tree) $src = if ($inner.Count -eq 1 -and $inner[0].PSIsContainer) { $inner[0].FullName } else { $tree } New-Item -ItemType Directory -Force -Path $store | Out-Null if (Test-Path $entry) { Remove-Item -Recurse -Force $entry } Move-Item $src $entry } finally { Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue } Write-Host ("uv installed: " + (& $uv --version)) -ForegroundColor Green } # --------------------------------------------------------------------------- # PM installs the tools, then the venv. On ARM64 PM prepares the compiler and # OpenSSL environment for that sync itself (pm/native_build.py), after its own # git is published, so every install path builds the same way. # Activation trusts the recorded tool digest. A direct setup re-checks it. # --------------------------------------------------------------------------- Write-Host 'Installing python + tools + dependencies via pm (hash-verified via uv.lock)...' -ForegroundColor Cyan Write-Host '(first run on a fresh checkout can take 1-5 minutes)' Push-Location $repo try { # PM can replace its uv entry only after the bootstrap uv has exited. # A bare version lets uv pick emulated x86_64 on Windows-on-ARM. $pyRequest = "cpython-$pyVersion-windows-$(if ($arch -eq 'arm64') { 'aarch64' } else { 'x86_64' })-none" & $uv python install --no-bin --no-registry $pyRequest if ($LASTEXITCODE -ne 0) { throw 'bootstrap Python installation failed' } $bootPy = (& $uv python find --managed-python $pyRequest) -join "`n" if ($LASTEXITCODE -ne 0 -or -not $bootPy) { throw 'bootstrap Python lookup failed' } & $bootPy.Trim() -m pm.cli install $(if ($RuntimeOnly) { '--trust-recorded' }) "--test-environment=$TestExtras" if ($LASTEXITCODE -ne 0) { throw 'pm install failed - see output above.' } } finally { Pop-Location } Write-Host 'Tools + dependencies installed (hash-verified via pm + uv.lock)' -ForegroundColor Green if ($RuntimeOnly) { exit 0 } # --------------------------------------------------------------------------- # Environment file # --------------------------------------------------------------------------- $envFile = Join-Path $repo '.env' if (-not (Test-Path $envFile)) { if (Test-Path (Join-Path $repo '.env.example')) { Copy-Item (Join-Path $repo '.env.example') $envFile Write-Host 'Created .env from template' -ForegroundColor Green } } else { Write-Host '.env exists' -ForegroundColor Green } # --------------------------------------------------------------------------- # Seed bundled skills into ~/.hermes/skills/ # --------------------------------------------------------------------------- $skillsDir = if ($env:HERMES_HOME) { Join-Path $env:HERMES_HOME 'skills' } else { Join-Path $HOME '.hermes/skills' } New-Item -ItemType Directory -Force -Path $skillsDir | Out-Null $sync = Join-Path $repo 'tools/skills_sync.py' $venvPy = Join-Path $repo 'venv/Scripts/python.exe' if ((Test-Path $sync) -and (Test-Path $venvPy)) { & $venvPy $sync 2>$null Write-Host 'Skills synced' -ForegroundColor Green } # --------------------------------------------------------------------------- # Done # --------------------------------------------------------------------------- Write-Host '' Write-Host 'Setup complete!' -ForegroundColor Green Write-Host '' Write-Host 'Next steps:' Write-Host '' Write-Host ' 1. Activate the dev environment (venv-style, in THIS session):' Write-Host ' .\activate.ps1' Write-Host '' Write-Host ' 2. Run the setup wizard to configure API keys:' Write-Host ' hermes setup' Write-Host '' Write-Host ' 3. Start chatting:' Write-Host ' hermes' Write-Host '' Write-Host 'Other commands:' Write-Host ' hermes pm install # Re-run the tool + dependency install' Write-Host ' hermes status # Check configuration' Write-Host ' hermes doctor # Diagnose issues' Write-Host ' deactivate # Undo the activation (restore PATH etc.)' Write-Host ''