Fix past meeting summary tool edge cases
PR and Push Build/Test / build-and-test (push) Successful in 10m54s

This commit is contained in:
2026-05-29 10:38:31 +02:00
parent 626640e26b
commit 689df3dbcb
6 changed files with 174 additions and 36 deletions
@@ -29,7 +29,7 @@ public sealed class BoundMeetingProjectResolver
return [];
}
var projectNames = await ReadMeetingProjectNamesAsync(artifacts.MeetingNotePath, cancellationToken);
var projectNames = await ReadMeetingProjectNamesAsync(artifacts, cancellationToken);
if (projectNames.Count == 0)
{
return [];
@@ -42,6 +42,13 @@ public sealed class BoundMeetingProjectResolver
.ToList();
}
public Task<HashSet<string>> ReadMeetingProjectNamesAsync(
MeetingSessionArtifacts artifacts,
CancellationToken cancellationToken = default)
{
return ReadMeetingProjectNamesAsync(artifacts.MeetingNotePath, cancellationToken);
}
private static async Task<HashSet<string>> ReadMeetingProjectNamesAsync(
string meetingNotePath,
CancellationToken cancellationToken)
+46 -33
View File
@@ -322,15 +322,16 @@ public sealed class MeetingSummaryTools
var summaries = await GetScopedPastSummariesAsync(scope.ProjectNames);
var effectivePageSize = Math.Clamp(page_size, 1, 100);
var effectivePage = Math.Max(1, page);
var requestedPage = Math.Max(1, page);
var totalPages = Math.Max(1, (int)Math.Ceiling(summaries.Count / (double)effectivePageSize));
var effectivePage = Math.Min(requestedPage, totalPages);
var pageItems = summaries
.Skip((effectivePage - 1) * effectivePageSize)
.Take(effectivePageSize)
.ToList();
var lines = new List<string>
{
$"page {Math.Min(effectivePage, totalPages)}/{totalPages}, page_size {effectivePageSize}, total {summaries.Count}"
$"page {effectivePage}/{totalPages}, page_size {effectivePageSize}, total {summaries.Count}"
};
lines.AddRange(pageItems.Select(summary =>
$"{summary.ToolPath} | {FormatSummaryStartTime(summary.StartTime)} | {summary.Title} | projects: {string.Join(", ", summary.Projects)}"));
@@ -499,22 +500,17 @@ public sealed class MeetingSummaryTools
private async Task<ProjectScope> ResolveCurrentMeetingProjectScopeAsync(string[]? projects)
{
var currentProjects = await ReadCurrentMeetingProjectNamesAsync();
if (currentProjects.Count == 0)
{
return new ProjectScope(new HashSet<string>(StringComparer.OrdinalIgnoreCase));
}
if (projects is null || projects.Length == 0)
{
return new ProjectScope(currentProjects.ToHashSet(StringComparer.OrdinalIgnoreCase));
}
var requested = projects
var requested = (projects ?? [])
.Where(project => !string.IsNullOrWhiteSpace(project))
.Select(project => project.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var currentProjects = await ReadCurrentMeetingProjectNamesAsync();
if (requested.Count == 0)
{
return new ProjectScope(currentProjects.ToHashSet(StringComparer.OrdinalIgnoreCase));
}
var outOfScope = requested
.Where(project => !currentProjects.Contains(project))
.Order(StringComparer.OrdinalIgnoreCase)
@@ -531,11 +527,7 @@ public sealed class MeetingSummaryTools
private async Task<HashSet<string>> ReadCurrentMeetingProjectNamesAsync()
{
var note = await ReadMeetingNoteAsync();
return note.Frontmatter.Projects
.Where(project => !string.IsNullOrWhiteSpace(project))
.Select(project => project.Trim())
.ToHashSet(StringComparer.OrdinalIgnoreCase);
return await projectResolver.ReadMeetingProjectNamesAsync(artifacts);
}
private async Task<List<BoundMeetingProject>> GetExistingProjectsInScopeAsync(IReadOnlySet<string> projectNames)
@@ -767,23 +759,25 @@ public sealed class MeetingSummaryTools
string keywords,
bool singleProject)
{
System.Text.RegularExpressions.Regex regex;
try
{
regex = new System.Text.RegularExpressions.Regex(keywords);
}
catch (ArgumentException ex)
{
return $"Search failed: invalid .NET regular expression: {ex.Message}";
}
try
{
var regex = new System.Text.RegularExpressions.Regex(keywords);
var matches = new List<string>();
foreach (var project in projects)
{
foreach (var file in Directory.EnumerateFiles(project.Path, "*", SearchOption.AllDirectories))
{
var lines = File.ReadAllLines(file);
for (var index = 0; index < lines.Length; index++)
{
if (regex.IsMatch(lines[index]))
{
var relative = Path.Combine(project.Name, Path.GetRelativePath(project.Path, file));
matches.Add($"{FormatSearchPath(relative, singleProject)}:{index + 1} {lines[index]}");
}
}
var relative = Path.Combine(project.Name, Path.GetRelativePath(project.Path, file));
AddFileMatches(matches, regex, file, FormatSearchPath(relative, singleProject));
}
}
@@ -794,9 +788,9 @@ public sealed class MeetingSummaryTools
return string.Join('\n', matches);
}
catch
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
{
return "";
return $"Search failed: {ex.Message}";
}
}
@@ -877,6 +871,7 @@ public sealed class MeetingSummaryTools
private async Task<PastMeetingSummaryResolution> ResolvePastSummaryAsync(string path)
{
path = NormalizePastMeetingSummaryToolPath(path);
if (string.IsNullOrWhiteSpace(path) || Path.IsPathRooted(path))
{
return PastMeetingSummaryResolution.Refused("Refused: summary path must be relative to the configured summaries folder.");
@@ -914,6 +909,15 @@ public sealed class MeetingSummaryTools
return new PastMeetingSummaryResolution(summary);
}
private static string NormalizePastMeetingSummaryToolPath(string path)
{
var toolPath = ToToolPath(path.Trim());
const string pastMeetingPrefix = "past-meetings/";
return toolPath.StartsWith(pastMeetingPrefix, StringComparison.OrdinalIgnoreCase)
? toolPath[pastMeetingPrefix.Length..]
: path;
}
private async Task<PastMeetingSummary?> TryReadPastSummaryAsync(string path, string summariesRoot)
{
var content = await File.ReadAllTextAsync(path);
@@ -923,8 +927,17 @@ public sealed class MeetingSummaryTools
return null;
}
var frontmatter = YamlDeserializer.Deserialize<SummaryFrontmatterYaml>(document.Frontmatter)
?? new SummaryFrontmatterYaml();
SummaryFrontmatterYaml frontmatter;
try
{
frontmatter = YamlDeserializer.Deserialize<SummaryFrontmatterYaml>(document.Frontmatter)
?? new SummaryFrontmatterYaml();
}
catch (YamlDotNet.Core.YamlException)
{
return null;
}
var projects = (frontmatter.Projects ?? [])
.Where(project => !string.IsNullOrWhiteSpace(project))
.Select(project => project.Trim())