Skip to content

Commit f44cf15

Browse files
ADO Lens 0.10.0
Environments per repository, code in another project, a tidier installer page. - Environment map keys `Repository:branch` ("ERP:staging=UAT (sbx03)"): a repository with keys of its own uses only those, so the same branch name can mean different environments - or none - in different repositories. `ado_trace` no longer reports "NOT merged yet" for branches a repository does not use. Plain keys (`staging=UAT`) work as before. - Detecting the flow (installer, `adolens setup`, `ado_settings`): with several repositories each branch is named with its repository; repositories without a commit in the last half year and work branches (`000000_TestBranch`, `123456_LoginForm_…`) are left out. - `adolens config --code-project <name>` / `ado_settings codeProject=<name>`: the project with the code, pull requests and pipelines when it is not the work items' one; `ado_file`, the code part of `ado_search`, `ado_pr`, `ado_pr_update`, `ado_pipeline`, `ado_changes` and `ado_label` look there. - Installer, environments page: a larger wizard, as many rows as fit (the rest is named, to set later in the settings), the repository shown next to each branch, and one drop-down for where to save instead of three options that did not fit.
1 parent ee79d8d commit f44cf15

21 files changed

Lines changed: 292 additions & 95 deletions

‎CHANGELOG.md‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Changelog
22

3+
## 0.10.0 — 2026-10-26
4+
5+
Environments per repository, code in another project, a tidier installer page.
6+
7+
- Environment map keys `Repository:branch` ("ERP:staging=UAT (sbx03)"): a repository with keys of its own uses only
8+
those, so the same branch name can mean different environments - or none - in different repositories. `ado_trace`
9+
no longer reports "NOT merged yet" for branches a repository does not use. Plain keys (`staging=UAT`) work as before.
10+
- Detecting the flow (installer, `adolens setup`, `ado_settings`): with several repositories each branch is named with
11+
its repository; repositories without a commit in the last half year and work branches (`000000_TestBranch`,
12+
`123456_LoginForm_…`) are left out.
13+
- `adolens config --code-project <name>` / `ado_settings codeProject=<name>`: the project with the code, pull requests
14+
and pipelines when it is not the work items' one; `ado_file`, the code part of `ado_search`, `ado_pr`,
15+
`ado_pr_update`, `ado_pipeline`, `ado_changes` and `ado_label` look there.
16+
- Installer, environments page: a larger wizard, as many rows as fit (the rest is named, to set later in the settings),
17+
the repository shown next to each branch, and one drop-down for where to save instead of three options that did not
18+
fit.
19+
320
## 0.9.5 — 2026-10-26
421

522
- `ado_file` by name works without Code Search (not installed or failing): the file lists of the TFVC Metadata
@@ -14,7 +31,7 @@
1431
- `ado_trace`: a branch earlier in the environment flow than the one a change was merged into is not reported as
1532
"NOT merged yet" (a change merged straight into master of a library repository says nothing about its dev branch).
1633
- Before "not yet", one question per source branch: completed PRs from the same source branch into that branch that
17-
are not linked to the item ("merged by PRs not linked to the item: PR 31002 (2021-09-01, staging <- 243623_…)").
34+
are not linked to the item ("merged by PRs not linked to the item: PR 31002 (2021-09-01, staging <- 123456_…)").
1835

1936
## 0.9.3 — 2026-10-19
2037

@@ -96,7 +113,7 @@ Fixes from testing on a real project.
96113
setup EXE already did; `uninstall.ps1` takes it off.
97114

98115
- Tool arguments in another JSON shape are read instead of failing with a bare "An error occurred invoking":
99-
`ids=714800` (a number for text), `workItems=[1,2]`, `file=1`, `top="20"`, `draft="true"`. This was behind the failing
116+
`ids=123456` (a number for text), `workItems=[1,2]`, `file=1`, `top="20"`, `draft="true"`. This was behind the failing
100117
`ado_update` (even with only a comment), `ado_pr_update action=create` with work items and `ado_attachments file=<n>`.
101118
An argument that still cannot be read says which and why.
102119
- `ado_attachments`: two files with the same name are listed by number to pick one.

‎README.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,19 @@ adolens config --connection fabrikam --env "Main-Deploy/Stage 1=UAT + SCM"
156156
`ado_trace` then lists the stages in that order with their labels ("UAT (sbx03): YES") and, for Git, also checks whether
157157
the work is merged into each mapped branch even when no release pipeline deploys it.
158158

159+
**Several repositories.** The same branch name can mean different things in different repositories — or nothing (a
160+
library merged straight into `master`). A key `Repository:branch` belongs to one repository; a repository with keys of
161+
its own uses only those, the others use the plain branch keys. The installer and `adolens setup` name branches this way
162+
when a project has more than one active repository, and leave out work branches (`000000_TestBranch`, `123456_…`):
163+
164+
```powershell
165+
adolens config --connection contoso --env "ERP:dev=DEV (sbx01)" --env "ERP:staging=UAT (sbx03)" --env "ERP:master=PROD" --env "Common:master=PROD"
166+
```
167+
168+
**Code in another project.** When the work items live in one project and the code, pull requests and pipelines in
169+
another, set it once: `adolens config --connection contoso --code-project ERP` (or ask Claude: `ado_settings
170+
codeProject=ERP`). `ado_file`, `ado_search` (code), `ado_pr`, `ado_pipeline` and `ado_label` then look there.
171+
159172
**One map for the whole team.** The map can live in Azure DevOps itself: a page "ADO Lens" in the project wiki with a
160173
two-column table (branch or release stage | environment), in flow order. Everyone using ADO Lens on the project reads
161174
it, so Product Owners configure nothing; a local map (`--env`) overrides it for one person. The installer, `adolens

‎README.pl.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@ adolens config --connection fabrikam --env "Main-Deploy/Stage 1=UAT + SCM"
157157
Wtedy `ado_trace` wypisuje etapy w tej kolejności z ich etykietami („UAT (sbx03): YES”), a dla Gita sprawdza też, czy
158158
praca jest scalona do każdej zmapowanej gałęzi, nawet gdy żaden pipeline jej nie wdraża.
159159

160+
**Kilka repozytoriów.** Ta sama nazwa gałęzi może znaczyć co innego w różnych repozytoriach — albo nic (biblioteka
161+
scalana prosto do `master`). Klucz `Repozytorium:gałąź` należy do jednego repozytorium; repozytorium z własnymi kluczami
162+
używa tylko ich, pozostałe — zwykłych kluczy gałęzi. Instalator i `adolens setup` nazywają tak gałęzie, gdy projekt ma
163+
więcej niż jedno aktywne repozytorium, i pomijają gałęzie robocze (`000000_TestBranch`, `123456_…`):
164+
165+
```powershell
166+
adolens config --connection contoso --env "ERP:dev=DEV (sbx01)" --env "ERP:staging=UAT (sbx03)" --env "ERP:master=PROD" --env "Common:master=PROD"
167+
```
168+
169+
**Kod w innym projekcie.** Gdy work itemy są w jednym projekcie, a kod, pull requesty i pipeline'y w innym, ustaw to
170+
raz: `adolens config --connection contoso --code-project ERP` (albo poproś Claude'a: `ado_settings codeProject=ERP`).
171+
Wtedy `ado_file`, `ado_search` (kod), `ado_pr`, `ado_pipeline` i `ado_label` szukają tam.
172+
160173
**Jedna mapa dla całego zespołu.** Mapa może mieszkać w samym Azure DevOps: strona „ADO Lens” w wiki projektu z tabelą
161174
w dwóch kolumnach (gałąź albo etap release'u | środowisko), w kolejności przepływu. Każdy, kto używa ADO Lens w projekcie,
162175
ją czyta, więc Product Ownerzy niczego nie konfigurują; lokalna mapa (`--env`) nadpisuje ją dla jednej osoby. Zapisują

‎packaging/inno/ado-lens.iss‎

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ OutputBaseFilename=ado-lens-setup-{#Version}
4141
Compression=lzma2/max
4242
SolidCompression=yes
4343
WizardStyle=modern
44+
; Taller and wider than the default: the environments page lists release stages and branches.
45+
WizardSizePercent=120,130
4446
UninstallDisplayName=ADO Lens
4547
UninstallDisplayIcon={app}\bin\adolens.exe
4648
LicenseFile={#Source}\LICENSE
@@ -88,6 +90,7 @@ en.EnvLater=Later: Start menu > ADO Lens - settings > Environments (change, add,
8890
en.EnvMine=Save for me only (this computer)
8991
en.EnvTeam=Save for the whole team (wiki page "ADO Lens" of the project)
9092
en.EnvSkip=Skip - Claude asks when needed
93+
en.EnvMore=... and %1 more: Start menu > ADO Lens - settings > Environments.
9194
en.EnvFailed=Saving the environments did not work:%n%n%1
9295
en.ReadmeUrl=https://github.com/willowdev-code/ado-lens#readme
9396
en.IconSettings=ADO Lens - settings
@@ -104,6 +107,7 @@ pl.EnvLater=Później: menu Start > ADO Lens - ustawienia > Środowiska (zmiana,
104107
pl.EnvMine=Zapisz tylko dla mnie (ten komputer)
105108
pl.EnvTeam=Zapisz dla całego zespołu (strona wiki "ADO Lens" w projekcie)
106109
pl.EnvSkip=Pomiń - Claude zapyta, gdy będzie trzeba
110+
pl.EnvMore=... i jeszcze %1: menu Start > ADO Lens - ustawienia > Środowiska.
107111
pl.EnvFailed=Nie udało się zapisać środowisk:%n%n%1
108112
pl.ReadmeUrl=https://github.com/willowdev-code/ado-lens/blob/main/README.pl.md
109113
pl.IconSettings=ADO Lens - ustawienia
@@ -170,7 +174,8 @@ var
170174
EnvKeys: TArrayOfString;
171175
EnvLabels: array of TNewStaticText;
172176
EnvEdits: array of TNewEdit;
173-
EnvMine, EnvTeam, EnvSkip: TNewRadioButton;
177+
EnvScope: TNewComboBox;
178+
EnvMore: TNewStaticText;
174179
EnvDetected: Boolean;
175180
176181
function SetEnvironmentVariable(lpName: String; lpValue: String): Boolean;
@@ -384,22 +389,7 @@ begin
384389
EnvInfo.Width := EnvPage.SurfaceWidth;
385390
EnvInfo.Height := ScaleY(40);
386391
EnvInfo.Caption := CustomMessage('EnvDetecting');
387-
EnvMine := TNewRadioButton.Create(EnvPage);
388-
EnvTeam := TNewRadioButton.Create(EnvPage);
389-
EnvSkip := TNewRadioButton.Create(EnvPage);
390-
EnvMine.Parent := EnvPage.Surface;
391-
EnvTeam.Parent := EnvPage.Surface;
392-
EnvSkip.Parent := EnvPage.Surface;
393-
EnvMine.Caption := CustomMessage('EnvMine');
394-
EnvTeam.Caption := CustomMessage('EnvTeam');
395-
EnvSkip.Caption := CustomMessage('EnvSkip');
396-
EnvMine.Width := EnvPage.SurfaceWidth;
397-
EnvTeam.Width := EnvPage.SurfaceWidth;
398-
EnvSkip.Width := EnvPage.SurfaceWidth;
399-
EnvMine.Checked := True;
400-
EnvMine.Visible := False;
401-
EnvTeam.Visible := False;
402-
EnvSkip.Visible := False;
392+
{ One drop-down for where to save, at the bottom above the hint: always visible, whatever the number of rows. }
403393
EnvLater := TNewStaticText.Create(EnvPage);
404394
EnvLater.Parent := EnvPage.Surface;
405395
EnvLater.AutoSize := False;
@@ -408,17 +398,37 @@ begin
408398
EnvLater.Height := ScaleY(30);
409399
EnvLater.Caption := CustomMessage('EnvLater');
410400
EnvLater.Top := EnvPage.SurfaceHeight - EnvLater.Height;
401+
EnvScope := TNewComboBox.Create(EnvPage);
402+
EnvScope.Parent := EnvPage.Surface;
403+
EnvScope.Style := csDropDownList;
404+
EnvScope.Width := EnvPage.SurfaceWidth;
405+
EnvScope.Items.Add(CustomMessage('EnvMine'));
406+
EnvScope.Items.Add(CustomMessage('EnvTeam'));
407+
EnvScope.Items.Add(CustomMessage('EnvSkip'));
408+
EnvScope.ItemIndex := 0;
409+
EnvScope.Top := EnvLater.Top - EnvScope.Height - ScaleY(6);
410+
EnvScope.Visible := False;
411+
EnvMore := TNewStaticText.Create(EnvPage);
412+
EnvMore.Parent := EnvPage.Surface;
413+
EnvMore.AutoSize := False;
414+
EnvMore.Width := EnvPage.SurfaceWidth;
415+
EnvMore.Height := ScaleY(16);
416+
EnvMore.Top := EnvScope.Top - EnvMore.Height - ScaleY(2);
417+
EnvMore.Visible := False;
411418
end;
412419
413420
{ Fills the environment page from "adolens setup --detect": the map already in use, or one row per stage or branch
414421
found with a suggested name. }
415422
procedure DetectEnvironments;
416423
var
417-
Exe, OutFile, Line, Key, Guess: String;
424+
Exe, OutFile, Line, Key, Guess, Caption: String;
418425
Lines: TArrayOfString;
419-
I, P, N, Top: Integer;
426+
I, P, N, Top, MaxRows, Skipped: Integer;
420427
Code: Integer;
421428
begin
429+
{ As many rows as fit between the text and the drop-down; the rest is named and can be set in the settings later. }
430+
MaxRows := (EnvMore.Top - (EnvInfo.Top + EnvInfo.Height + ScaleY(4))) div ScaleY(26);
431+
Skipped := 0;
422432
EnvDetected := True;
423433
Exe := ExpandConstant('{tmp}\adolens.exe');
424434
OutFile := ExpandConstant('{tmp}\adolens-envs.txt');
@@ -437,7 +447,9 @@ begin
437447
EnvInfo.Caption := FmtMessage(CustomMessage('EnvExisting'), [Copy(Line, 1, P - 1), Copy(Line, P + 1, Length(Line))]);
438448
Exit;
439449
end;
440-
if (Copy(Line, 1, 4) = 'row' + #9) and (N < 8) then
450+
if (Copy(Line, 1, 4) = 'row' + #9) and (N >= MaxRows) then
451+
Skipped := Skipped + 1
452+
else if Copy(Line, 1, 4) = 'row' + #9 then
441453
begin
442454
Delete(Line, 1, 4);
443455
P := Pos(#9, Line);
@@ -459,7 +471,14 @@ begin
459471
Top := EnvInfo.Top + EnvInfo.Height + ScaleY(4) + (N - 1) * ScaleY(26);
460472
EnvLabels[N - 1] := TNewStaticText.Create(EnvPage);
461473
EnvLabels[N - 1].Parent := EnvPage.Surface;
462-
EnvLabels[N - 1].Caption := Key;
474+
{ "D365FO:staging" is the branch staging of the repository D365FO. }
475+
Caption := Key;
476+
P := Pos(':', Key);
477+
if (P > 1) and (Pos('/', Key) = 0) then
478+
Caption := Copy(Key, P + 1, Length(Key)) + ' (' + Copy(Key, 1, P - 1) + ')';
479+
EnvLabels[N - 1].Caption := Caption;
480+
EnvLabels[N - 1].AutoSize := False;
481+
EnvLabels[N - 1].Height := ScaleY(18);
463482
EnvLabels[N - 1].Top := Top + ScaleY(3);
464483
EnvLabels[N - 1].Width := EnvPage.SurfaceWidth div 2 - ScaleX(8);
465484
EnvEdits[N - 1] := TNewEdit.Create(EnvPage);
@@ -476,13 +495,12 @@ begin
476495
Exit;
477496
end;
478497
EnvInfo.Caption := CustomMessage('EnvText');
479-
Top := EnvInfo.Top + EnvInfo.Height + ScaleY(10) + N * ScaleY(26);
480-
EnvMine.Top := Top;
481-
EnvTeam.Top := Top + ScaleY(20);
482-
EnvSkip.Top := Top + ScaleY(40);
483-
EnvMine.Visible := True;
484-
EnvTeam.Visible := True;
485-
EnvSkip.Visible := True;
498+
if Skipped > 0 then
499+
begin
500+
EnvMore.Caption := FmtMessage(CustomMessage('EnvMore'), [IntToStr(Skipped)]);
501+
EnvMore.Visible := True;
502+
end;
503+
EnvScope.Visible := True;
486504
end;
487505
488506
{ Saves the names typed on the environment page; returns False to stay on the page when it failed. }
@@ -492,7 +510,7 @@ var
492510
I, Code: Integer;
493511
begin
494512
Result := True;
495-
if EnvSkip.Checked or (GetArrayLength(EnvKeys) = 0) then Exit;
513+
if (EnvScope.ItemIndex = 2) or (GetArrayLength(EnvKeys) = 0) then Exit;
496514
Env := '';
497515
for I := 0 to GetArrayLength(EnvKeys) - 1 do
498516
begin
@@ -506,7 +524,7 @@ begin
506524
end;
507525
end;
508526
if Env = '' then Exit;
509-
if EnvTeam.Checked then Scope := 'team' else Scope := 'local';
527+
if EnvScope.ItemIndex = 1 then Scope := 'team' else Scope := 'local';
510528
Code := RunAdoLens(ExpandConstant('{tmp}\adolens.exe'), 'setup --env "' + Env + '" --scope ' + Scope, Output);
511529
if Code <> 0 then
512530
begin

‎src/AdoLens/AdoLens.csproj‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<AssemblyTitle>ADO Lens for Azure DevOps</AssemblyTitle>
2424
<Description>Azure DevOps (work items, boards, wiki, Git and TFVC, pipelines) served to AI assistants over MCP</Description>
2525
<Copyright>Copyright © 2026 WillowDev</Copyright>
26-
<Version>0.9.5</Version>
26+
<Version>0.10.0</Version>
2727
</PropertyGroup>
2828

2929
<ItemGroup>

‎src/AdoLens/Cli.cs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ logout remove the stored PAT of a connection --connection <name>
2727
connections list the connections
2828
config show or change settings
2929
--connection <name> --project <name> --team <name> (defaults of a connection)
30+
--code-project <name> (code, pull requests and pipelines in another project; "" clears)
3031
--default-connection <name> --mode po|dev|full --usage-log true|false
3132
--show-tokens true|false --tool-loading auto|demand|all --add-toolset <set> --remove-toolset <set> --confirm-above <n>
3233
--language pl|en|auto (settings console and installer texts)
@@ -243,7 +244,7 @@ public int Connections()
243244
{
244245
var pat = CredentialStore.Read(name) is not null ? "PAT stored" : "no stored PAT";
245246
Console.WriteLine($"{(name.Equals(settings.DefaultConnection, StringComparison.OrdinalIgnoreCase) ? "*" : " ")} {name,-16} " +
246-
$"https://dev.azure.com/{c.Organization} project {c.Project ?? "-"} team {c.Team ?? "-"} ({pat})" +
247+
$"https://dev.azure.com/{c.Organization} project {c.Project ?? "-"}{(c.CodeProject is { } cp ? $" code in {cp}" : "")} team {c.Team ?? "-"} ({pat})" +
247248
(c.Environments.Count > 0 ? $"\n {"",-16} environments: {string.Join(", ", c.Environments.Select(e => $"{e.Key} = {e.Value}"))}" : "") +
248249
(c.LabelLanguages.Count > 0 ? $"\n {"",-16} label languages: {string.Join(", ", c.LabelLanguages)}" : ""));
249250
}
@@ -262,6 +263,7 @@ public int Config()
262263
throw new ArgumentException($"no connection named '{name}' - add it with 'adolens login --connection {name}'");
263264
if (cl.Opt("project") is { } p) { conn.Project = NullIfEmpty(p); changed = true; }
264265
if (cl.Opt("team") is { } t) { conn.Team = NullIfEmpty(t); changed = true; }
266+
if (cl.Opt("code-project") is { } cp) { conn.CodeProject = NullIfEmpty(cp); changed = true; }
265267
while (cl.Opt("env") is { } e)
266268
{
267269
var eq = e.IndexOf('=');

‎src/AdoLens/CommandLine.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public sealed partial class CommandLine
2323
["pr"] = ["connection=", "project=", "repo=", "list=", "detail=", "merged=", "since=", "top="],
2424
["pipeline"] = ["connection=", "project=", "definition=", "branch=", "top=", "detail=", "artifacts", "artifact=", "part=", "save", "to=", "deployments", "stage="],
2525
["changes"] = ["connection=", "repo=", "file=", "detail="],
26-
["config"] = ["env=", "remove-env=", "connection=", "default-connection=", "project=", "team=", "mode=", "usage-log=", "add-toolset=", "remove-toolset=", "confirm-above=", "show-tokens=", "tool-loading=", "language=", "label-languages=", "download-dir="],
26+
["config"] = ["env=", "remove-env=", "connection=", "default-connection=", "project=", "code-project=", "team=", "mode=", "usage-log=", "add-toolset=", "remove-toolset=", "confirm-above=", "show-tokens=", "tool-loading=", "language=", "label-languages=", "download-dir="],
2727
["item"] = ["connection=", "include=", "detail="],
2828
["query"] = ["connection=", "project=", "wiql=", "query=", "top="],
2929
["create"] = ["connection=", "project=", "type=", "title=", "description=", "criteria=", "assign=", "parent=", "iteration=", "area=", "tags=", "state=", "field=", "template="],

0 commit comments

Comments
 (0)