using MeetingAssistant.MeetingNotes; using YamlDotNet.Core; using YamlDotNet.Serialization; namespace MeetingAssistant.Summary; public sealed record ProjectMetadata(string Id, string DisplayName, string Description) { public static async Task BuildContextAsync(BoundMeetingProject project, CancellationToken cancellationToken = default) { var metadata = await ReadAsync(project, cancellationToken); var context = $"# {metadata.Id}\nDisplay name: {metadata.DisplayName}\nDescription: {metadata.Description}"; var agentsPath = Path.Combine(project.Path, "AGENTS.md"); if (File.Exists(agentsPath)) { var instructions = await File.ReadAllTextAsync(agentsPath, cancellationToken); if (!string.IsNullOrWhiteSpace(instructions)) context += "\n\n" + instructions.Trim(); } return context; } public static bool IsValidContent(string content) => Parse(content) is not null; public static async Task ReadAsync(BoundMeetingProject project, CancellationToken cancellationToken = default) { var path = Path.Combine(project.Path, "PROJECT.md"); var metadata = File.Exists(path) ? Parse(await File.ReadAllTextAsync(path, cancellationToken)) : null; return new(project.Name, string.IsNullOrWhiteSpace(metadata?.Name) ? project.Name : metadata.Name.Trim(), metadata?.Description?.Trim() ?? ""); } private static Frontmatter? Parse(string content) { try { var document = MarkdownDocumentParser.SplitOptional(content); if (!document.HasFrontmatter) return null; var metadata = FrontmatterYamlDeserializer.Create().Deserialize(document.Frontmatter); return string.IsNullOrWhiteSpace(metadata?.Name) || metadata.Description is null || metadata.Description.Length > 256 ? null : metadata; } catch (YamlException) { return null; } catch (InvalidDataException) { return null; } } private sealed class Frontmatter { [YamlMember(Alias = "name")] public string? Name { get; set; } [YamlMember(Alias = "description")] public string? Description { get; set; } } }