Forge
csharpdeeb25a2
1#nullable enable
2
3using System.Text.Json;
4using CascadeIDE.Services;
5
6namespace CascadeIDE.Models.Intercom;
7
8/// <summary>Канонический якорь вложения Intercom (ADR 0128 §3). Wire/event log — те же имена полей.</summary>
9public sealed record AttachmentAnchor
10{
11 public string? Id { get; init; }
12
13 public string? AttachmentShape { get; init; }
14
15 public string? DisplayLabel { get; init; }
16
17 /// <summary>Workspace-relative или абсолютный путь после resolve @ send.</summary>
18 public string? File { get; init; }
19
20 public int? LineStart { get; init; }
21
22 public int? LineEnd { get; init; }
23
24 public string? MemberKey { get; init; }
25
26 /// <summary>JSON-объект syntaxScope (kind, indexInParent, parentMemberKey, …) — v2+.</summary>
27 public JsonElement? SyntaxScope { get; init; }
28
29 public string? Excerpt { get; init; }
30
31 public int? ProseStart { get; init; }
32
33 public int? ProseLength { get; init; }
34
35 public string? ResolvedAtUtc { get; init; }
36
37 public string? ResolveOutcome { get; init; }
38
39 public static bool TryParseFromJsonElement(JsonElement root, out AttachmentAnchor anchor, out string error)
40 {
41 anchor = new AttachmentAnchor();
42 error = "";
43
44 if (root.ValueKind != JsonValueKind.Object)
45 {
46 error = "anchor_json должен быть JSON-объектом.";
47 return false;
48 }
49
50 anchor = new AttachmentAnchor
51 {
52 Id = readString(root, "id"),
53 AttachmentShape = readString(root, "attachmentShape") ?? readString(root, "attachment_shape"),
54 DisplayLabel = readString(root, "displayLabel") ?? readString(root, "display_label"),
55 File = readString(root, "file"),
56 LineStart = readInt(root, "lineStart") ?? readInt(root, "line_start"),
57 LineEnd = readInt(root, "lineEnd") ?? readInt(root, "line_end"),
58 MemberKey = readString(root, "memberKey") ?? readString(root, "member_key"),
59 SyntaxScope = root.TryGetProperty("syntaxScope", out var ss) ? ss
60 : root.TryGetProperty("syntax_scope", out var ss2) ? ss2 : null,
61 Excerpt = readString(root, "excerpt"),
62 ProseStart = readInt(root, "proseStart") ?? readInt(root, "prose_start"),
63 ProseLength = readInt(root, "proseLength") ?? readInt(root, "prose_length"),
64 ResolvedAtUtc = readString(root, "resolvedAtUtc") ?? readString(root, "resolved_at_utc"),
65 ResolveOutcome = readString(root, "resolveOutcome") ?? readString(root, "resolve_outcome"),
66 };
67
68 if (string.IsNullOrWhiteSpace(anchor.File))
69 {
70 error = "В anchor отсутствует file.";
71 return false;
72 }
73
74 return true;
75 }
76
77 public static bool TryParseFlatArgs(
78 IReadOnlyDictionary<string, JsonElement>? args,
79 out AttachmentAnchor anchor,
80 out int? durationMs,
81 out string error)
82 {
83 anchor = new AttachmentAnchor();
84 durationMs = null;
85 error = "";
86
87 if (args is null)
88 {
89 error = "Отсутствуют аргументы.";
90 return false;
91 }
92
93 durationMs = McpCommandJsonArgs.OptionalInt32(args, "duration_ms");
94
95 var file = McpCommandJsonArgs.String(args, "file");
96 if (string.IsNullOrWhiteSpace(file))
97 {
98 error = "Отсутствует file (или передайте anchor_json).";
99 return false;
100 }
101
102 anchor = new AttachmentAnchor
103 {
104 Id = McpCommandJsonArgs.String(args, "id"),
105 AttachmentShape = McpCommandJsonArgs.String(args, "attachment_shape"),
106 DisplayLabel = McpCommandJsonArgs.String(args, "display_label"),
107 File = file,
108 LineStart = McpCommandJsonArgs.OptionalInt32(args, "line_start"),
109 LineEnd = McpCommandJsonArgs.OptionalInt32(args, "line_end"),
110 MemberKey = McpCommandJsonArgs.String(args, "member_key"),
111 Excerpt = McpCommandJsonArgs.String(args, "excerpt"),
112 };
113
114 if (args.TryGetValue("syntax_scope", out var scopeEl) && scopeEl.ValueKind == JsonValueKind.Object)
115 anchor = anchor with { SyntaxScope = scopeEl };
116
117 return true;
118 }
119
120 public static bool TryParseFlatArgs(IReadOnlyDictionary<string, JsonElement>? args, out AttachmentAnchor anchor, out string error)
121 {
122 if (!TryParseFlatArgs(args, out anchor, out _, out error))
123 return false;
124 return true;
125 }
126
127 private static string? readString(JsonElement obj, string name) =>
128 obj.TryGetProperty(name, out var el) && el.ValueKind == JsonValueKind.String ? el.GetString() : null;
129
130 private static int? readInt(JsonElement obj, string name)
131 {
132 if (!obj.TryGetProperty(name, out var el) || el.ValueKind != JsonValueKind.Number || !el.TryGetInt32(out var v))
133 return null;
134 return v;
135 }
136}
137
138/// <summary>Разбор <c>intercom.reveal_attachment</c> (ADR 0128 §8, 0130 фаза 1).</summary>
139public static class IntercomRevealAttachmentMcpArgs
140{
141 public static bool TryParse(
142 IReadOnlyDictionary<string, JsonElement>? args,
143 out AttachmentAnchor anchor,
144 out bool? selectExplicit,
145 out int? durationMs,
146 out string error)
147 {
148 anchor = new AttachmentAnchor();
149 selectExplicit = null;
150 durationMs = null;
151 error = "";
152
153 if (args is null)
154 {
155 error = "Отсутствуют аргументы.";
156 return false;
157 }
158
159 if (args.TryGetValue("anchor_json", out var anchorEl))
160 {
161 if (!AttachmentAnchor.TryParseFromJsonElement(anchorEl, out anchor, out error))
162 return false;
163 }
164 else if (!AttachmentAnchor.TryParseFlatArgs(args, out anchor, out durationMs, out error))
165 {
166 return false;
167 }
168
169 if (durationMs is null)
170 durationMs = McpCommandJsonArgs.OptionalInt32(args, "duration_ms");
171
172 if (args.TryGetValue("select", out var selEl))
173 {
174 selectExplicit = selEl.ValueKind switch
175 {
176 JsonValueKind.True => true,
177 JsonValueKind.False => false,
178 _ => null,
179 };
180 if (selectExplicit is null)
181 {
182 error = "select должен быть true или false.";
183 return false;
184 }
185 }
186
187 return true;
188 }
189}
190
191/// <summary>Args для <c>editor.select_code</c> / <c>editor.reveal_code</c> (ADR 0131).</summary>
192public static class EditorCodeRefMcpArgs
193{
194 public static bool TryParse(
195 IReadOnlyDictionary<string, JsonElement>? args,
196 out string codeRef,
197 out string? activeFile,
198 out int? durationMs,
199 out string error)
200 {
201 codeRef = "";
202 activeFile = null;
203 durationMs = null;
204 error = "";
205
206 if (args is null)
207 {
208 error = "Отсутствуют аргументы.";
209 return false;
210 }
211
212 codeRef = McpCommandJsonArgs.String(args, "code_ref") ?? "";
213 if (string.IsNullOrWhiteSpace(codeRef))
214 {
215 error = "Отсутствует code_ref.";
216 return false;
217 }
218
219 activeFile = McpCommandJsonArgs.String(args, "active_file");
220 durationMs = McpCommandJsonArgs.OptionalInt32(args, "duration_ms");
221 return true;
222 }
223}
224
View only · write via MCP/CIDE