| 1 | namespace CascadeIDE.Features.Chat; |
| 2 | |
| 3 | /// <summary>Сквозная линия продукта (spine) сессии — ортогональна тредам ([ADR 0096](../../docs/adr/0096-intercom-topic-card-summary-and-product-spine.md)).</summary> |
| 4 | public sealed record ChatProductSpine( |
| 5 | string LineTitle, |
| 6 | string CurrentFocus, |
| 7 | IReadOnlyList<string> Milestones, |
| 8 | bool IncludeInAgentContext) |
| 9 | { |
| 10 | public static ChatProductSpine Empty { get; } = new("", "", [], false); |
| 11 | |
| 12 | public bool HasContent => |
| 13 | !string.IsNullOrWhiteSpace(LineTitle) |
| 14 | || !string.IsNullOrWhiteSpace(CurrentFocus) |
| 15 | || Milestones.Count > 0; |
| 16 | |
| 17 | public bool HasFocusOrMilestones => |
| 18 | !string.IsNullOrWhiteSpace(CurrentFocus) || Milestones.Count > 0; |
| 19 | |
| 20 | public string FormatCardBody() |
| 21 | { |
| 22 | var lines = new List<string>(); |
| 23 | if (!string.IsNullOrWhiteSpace(CurrentFocus)) |
| 24 | lines.Add(CurrentFocus.Trim()); |
| 25 | foreach (var milestone in Milestones) |
| 26 | { |
| 27 | if (string.IsNullOrWhiteSpace(milestone)) |
| 28 | continue; |
| 29 | lines.Add("• " + milestone.Trim()); |
| 30 | } |
| 31 | |
| 32 | return lines.Count == 0 ? "Задай фокус линии и вехи над чатом." : string.Join('\n', lines); |
| 33 | } |
| 34 | |
| 35 | public string? BuildAgentContextPrefix() |
| 36 | { |
| 37 | if (!IncludeInAgentContext || !HasContent) |
| 38 | return null; |
| 39 | |
| 40 | var lines = new List<string> { "[Продуктовая линия — сжатый срез]" }; |
| 41 | if (!string.IsNullOrWhiteSpace(LineTitle)) |
| 42 | lines.Add($"Линия: {LineTitle.Trim()}"); |
| 43 | if (!string.IsNullOrWhiteSpace(CurrentFocus)) |
| 44 | lines.Add($"Фокус: {CurrentFocus.Trim()}"); |
| 45 | var bullets = Milestones |
| 46 | .Where(m => !string.IsNullOrWhiteSpace(m)) |
| 47 | .Take(3) |
| 48 | .Select(m => $"• {m.Trim()}") |
| 49 | .ToList(); |
| 50 | if (bullets.Count > 0) |
| 51 | lines.AddRange(bullets); |
| 52 | lines.Add("---"); |
| 53 | return string.Join(Environment.NewLine, lines); |
| 54 | } |
| 55 | |
| 56 | public static IReadOnlyList<string> ParseMilestonesText(string? text) => |
| 57 | (text ?? "") |
| 58 | .Replace("\r", "") |
| 59 | .Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) |
| 60 | .Where(line => line.Length > 0) |
| 61 | .Take(8) |
| 62 | .ToList(); |
| 63 | |
| 64 | public static string JoinMilestonesText(IReadOnlyList<string> milestones) => |
| 65 | string.Join(Environment.NewLine, milestones); |
| 66 | } |
| 67 | |