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 .github/install-inno.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Makes sure Inno Setup 6 (ISCC.exe) is available on a GitHub runner: uses the preinstalled one, else installs it.
$ErrorActionPreference = 'Stop'
$known = @("${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe", "$env:ProgramFiles\Inno Setup 6\ISCC.exe")
$iscc = $known | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $iscc) {
choco install innosetup --no-progress -y
if ($LASTEXITCODE -ne 0) { throw 'installing Inno Setup failed' }
$iscc = $known | Where-Object { Test-Path $_ } | Select-Object -First 1
}
if (-not $iscc) { throw 'Inno Setup 6 (ISCC.exe) not found after installing' }
Write-Host "Inno Setup: $iscc"
25 changes: 24 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [main]
branches: [main, dev]
pull_request:
branches: [main]
workflow_dispatch:
Expand Down Expand Up @@ -37,3 +37,26 @@ jobs:

- name: Test
run: dotnet test xpp-lens.sln -c Release --no-build --logger "console;verbosity=normal"

# The setup program is built on every push, so a broken Inno script shows up before a release.
installer:
name: installer
runs-on: windows-latest
timeout-minutes: 20
env:
DOTNET_CLI_TELEMETRY_OPTOUT: '1'
DOTNET_NOLOGO: '1'
steps:
- uses: actions/checkout@v7

- uses: actions/setup-dotnet@v6
with:
dotnet-version: 9.0.x

- name: Inno Setup
shell: pwsh
run: ./.github/install-inno.ps1

- name: Package and setup program
shell: powershell
run: .\pack.ps1 -OutDir dist -Installer
14 changes: 11 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,20 @@ jobs:
shell: powershell
run: .\release-notes.ps1 -Version $env:VERSION -OutFile dist\notes.md

- name: Package
- name: Inno Setup
shell: pwsh
run: ./.github/install-inno.ps1

- name: Package and setup program
shell: powershell
run: .\pack.ps1 -OutDir dist
run: .\pack.ps1 -OutDir dist -Installer

# vX.Y.Z-beta.N is a test release: marked as pre-release, never "Latest", so 'xpplens update' does not offer it
# ('xpplens update --pre' does).
- name: Publish the release
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: gh release create $env:TAG "dist/xpp-lens-$env:VERSION.zip" --title "xpp-lens $env:VERSION" --notes-file dist/notes.md --verify-tag --latest
run: |
$kind = if ($env:TAG -match '-') { '--prerelease' } else { '--latest' }
gh release create $env:TAG "dist/xpp-lens-$env:VERSION.zip" "dist/xpp-lens-setup-$env:VERSION.exe" --title "xpp-lens $env:VERSION" --notes-file dist/notes.md --verify-tag $kind
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@
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.3.0 — 2026-10-26

### Added
- Setup program `xpp-lens-setup-X.Y.Z.exe` (Inno Setup): a wizard in Polish or English for the current user, no
administrator rights. It finds PackagesLocalDirectory (`xpplens detect`, or choose the folder), asks for the label
languages and whether to index the Microsoft standard, registers xpp-lens in Claude Desktop and Claude Code and
offers to build the index. It puts `xpplens` on the user's PATH, adds Start menu entries (status, update the index,
check for updates, uninstall) and an entry in Windows' installed apps; uninstalling asks whether to remove the index.
An installation made by `install.ps1` (`C:\Tools\xpp-lens`) is taken over in place with its settings and index.
- `xpplens update --install` runs the setup program of the release silently into the current installation (SHA-256
checked, `--restart-claude` passed on); releases without one still go through the ZIP and `install.ps1`.
- `restart-claude.ps1`: the Claude Desktop restart of the installers, shared by `install.ps1` and the setup program.
- Test releases: a version can first be published as `X.Y.Z-beta.N`, marked as a pre-release on GitHub.
`xpplens update` never offers it; `xpplens update --install --pre` installs the newest release including test
releases, so a new version can be tried on one machine before everyone gets it. The regular `X.Y.Z` later
replaces a test release through the usual `xpplens update --install`.
- `xpplens version`, `status` and the MCP server report the full version of a test release (`1.3.0-beta.1`); versions
compare with test releases before their final version (`1.3.0-beta.2` < `1.3.0`).

### Development
- Work happens on the branch `dev`; `main` always holds the last regular release. `release.ps1 -Version X.Y.Z -Pre`
publishes a test release from `dev`; `release.ps1 -Version X.Y.Z` opens the pull request `dev` → `main`, waits
for its CI, merges it (merge commit), tags the merge and publishes the release, then moves `dev` to `main`.
CI also runs for pushes to `dev`. The release notes of a test release come from the section of its final version.

## 1.2.9 — 2026-10-14

### Fixed
Expand Down
35 changes: 24 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ An independent project, inspired by [Graft](https://github.com/trailhq/Graft) (a

## Installing on a new machine

The install package is not part of the repository (`dist\` is excluded by `.gitignore`). There are two ways:
The install package is not part of the repository (`dist\` is excluded by `.gitignore`). There are three ways:

**A. From a release package** — nothing needs to be installed on the target machine, not even .NET.
**A. Setup program** (from 1.3.0) — download `xpp-lens-setup-X.Y.Z.exe` from the **Releases** tab and run it. A
wizard in Polish or English, for the current user, no administrator rights: it finds `PackagesLocalDirectory`, asks for
the label languages and whether to index the Microsoft standard, registers xpp-lens in Claude and offers to build the
index. It puts `xpplens` on your PATH, adds Start menu entries (status, update the index, check for updates) and an
entry in Windows' installed apps. An installation made by `install.ps1` in `C:\Tools\xpp-lens` is taken over in place,
with its settings and index. The program is not signed yet: when Windows shows "Windows protected your PC", choose
"More info" → "Run anyway".

**B. From the release ZIP** — nothing needs to be installed on the target machine, not even .NET.

1. Download `xpp-lens-X.Y.Z.zip` from the **Releases** tab of this repository.
2. Unzip it and run in a regular PowerShell window (not as administrator):
Expand All @@ -24,12 +32,12 @@ The install package is not part of the repository (`dist\` is excluded by `.giti
powershell -ExecutionPolicy Bypass -File .\xpp-lens\install.ps1 -Languages en-US,pl
```

**B. From source** — requires the .NET 9 SDK.
**C. From source** — requires the .NET 9 SDK (and Inno Setup 6 for the setup program).

```powershell
git clone https://github.com/willowdev-code/xpp-lens.git C:\Dev\xpp-lens
cd C:\Dev\xpp-lens
.\pack.ps1 # creates dist\xpp-lens\ and dist\xpp-lens-X.Y.Z.zip
.\pack.ps1 # dist\xpp-lens\, dist\xpp-lens-X.Y.Z.zip (and the setup program when Inno Setup is there)
powershell -ExecutionPolicy Bypass -File .\dist\xpp-lens\install.ps1 -Languages en-US,pl
```

Expand All @@ -39,11 +47,12 @@ builds the index (custom models take about a minute, the Microsoft standard 20
Claude may stay open: start a new session afterwards (or restart Claude Desktop) to load the server.

Updating an installation: `xpplens update` shows whether a newer release exists; `xpplens update --install` downloads
it (SHA-256 checked) and runs its installer — settings and index are kept. Running `install.ps1` from a newer package
does the same. Claude does not have to be closed: an `xpplens.exe` used by an open session is renamed to `*.old`
it (SHA-256 checked) and runs its installer — from 1.3.0 the setup program, silently, into the current installation;
settings and index are kept. Running the setup program or `install.ps1` from a newer package does the same. Claude does not have to be closed: an `xpplens.exe` used by an open session is renamed to `*.old`
(deleted later), so that session keeps the old version and new sessions start the new one. At the end the installer
offers to restart Claude Desktop (`xpplens update --install --restart-claude` or `install.ps1 -RestartClaude`: without
asking); in Claude Code `/mcp` → reconnect xpp-lens does the same for one session.
asking); in Claude Code `/mcp` → reconnect xpp-lens does the same for one session. Test releases (`X.Y.Z-beta.N`)
are offered only with `--pre`: `xpplens update --install --pre`.

Useful parameters:

Expand Down Expand Up @@ -255,15 +264,19 @@ Adding a test: put the XML the case needs into `Fixtures` (keep names neutral

### Releasing a new version

Add the section `## X.Y.Z — unreleased` to `CHANGELOG.md`, then:
Work happens on the branch `dev`; `main` always holds the last regular release. Add the section
`## X.Y.Z — unreleased` to `CHANGELOG.md`, then on `dev`:

```powershell
.\release.ps1 -Version X.Y.Z # everything below, stops at the first failure
.\release.ps1 -Version X.Y.Z -Pre # test release X.Y.Z-beta.N (pre-release; xpplens update --install --pre)
.\release.ps1 -Version X.Y.Z # regular release: pull request dev -> main, merge, tag, publish
.\release.ps1 -Version X.Y.Z -DryRun # checks, scan, build and tests only
```

It dates the section, sets `<Version>`, scans every file for private names, builds and tests, commits and pushes
`main`, waits for CI, tags, waits for the Release workflow and checks the published package (SHA-256, layout).
Both stop at the first failure. They set `<Version>`, scan every file for private names, build and test, commit
and push `dev` and wait for CI. A test release is then tagged on `dev`; a regular release dates the CHANGELOG
section, merges `dev` into `main` through a pull request (merge commit) and is tagged on `main`, after which `dev` is
moved to `main`. Both wait for the Release workflow and check the published package (SHA-256, layout, kind).
The private names (customers, partners, people) come from a local list that is never part of the repository:
`-ScanList <file>`, the `XPPLENS_SCAN_LIST` variable, or `%USERPROFILE%\.claude\skills\xpp-lens-release\scan-names.txt`
— one regular expression per line, `!word` for allowed words. Without the list the release stops.
Expand Down
35 changes: 24 additions & 11 deletions README.pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@ Niezależny projekt inspirowany [Graftem](https://github.com/trailhq/Graft) (gra

## Instalacja na nowej maszynie

Paczki instalacyjnej nie ma w repozytorium (`dist\` jest pomijany przez `.gitignore`). Są dwie drogi:
Paczki instalacyjnej nie ma w repozytorium (`dist\` jest pomijany przez `.gitignore`). Są trzy drogi:

**A. Z gotowej paczki** — na maszynie docelowej nie trzeba niczego instalować, nawet .NET.
**A. Instalator** (od 1.3.0) — pobierz `xpp-lens-setup-X.Y.Z.exe` z zakładki **Releases** i uruchom. Kreator po polsku
albo po angielsku, dla bieżącego użytkownika, bez uprawnień administratora: wykrywa `PackagesLocalDirectory`, pyta
o języki etykiet i o indeksowanie standardu Microsoftu, rejestruje xpp-lens w Claude i proponuje zbudowanie indeksu.
Dopisuje `xpplens` do PATH, dodaje skróty w menu Start (status, aktualizacja indeksu, sprawdzenie aktualizacji) i wpis
w „Aplikacjach” Windows. Instalację zrobioną przez `install.ps1` w `C:\Tools\xpp-lens` przejmuje w miejscu, razem
z ustawieniami i indeksem. Program nie jest jeszcze podpisany: gdy Windows pokaże „System Windows ochronił komputer”,
wybierz „Więcej informacji” → „Uruchom mimo to”.

**B. Z paczki ZIP** — na maszynie docelowej nie trzeba niczego instalować, nawet .NET.

1. Pobierz `xpp-lens-X.Y.Z.zip` z zakładki **Releases** tego repozytorium.
2. Rozpakuj i w zwykłym PowerShellu (nie jako administrator) uruchom:
Expand All @@ -24,12 +32,12 @@ Paczki instalacyjnej nie ma w repozytorium (`dist\` jest pomijany przez `.gitign
powershell -ExecutionPolicy Bypass -File .\xpp-lens\install.ps1 -Languages en-US,pl
```

**B. Z kodu źródłowego** — wymaga .NET 9 SDK.
**C. Z kodu źródłowego** — wymaga .NET 9 SDK (i Inno Setup 6 dla instalatora).

```powershell
git clone https://github.com/willowdev-code/xpp-lens.git C:\Dev\xpp-lens
cd C:\Dev\xpp-lens
.\pack.ps1 # tworzy dist\xpp-lens\ i dist\xpp-lens-X.Y.Z.zip
.\pack.ps1 # dist\xpp-lens\, dist\xpp-lens-X.Y.Z.zip (i instalator, gdy jest Inno Setup)
powershell -ExecutionPolicy Bypass -File .\dist\xpp-lens\install.ps1 -Languages en-US,pl
```

Expand All @@ -39,11 +47,12 @@ układu katalogów), zapisuje konfigurację, rejestruje serwer w Claude Desktop
otwarty: po instalacji zacznij nową sesję (albo uruchom ponownie Claude Desktop), żeby wczytał serwer.

Aktualizacja instalacji: `xpplens update` pokazuje, czy jest nowsze wydanie; `xpplens update --install` pobiera je
(z kontrolą SHA-256) i uruchamia jego instalator — ustawienia i indeks zostają. To samo daje uruchomienie `install.ps1`
z nowszej paczki. Nie trzeba zamykać Claude'a: `xpplens.exe` używany przez otwartą sesję dostaje nazwę `*.old`
(z kontrolą SHA-256) i uruchamia jego instalator — od 1.3.0 kreator, po cichu, do bieżącej instalacji; ustawienia
i indeks zostają. To samo daje uruchomienie instalatora albo `install.ps1` z nowszej paczki. Nie trzeba zamykać Claude'a: `xpplens.exe` używany przez otwartą sesję dostaje nazwę `*.old`
(usuwaną później), więc ta sesja działa dalej na starej wersji, a nowe sesje startują już na nowej. Na końcu instalator
proponuje ponowne uruchomienie Claude Desktop (`xpplens update --install --restart-claude` albo
`install.ps1 -RestartClaude` — bez pytania); w Claude Code to samo dla jednej sesji daje `/mcp` → reconnect xpp-lens.
Wydania testowe (`X.Y.Z-beta.N`) są proponowane tylko z `--pre`: `xpplens update --install --pre`.

Przydatne parametry:

Expand Down Expand Up @@ -256,16 +265,20 @@ w `QueryTests.cs`, który woła zapytanie i sprawdza tekst odpowiedzi. Uruchamia

### Wydanie nowej wersji

Dopisz w `CHANGELOG.md` sekcję `## X.Y.Z — unreleased`, potem:
Praca idzie na gałęzi `dev`; `main` zawsze zawiera ostatnie zwykłe wydanie. Dopisz w `CHANGELOG.md` sekcję
`## X.Y.Z — unreleased`, potem na `dev`:

```powershell
.\release.ps1 -Version X.Y.Z # wszystko poniżej, staje na pierwszym błędzie
.\release.ps1 -Version X.Y.Z -Pre # wydanie testowe X.Y.Z-beta.N (pre-release; xpplens update --install --pre)
.\release.ps1 -Version X.Y.Z # zwykłe wydanie: pull request dev -> main, scalenie, tag, publikacja
.\release.ps1 -Version X.Y.Z -DryRun # tylko kontrole, skan, build i testy
```

Skrypt wstawia datę w sekcji, ustawia `<Version>`, skanuje wszystkie pliki pod nazwy prywatne, buduje i testuje,
robi commit i push `main`, czeka na CI, zakłada tag, czeka na workflow Release i sprawdza opublikowaną paczkę
(SHA-256, układ). Nazwy prywatne (klienci, partnerzy, ludzie) pochodzą z lokalnej listy, która nigdy nie trafia do
Oba warianty stają na pierwszym błędzie. Ustawiają `<Version>`, skanują wszystkie pliki pod nazwy prywatne,
budują i testują, robią commit i push `dev` i czekają na CI. Wydanie testowe dostaje tag na `dev`; zwykłe wstawia
datę w sekcji CHANGELOG, scala `dev` do `main` przez pull request (zwykłe scalanie) i dostaje tag na `main`, po czym
`dev` przesuwa się do `main`. Oba czekają na workflow Release i sprawdzają opublikowaną paczkę (SHA-256, układ,
rodzaj wydania). Nazwy prywatne (klienci, partnerzy, ludzie) pochodzą z lokalnej listy, która nigdy nie trafia do
repozytorium: `-ScanList <plik>`, zmienna `XPPLENS_SCAN_LIST` albo
`%USERPROFILE%\.claude\skills\xpp-lens-release\scan-names.txt` — jedno wyrażenie regularne w linii, `!słowo` dla
dozwolonych słów. Bez listy wydanie się zatrzymuje.
Expand Down
52 changes: 3 additions & 49 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -50,38 +50,6 @@ $ErrorActionPreference = 'Stop'
function Say($m) { Write-Host " $m" }
function Head($m) { Write-Host "`n$m" -ForegroundColor Cyan }

# How to start Claude Desktop again, from the path of its running process; $null when it cannot be worked out.
function Get-ClaudeStart([string]$appPath) {
if ($appPath -match '\\WindowsApps\\(?<folder>[^\\]+)\\') {
# Store / MSIX app, started through its application id. The folder ("Claude_2.1.0.0_x64__pzs8sxrjxfjjc") gives
# the package family ("Claude_pzs8sxrjxfjjc"); after Claude updated itself the running folder is an older
# version than the installed package, so the family is matched, never the path.
$folder = $Matches['folder']
if ($folder -notmatch '^(?<name>[^_]+)_.*__(?<publisher>[^_]+)$') { return $null }
$family = "$($Matches['name'])_$($Matches['publisher'])"
$aumid = $null
try {
$aumid = (Get-StartApps -ErrorAction Stop | Where-Object { $_.AppID -like "$family!*" } | Select-Object -First 1).AppID
}
catch { }
if (-not $aumid) {
try {
$pkg = Get-AppxPackage -ErrorAction Stop | Where-Object { $_.PackageFamilyName -eq $family } | Select-Object -First 1
if ($pkg) { $aumid = "$family!" + @((Get-AppxPackageManifest $pkg).Package.Applications.Application)[0].Id }
}
catch { }
}
if ($aumid) { return "shell:AppsFolder\$aumid" }
return $null
}
# Per-user install: the launcher above the versioned "app-x.y.z" folders starts the current version.
if ($appPath -match '^(?<root>.+)\\app-[^\\]+\\[^\\]+$' -and (Test-Path (Join-Path $Matches['root'] 'claude.exe'))) {
return (Join-Path $Matches['root'] 'claude.exe')
}
if ($appPath -and (Test-Path $appPath)) { return $appPath }
return $null
}

$src = $PSScriptRoot
if (-not (Test-Path (Join-Path $src 'bin\xpplens.exe'))) {
throw "$src\bin\xpplens.exe not found - run install.ps1 from an unpacked xpp-lens package."
Expand Down Expand Up @@ -146,7 +114,7 @@ else {
Copy-Item $f.FullName $dst -Force
}
Get-ChildItem (Join-Path $src 'bin') -Directory | ForEach-Object { Copy-Item $_.FullName $binDst -Recurse -Force }
foreach ($f in 'install.ps1', 'uninstall.ps1', 'README.md', 'README.pl.md', 'CHANGELOG.md', 'LICENSE') {
foreach ($f in 'install.ps1', 'uninstall.ps1', 'restart-claude.ps1', 'README.md', 'README.pl.md', 'CHANGELOG.md', 'LICENSE') {
if (Test-Path (Join-Path $src $f)) { Copy-Item (Join-Path $src $f) $InstallDir -Force }
}
}
Expand Down Expand Up @@ -233,22 +201,8 @@ if ($desktop.Count -gt 0) {
$restart = $answer -match '^\s*(y|yes|t|tak)\s*$'
}
if ($restart) {
# Worked out before Claude is stopped: when it cannot be started again, it is not stopped either.
$start = Get-ClaudeStart $desktop[0].Path
if (-not $start) {
Say "Claude Desktop could not be located for a restart - it was left running; restart it yourself to use $version."
}
else {
$desktop | Stop-Process -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
try {
Start-Process $start -ErrorAction Stop
Say "Claude Desktop restarted."
}
catch {
Say "Claude Desktop was closed but did not start again ($($_.Exception.Message)) - start it from the Start menu."
}
}
# Shared with the setup program: works out how to start Claude before stopping it.
& (Join-Path $src 'restart-claude.ps1') -Version $version
}
else {
Say "Claude Desktop: open sessions keep the old version until it is restarted; new sessions use $version."
Expand Down
Loading
Loading