Implement meeting assistant v1

This commit is contained in:
2026-05-20 02:06:16 +02:00
parent 90df1edc03
commit 0297bcc0f6
120 changed files with 11883 additions and 180 deletions
@@ -0,0 +1,148 @@
using System.Text;
namespace MeetingAssistant.MeetingNotes;
public sealed class MeetingArtifactFrontmatter
{
public string Title { get; set; } = "";
public DateTimeOffset? StartTime { get; set; }
public DateTimeOffset? EndTime { get; set; }
public string Meeting { get; set; } = "";
public string Transcript { get; set; } = "";
public string AssistantContext { get; set; } = "";
public string Summary { get; set; } = "";
public string? State { get; set; }
public string? Agenda { get; set; }
public DateTimeOffset? ScheduledEnd { get; set; }
}
public static class MeetingArtifactFrontmatterRenderer
{
public static string Render(
MeetingArtifactFrontmatter frontmatter,
string body)
{
var builder = new StringBuilder();
builder.AppendLine("---");
AppendScalar(builder, "title", frontmatter.Title);
AppendDateTime(builder, "start_time", frontmatter.StartTime);
AppendDateTime(builder, "end_time", frontmatter.EndTime);
AppendQuoted(builder, "meeting", frontmatter.Meeting);
AppendQuoted(builder, "transcript", frontmatter.Transcript);
AppendQuoted(builder, "assistant_context", frontmatter.AssistantContext);
AppendQuoted(builder, "summary", frontmatter.Summary);
if (!string.IsNullOrWhiteSpace(frontmatter.State))
{
AppendScalar(builder, "state", frontmatter.State);
}
if (frontmatter.Agenda is not null)
{
AppendBlockScalar(builder, "agenda", frontmatter.Agenda);
}
if (frontmatter.ScheduledEnd is not null)
{
AppendDateTime(builder, "scheduled_end", frontmatter.ScheduledEnd);
}
builder.AppendLine("---");
builder.AppendLine();
builder.Append(body.TrimStart('\r', '\n'));
return builder.ToString();
}
public static (string Frontmatter, string Body) Split(string content)
{
var document = MarkdownDocumentParser.SplitOptional(content);
return (document.Frontmatter, document.Body);
}
public static MeetingArtifactFrontmatter Create(
MeetingSessionArtifacts artifacts,
MeetingNote meetingNote,
string title,
string sourceNotePath,
string? state = null)
{
return new MeetingArtifactFrontmatter
{
Title = title,
StartTime = meetingNote.Frontmatter.StartTime,
EndTime = meetingNote.Frontmatter.EndTime,
Meeting = ObsidianLink.ToWikiLink(artifacts.MeetingNotePath, sourceNotePath, "Meeting Note"),
Transcript = ObsidianLink.ToWikiLink(artifacts.TranscriptPath, sourceNotePath, "Transcript"),
AssistantContext = ObsidianLink.ToWikiLink(artifacts.AssistantContextPath, sourceNotePath, "Assistant Context"),
Summary = ObsidianLink.ToWikiLink(artifacts.SummaryPath, sourceNotePath, "Summary"),
State = state
};
}
public static string DefaultTitle(MeetingNote meetingNote, string fallback)
{
return string.IsNullOrWhiteSpace(meetingNote.Frontmatter.Title)
? fallback
: meetingNote.Frontmatter.Title;
}
private static void AppendScalar(StringBuilder builder, string key, string? value)
{
builder.Append(key);
builder.Append(": ");
builder.AppendLine(string.IsNullOrWhiteSpace(value) ? "\"\"" : EscapeListItem(value));
}
private static void AppendQuoted(StringBuilder builder, string key, string value)
{
builder.Append(key);
builder.Append(": ");
builder.AppendLine(EscapeQuoted(value));
}
private static void AppendDateTime(StringBuilder builder, string key, DateTimeOffset? value)
{
builder.Append(key);
builder.Append(": ");
builder.AppendLine(value is null ? "\"\"" : EscapeQuoted(value.Value.ToString("O")));
}
private static void AppendBlockScalar(StringBuilder builder, string key, string value)
{
if (string.IsNullOrEmpty(value))
{
builder.Append(key);
builder.AppendLine(": \"\"");
return;
}
builder.Append(key);
builder.AppendLine(": |-");
foreach (var line in value.Replace("\r\n", "\n", StringComparison.Ordinal).Replace('\r', '\n').Split('\n'))
{
builder.Append(" ");
builder.AppendLine(line);
}
}
private static string EscapeQuoted(string value)
{
return $"\"{value.Replace("\"", "\\\"", StringComparison.Ordinal)}\"";
}
private static string EscapeListItem(string value)
{
return value.Contains(':', StringComparison.Ordinal) || value.StartsWith("[", StringComparison.Ordinal)
? EscapeQuoted(value)
: value;
}
}