Forge
csharpdeeb25a2
1using System.Diagnostics.CodeAnalysis;
2using System.Text.Json;
3using CascadeIDE.Contracts;
4using CascadeIDE.Features.Workspace.Application;
5
6namespace CascadeIDE.Features.IdeMcp.Application;
7
8/// <summary>
9/// Application-level orchestrator helpers for IDE MCP workspace actions.
10/// Keeps payload shaping and JSON guards out of MainWindowViewModel.
11/// </summary>
12[ApplicationOrchestrator]
13public static class IdeMcpWorkspaceOrchestrator
14{
15 public static object BuildIdeStatePayload(IdeMcpIdeStateUiCapture ui, JsonElement diagnostics)
16 {
17 var d = ui.DebugSnapshot;
18 return new
19 {
20 solution_path = ui.SolutionPath,
21 current_file_path = ui.CurrentFilePath,
22 selected_solution_path = ui.SelectedSolutionPath,
23 editor = new
24 {
25 content_length = ui.EditorTextLength,
26 selection_start = ui.SelectionStart ?? 0,
27 selection_length = ui.SelectionLength ?? 0
28 },
29 breakpoints = new
30 {
31 current_file = ui.CurrentFileBreakpoints,
32 total_count = d.Breakpoints.Count
33 },
34 debug = new
35 {
36 position_file = d.StoppedFile,
37 position_line = d.StoppedLine,
38 has_active_session = d.HasActiveSession,
39 is_stopped = d.IsExecutionStopped,
40 stack_count = d.StackFrames.Count,
41 variables_count = d.VariableRootScopes.Sum(g => g.Roots.Count)
42 },
43 build = new
44 {
45 is_visible = ui.IsBuildOutputVisible,
46 output_preview = ui.BuildOutputPreview,
47 binlog_path = ui.BinlogPath
48 },
49 terminal = new { is_visible = ui.IsTerminalVisible },
50 ui_mode = ui.UiMode,
51 panels = new
52 {
53 pfd_region_expanded = ui.IsPfdRegionExpanded,
54 build_output = ui.IsBuildOutputVisible,
55 mfd_region_expanded = ui.IsMfdRegionExpanded,
56 git = ui.IsGitPanelVisible,
57 instrumentation_dock = ui.IsInstrumentationDockVisible
58 },
59 safety_level = ui.SafetyLevel,
60 editor_group_count = ui.EditorGroupCount,
61 agent_trace_step_count = ui.AgentTraceStepCount,
62 is_autonomous_running = ui.IsAutonomousRunning,
63 diagnostics,
64 cockpit_surface = ui.CockpitSurface
65 };
66 }
67
68 public static string SerializeIdeState(object state) =>
69 JsonSerializer.Serialize(state);
70
71 public static string SerializeCockpitSurface(object snapshot) =>
72 JsonSerializer.Serialize(snapshot);
73
74 public static string SerializeSolutionInfo(
75 string? solutionPath,
76 string? currentFilePath,
77 IReadOnlyList<string> projectPaths,
78 string? selectedSolutionPath) =>
79 JsonSerializer.Serialize(new
80 {
81 solution_path = solutionPath ?? "",
82 current_file_path = currentFilePath ?? "",
83 project_paths = projectPaths,
84 selected_solution_path = selectedSolutionPath ?? ""
85 });
86
87 public static string SerializeBuildOutput(string? text, string background, string foreground) =>
88 JsonSerializer.Serialize(new
89 {
90 text = text ?? "",
91 theme = new
92 {
93 background,
94 foreground
95 }
96 });
97
98 public static string SerializeWorkspaceNotLoadedError() =>
99 JsonSerializer.Serialize(new { error = "No workspace loaded." });
100
101 public static string SerializeInvalidWorkspaceRootError() =>
102 JsonSerializer.Serialize(new { error = "Invalid workspace root." });
103
104 /// <summary>
105 /// Корень каталога для MCP <c>search_workspace_text</c> (как у палитры GoTo): из пути решения через <see cref="BreakpointsFileService.GetWorkspaceRoot"/>.
106 /// </summary>
107 public static bool TryResolveWorkspaceRootForRipgrep(
108 string? solutionPathTrimmedOrEmpty,
109 [NotNullWhen(true)] out string? root,
110 out string errorJson)
111 {
112 root = null;
113 if (string.IsNullOrWhiteSpace(solutionPathTrimmedOrEmpty))
114 {
115 errorJson = SerializeWorkspaceNotLoadedError();
116 return false;
117 }
118
119 if (!WorkspaceBreakpointsRootPresentation.TryResolveExistingDirectory(solutionPathTrimmedOrEmpty, out var candidate))
120 {
121 errorJson = SerializeInvalidWorkspaceRootError();
122 return false;
123 }
124
125 root = candidate;
126 errorJson = "";
127 return true;
128 }
129
130 public static JsonElement ParseDiagnosticsOrEmpty(string diagnosticsJson)
131 {
132 try
133 {
134 return JsonSerializer.Deserialize<JsonElement>(diagnosticsJson);
135 }
136 catch
137 {
138 return JsonSerializer.SerializeToElement(Array.Empty<object>());
139 }
140 }
141
142 public static string BuildTruncatedOutputPreview(string? buildOutput, int maxChars)
143 {
144 var text = buildOutput ?? "";
145 return text.Length > maxChars ? text[..maxChars] + "\n... (output truncated)" : text;
146 }
147}
148
View only · write via MCP/CIDE