using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace AxCopilot.Services.Agent; public class RegexTool : IAgentTool { private const int MaxTimeout = 5000; public string Name => "regex_tool"; public string Description => "Regular expression tool. Actions: 'test' — check if text matches a pattern; 'match' — find all matches with groups; 'replace' — replace matches with replacement string; 'split' — split text by pattern; 'extract' — extract named/numbered groups from first match."; public ToolParameterSchema Parameters { get { ToolParameterSchema toolParameterSchema = new ToolParameterSchema(); Dictionary dictionary = new Dictionary(); ToolProperty obj = new ToolProperty { Type = "string", Description = "Action to perform" }; int num = 5; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); span[0] = "test"; span[1] = "match"; span[2] = "replace"; span[3] = "split"; span[4] = "extract"; obj.Enum = list; dictionary["action"] = obj; dictionary["pattern"] = new ToolProperty { Type = "string", Description = "Regular expression pattern" }; dictionary["text"] = new ToolProperty { Type = "string", Description = "Text to process" }; dictionary["replacement"] = new ToolProperty { Type = "string", Description = "Replacement string for replace action (supports $1, $2, ${name})" }; dictionary["flags"] = new ToolProperty { Type = "string", Description = "Regex flags: 'i' (ignore case), 'm' (multiline), 's' (singleline). Combine: 'im'" }; toolParameterSchema.Properties = dictionary; num = 3; List list2 = new List(num); CollectionsMarshal.SetCount(list2, num); Span span2 = CollectionsMarshal.AsSpan(list2); span2[0] = "action"; span2[1] = "pattern"; span2[2] = "text"; toolParameterSchema.Required = list2; return toolParameterSchema; } } public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken)) { string text = args.GetProperty("action").GetString() ?? ""; string pattern = args.GetProperty("pattern").GetString() ?? ""; string text2 = args.GetProperty("text").GetString() ?? ""; JsonElement value; string replacement = (args.TryGetProperty("replacement", out value) ? (value.GetString() ?? "") : ""); JsonElement value2; string flags = (args.TryGetProperty("flags", out value2) ? (value2.GetString() ?? "") : ""); try { RegexOptions options = ParseFlags(flags); Regex regex = new Regex(pattern, options, TimeSpan.FromMilliseconds(5000.0)); if (1 == 0) { } ToolResult result = text switch { "test" => Test(regex, text2), "match" => Match(regex, text2), "replace" => Replace(regex, text2, replacement), "split" => Split(regex, text2), "extract" => Extract(regex, text2), _ => ToolResult.Fail("Unknown action: " + text), }; if (1 == 0) { } return Task.FromResult(result); } catch (RegexMatchTimeoutException) { return Task.FromResult(ToolResult.Fail("정규식 실행 시간 초과 (ReDoS 방지). 패턴을 간소화하세요.")); } catch (Exception ex2) { return Task.FromResult(ToolResult.Fail("정규식 오류: " + ex2.Message)); } } private static RegexOptions ParseFlags(string flags) { RegexOptions regexOptions = RegexOptions.None; foreach (char c in flags) { RegexOptions regexOptions2 = regexOptions; if (1 == 0) { } RegexOptions regexOptions3 = c switch { 'i' => RegexOptions.IgnoreCase, 'm' => RegexOptions.Multiline, 's' => RegexOptions.Singleline, _ => RegexOptions.None, }; if (1 == 0) { } regexOptions = regexOptions2 | regexOptions3; } return regexOptions; } private static ToolResult Test(Regex regex, string text) { return ToolResult.Ok(regex.IsMatch(text) ? "✓ Pattern matches" : "✗ No match"); } private static ToolResult Match(Regex regex, string text) { MatchCollection matchCollection = regex.Matches(text); if (matchCollection.Count == 0) { return ToolResult.Ok("No matches found."); } StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(17, 1, stringBuilder2); handler.AppendLiteral("Found "); handler.AppendFormatted(matchCollection.Count); handler.AppendLiteral(" match(es):"); stringBuilder3.AppendLine(ref handler); int num = Math.Min(matchCollection.Count, 50); for (int i = 0; i < num; i++) { Match match = matchCollection[i]; stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(24, 4, stringBuilder2); handler.AppendLiteral("\n["); handler.AppendFormatted(i); handler.AppendLiteral("] \""); handler.AppendFormatted(Truncate(match.Value, 200)); handler.AppendLiteral("\" (index "); handler.AppendFormatted(match.Index); handler.AppendLiteral(", length "); handler.AppendFormatted(match.Length); handler.AppendLiteral(")"); stringBuilder4.AppendLine(ref handler); if (match.Groups.Count > 1) { for (int j = 1; j < match.Groups.Count; j++) { Group obj = match.Groups[j]; string text2 = regex.GroupNameFromNumber(j); string value = ((text2 != j.ToString()) ? ("'" + text2 + "'") : $"${j}"); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(14, 2, stringBuilder2); handler.AppendLiteral(" Group "); handler.AppendFormatted(value); handler.AppendLiteral(": \""); handler.AppendFormatted(Truncate(obj.Value, 100)); handler.AppendLiteral("\""); stringBuilder5.AppendLine(ref handler); } } } if (matchCollection.Count > num) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2); handler.AppendLiteral("\n... and "); handler.AppendFormatted(matchCollection.Count - num); handler.AppendLiteral(" more matches"); stringBuilder6.AppendLine(ref handler); } return ToolResult.Ok(stringBuilder.ToString()); } private static ToolResult Replace(Regex regex, string text, string replacement) { string text2 = regex.Replace(text, replacement); int count = regex.Matches(text).Count; if (text2.Length > 8000) { text2 = text2.Substring(0, 8000) + "\n... (truncated)"; } return ToolResult.Ok($"Replaced {count} occurrence(s):\n\n{text2}"); } private static ToolResult Split(Regex regex, string text) { string[] array = regex.Split(text); StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(18, 1, stringBuilder2); handler.AppendLiteral("Split into "); handler.AppendFormatted(array.Length); handler.AppendLiteral(" parts:"); stringBuilder3.AppendLine(ref handler); int num = Math.Min(array.Length, 100); for (int i = 0; i < num; i++) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(7, 2, stringBuilder2); handler.AppendLiteral(" ["); handler.AppendFormatted(i); handler.AppendLiteral("] \""); handler.AppendFormatted(Truncate(array[i], 200)); handler.AppendLiteral("\""); stringBuilder4.AppendLine(ref handler); } if (array.Length > num) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(20, 1, stringBuilder2); handler.AppendLiteral("\n... and "); handler.AppendFormatted(array.Length - num); handler.AppendLiteral(" more parts"); stringBuilder5.AppendLine(ref handler); } return ToolResult.Ok(stringBuilder.ToString()); } private static ToolResult Extract(Regex regex, string text) { Match match = regex.Match(text); if (!match.Success) { return ToolResult.Ok("No match found."); } StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2); handler.AppendLiteral("Match: \""); handler.AppendFormatted(Truncate(match.Value, 300)); handler.AppendLiteral("\""); stringBuilder3.AppendLine(ref handler); if (match.Groups.Count > 1) { stringBuilder.AppendLine("\nGroups:"); for (int i = 1; i < match.Groups.Count; i++) { Group obj = match.Groups[i]; string text2 = regex.GroupNameFromNumber(i); string value = ((text2 != i.ToString()) ? ("'" + text2 + "'") : $"${i}"); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(6, 2, stringBuilder2); handler.AppendLiteral(" "); handler.AppendFormatted(value); handler.AppendLiteral(": \""); handler.AppendFormatted(Truncate(obj.Value, 200)); handler.AppendLiteral("\""); stringBuilder4.AppendLine(ref handler); } } return ToolResult.Ok(stringBuilder.ToString()); } private static string Truncate(string s, int maxLen) { return (s.Length <= maxLen) ? s : (s.Substring(0, maxLen) + "…"); } }