Forge
csharpdeeb25a2
1#nullable enable
2using CascadeIDE.Services;
3
4namespace CascadeIDE.Features.Chat;
5
6/// <summary>Локальные действия Intercom из slash (<c>kind=intercom</c>).</summary>
7public static class ChatSlashIntercomActions
8{
9 public static bool TryExecute(
10 string slashPath,
11 string? argsTail,
12 Guid selectedThreadId,
13 Action<Guid> selectThread,
14 Action<bool> setOverviewMode,
15 ChatSurfaceSnapshot snapshot,
16 out ChatSlashIntercomResult result,
17 Action<TopicPickerPresentation>? setTopicPicker = null,
18 Func<string, TopicCreateResult>? createTopicWithTitle = null,
19 Func<Guid, string, TopicRenameResult>? renameTopicWithTitle = null,
20 Func<string, string?, ChatSlashIntercomResult>? tryAttachSlash = null,
21 Func<int, int, string>? selectMessageByOrdinalRangeInDetailLane = null,
22 Func<IReadOnlyList<ParametricIntRange>, string>? selectMessagesByOrdinalRangesInDetailLane = null,
23 Func<string>? clearMessageSelectionInDetailLane = null,
24 Func<string?, string>? findMessagesForCodeRef = null,
25 Func<string?, string>? relateMessageRangeToCodeRef = null,
26 Func<string>? listMessageAnchors = null,
27 Func<string?, string>? peekAnchorById = null,
28 Func<string, string?, CancellationToken, Task<ChatSlashIntercomResult>>? runIntercomAdmin = null)
29 {
30 result = ChatSlashIntercomResult.Fail("");
31 if (!IntentSlashCatalog.TryGetRoute(slashPath, out var route)
32 || route.ExecutionKind != ChatSlashCommandExecutionKind.LocalIntercom)
33 {
34 return false;
35 }
36
37 if (string.IsNullOrWhiteSpace(route.IntercomHandlerId)
38 || !ChatSlashIntercomHandlers.TryExecute(
39 route.IntercomHandlerId,
40 new ChatSlashIntercomHandlers.Context(
41 argsTail,
42 selectedThreadId,
43 selectThread,
44 setOverviewMode,
45 snapshot,
46 setTopicPicker,
47 createTopicWithTitle,
48 renameTopicWithTitle,
49 tryAttachSlash,
50 selectMessageByOrdinalRangeInDetailLane,
51 selectMessagesByOrdinalRangesInDetailLane,
52 clearMessageSelectionInDetailLane,
53 findMessagesForCodeRef,
54 relateMessageRangeToCodeRef,
55 listMessageAnchors,
56 peekAnchorById,
57 runIntercomAdmin),
58 out result))
59 {
60 result = ChatSlashIntercomResult.Fail($"Неизвестное действие: {slashPath}");
61 return true;
62 }
63
64 return true;
65 }
66
67 internal static ChatSlashIntercomResult CreateTopic(
68 string? title,
69 Func<string, TopicCreateResult>? createTopicWithTitle)
70 {
71 if (createTopicWithTitle is null)
72 return ChatSlashIntercomResult.Fail("Создание тем недоступно.");
73
74 var create = createTopicWithTitle(title ?? "");
75 return create.Success
76 ? ChatSlashIntercomResult.Ok(create.Message)
77 : ChatSlashIntercomResult.Fail(create.Message);
78 }
79
80 internal static ChatSlashIntercomResult RenameTopic(
81 Guid selectedThreadId,
82 string? title,
83 Func<Guid, string, TopicRenameResult>? renameTopicWithTitle)
84 {
85 if (renameTopicWithTitle is null)
86 return ChatSlashIntercomResult.Fail("Переименование тем недоступно.");
87
88 var rename = renameTopicWithTitle(selectedThreadId, title ?? "");
89 return rename.Success
90 ? ChatSlashIntercomResult.Ok(rename.Message)
91 : ChatSlashIntercomResult.Fail(rename.Message);
92 }
93
94 internal static ChatSlashIntercomResult ShowTopicPicker(
95 TopicPickerPresentation mode,
96 Action<TopicPickerPresentation>? setTopicPicker,
97 Action<bool> setOverviewMode,
98 ChatSurfaceSnapshot snapshot)
99 {
100 if (setTopicPicker is null)
101 return ChatSlashIntercomResult.Fail("Интерактивный список тем недоступен. Для агента: /intercom topic list text.");
102
103 if (snapshot.State.Threads.Count == 0)
104 return ChatSlashIntercomResult.Fail(ChatThreadPresentation.EmptyTopicsHint);
105
106 setOverviewMode(false);
107 setTopicPicker(mode);
108 var text = mode == TopicPickerPresentation.Tree
109 ? "Дерево тем в ленте — клик по строке, чтобы открыть."
110 : "Список тем в ленте — клик по строке, чтобы открыть.";
111 return ChatSlashIntercomResult.Ok(text);
112 }
113
114 public static ChatSlashIntercomResult OpenTopic(
115 string? query,
116 Guid selectedThreadId,
117 Action<Guid> selectThread,
118 Action<bool> setOverviewMode,
119 ChatSurfaceSnapshot snapshot)
120 {
121 if (!ChatSlashTopicResolver.TryResolve(query, snapshot, selectedThreadId, out var thread, out var error))
122 return ChatSlashIntercomResult.Fail(error ?? "Не удалось открыть тему.");
123
124 selectThread(thread.ThreadId);
125 setOverviewMode(false);
126 return ChatSlashIntercomResult.Ok($"Открыта тема: {thread.Title}");
127 }
128
129 public static ChatSlashIntercomResult OpenTopicCards(Action<bool> setOverviewMode, ChatSurfaceSnapshot snapshot)
130 {
131 setOverviewMode(true);
132 if (!snapshot.ProductSpine.HasContent)
133 return ChatSlashIntercomResult.Ok(
134 "Картотека тем. Spine пуст — /intercom spine set <текст>. Открыть тему: /intercom topic open <имя>.");
135
136 var title = ChatProductSpinePresentation.ResolveLineTitle(snapshot.ProductSpine);
137 return ChatSlashIntercomResult.Ok(
138 $"Картотека тем: spine «{title}». /intercom topic open <имя> — открыть тему.");
139 }
140}
141
View only · write via MCP/CIDE