| 1 | using System.Text.Json; |
| 2 | |
| 3 | namespace CascadeIDE.Services; |
| 4 | |
| 5 | /// <summary> |
| 6 | /// Callbacks from IDE MCP server into the application (UI/ViewModel). |
| 7 | /// </summary> |
| 8 | public interface IIdeMcpActions |
| 9 | { |
| 10 | /// <summary>Выполнить команду по коду (IdeCommands). args — аргументы в том же формате, что у соответствующих тулов. Унифицированный вход для MCP и будущих меню/хоткеев. Реализация в главном окне маршалит на UI-поток.</summary> |
| 11 | Task<string> ExecuteCommandAsync(string commandId, IReadOnlyDictionary<string, JsonElement>? args, CancellationToken cancellationToken = default); |
| 12 | |
| 13 | void OpenFile(string path); |
| 14 | /// <summary>Загрузить решение (.sln / .slnx / .slnf), один проект (.csproj / .fsproj) или каталог — дерево в обозревателе обновится (fire-and-forget на UI, для меню).</summary> |
| 15 | void LoadSolution(string path); |
| 16 | |
| 17 | /// <summary>То же, что <see cref="LoadSolution"/>, но дожидается <c>LoadSolutionAsync</c> и возвращает <c>OK</c> или текст ошибки (для MCP <c>load_solution</c>).</summary> |
| 18 | Task<string> LoadSolutionAndWaitAsync(string path, CancellationToken cancellationToken = default); |
| 19 | /// <summary>Выделить диапазон в редакторе (1-based line/column). Если файл не открыт — открыть.</summary> |
| 20 | void SelectInEditor(string? filePath, int startLine, int startColumn, int endLine, int endColumn); |
| 21 | /// <summary>Текущее состояние редактора (файл, каретка, выделение, content_length, is_empty, content_preview). JSON. maxPreviewChars: 0 = без превью, null/2000 = первые 2000 символов.</summary> |
| 22 | Task<string> GetEditorStateAsync(int? maxPreviewChars = null); |
| 23 | /// <summary>Содержимое редактора по строкам (1-based). JSON: file_path, start_line, end_line, content.</summary> |
| 24 | Task<string> GetEditorContentRangeAsync(int startLine, int endLine); |
| 25 | /// <summary>Полный текст открытой вкладки из модели документа. JSON: file_path, length, truncated, is_dirty, text; или error. filePath null — текущий файл; maxChars — опциональная обрезка.</summary> |
| 26 | Task<string> GetOpenDocumentTextAsync(string? filePath, int? maxChars); |
| 27 | /// <summary>Применить правку: заменить диапазон (1-based). Открывает файл при необходимости; любая открытая вкладка.</summary> |
| 28 | Task<string> ApplyEditAsync(string filePath, int startLine, int startColumn, int endLine, int endColumn, string newText); |
| 29 | /// <summary>Прочитать текст файла workspace с диска. JSON.</summary> |
| 30 | Task<string> ReadWorkspaceFileAsync(string filePath, int? offset, int? limit, int? maxChars); |
| 31 | /// <summary>Сохранить на диск (буфер вкладки или content). JSON.</summary> |
| 32 | Task<string> SaveDocumentAsync(string? filePath, string? content); |
| 33 | /// <summary>Перейти на позицию (и опционально выделить до end). Если файл не открыт — открыть.</summary> |
| 34 | void GoToPosition(string? filePath, int line, int column, int? endLine = null, int? endColumn = null); |
| 35 | void RevealEditorRange(string? filePath, int startLine, int endLine, int? durationMs); |
| 36 | /// <summary>Информация о решении и открытом файле. JSON.</summary> |
| 37 | string GetSolutionInfo(); |
| 38 | /// <summary>Список файлов в дереве решения (обозреватель). JSON: file_entries [{ path, title }]. Выполняется в UI-потоке.</summary> |
| 39 | Task<string> GetSolutionFilesAsync(); |
| 40 | /// <summary>Поиск текста по корню workspace через внешний <c>rg</c> (ripgrep). JSON: matches [{ path, line_number, line_text }], truncated, error?.</summary> |
| 41 | Task<string> SearchWorkspaceTextAsync(string pattern, string? subPath, bool fixedString, string? glob, int maxMatches, string? rgPath); |
| 42 | /// <summary>Краткая справка из интернета (HTTPS, DuckDuckGo Instant Answer: abstract, связанные темы; без API-ключа). Запрос уходит на duckduckgo.com. JSON: query, heading, abstract_text, abstract_url, answer?, related_topics[], backend, note? | offline_or_error.</summary> |
| 43 | Task<string> SearchWebPublicQueryAsync(string query, CancellationToken cancellationToken = default); |
| 44 | /// <summary>Загрузить публичный HTTPS-документ по URL, вернуть тело как читаемый текст (HTML упрощён). JSON: url, resolved_url, text, extraction, лимиты. Только https; блок частных хостов — базовый SSRF-фильтр.</summary> |
| 45 | Task<string> FetchWebPublicUrlAsync(string url, int? maxChars, CancellationToken cancellationToken = default); |
| 46 | /// <summary>Диагностики текущего открытого .cs файла (ошибки/предупреждения Roslyn). JSON: массив { id, message, severity, line, column }. Не-C# — [].</summary> |
| 47 | Task<string> GetCurrentFileDiagnosticsAsync(); |
| 48 | /// <summary>Семантическая навигация (ADR 0039, CNC): режим <c>related</c> — список связанных файлов; <c>subgraph</c> — узлы и рёбра с капами. В JSON subgraph опционально <c>graph_kind</c> (<c>code_intent_code_navigation_map</c> | <c>related_files</c> | <c>repository_module_tree</c>, ADR 0065; устар. <c>code_intent_semantic_map</c> — принимается). Без <c>file_path</c> — якорь = текущий файл. Опционально <c>preset</c> (имя из <c>settings.toml</c> секции <c>[code_navigation]</c>), <c>include_kinds</c> / <c>exclude_kinds</c>; <c>level</c> (file/controlFlow) — override уровня карты на один вызов. JSON.</summary> |
| 49 | Task<string> GetCodeNavigationContextAsync(string mode, string? filePath, int? line, int? column, int? maxRelated, int? maxNodes, int? maxEdges, string? preset, IReadOnlyList<string>? includeKinds, IReadOnlyList<string>? excludeKinds, string? level); |
| 50 | /// <summary>Запустить сборку решения, вернуть вывод.</summary> |
| 51 | Task<string> BuildAsync(); |
| 52 | /// <summary>Запустить сборку и вернуть структурированный результат: success, exit_code, errors[], warnings[], raw_output (обрезано). JSON.</summary> |
| 53 | Task<string> BuildStructuredAsync(); |
| 54 | /// <summary>Запустить тесты решения (dotnet test) и вернуть структурированный результат: success, total, passed, failed, skipped, failed_tests[] (name, message?, duration_ms?). JSON.</summary> |
| 55 | Task<string> RunTestsAsync(); |
| 56 | /// <summary>Запустить затронутые тесты (по переданным путям) или fallback на полный прогон. JSON: success, total, passed, failed, skipped, failed_tests[], mode, filter.</summary> |
| 57 | Task<string> RunAffectedTestsAsync(IReadOnlyList<string>? changedPaths = null); |
| 58 | /// <summary>Запустить code cleanup через dotnet format для решения. Опционально includePath — конкретный файл/путь для точечной чистки. JSON: success, exit_code, raw_output.</summary> |
| 59 | Task<string> RunCodeCleanupAsync(string? includePath = null); |
| 60 | /// <summary>Посчитать метрики кода (LOC, классы, методы, cyclomatic complexity) для current_file/file/path/solution. JSON.</summary> |
| 61 | Task<string> GetCodeMetricsAsync(string? scope = null, string? path = null); |
| 62 | /// <summary>Одна сводка состояния IDE: solution/current file/selection/debug/build output/diagnostics. JSON.</summary> |
| 63 | Task<string> GetIdeStateAsync(); |
| 64 | /// <summary>Только CDS (<c>CockpitSurfaceState</c>): тот же JSON, что поле <c>cockpit_surface</c> в <see cref="GetIdeStateAsync"/>.</summary> |
| 65 | Task<string> GetCockpitSurfaceAsync(); |
| 66 | /// <summary>Диагностика загрузки UI-режимов (UiModes TOML vs встроенный список, Flight в меню). JSON.</summary> |
| 67 | Task<string> GetUiModesDiagnosticsAsync(); |
| 68 | /// <summary>Git status в каталоге решения/workspace. JSON с short/branch/output.</summary> |
| 69 | Task<string> GitStatusAsync(); |
| 70 | /// <summary>Git diff в каталоге решения/workspace. Опционально path, staged. JSON.</summary> |
| 71 | Task<string> GitDiffAsync(string? path = null, bool staged = false); |
| 72 | /// <summary>Git commit в каталоге решения/workspace: message обязателен, paths опциональны (иначе add -A). JSON.</summary> |
| 73 | Task<string> GitCommitAsync(string message, IReadOnlyList<string>? paths = null); |
| 74 | /// <summary>Git push в каталоге решения/workspace. Опционально remote/branch; dry_run — <c>git push --dry-run</c>. JSON.</summary> |
| 75 | Task<string> GitPushAsync(string? remote = null, string? branch = null, bool dryRun = false); |
| 76 | /// <summary>Git log (--oneline). Опционально n (число коммитов). JSON.</summary> |
| 77 | Task<string> GitLogAsync(int n = 20); |
| 78 | /// <summary>Git fetch. Опционально remote, all, prune; dry_run — <c>git fetch --dry-run</c>. JSON.</summary> |
| 79 | Task<string> GitFetchAsync(string? remote = null, bool all = false, bool prune = false, bool dryRun = false); |
| 80 | /// <summary>Git pull. Оба remote и branch или ни одного; ff_only по умолчанию true; dry_run — <c>git pull --dry-run</c> (Git 2.27+). JSON.</summary> |
| 81 | Task<string> GitPullAsync(string? remote = null, string? branch = null, bool ffOnly = true, bool dryRun = false); |
| 82 | /// <summary>Git branch: list | create | delete. JSON.</summary> |
| 83 | Task<string> GitBranchAsync(string? action = null, string? name = null, string? startPoint = null, bool force = false); |
| 84 | /// <summary>Git show. rev обязателен. JSON.</summary> |
| 85 | Task<string> GitShowAsync(string rev, string? path = null, bool statOnly = false); |
| 86 | /// <summary>Git submodule: status | update. JSON.</summary> |
| 87 | Task<string> GitSubmoduleAsync(string? action = null, string? path = null, bool recursive = true); |
| 88 | /// <summary>Git preflight: классификация изменений на semantic/whitespace/eol/bom + untracked. JSON.</summary> |
| 89 | Task<string> GitPreflightAsync(bool staged = false, bool includeUntracked = true, bool includePatches = true); |
| 90 | /// <summary>Git preflight safe-fix: apply renormalize и вернуть обновлённую классификацию. JSON.</summary> |
| 91 | Task<string> GitPreflightFixSafeAsync(bool includePatches = true); |
| 92 | /// <summary>Текущий текст панели «Вывод сборки» и цвета её оформления (background, foreground). JSON. Чтобы агент видел содержимое панели.</summary> |
| 93 | string GetBuildOutput(); |
| 94 | void SetBreakpoint(string filePath, int line, string? condition = null); |
| 95 | void RemoveBreakpoint(string filePath, int line); |
| 96 | void ShowPreview(string title, string content); |
| 97 | /// <summary>Показать превью текущего файла из редактора в отдельном окне. Контент берётся из IDE, не передаётся по MCP — удобно для длинных .md.</summary> |
| 98 | void ShowEditorPreview(); |
| 99 | Task<string> RequestConfirmationAsync(string message, CancellationToken cancellationToken = default); |
| 100 | void FocusEditor(); |
| 101 | /// <summary>Параметры темы UI (цвета, фоны, кнопки, шрифты). JSON.</summary> |
| 102 | string GetUiTheme(); |
| 103 | /// <summary>Применить тему UI из JSON (тот же формат, что get_ui_theme). Выполняется в UI-потоке. Возвращает "OK" или сообщение об ошибке (например, невалидный JSON).</summary> |
| 104 | Task<string> SetUiThemeAsync(string themeJson); |
| 105 | /// <summary>Дерево элементов UI: тип, имя, видимость, границы (x,y,w,h), контент, дочерние. JSON. Вызов на UI-потоке.</summary> |
| 106 | Task<string> GetUiLayoutAsync(); |
| 107 | /// <summary>Цвета фона и текста под текущим курсором мыши. JSON: type, name, background, foreground (hex). Вызов на UI-потоке.</summary> |
| 108 | Task<string> GetColorsUnderCursorAsync(); |
| 109 | /// <summary>Эффективный вид любого контрола: тип, имя, bounds, visible, content, background, foreground, border, font. Без name — под курсором; с name — поиск по имени в дереве. Вызов на UI-потоке.</summary> |
| 110 | Task<string> GetControlAppearanceAsync(string? name); |
| 111 | /// <summary>Применить к контролу параметры layout на лету (margin, grid_row/column, canvas_left/top, dock). JSON. Вызов на UI-потоке.</summary> |
| 112 | Task<string> SetControlLayoutAsync(string controlName, string layoutJson); |
| 113 | /// <summary>Добавить контрол в конец Children панели (только Debug-сборка). parent_name — имя Panel, control_type — Button|TextBlock|Border, content — текст, name — опционально.</summary> |
| 114 | Task<string> AddControlAsync(string parentName, string controlType, string? content, string? name); |
| 115 | /// <summary>Установить текст в контрол с вводом (TextBox и т.п.) по имени. Вызов на UI-потоке.</summary> |
| 116 | Task<string> SetControlTextAsync(string controlName, string text); |
| 117 | /// <summary>Клик по контролу: без name — под курсором (должен быть Button); с name — по имени. Вызов на UI-потоке.</summary> |
| 118 | Task<string> ClickControlAsync(string? controlName); |
| 119 | /// <summary>Отправить сочетание клавиш в эффективный контрол (под курсором или по name). keys — текст вида Ctrl+Enter, Alt+F4. Вызов на UI-потоке.</summary> |
| 120 | Task<string> SendKeysAsync(string? controlName, string keys); |
| 121 | /// <summary>Выбрать сообщение в чате по индексу (0-based, полный ChatMessages) для сценариев MCP/агента.</summary> |
| 122 | Task<string> SelectChatMessageAsync(int index); |
| 123 | /// <summary>Выбрать по номеру gutter (1-based) в активной detail-ветке; <paramref name="endOrdinal"/> — конец диапазона (активным станет он).</summary> |
| 124 | Task<string> SelectChatMessageByOrdinalAsync(int ordinal, int endOrdinal); |
| 125 | /// <summary>Получить выбранное сообщение чата в JSON (глобальный индекс + feed_ordinal в ветке).</summary> |
| 126 | Task<string> GetSelectedChatMessageAsync(); |
| 127 | /// <summary>Найти сообщения ветки по коду (inferred attach, ADR 0137). returns: json.</summary> |
| 128 | Task<string> FindIntercomMessagesForCodeAsync(IReadOnlyDictionary<string, JsonElement>? args); |
| 129 | |
| 130 | /// <summary>Явная связь диапазона gutter-сообщений с кодом (ADR 0137).</summary> |
| 131 | Task<string> RelateIntercomMessageRangeToCodeAsync(IReadOnlyDictionary<string, JsonElement>? args); |
| 132 | /// <summary>Заменить текст ответа ассистента по message_id; в event log пишется message_edited.</summary> |
| 133 | Task<string> EditChatAssistantMessageAsync(string messageId, string newContent, string? reason = null); |
| 134 | /// <summary>Экспорт текущего чата в Markdown; при write_file — запись в .cascade-ide/chat-sessions/exports/.</summary> |
| 135 | Task<string> ExportChatReadableAsync(bool writeFile = false, string? fileName = null); |
| 136 | /// <summary>Передать фокус на эффективный контрол: по имени или на элемент под курсором. Вызов на UI-потоке.</summary> |
| 137 | Task<string> SetFocusAsync(string? controlName); |
| 138 | /// <summary>Подсветить эффективный контрол (рамка/оверлей), чтобы пользователь видел, где агент «находится». Без name — под курсором; с name — по имени. Вызов на UI-потоке.</summary> |
| 139 | Task<string> HighlightControlAsync(string? controlName); |
| 140 | /// <summary>Изменить размер панели: solution_explorer, chat — width (px); build_output, terminal — height (px). Вызов на UI-потоке.</summary> |
| 141 | Task<string> SetPanelSizeAsync(string panel, double? width, double? height); |
| 142 | /// <summary>Список языков/расширений редактора с подсветкой синтаксиса. JSON: массив { "extension", "language" }.</summary> |
| 143 | string GetSupportedEditorLanguages(); |
| 144 | |
| 145 | /// <summary>JSON: канонический снимок встроенной DAP-сессии (стек, переменные, останов) — тот же источник, что UI (ADR 0002).</summary> |
| 146 | Task<string> GetDebugSnapshotAsync(CancellationToken cancellationToken = default); |
| 147 | |
| 148 | /// <summary>Записать заметки агента. Формат и структура на усмотрение агента. Хранятся в каталоге решения в .cascade-ide/agent-notes (расширение не задано — агент может писать markdown, json, текст). Без открытого решения — ошибка.</summary> |
| 149 | Task<string> WriteAgentNotesAsync(string content, CancellationToken cancellationToken = default); |
| 150 | /// <summary>Прочитать заметки агента. Возвращает содержимое файла или пустую строку, если файла нет или решение не загружено.</summary> |
| 151 | Task<string> ReadAgentNotesAsync(CancellationToken cancellationToken = default); |
| 152 | /// <summary>Добавить блок в конец заметок агента без полной перезаписи. Возвращает "OK" или ошибку.</summary> |
| 153 | Task<string> AppendAgentNotesAsync(string content, CancellationToken cancellationToken = default); |
| 154 | /// <summary>Список ревизий заметок агента. JSON: массив { file, size_bytes, modified_utc }.</summary> |
| 155 | Task<string> ListAgentNotesRevisionsAsync(int? limit = null, CancellationToken cancellationToken = default); |
| 156 | /// <summary>Откатить заметки к ревизии (или к последней, если revisionFile null). Возвращает OK/NO_CHANGES с именем ревизии.</summary> |
| 157 | Task<string> RollbackAgentNotesAsync(string? revisionFile = null, CancellationToken cancellationToken = default); |
| 158 | /// <summary>Прочитать только горячий контекст (L0/L1) без архивного хвоста. JSON: active_scope, loaded_sections, content.</summary> |
| 159 | Task<string> ReadHotContextAsync(string? activeScope = null, CancellationToken cancellationToken = default); |
| 160 | /// <summary>Router-first контекст пакет по запросу. JSON: assembled_context, loaded_sections, scores.</summary> |
| 161 | Task<string> RouteContextAsync(string query, string? activeScope = null, int? maxSections = null, int? maxChars = null, CancellationToken cancellationToken = default); |
| 162 | /// <summary>Health-check памяти по hot-context бюджетам. JSON.</summary> |
| 163 | Task<string> MemoryHealthAsync(string? activeScope = null, CancellationToken cancellationToken = default); |
| 164 | /// <summary>Ужать hot-context (preview/apply). JSON.</summary> |
| 165 | Task<string> CompactHotContextAsync(bool apply = false, CancellationToken cancellationToken = default); |
| 166 | /// <summary>Поиск по архивной ревизии заметок (или последней), с контекстом строк. JSON.</summary> |
| 167 | Task<string> ExtractFromArchiveAsync(string query, string? revisionFile = null, int? headLimit = null, int? contextLines = null, CancellationToken cancellationToken = default); |
| 168 | |
| 169 | /// <summary>Вставить/обновить секцию в заметках агента. section_id — стабильный идентификатор, content — новое содержимое секции. Возвращает "OK" или ошибку.</summary> |
| 170 | Task<string> UpsertAgentNotesSectionAsync(string sectionId, string content, CancellationToken cancellationToken = default); |
| 171 | /// <summary>Поиск по заметкам агента (case-insensitive). Возвращает JSON: matches [{ line, text }].</summary> |
| 172 | Task<string> SearchAgentNotesAsync(string query, int? headLimit = null, CancellationToken cancellationToken = default); |
| 173 | /// <summary>Прочитать knowledge-файл. Корень: knowledge_path, knowledge_root_id или primary из TOML.</summary> |
| 174 | Task<string> ReadKnowledgeFileAsync(string filePath, int? offset = null, int? limit = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default); |
| 175 | /// <summary>Список knowledge-файлов. subdir — относительный подкаталог (например "work").</summary> |
| 176 | Task<string> ListKnowledgeFilesAsync(string? subdir = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default); |
| 177 | /// <summary>Записать knowledge-файл (полная замена). Запись только в primary; read-only roots отклоняются.</summary> |
| 178 | Task<string> WriteKnowledgeFileAsync(string filePath, string content, string? knowledgePath = null, bool saveRevision = true, string? knowledgeRootId = null, CancellationToken cancellationToken = default); |
| 179 | /// <summary>Добавить блок в конец knowledge-файла.</summary> |
| 180 | Task<string> AppendKnowledgeFileAsync(string filePath, string content, string? knowledgePath = null, bool saveRevision = true, string? knowledgeRootId = null, CancellationToken cancellationToken = default); |
| 181 | /// <summary>Вставить/обновить секцию в knowledge-файле по section_id.</summary> |
| 182 | Task<string> UpsertKnowledgeSectionAsync(string filePath, string sectionId, string content, string? knowledgePath = null, bool saveRevision = true, string? knowledgeRootId = null, CancellationToken cancellationToken = default); |
| 183 | /// <summary>Удалить knowledge-файл.</summary> |
| 184 | Task<string> DeleteKnowledgeFileAsync(string filePath, string? knowledgePath = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default); |
| 185 | /// <summary>Удалить секцию из knowledge-файла.</summary> |
| 186 | Task<string> DeleteKnowledgeSectionAsync(string filePath, string sectionId, string? knowledgePath = null, string? knowledgeRootId = null, CancellationToken cancellationToken = default); |
| 187 | |
| 188 | /// <summary>Статус гибридного индекса (паритет MCP <c>codebase_index_status</c>). JSON.</summary> |
| 189 | Task<string> CodebaseIndexStatusAsync(string? workspacePath, string? solutionPath, CancellationToken cancellationToken = default); |
| 190 | /// <summary>Гибридный поиск (паритет <c>codebase_index_search</c>). JSON.</summary> |
| 191 | Task<string> CodebaseIndexSearchAsync(string? workspacePath, string? solutionPath, string query, int topN, string? pathPrefix, IReadOnlyList<string>? excludePathPrefixes, IReadOnlyList<string>? extensions, bool semantic, double alpha, double beta, int vecTopK, CancellationToken cancellationToken = default); |
| 192 | /// <summary>Explain hit (паритет <c>codebase_index_explain</c>). JSON.</summary> |
| 193 | Task<string> CodebaseIndexExplainAsync(string? workspacePath, string? solutionPath, long hitId, CancellationToken cancellationToken = default); |
| 194 | /// <summary>Reindex (паритет <c>codebase_index_reindex</c>; после успеха публикуется снимок в DataBus для MFD). JSON.</summary> |
| 195 | Task<string> CodebaseIndexReindexAsync(string? workspacePath, string? solutionPath, bool fullRebuild, CancellationToken cancellationToken = default); |
| 196 | |
| 197 | /// <summary>Verification ladder (ADR 0148). args: policy?, sandbox_profile?, solution_path?; JSON.</summary> |
| 198 | Task<string> IdeAgentVerifyAsync(string? policy, string? sandboxProfile, string? solutionPath, CancellationToken cancellationToken = default); |
| 199 | |
| 200 | Task<string> IdeAgentVerifyBatchAsync( |
| 201 | string? policy, |
| 202 | string? sandboxProfile, |
| 203 | string? solutionPath, |
| 204 | bool useWorktree, |
| 205 | CancellationToken cancellationToken = default); |
| 206 | |
| 207 | Task<string> IdeAgentCancelAsync(CancellationToken cancellationToken = default); |
| 208 | |
| 209 | Task<string> IdeAgentStatusAsync(CancellationToken cancellationToken = default); |
| 210 | |
| 211 | Task<string> IdeAgentLastAsync(CancellationToken cancellationToken = default); |
| 212 | |
| 213 | /// <summary>Prepare sandbox substrate. args: profile?, workspace_root?; JSON.</summary> |
| 214 | Task<string> IdeAgentSandboxPrepareAsync(string? profile, string? workspaceRoot, CancellationToken cancellationToken = default); |
| 215 | } |
| 216 | |