Forge
csharpdeeb25a2
1#nullable enable
2
3using AgentNotes.Core;
4using CascadeIDE.Features.Workspace.DataAcquisition;
5using CascadeIDE.Services;
6
7namespace CascadeIDE.Features.CasaField.Application;
8
9/// <summary>Find markdown heading line for a KB section title.</summary>
10public static class KbSectionLineResolver
11{
12 public static int? TryFindSectionLine(string? workspaceRoot, string knowledgeDocPath, string? sectionTitle)
13 {
14 if (string.IsNullOrWhiteSpace(sectionTitle) || string.IsNullOrWhiteSpace(knowledgeDocPath))
15 return null;
16
17 if (!TryReadKnowledgeMarkdown(workspaceRoot, knowledgeDocPath, out var markdown))
18 return null;
19
20 return FindSectionLineInMarkdown(markdown, sectionTitle);
21 }
22
23 public static int? FindSectionLineInMarkdown(string markdown, string sectionTitle)
24 {
25 var target = Normalize(sectionTitle);
26 if (target.Length == 0)
27 return null;
28
29 var lines = markdown.Replace("\r\n", "\n").Split('\n');
30 for (var i = 0; i < lines.Length; i++)
31 {
32 var line = lines[i];
33 if (!line.TrimStart().StartsWith('#'))
34 continue;
35
36 var heading = ExtractHeadingText(line);
37 var hNorm = Normalize(heading);
38 if (hNorm.Length == 0)
39 continue;
40
41 if (string.Equals(hNorm, target, StringComparison.OrdinalIgnoreCase)
42 || hNorm.Contains(target, StringComparison.OrdinalIgnoreCase)
43 || target.Contains(hNorm, StringComparison.OrdinalIgnoreCase))
44 return i + 1;
45 }
46
47 return null;
48 }
49
50 internal static string Normalize(string text)
51 {
52 var s = text.Trim();
53 var slash = s.LastIndexOf('/');
54 if (slash >= 0 && slash < s.Length - 1)
55 s = s[(slash + 1)..].Trim();
56 return s;
57 }
58
59 private static string ExtractHeadingText(string line)
60 {
61 var t = line.TrimStart();
62 var i = 0;
63 while (i < t.Length && t[i] == '#')
64 i++;
65 return t[i..].Trim();
66 }
67
68 private static bool TryReadKnowledgeMarkdown(string? workspaceRoot, string knowledgeDocPath, out string markdown)
69 {
70 markdown = "";
71 var notesWs = McpAgentNotesService.ResolveNotesWorkspacePath(workspaceRoot);
72 if (string.IsNullOrEmpty(notesWs))
73 return false;
74
75 try
76 {
77 var storage = new NotesStorage();
78 markdown = storage.ReadKnowledgeFile(knowledgePath: null, filePath: knowledgeDocPath);
79 return !string.IsNullOrEmpty(markdown);
80 }
81 catch
82 {
83 return false;
84 }
85 }
86}
87
View only · write via MCP/CIDE