Public Access
PR and Push Build/Test / build-and-test (push) Successful in 12m0s
Append successful offline recovery with a duplicate-warning marker and retain failed replay sources. Offer newly available microphones through confirmation notifications. Add project discovery metadata and blocking association approval, plus nonblocking workflow requests that start a separate settings-agent conversation. Include regression tests, OpenSpec changes, and verified local Release startup.
56 lines
2.3 KiB
C#
56 lines
2.3 KiB
C#
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<string> 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<ProjectMetadata> 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<Frontmatter>(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; }
|
|
}
|
|
}
|