From 430f1a8c0913e404526c7aa47e091f72ff97d184 Mon Sep 17 00:00:00 2001 From: WillowDev <216433317+willowdev-code@users.noreply.github.com> Date: Fri, 16 Oct 2026 14:30:27 +0200 Subject: [PATCH 1/2] PR and pipeline reading in every mode, work items of a change, faster trace, history of a branch, clearer environment map --- CHANGELOG.md | 14 ++++ README.md | 6 +- README.pl.md | 6 +- src/AdoLens/Knowledge.cs | 17 +++++ src/AdoLens/McpTools.cs | 21 ++++-- src/AdoLens/Queries.cs | 10 ++- src/AdoLens/Trace.cs | 91 +++++++++++++++++--------- tests/AdoLens.Tests/KnowledgeTests.cs | 9 +++ tests/AdoLens.Tests/PrPipelineTests.cs | 2 +- tests/AdoLens.Tests/SetupFlowTests.cs | 2 +- tests/AdoLens.Tests/SetupTests.cs | 2 +- tests/AdoLens.Tests/TraceTests.cs | 4 +- 12 files changed, 134 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c79c593..f017607 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.7.3 — unreleased + +Fixes from a real question: "pull requests merged into staging after release X, with their work items and tags". + +- `ado_pr` and `ado_pipeline` (reading: pull request lists and details, merged PRs, builds, deployments, artifacts) are + in every mode now, the Product Owner one too; creating and completing PRs and running pipelines stay for developers. +- `ado_changes` of a pull request, changeset or commit lists its work items with type, state and tags. +- `ado_trace` checks the release stages in parallel and skips deployments older than the change: it no longer runs + out of time on items with several pull requests. +- `ado_file path=/ repo= history=true version=`: the history of a whole repository or folder on a branch, + with pull requests and work items. An environment name given as the branch ("sbx03") is turned into its branch. +- The environment map reads "UAT (sbx03) = branch staging" instead of "UAT (sbx03) [staging]". +- The server instructions name the tools for "what went into a branch or an environment". + ## 0.7.2 — 2026-10-16 - `ado_pr merged=` (`adolens pr --merged staging --since 7d`): pull requests completed into a branch, newest diff --git a/README.md b/README.md index 1d2debe..728c8c2 100644 --- a/README.md +++ b/README.md @@ -177,11 +177,11 @@ never a permission. |---|---|:-:|:-:|:-:| | `core` | `ado_status`, `ado_settings`, `ado_upgrade`, `ado_fields` | ✓ | ✓ | ✓ | | `work` | `ado_work_item`, `ado_query`, `ado_create`, `ado_update`, `ado_comment`, `ado_link`, `ado_search`, `ado_undo`, `ado_attachments`, `ado_create_tree` | ✓ | ✓ | ✓ | -| `pr` | `ado_pr`, `ado_pr_update` | – | ✓ | ✓ | -| `pipelines` | `ado_pipeline`, `ado_pipeline_run` | – | ✓ | ✓ | +| `pr` | `ado_pr_update` | – | ✓ | ✓ | +| `pipelines` | `ado_pipeline_run` | – | ✓ | ✓ | | `boards` | `ado_sprint`, `ado_board`, `ado_backlog`, `ado_my_day`, `ado_health`, `ado_release_notes`, `ado_metrics` | ✓ | – | ✓ | | `wiki` | `ado_wiki`, `ado_wiki_write` | ✓ | ✓ | ✓ | -| `trace` | `ado_trace`, `ado_changes`, `ado_file`, `ado_label`, `ado_review` | ✓ | ✓ | ✓ | +| `trace` | `ado_trace`, `ado_changes`, `ado_file`, `ado_label`, `ado_review`, `ado_pr`, `ado_pipeline` | ✓ | ✓ | ✓ | | `recipes` | `ado_recipe` | ✓ | ✓ | ✓ | | `test` | `ado_tests`, `ado_test_create` | ✓ | – | ✓ | | `test`, `recipes` | planned | ✓ | (recipes) | ✓ | diff --git a/README.pl.md b/README.pl.md index 33afb9f..fa1fc64 100644 --- a/README.pl.md +++ b/README.pl.md @@ -178,11 +178,11 @@ Nigdy nie jest uprawnieniem. |---|---|:-:|:-:|:-:| | `core` | `ado_status`, `ado_settings`, `ado_upgrade`, `ado_fields` | ✓ | ✓ | ✓ | | `work` | `ado_work_item`, `ado_query`, `ado_create`, `ado_update`, `ado_comment`, `ado_link`, `ado_search`, `ado_undo`, `ado_attachments`, `ado_create_tree` | ✓ | ✓ | ✓ | -| `pr` | `ado_pr`, `ado_pr_update` | – | ✓ | ✓ | -| `pipelines` | `ado_pipeline`, `ado_pipeline_run` | – | ✓ | ✓ | +| `pr` | `ado_pr_update` | – | ✓ | ✓ | +| `pipelines` | `ado_pipeline_run` | – | ✓ | ✓ | | `boards` | `ado_sprint`, `ado_board`, `ado_backlog`, `ado_my_day`, `ado_health`, `ado_release_notes`, `ado_metrics` | ✓ | – | ✓ | | `wiki` | `ado_wiki`, `ado_wiki_write` | ✓ | ✓ | ✓ | -| `trace` | `ado_trace`, `ado_changes`, `ado_file`, `ado_label`, `ado_review` | ✓ | ✓ | ✓ | +| `trace` | `ado_trace`, `ado_changes`, `ado_file`, `ado_label`, `ado_review`, `ado_pr`, `ado_pipeline` | ✓ | ✓ | ✓ | | `recipes` | `ado_recipe` | ✓ | ✓ | ✓ | | `test` | `ado_tests`, `ado_test_create` | ✓ | – | ✓ | | `test`, `recipes` | w planie | ✓ | (recipes) | ✓ | diff --git a/src/AdoLens/Knowledge.cs b/src/AdoLens/Knowledge.cs index 895ab29..bd10ce7 100644 --- a/src/AdoLens/Knowledge.cs +++ b/src/AdoLens/Knowledge.cs @@ -151,6 +151,18 @@ sealed record FileRef(bool Tfvc, string Path, string? RepoId, string? RepoName, { path = path.Trim().Replace('\\', '/'); if (path.StartsWith("$/", StringComparison.Ordinal)) return (new FileRef(true, path, null, null, project), null); + // The whole repository ("/") or a folder ("src/"): no file search, the repository is needed. + if (path == "/" || path.EndsWith('/')) + { + if (repo is not null) + { + var r = await session.Code.RepoAsync(repo, project, ct); + return (new FileRef(false, "/" + path.Trim('/'), r.Id, r.Name, r.Project ?? project), null); + } + var all = await session.Client.GetListAsync($"{AdoClient.Scope(project)}_apis/git/repositories", ct); + if (all.Count == 1) return (new FileRef(false, "/" + path.Trim('/'), Json.Str(all[0], "id"), Json.Str(all[0], "name"), project), null); + return (null, $"{path} is a folder: give repo= ({string.Join(", ", all.Select(x => Json.Str(x, "name")).Take(30))})"); + } if (repo is not null && path.Contains('/')) { var r = await session.Code.RepoAsync(repo, project, ct); @@ -190,7 +202,12 @@ public static async Task FileAsync(Lens lens, string? connection, string var name = session.Project(project); var (file, choices) = await ResolveAsync(session, name, path, repo, ct); if (file is null) return choices!; + // An environment name ("sbx03", "UAT") instead of a branch: the branch of the environment map. + if (version is not null && session.Config.Environments.FirstOrDefault(e => !e.Key.Contains('/') && + (e.Value.Equals(version, StringComparison.OrdinalIgnoreCase) || e.Value.Contains(version, StringComparison.OrdinalIgnoreCase))) is { Key: { } branch }) + version = branch; if (history) return await HistoryAsync(session, file, version, top, ct); + if (path.Trim() == "/" || path.TrimEnd().EndsWith('/')) return $"{path} is a folder: history=true lists its changes"; string? text; string where; diff --git a/src/AdoLens/McpTools.cs b/src/AdoLens/McpTools.cs index 80727e4..c0ea5a2 100644 --- a/src/AdoLens/McpTools.cs +++ b/src/AdoLens/McpTools.cs @@ -36,6 +36,8 @@ public static class McpTools """, [Toolsets.Trace] = """ - Checking work against what was asked: ado_trace, then read the diffs with ado_changes - never assume. + - What went into a branch or an environment: ado_pr merged= since=