Forge
csharpdeeb25a2
1using CascadeIDE.Cockpit.DataBus;
2using CascadeIDE.ViewModels;
3using CascadeIDE.Features.IdeMcp.Application;
4using CascadeIDE.Models;
5using CascadeIDE.Services;
6
7namespace CascadeIDE.Features.IdeMcp.Application;
8
9internal sealed partial class MainWindowIdeMcpHost
10{
11
12 /// <summary>Код выхода и успех для финального <see cref="BuildStateChanged"/> после MCP-операции на панели сборки.</summary>
13 private sealed class IdeMcpMutableBuildPhaseOutcome
14 {
15 public int? ExitCode;
16 public bool? Succeeded;
17 }
18
19 /// <summary>
20 /// Пара событий «сборка идёт» / «сборка завершилась» на IDE DataBus (ADR 0099); тело задаёт <see cref="IdeMcpMutableBuildPhaseOutcome"/> до return.
21 /// </summary>
22 private async Task<T> WithIdeMcpPublishedBuildStateAsync<T>(Func<IdeMcpMutableBuildPhaseOutcome, Task<T>> body)
23 {
24 await _host.PublishIdeBuildStateOnUiAsync(new BuildStateChanged(true)).ConfigureAwait(false);
25 var outcome = new IdeMcpMutableBuildPhaseOutcome();
26 try
27 {
28 return await body(outcome).ConfigureAwait(false);
29 }
30 finally
31 {
32 await _host.PublishIdeBuildStateOnUiAsync(new BuildStateChanged(false, outcome.ExitCode, outcome.Succeeded))
33 .ConfigureAwait(false);
34 }
35 }
36
37 /// <summary>Диагностики открытого .cs файла (ошибки и предупреждения Roslyn). JSON: массив { id, message, severity, line, column }. Для не-C# или при отсутствии файла — [].</summary>
38 public async Task<string> GetCurrentFileDiagnosticsAsync()
39 {
40 var (path, text) = await UiScheduler.Default.InvokeAsync(() => (_host.CurrentFilePath ?? "", _host.EditorText ?? ""));
41 return await Task.Run(() => _host.McpContextMinimizer.GetDiagnosticsJson(path, text)).ConfigureAwait(false);
42 }
43
44 /// <summary>Список файлов и дерево решения. file_entries — плоский список с path, title, relative_path. solution_tree — иерархия (solution → projects → folders → files). Выполняется в UI-потоке.</summary>
45 public Task<string> GetSolutionFilesAsync() =>
46 UiScheduler.Default.InvokeAsync(() =>
47 IdeMcpBuildTestOrchestrator.BuildSolutionFilesJson(_host.Workspace.SolutionPath, _host.Workspace.SolutionRoots));
48 public async Task<string> BuildAsync()
49 {
50 var path = await UiScheduler.Default.InvokeAsync(() => _host.Workspace.SolutionPath ?? "");
51 if (!IdeMcpSolutionPathAvailability.IsRunnableSolutionFile(path))
52 {
53 var surf = IdeMcpBuildTestOrchestrator.BuildMissingSolutionPanelSurface();
54 UiScheduler.Default.Post(() =>
55 {
56 _host.BuildOutputPanel.Set(surf.BuildOutputPanelFullText);
57 _host.IsBuildOutputVisible = true;
58 });
59 return surf.McpReplyText;
60 }
61 var pathCopy = path;
62 await UiScheduler.Default.InvokeAsync(() =>
63 {
64 _host.BuildOutputPanel.Set(IdeMcpBuildTestOrchestrator.BuildOperationHeader("РЎР±РѕСЂРєР°", pathCopy));
65 _host.IsBuildOutputVisible = true;
66 }).ConfigureAwait(false);
67
68 return await WithIdeMcpPublishedBuildStateAsync(async outcome =>
69 {
70 try
71 {
72 void AppendBuildChunk(string chunk) => _host.BuildOutputPanel.Append(chunk);
73 var (outStr, success, exitCode, binlogPath) = await _host.McpBuildTest
74 .BuildWithBinlogAsync(path, AppendBuildChunk, cancellationToken: default)
75 .ConfigureAwait(false);
76 outcome.ExitCode = exitCode;
77 outcome.Succeeded = success;
78
79 await UiScheduler.Default.InvokeAsync(() =>
80 {
81 _host.BuildOutputPanel.FlushPending();
82 _host.McpLastBuildBinlogPath = binlogPath;
83 }).ConfigureAwait(false);
84 return outStr;
85 }
86 catch (Exception ex)
87 {
88 outcome.Succeeded = false;
89 var surf = IdeMcpBuildTestOrchestrator.FailedBuildPanelSurface(ex.Message);
90 UiScheduler.Default.Post(() =>
91 {
92 _host.BuildOutputPanel.Set(surf.BuildOutputPanelFullText);
93 _host.IsBuildOutputVisible = true;
94 });
95 return surf.McpReplyText;
96 }
97 }).ConfigureAwait(false);
98 }
99 public async Task<string> BuildStructuredAsync()
100 {
101 var raw = await this.BuildAsync().ConfigureAwait(false);
102 return Services.McpDotnetBuildTestService.SerializeStructuredBuild(raw, _host.McpLastBuildBinlogPath);
103 }
104 public async Task<string> RunCodeCleanupAsync(string? includePath)
105 {
106 var path = await UiScheduler.Default.InvokeAsync(() => _host.Workspace.SolutionPath ?? "");
107 if (!IdeMcpSolutionPathAvailability.IsRunnableSolutionFile(path))
108 return IdeMcpBuildTestOrchestrator.SerializeCodeCleanupFailure(IdeMcpBuildTestOrchestrator.MissingSolutionMessage());
109
110 var pathCopy = path;
111 await UiScheduler.Default.InvokeAsync(() =>
112 {
113 _host.BuildOutputPanel.Set(IdeMcpBuildTestOrchestrator.BuildOperationHeader("Code cleanup", pathCopy));
114 _host.IsBuildOutputVisible = true;
115 }).ConfigureAwait(false);
116
117 return await WithIdeMcpPublishedBuildStateAsync(async outcome =>
118 {
119 try
120 {
121 void AppendBuildChunk(string chunk) => _host.BuildOutputPanel.Append(chunk);
122 var (success, exitCode, outStr) = await _host.McpBuildTest
123 .RunCodeCleanupAsync(path, includePath, AppendBuildChunk, cancellationToken: default)
124 .ConfigureAwait(false);
125 outcome.ExitCode = exitCode;
126 outcome.Succeeded = success;
127 var rawTruncated = IdeMcpBuildTestOrchestrator.BuildTruncatedRawOutput(outStr, 4000);
128
129 await UiScheduler.Default.InvokeAsync(() => _host.BuildOutputPanel.FlushPending()).ConfigureAwait(false);
130
131 return IdeMcpBuildTestOrchestrator.SerializeCodeCleanupResult(success, exitCode, rawTruncated);
132 }
133 catch (Exception ex)
134 {
135 outcome.Succeeded = false;
136 return IdeMcpBuildTestOrchestrator.SerializeCodeCleanupFailure(ex.Message);
137 }
138 }).ConfigureAwait(false);
139 }
140
141}
142
View only · write via MCP/CIDE