From c3f3653ec2e297d0c25e307f1c04a58cfeb39626 Mon Sep 17 00:00:00 2001 From: WillowDev <216433317+willowdev-code@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:43:28 +0100 Subject: [PATCH 1/2] install.ps1 does what the setup program does: PATH, Start menu, installed apps --- CHANGELOG.md | 11 +++++ README.md | 4 +- README.pl.md | 4 +- install.ps1 | 80 +++++++++++++++++++++++++++++++++++++ packaging/inno/xpp-lens.iss | 4 ++ uninstall.ps1 | 26 ++++++++++++ 6 files changed, 127 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07cedcc..a894bcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to xpp-lens (called xpp-graft up to 1.2.0). Versions follow `` in `src/XppLens/XppLens.csproj`; release tags are `vX.Y.Z`. +## 1.4.2 + +### Fixed +- An installation by `install.ps1` (the ZIP package, and every update from a version before 1.3.0) left `xpplens` + off the PATH ("The term 'xpplens' is not recognized"), with no Start menu entries and no entry in Windows' installed + apps. `install.ps1` now does what the setup program does: the program folder on the user's PATH (kept as + `REG_EXPAND_SZ`, so `%VAR%` entries keep working; new terminals see it at once), the same Start menu entries with + the icon, and an installed-apps entry that runs `uninstall.ps1`. When the setup program installed xpp-lens for the + user, its entries stay; the setup program replaces those of `install.ps1` when it takes a folder over. + `uninstall.ps1` removes the PATH entry, and the Start menu and installed-apps entries `install.ps1` made. + ## 1.4.1 ### Fixed diff --git a/README.md b/README.md index 0133cd4..87d187b 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,9 @@ powershell -ExecutionPolicy Bypass -File .\dist\xpp-lens\install.ps1 -Languages ``` The installer copies the files to `C:\Tools\xpp-lens`, detects `PackagesLocalDirectory` (from the AOS `web.config` -or the folder layout), writes the configuration, registers the server in Claude Desktop and Claude Code, and finally +or the folder layout), writes the configuration, registers the server in Claude Desktop and Claude Code, and — like +the setup program — puts `xpplens` on your PATH, adds the Start menu entries and an entry in Windows' installed apps +(when the setup program installed xpp-lens for you, its own entries stay), installs the skills, and finally builds the index (custom models take about a minute, the Microsoft standard 20–90 minutes once, depending on the disk). Claude may stay open: start a new session afterwards (or restart Claude Desktop) to load the server. diff --git a/README.pl.md b/README.pl.md index 14cb797..c7df831 100644 --- a/README.pl.md +++ b/README.pl.md @@ -42,7 +42,9 @@ powershell -ExecutionPolicy Bypass -File .\dist\xpp-lens\install.ps1 -Languages ``` Instalator kopiuje pliki do `C:\Tools\xpp-lens`, wykrywa `PackagesLocalDirectory` (z `web.config` AOS-a albo -układu katalogów), zapisuje konfigurację, rejestruje serwer w Claude Desktop i Claude Code, a na końcu buduje indeks +układu katalogów), zapisuje konfigurację, rejestruje serwer w Claude Desktop i Claude Code, a tak jak program +instalacyjny dopisuje `xpplens` do PATH, dodaje skróty w menu Start i wpis w „Aplikacjach” Windows (gdy xpp-lens +zainstalował u ciebie program instalacyjny, zostają jego wpisy), instaluje skille, a na końcu buduje indeks (modele własne ok. minuty, standard Microsoftu jednorazowo 20–90 minut, zależnie od dysku). Claude może zostać otwarty: po instalacji zacznij nową sesję (albo uruchom ponownie Claude Desktop), żeby wczytał serwer. diff --git a/install.ps1 b/install.ps1 index da785d9..07a701e 100644 --- a/install.ps1 +++ b/install.ps1 @@ -130,6 +130,86 @@ else { $exe = Join-Path $InstallDir 'bin\xpplens.exe' Say "done: $exe" +# The program folder on the user's PATH, as the setup program does: "xpplens" then works in any new terminal. +# Read and written raw (REG_EXPAND_SZ), so entries such as %USERPROFILE%\... keep expanding. +$binFull = (Resolve-Path $binDst).Path.TrimEnd('\') +$envKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $true) +try { + $rawPath = [string]$envKey.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) + $items = @($rawPath -split ';' | Where-Object { $_ }) + if ($items | Where-Object { [Environment]::ExpandEnvironmentVariables($_).TrimEnd('\') -ieq $binFull }) { + Say "already on your PATH: $binFull" + } + else { + $envKey.SetValue('Path', (@($items) + $binFull) -join ';', [Microsoft.Win32.RegistryValueKind]::ExpandString) + # Tell Explorer, so terminals opened from now on see it (WM_SETTINGCHANGE "Environment"). + Add-Type -Namespace XppLensSetup -Name Native -MemberDefinition @' +[DllImport("user32.dll", CharSet = CharSet.Unicode)] +public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint msg, UIntPtr wParam, string lParam, uint flags, uint timeout, out UIntPtr result); +'@ + $ignored = [UIntPtr]::Zero + [void][XppLensSetup.Native]::SendMessageTimeout([IntPtr]0xffff, 0x1A, [UIntPtr]::Zero, 'Environment', 2, 5000, [ref]$ignored) + $env:Path = "$env:Path;$binFull" + Say "added to your PATH: $binFull (open a new terminal to use 'xpplens')" + } +} +finally { + $envKey.Close() +} + +# Start menu entries and an entry in Windows' installed apps, as the setup program makes them. When the setup +# program installed xpp-lens for this user, its own entries stay (they are the same, and its uninstaller owns +# them); the setup program removes these when it takes over a folder installed by this script. +$setupKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\{B3C9E031-73CD-4356-AC30-B8EC91C3C528}_is1' +if (Test-Path $setupKey) { + Say "Start menu and installed apps: kept from the setup program ($((Get-ItemProperty $setupKey).InstallLocation))" +} +else { + $pl = if ($SkillsLanguage) { $SkillsLanguage -eq 'pl' } else { (Get-UICulture).TwoLetterISOLanguageName -eq 'pl' } + # The setup program's names (Polish without diacritics in this ASCII script: "sprawdz" gets its z-acute here). + $names = if ($pl) { + @{ status = 'xpp-lens - status'; build = 'xpp-lens - aktualizacja indeksu'; update = "xpp-lens - sprawd$([char]0x17A) aktualizacje" + uninstall = 'Odinstaluj xpp-lens'; console = 'Konsola z poleceniami xpplens' } + } + else { + @{ status = 'xpp-lens - status'; build = 'xpp-lens - update the index'; update = 'xpp-lens - check for updates' + uninstall = 'Uninstall xpp-lens'; console = 'Console with the xpplens commands' } + } + $group = Join-Path ([Environment]::GetFolderPath('Programs')) 'xpp-lens' + if (Test-Path $group) { Remove-Item $group -Recurse -Force } + New-Item -ItemType Directory -Force -Path $group | Out-Null + $shell = New-Object -ComObject WScript.Shell + function New-Shortcut($name, $target, $arguments, $workDir, $description) { + $lnk = $shell.CreateShortcut((Join-Path $group "$name.lnk")) + $lnk.TargetPath = $target + $lnk.Arguments = $arguments + $lnk.WorkingDirectory = $workDir + $lnk.IconLocation = "$exe,0" + if ($description) { $lnk.Description = $description } + $lnk.Save() + } + New-Shortcut 'xpp-lens' $env:ComSpec "/k `"title xpp-lens & set `"PATH=$binFull;%PATH%`" & xpplens help`"" $env:USERPROFILE $names.console + foreach ($c in 'status', 'build', 'update') { + New-Shortcut $names[$c] $env:ComSpec "/k `"`"$exe`" $c`"" $InstallDir $null + } + $uninstallCmd = "-NoProfile -ExecutionPolicy Bypass -File `"$(Join-Path $InstallDir 'uninstall.ps1')`"" + New-Shortcut $names.uninstall 'powershell.exe' $uninstallCmd $InstallDir $null + + $appKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\xpp-lens' + New-Item -Path $appKey -Force | Out-Null + $appVersion = ([string](& $exe version)).Split(' ')[1] + $sizeKb = [int]((Get-ChildItem $InstallDir -Recurse -File | Measure-Object Length -Sum).Sum / 1KB) + $values = @{ + DisplayName = 'xpp-lens'; DisplayVersion = $appVersion; Publisher = 'WillowDev'; DisplayIcon = $exe + InstallLocation = $dstFull; URLInfoAbout = 'https://github.com/willowdev-code/xpp-lens' + UninstallString = "powershell.exe $uninstallCmd" + } + foreach ($v in $values.GetEnumerator()) { Set-ItemProperty $appKey -Name $v.Key -Value $v.Value } + foreach ($v in 'NoModify', 'NoRepair') { New-ItemProperty $appKey -Name $v -Value 1 -PropertyType DWord -Force | Out-Null } + New-ItemProperty $appKey -Name EstimatedSize -Value $sizeKb -PropertyType DWord -Force | Out-Null + Say "Start menu entries and an entry in installed apps (xpp-lens $appVersion)" +} + Head "2/5 PackagesLocalDirectory folder" if (-not $PackagesDir) { $found = @(& $exe detect | Where-Object { $_ -match "`t" }) diff --git a/packaging/inno/xpp-lens.iss b/packaging/inno/xpp-lens.iss index 0ed1af5..f97fd94 100644 --- a/packaging/inno/xpp-lens.iss +++ b/packaging/inno/xpp-lens.iss @@ -55,6 +55,8 @@ Name: "pl"; MessagesFile: "compiler:Languages\Polish.isl" [InstallDelete] ; The package's copy of the skills is replaced whole, so a skill dropped from the package does not linger. Type: filesandordirs; Name: "{app}\skills" +; Start menu entries of an earlier installation (install.ps1 names them in its own language): made again below. +Type: filesandordirs; Name: "{group}" [Files] Source: "{#Source}\*"; DestDir: "{app}"; Flags: recursesubdirs ignoreversion @@ -414,6 +416,8 @@ var begin if CurStep <> ssPostInstall then Exit; Exe := ExpandConstant('{app}\bin\xpplens.exe'); + { The installed-apps entry of install.ps1 (taken over here): this program's own entry replaces it. } + RegDeleteKeyIncludingSubkeys(HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\xpp-lens'); if not WasUpdate then begin Langs := Trim(LangPage.Values[0]); diff --git a/uninstall.ps1 b/uninstall.ps1 index bb59cda..bf8acc0 100644 --- a/uninstall.ps1 +++ b/uninstall.ps1 @@ -57,6 +57,32 @@ if (Test-Path $exe) { } else { Say "$exe not found - skipping unregistration and skills" } +# The program folder off the user's PATH (install.ps1 and the setup program put it there). +$binFull = [IO.Path]::GetFullPath((Join-Path $InstallDir 'bin')).TrimEnd('\') +$envKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $true) +try { + $rawPath = [string]$envKey.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames) + $items = @($rawPath -split ';' | Where-Object { $_ }) + $kept = @($items | Where-Object { [Environment]::ExpandEnvironmentVariables($_).TrimEnd('\') -ine $binFull }) + if ($kept.Count -ne $items.Count) { + $envKey.SetValue('Path', $kept -join ';', [Microsoft.Win32.RegistryValueKind]::ExpandString) + Say "removed from your PATH: $binFull" + } +} +finally { + $envKey.Close() +} + +# The Start menu entries and the installed-apps entry made by install.ps1 (the setup program's own go with its +# uninstaller). +$appKey = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\xpp-lens' +if ((Test-Path $appKey) -and ([string](Get-ItemProperty $appKey).InstallLocation).TrimEnd('\') -ieq [IO.Path]::GetFullPath($InstallDir).TrimEnd('\')) { + Remove-Item $appKey -Recurse -Force + $group = Join-Path ([Environment]::GetFolderPath('Programs')) 'xpp-lens' + if (Test-Path $group) { Remove-Item $group -Recurse -Force } + Say "removed the Start menu entries and the entry in installed apps" +} + $target = [IO.Path]::GetFullPath($InstallDir).TrimEnd('\') + '\' Get-CimInstance Win32_Process -Filter "Name='xpplens.exe'" | Where-Object { $_.ExecutablePath -and $_.ExecutablePath.StartsWith($target, [StringComparison]::OrdinalIgnoreCase) } | From bf71c46c3d1093c1b795f31273d14b713af79202 Mon Sep 17 00:00:00 2001 From: WillowDev <216433317+willowdev-code@users.noreply.github.com> Date: Tue, 27 Oct 2026 16:43:54 +0100 Subject: [PATCH 2/2] xpp-lens 1.4.2 ### Fixed - An installation by `install.ps1` (the ZIP package, and every update from a version before 1.3.0) left `xpplens` off the PATH ("The term 'xpplens' is not recognized"), with no Start menu entries and no entry in Windows' installed apps. `install.ps1` now does what the setup program does: the program folder on the user's PATH (kept as `REG_EXPAND_SZ`, so `%VAR%` entries keep working; new terminals see it at once), the same Start menu entries with the icon, and an installed-apps entry that runs `uninstall.ps1`. When the setup program installed xpp-lens for the user, its entries stay; the setup program replaces those of `install.ps1` when it takes a folder over. `uninstall.ps1` removes the PATH entry, and the Start menu and installed-apps entries `install.ps1` made. --- src/XppLens/XppLens.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/XppLens/XppLens.csproj b/src/XppLens/XppLens.csproj index f08f366..b059f0b 100644 --- a/src/XppLens/XppLens.csproj +++ b/src/XppLens/XppLens.csproj @@ -22,7 +22,7 @@ xpp-lens — X++ code index for MCP X++ (Dynamics 365 F&O) code index served to AI assistants over MCP Copyright © 2026 WillowDev - 1.4.1 + 1.4.2