Forge
csharpdeeb25a2
1using System.Text.Json;
2using System.Text.Json.Serialization;
3using HybridCodebaseIndex.Core;
4
5namespace CascadeIDE.Features.HybridIndex.McpParity;
6
7/// <summary>JSON-ответы ide_execute для команд <c>codebase_index_*</c> — паритет с MCP-пакетом hybrid-codebase-index (ToolHandlers).</summary>
8internal static class CodebaseIndexIdeJsonResponses
9{
10 private static readonly JsonSerializerOptions JsonOut = new()
11 {
12 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
13 WriteIndented = false,
14 DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
15 };
16
17 private static readonly JsonSerializerOptions JsonOutWithNulls = new()
18 {
19 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
20 WriteIndented = false,
21 DefaultIgnoreCondition = JsonIgnoreCondition.Never,
22 };
23
24 public static string SerializeSearch(SearchResponse response, string? err)
25 {
26 var dto = new SearchResultDto(
27 Err: err,
28 IndexFormatVersion: response.IndexFormatVersion,
29 Query: response.Query,
30 DatabasePath: response.DatabasePath,
31 Hits: response.Hits.Select(static h => new HitDto(
32 h.HitId,
33 h.Path,
34 h.Extension,
35 h.HitKind,
36 h.RankScore,
37 h.FtsScore,
38 h.VecScore,
39 h.Snippet,
40 h.LineStart,
41 h.LineEnd,
42 h.ChunkCharCount,
43 h.LastWriteUtcIso)).ToList());
44 return JsonSerializer.Serialize(dto, JsonOut);
45 }
46
47 public static string SerializeExplain(ExplainHitResponse resp)
48 {
49 var dto = new ExplainResultDto(
50 Err: resp.Err,
51 IndexFormatVersion: resp.IndexFormatVersion,
52 DatabasePath: resp.DatabasePath,
53 Hit: resp.Hit is null
54 ? null
55 : new HitDto(
56 resp.Hit.HitId,
57 resp.Hit.Path,
58 resp.Hit.Extension,
59 resp.Hit.HitKind,
60 resp.Hit.RankScore,
61 resp.Hit.FtsScore,
62 resp.Hit.VecScore,
63 resp.Hit.Snippet,
64 resp.Hit.LineStart,
65 resp.Hit.LineEnd,
66 resp.Hit.ChunkCharCount,
67 resp.Hit.LastWriteUtcIso));
68 return JsonSerializer.Serialize(dto, JsonOut);
69 }
70
71 /// <summary>
72 /// JSON для <c>codebase_index_status</c> и паритет с внешним MCP hybrid-codebase-index.
73 /// <para>
74 /// Это надмножество полезной нагрузки типизированного события <see cref="CascadeIDE.Cockpit.DataBus.HybridIndexStateChanged"/>.
75 /// Свертку ядра <c>IndexStatus</c> в узкий контракт шины выполняют CCU (<see cref="CascadeIDE.Cockpit.ComputingUnits.HybridIndex.HybridIndexStateChangedUnit"/>).
76 /// </para>
77 /// </summary>
78 public static string SerializeStatus(IndexStatus st)
79 {
80 var dto = new StatusResultDto(
81 IndexFormatVersion: st.IndexFormatVersion,
82 DatabasePath: st.DatabasePath,
83 DatabaseExists: st.DatabaseExists,
84 DocumentCount: st.DocumentCount,
85 DocumentCountMayBeStale: st.DocumentCountMayBeStale,
86 IndexedAtIso: st.IndexedAtIso,
87 WorkspaceRoot: st.WorkspaceRootNormalized,
88 LastReindexError: st.LastReindexError,
89 LastReindexErrorAtIso: st.LastReindexErrorAtIso,
90 SettingsSource: st.SettingsSource,
91 SettingsParseError: st.SettingsParseError,
92 EffectiveSettings: st.EffectiveSettings is null
93 ? null
94 : new EffectiveSettingsDto(
95 st.EffectiveSettings.IncludeCsInFts,
96 st.EffectiveSettings.ExtraIncludeRoots.ToList(),
97 st.EffectiveSettings.ExcludeRoots.ToList(),
98 st.EffectiveSettings.EffectiveExtensions.ToList(),
99 st.EffectiveSettings.ExcludePathSegments.ToList(),
100 st.EffectiveSettings.IgnoreFiles.ToList(),
101 st.EffectiveSettings.MaxIndexedFileBytes,
102 st.EffectiveSettings.ChunkLines,
103 st.EffectiveSettings.ChunkOverlapLines,
104 st.EffectiveSettings.BinaryProbeBytes),
105 ReindexState: st.ReindexState,
106 ReindexStartedAtIso: st.ReindexStartedAtIso);
107 return JsonSerializer.Serialize(dto, JsonOutWithNulls);
108 }
109
110 public static string SerializeReindex(ReindexSummary summary)
111 {
112 var dto = new ReindexResultDto(
113 IndexFormatVersion: summary.IndexFormatVersion,
114 DatabasePath: summary.DatabasePath,
115 FilesIndexed: summary.FilesIndexed,
116 FilesSkippedTooLarge: summary.FilesSkippedTooLarge,
117 FilesSkippedBinary: summary.FilesSkippedBinary,
118 FilesSkippedExcluded: summary.FilesSkippedExcluded,
119 SkippedReasonCounts: summary.SkippedReasonCounts,
120 SkippedTopPathPrefixes: summary.SkippedTopPathPrefixes.Select(static p => new TopPrefixDto(p.PathPrefix, p.Count)).ToList(),
121 SkippedSample: summary.SkippedSample.Select(static s => new SkippedDto(s.Path, s.Reason)).ToList(),
122 DurationMs: (long)summary.Duration.TotalMilliseconds);
123 return JsonSerializer.Serialize(dto, JsonOut);
124 }
125
126 private sealed record SearchResultDto(
127 string? Err,
128 int IndexFormatVersion,
129 string Query,
130 string DatabasePath,
131 List<HitDto> Hits);
132
133 private sealed record HitDto(
134 long HitId,
135 string Path,
136 string Extension,
137 string HitKind,
138 double RankScore,
139 double? FtsScore,
140 double? VecScore,
141 string? Snippet,
142 int LineStart,
143 int LineEnd,
144 int ChunkCharCount,
145 string? LastWriteUtcIso);
146
147 private sealed record ExplainResultDto(
148 string? Err,
149 int IndexFormatVersion,
150 string DatabasePath,
151 HitDto? Hit);
152
153 private sealed record StatusResultDto(
154 int IndexFormatVersion,
155 string DatabasePath,
156 bool DatabaseExists,
157 int DocumentCount,
158 bool DocumentCountMayBeStale,
159 string? IndexedAtIso,
160 string? WorkspaceRoot,
161 string? LastReindexError,
162 string? LastReindexErrorAtIso,
163 string SettingsSource,
164 string? SettingsParseError,
165 EffectiveSettingsDto? EffectiveSettings,
166 string? ReindexState,
167 string? ReindexStartedAtIso);
168
169 private sealed record EffectiveSettingsDto(
170 bool IncludeCsInFts,
171 List<string> ExtraIncludeRoots,
172 List<string> ExcludeRoots,
173 List<string> EffectiveExtensions,
174 List<string> ExcludePathSegments,
175 List<string> IgnoreFiles,
176 long MaxIndexedFileBytes,
177 int ChunkLines,
178 int ChunkOverlapLines,
179 int BinaryProbeBytes);
180
181 private sealed record ReindexResultDto(
182 int IndexFormatVersion,
183 string DatabasePath,
184 int FilesIndexed,
185 int FilesSkippedTooLarge,
186 int FilesSkippedBinary,
187 int FilesSkippedExcluded,
188 IReadOnlyDictionary<string, int> SkippedReasonCounts,
189 List<TopPrefixDto> SkippedTopPathPrefixes,
190 List<SkippedDto> SkippedSample,
191 long DurationMs);
192
193 private sealed record TopPrefixDto(string PathPrefix, int Count);
194
195 private sealed record SkippedDto(string Path, string Reason);
196}
197
View only · write via MCP/CIDE