Files
AX-Copilot-Codex/.decompiledproj/AxCopilot/Services/Agent/ProjectRulesService.cs

210 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace AxCopilot.Services.Agent;
public static class ProjectRulesService
{
public class ProjectRule
{
public string FilePath { get; set; } = "";
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string AppliesTo { get; set; } = "";
public string When { get; set; } = "always";
public string Body { get; set; } = "";
}
private static readonly Regex FrontMatterRegex = new Regex("^---\\s*\\n(.*?)\\n---\\s*\\n", RegexOptions.Compiled | RegexOptions.Singleline);
private static readonly Regex YamlKeyValue = new Regex("^\\s*(\\w[\\w-]*)\\s*:\\s*(.+?)\\s*$", RegexOptions.Multiline | RegexOptions.Compiled);
public static List<ProjectRule> LoadRules(string workFolder)
{
List<ProjectRule> list = new List<ProjectRule>();
if (string.IsNullOrEmpty(workFolder))
{
return list;
}
string text = FindRulesDirectory(workFolder);
if (text == null)
{
return list;
}
try
{
string[] files = Directory.GetFiles(text, "*.md");
foreach (string filePath in files)
{
ProjectRule projectRule = ParseRuleFile(filePath);
if (projectRule != null)
{
list.Add(projectRule);
}
}
}
catch
{
}
return list;
}
public static List<ProjectRule> FilterRules(List<ProjectRule> rules, string when = "always", IEnumerable<string>? filePaths = null)
{
List<ProjectRule> list = new List<ProjectRule>();
foreach (ProjectRule rule in rules)
{
string text = rule.When.ToLowerInvariant().Trim();
if (text != "always" && text != when.ToLowerInvariant())
{
continue;
}
if (!string.IsNullOrEmpty(rule.AppliesTo) && filePaths != null)
{
string pattern = rule.AppliesTo.Trim();
if (!filePaths.Any((string fp) => MatchesGlob(fp, pattern)))
{
continue;
}
}
list.Add(rule);
}
return list;
}
public static string FormatForSystemPrompt(List<ProjectRule> rules)
{
if (rules.Count == 0)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("\n## 프로젝트 규칙 (.ax/rules/)");
stringBuilder.AppendLine("아래 규칙을 반드시 준수하세요:\n");
foreach (ProjectRule rule in rules)
{
if (!string.IsNullOrEmpty(rule.Name))
{
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder3 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(4, 1, stringBuilder2);
handler.AppendLiteral("### ");
handler.AppendFormatted(rule.Name);
stringBuilder3.AppendLine(ref handler);
}
if (!string.IsNullOrEmpty(rule.Description))
{
StringBuilder stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(3, 1, stringBuilder2);
handler.AppendLiteral("*");
handler.AppendFormatted(rule.Description);
handler.AppendLiteral("*\n");
stringBuilder4.AppendLine(ref handler);
}
stringBuilder.AppendLine(rule.Body.Trim());
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
}
internal static string? FindRulesDirectory(string workFolder)
{
string text = workFolder;
for (int i = 0; i < 3; i++)
{
if (string.IsNullOrEmpty(text))
{
break;
}
string text2 = Path.Combine(text, ".ax", "rules");
if (Directory.Exists(text2))
{
return text2;
}
text = Directory.GetParent(text)?.FullName;
}
return null;
}
internal static ProjectRule? ParseRuleFile(string filePath)
{
try
{
string text = File.ReadAllText(filePath, Encoding.UTF8);
if (string.IsNullOrWhiteSpace(text))
{
return null;
}
ProjectRule projectRule = new ProjectRule
{
FilePath = filePath,
Name = Path.GetFileNameWithoutExtension(filePath)
};
Match match = FrontMatterRegex.Match(text);
if (match.Success)
{
string value = match.Groups[1].Value;
foreach (Match item in YamlKeyValue.Matches(value))
{
string text2 = item.Groups[1].Value.ToLowerInvariant();
string text3 = item.Groups[2].Value.Trim().Trim('"', '\'');
switch (text2)
{
case "name":
projectRule.Name = text3;
break;
case "description":
projectRule.Description = text3;
break;
case "applies-to":
case "appliesto":
projectRule.AppliesTo = text3;
break;
case "when":
projectRule.When = text3;
break;
}
}
string text4 = text;
int num = match.Index + match.Length;
projectRule.Body = text4.Substring(num, text4.Length - num);
}
else
{
projectRule.Body = text;
}
return string.IsNullOrWhiteSpace(projectRule.Body) ? null : projectRule;
}
catch
{
return null;
}
}
private static bool MatchesGlob(string path, string pattern)
{
if (pattern.StartsWith("*."))
{
string text = pattern;
return path.EndsWith(text.Substring(1, text.Length - 1), StringComparison.OrdinalIgnoreCase);
}
if (pattern.StartsWith("**/"))
{
string text = pattern;
string pattern2 = text.Substring(3, text.Length - 3);
return MatchesGlob(Path.GetFileName(path), pattern2);
}
return Path.GetFileName(path).Equals(pattern, StringComparison.OrdinalIgnoreCase);
}
}