Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to xpp-lens (called xpp-graft up to 1.2.0). Versions follow `<Version>` 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
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
4 changes: 3 additions & 1 deletion README.pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
80 changes: 80 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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" })
Expand Down
4 changes: 4 additions & 0 deletions packaging/inno/xpp-lens.iss
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]);
Expand Down
2 changes: 1 addition & 1 deletion src/XppLens/XppLens.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<AssemblyTitle>xpp-lens — X++ code index for MCP</AssemblyTitle>
<Description>X++ (Dynamics 365 F&amp;O) code index served to AI assistants over MCP</Description>
<Copyright>Copyright © 2026 WillowDev</Copyright>
<Version>1.4.1</Version>
<Version>1.4.2</Version>
</PropertyGroup>

<ItemGroup>
Expand Down
26 changes: 26 additions & 0 deletions uninstall.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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) } |
Expand Down
Loading