@yancyyu/openhermit 1.6.37 → 1.6.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-DYT3bwFR.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-Dbt_EU-e.js → _basePickBy-B7kSYPxr.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-DWo68sXI.js → _baseUniq-CnjxqwAk.js} +1 -1
  5. package/dist-renderer/assets/{arc-DXH1iZQK.js → arc-CLeZuINP.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-cjffS2Qr.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-BKdZF02Y.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-CN27pqaI.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
  9. package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-CXPCI7g_.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-BGAXQZRC.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-TPDaA_KQ.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-D1ADe_tq.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-Beimtg3a.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-OjNBu854.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-DinqvbH8.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-BfFtlPSZ.js → chunk-TZMSLE5B-Ds1hInvp.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-CBYCBVRl.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CBYCBVRl.js +1 -0
  20. package/dist-renderer/assets/clone-DcMF6Psb.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-D9z9Dgt7.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-n1g-DhEE.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-BvxFq-BE.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-wVnJuwza.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-B707WJQw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-C-_1dGHs.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CMTSi3H6.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DZ0bNrAA.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DNVfGooQ.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
  30. package/dist-renderer/assets/{graph-865j_tM_.js → graph-CsoEwUhS.js} +1 -1
  31. package/dist-renderer/assets/{index-C_F9N5x-.js → index-BWPWmJNo.js} +1 -1
  32. package/dist-renderer/assets/{index-LwDIsXJN.js → index-Bu2R-Se7.js} +586 -740
  33. package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
  34. package/dist-renderer/assets/{index-DuUaf8at.js → index-D-3KgskL.js} +1 -1
  35. package/dist-renderer/assets/{index-BTx1nc4T.js → index-DGEBzLNT.js} +1 -1
  36. package/dist-renderer/assets/{index-2EW-eu3q.js → index-NhHNs2Oo.js} +1 -1
  37. package/dist-renderer/assets/{index-4dEMStJj.js → index-h17WuEyf.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-CyqtElLq.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BvjQ0Hm0.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CJJ-k0zT.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
  41. package/dist-renderer/assets/{layout-CnV6rQAG.js → layout-C3XWrpwo.js} +1 -1
  42. package/dist-renderer/assets/{linear-Cw3UQgyX.js → linear-OEEcn8KN.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-C5tDaGSK.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-CiIpPsau.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-C3gtowNj.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CXBTrAnU.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-wziX77xG.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-sYqopcrj.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
  49. package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
  50. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bl1-0_Cp.js → stateDiagram-FKZM4ZOC-B2FY5qqi.js} +1 -1
  51. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DcoMiR8H.js +1 -0
  52. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CIRjJUBo.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
  53. package/dist-renderer/assets/{treemap-GDKQZRPO-CVPuNe1n.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
  54. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-3nT9yHwp.js → xychartDiagram-PRI3JC2R-J6VVJzRq.js} +1 -1
  55. package/dist-renderer/index.html +20 -53
  56. package/package.json +25 -18
  57. package/src/main/ipc/extensions.ts +30 -50
  58. package/src/main/server.ts +890 -247
  59. package/src/main/services/extensions/ExtensionFacadeService.ts +4 -56
  60. package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
  61. package/src/main/services/extensions/library/McpLibraryService.ts +243 -0
  62. package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
  63. package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
  64. package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
  65. package/src/main/services/session-intelligence/UsageTelemetryService.ts +14 -1
  66. package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
  67. package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
  68. package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
  69. package/src/main/services/teams-mvp/TaskDispatchService.ts +32 -8
  70. package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
  71. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
  72. package/src/main/utils/teamProjectResolution.ts +15 -0
  73. package/src/renderer/App.tsx +8 -4
  74. package/src/renderer/api/httpClient.ts +174 -38
  75. package/src/renderer/api/providers.ts +23 -2
  76. package/src/renderer/assets/participant-avatars/01.svg +3 -0
  77. package/src/renderer/assets/participant-avatars/02.svg +3 -0
  78. package/src/renderer/assets/participant-avatars/03.svg +3 -0
  79. package/src/renderer/assets/participant-avatars/04.svg +3 -0
  80. package/src/renderer/assets/participant-avatars/05.svg +3 -0
  81. package/src/renderer/assets/participant-avatars/06.svg +3 -0
  82. package/src/renderer/assets/participant-avatars/07.svg +3 -0
  83. package/src/renderer/assets/participant-avatars/08.svg +3 -0
  84. package/src/renderer/assets/participant-avatars/09.svg +3 -0
  85. package/src/renderer/assets/participant-avatars/10.svg +3 -0
  86. package/src/renderer/assets/participant-avatars/11.svg +3 -0
  87. package/src/renderer/assets/participant-avatars/12.svg +3 -0
  88. package/src/renderer/assets/participant-avatars/13.svg +3 -0
  89. package/src/renderer/components/common/TerminalPane.tsx +213 -0
  90. package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
  91. package/src/renderer/components/extensions/ExtensionStoreView.tsx +12 -221
  92. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  93. package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
  94. package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
  95. package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
  96. package/src/renderer/components/extensions/plugins/PluginCard.tsx +10 -2
  97. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +40 -22
  98. package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
  99. package/src/renderer/components/layout/PaneContent.tsx +8 -1
  100. package/src/renderer/components/layout/Sidebar.tsx +11 -54
  101. package/src/renderer/components/layout/SortableTab.tsx +20 -31
  102. package/src/renderer/components/layout/TabBar.tsx +1 -1
  103. package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
  104. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
  105. package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
  106. package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
  107. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
  108. package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
  109. package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
  110. package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
  111. package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
  112. package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
  113. package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
  114. package/src/renderer/components/schedules/calendar/index.ts +2 -0
  115. package/src/renderer/components/schedules/calendar/types.ts +44 -0
  116. package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
  117. package/src/renderer/components/settings/SettingsView.tsx +30 -35
  118. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
  119. package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
  120. package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
  121. package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
  122. package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
  123. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  124. package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
  125. package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
  126. package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
  127. package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
  128. package/src/renderer/components/settings/sections/index.ts +0 -1
  129. package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
  130. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
  131. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
  132. package/src/renderer/components/splash/splashScene.ts +121 -929
  133. package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
  134. package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
  135. package/src/renderer/components/tasks/TasksView.tsx +112 -134
  136. package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
  137. package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
  138. package/src/renderer/components/team/TeamDetailView.tsx +325 -114
  139. package/src/renderer/components/team/TeamListView.tsx +108 -123
  140. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
  141. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
  142. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
  143. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
  144. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
  145. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
  146. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
  147. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
  148. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
  149. package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
  150. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
  151. package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
  152. package/src/renderer/components/team/members/MemberCard.tsx +14 -47
  153. package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
  154. package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
  155. package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
  156. package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
  157. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
  158. package/src/renderer/components/team/tools/AddMcpInline.tsx +57 -0
  159. package/src/renderer/components/team/tools/AddSkillInline.tsx +61 -0
  160. package/src/renderer/components/team/tools/McpChip.tsx +45 -0
  161. package/src/renderer/components/team/tools/SkillChip.tsx +35 -0
  162. package/src/renderer/components/team/tools/ToolsSection.tsx +556 -0
  163. package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
  164. package/src/renderer/index.css +39 -22
  165. package/src/renderer/index.html +17 -50
  166. package/src/renderer/store/index.ts +2 -1
  167. package/src/renderer/store/slices/scheduleSlice.ts +1 -1
  168. package/src/renderer/store/slices/teamSlice.ts +45 -168
  169. package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
  170. package/src/renderer/utils/memberHelpers.ts +5 -17
  171. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
  172. package/src/renderer/utils/providerSlashCommands.ts +0 -5
  173. package/src/renderer/utils/scheduleFormatters.ts +3 -1
  174. package/src/renderer/utils/teamMessageFiltering.ts +14 -1
  175. package/src/renderer/utils/teamModelAvailability.ts +18 -2
  176. package/src/shared/types/api.ts +121 -2
  177. package/src/shared/types/ccConnect.ts +2 -0
  178. package/src/shared/types/extensions/api.ts +9 -0
  179. package/src/shared/types/extensions/index.ts +4 -0
  180. package/src/shared/types/extensions/mcp.ts +41 -0
  181. package/src/shared/types/index.ts +3 -0
  182. package/src/shared/types/systemManager.ts +49 -0
  183. package/src/shared/types/team.ts +29 -0
  184. package/src/shared/types/terminal.ts +4 -2
  185. package/src/shared/utils/extensionNormalizers.ts +29 -0
  186. package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
  187. package/dist-renderer/assets/ProjectEditorOverlay-Va_Vz-zz.js +0 -52
  188. package/dist-renderer/assets/channel-5dJIx68e.js +0 -1
  189. package/dist-renderer/assets/classDiagram-2ON5EDUG-BMGXWJ2d.js +0 -1
  190. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BMGXWJ2d.js +0 -1
  191. package/dist-renderer/assets/clone-D7FWfGY9.js +0 -1
  192. package/dist-renderer/assets/index-B2z_IyRH.css +0 -1
  193. package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
  194. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DOYYvDbi.js +0 -1
  195. package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +0 -190
  196. package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +0 -150
  197. package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +0 -381
  198. package/src/main/services/extensions/install/McpInstallService.ts +0 -407
  199. package/src/main/services/extensions/state/McpInstallationStateService.ts +0 -42
  200. package/src/renderer/components/extensions/mcp/McpServerCard.tsx +0 -314
  201. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +0 -765
  202. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +0 -593
  203. package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +0 -372
  204. package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +0 -343
  205. package/src/renderer/components/extensions/skills/SkillsPanel.tsx +0 -659
@@ -0,0 +1,556 @@
1
+ /*
2
+ * ToolsSection — per-worker capabilities management.
3
+ * MCP uses a cc-switch style model: global templates create project instances.
4
+ */
5
+
6
+ import { useCallback, useEffect, useMemo, useState } from 'react';
7
+
8
+ import { api } from '@renderer/api';
9
+ import { confirm } from '@renderer/components/common/ConfirmDialog';
10
+ import { Button } from '@renderer/components/ui/button';
11
+ import { Input } from '@renderer/components/ui/input';
12
+ import { McpLibraryEnableDialog } from '@renderer/components/extensions/mcp/McpLibraryEnableDialog';
13
+ import { useStore } from '@renderer/store';
14
+ import { Plus, RefreshCw, Wrench } from 'lucide-react';
15
+
16
+ import { AddMcpInline } from './AddMcpInline';
17
+ import { AddSkillInline } from './AddSkillInline';
18
+ import { McpChip } from './McpChip';
19
+ import { SkillChip } from './SkillChip';
20
+
21
+ import type { McpLibraryEntry, SkillCatalogItem } from '@shared/types/extensions';
22
+
23
+ interface ToolsSectionProps {
24
+ teamName: string;
25
+ projectPath: string | null;
26
+ harnessType?: string;
27
+ }
28
+
29
+ function summarizeMcp(entry: McpLibraryEntry): string {
30
+ if (entry.installSpec.type === 'stdio') {
31
+ return `stdio · ${entry.installSpec.npmPackage}${entry.installSpec.npmVersion ? `@${entry.installSpec.npmVersion}` : ''}`;
32
+ }
33
+ return `${entry.installSpec.transportType} · ${entry.installSpec.url}`;
34
+ }
35
+
36
+ export const ToolsSection = ({
37
+ teamName,
38
+ projectPath,
39
+ harnessType,
40
+ }: ToolsSectionProps): React.JSX.Element => {
41
+ // ── Store selectors ──
42
+ const mcpByPath = useStore((s) => s.mcpInstalledServersByProjectPath);
43
+ const diagnosticsByPath = useStore((s) => s.mcpDiagnosticsByProjectPath);
44
+ const skillsByPath = useStore((s) => s.skillsProjectCatalogByProjectPath);
45
+ const mcpFetchInstalled = useStore((s) => s.mcpFetchInstalled);
46
+ const runMcpDiagnostics = useStore((s) => s.runMcpDiagnostics);
47
+ const fetchSkillsCatalog = useStore((s) => s.fetchSkillsCatalog);
48
+ const fetchSkillDetail = useStore((s) => s.fetchSkillDetail);
49
+ const applySkillImport = useStore((s) => s.applySkillImport);
50
+ const previewSkillImport = useStore((s) => s.previewSkillImport);
51
+ const uninstallMcpServer = useStore((s) => s.uninstallMcpServer);
52
+ const deleteSkill = useStore((s) => s.deleteSkill);
53
+ const skillDetailsById = useStore((s) => s.skillsDetailsById);
54
+
55
+ // ── Local state ──
56
+ const [addingMcp, setAddingMcp] = useState(false);
57
+ const [addingSkill, setAddingSkill] = useState(false);
58
+ const [mcpLibrary, setMcpLibrary] = useState<McpLibraryEntry[]>([]);
59
+ const [userSkills, setUserSkills] = useState<SkillCatalogItem[]>([]);
60
+ const [libraryLoading, setLibraryLoading] = useState(false);
61
+ const [libraryError, setLibraryError] = useState<string | null>(null);
62
+ const [selectedMcpTemplate, setSelectedMcpTemplate] = useState<McpLibraryEntry | null>(null);
63
+ const [importingMcpTemplates, setImportingMcpTemplates] = useState(false);
64
+ const [enablingSkillId, setEnablingSkillId] = useState<string | null>(null);
65
+ const [mcpSearch, setMcpSearch] = useState('');
66
+ const [skillSearch, setSkillSearch] = useState('');
67
+ const [showAllMcp, setShowAllMcp] = useState(false);
68
+ const [showAllSkills, setShowAllSkills] = useState(false);
69
+ const [applyNotice, setApplyNotice] = useState<string | null>(null);
70
+
71
+ // ── Derived data ──
72
+ const mcpServers = useMemo(
73
+ () => (projectPath ? (mcpByPath[projectPath] ?? []) : []),
74
+ [mcpByPath, projectPath]
75
+ );
76
+ // Diagnostics are stored keyed by `getMcpDiagnosticKey(name, scope)`, but the
77
+ // scope is not always present (e.g. the text-mode CLI parser omits it), so the
78
+ // scoped key can't be reconstructed reliably from an installed entry. Index by
79
+ // server name instead — chip rows already assume names are unique per project.
80
+ const diagnosticByName = useMemo(() => {
81
+ const record = projectPath ? (diagnosticsByPath[projectPath] ?? {}) : {};
82
+ return Object.fromEntries(Object.values(record).map((d) => [d.name, d] as const));
83
+ }, [diagnosticsByPath, projectPath]);
84
+
85
+ // Deduplicate skills by name (same skill may appear from multiple roots)
86
+ const skills = useMemo(() => {
87
+ const raw = projectPath ? (skillsByPath[projectPath] ?? []) : [];
88
+ const seen = new Set<string>();
89
+ return raw.filter((skill) => {
90
+ const key = skill.name.toLowerCase();
91
+ if (seen.has(key)) return false;
92
+ seen.add(key);
93
+ return true;
94
+ });
95
+ }, [skillsByPath, projectPath]);
96
+
97
+ const installedMcpNames = useMemo(
98
+ () => new Set(mcpServers.map((entry) => entry.name.toLowerCase())),
99
+ [mcpServers]
100
+ );
101
+ const installedSkillNames = useMemo(
102
+ () => new Set(skills.map((skill) => skill.name.toLowerCase())),
103
+ [skills]
104
+ );
105
+ const availableMcpLibrary = useMemo(() => {
106
+ const query = mcpSearch.trim().toLowerCase();
107
+ return mcpLibrary.filter((entry) => {
108
+ if (!query) return true;
109
+ return [entry.name, entry.description ?? '', summarizeMcp(entry)]
110
+ .join(' ')
111
+ .toLowerCase()
112
+ .includes(query);
113
+ });
114
+ }, [mcpLibrary, mcpSearch]);
115
+ const availableUserSkills = useMemo(() => {
116
+ const query = skillSearch.trim().toLowerCase();
117
+ return userSkills.filter((skill) => {
118
+ if (installedSkillNames.has(skill.name.toLowerCase())) return false;
119
+ if (!query) return true;
120
+ return [skill.name, skill.description ?? '', skill.folderName]
121
+ .join(' ')
122
+ .toLowerCase()
123
+ .includes(query);
124
+ });
125
+ }, [installedSkillNames, skillSearch, userSkills]);
126
+ const visibleMcpLibrary = showAllMcp ? availableMcpLibrary : availableMcpLibrary.slice(0, 6);
127
+ const visibleUserSkills = showAllSkills ? availableUserSkills : availableUserSkills.slice(0, 6);
128
+ const refreshLibraries = useCallback(async (): Promise<void> => {
129
+ setLibraryLoading(true);
130
+ setLibraryError(null);
131
+ try {
132
+ const [libraryEntries, globalSkills] = await Promise.all([
133
+ api.mcpRegistry?.libraryList?.() ?? Promise.resolve([]),
134
+ api.skills?.list?.() ?? Promise.resolve([]),
135
+ ]);
136
+ setMcpLibrary(libraryEntries);
137
+ setUserSkills(globalSkills);
138
+ } catch (err) {
139
+ setLibraryError(err instanceof Error ? err.message : '加载全局能力库失败');
140
+ } finally {
141
+ setLibraryLoading(false);
142
+ }
143
+ }, []);
144
+
145
+ // ── Fetch data on mount ──
146
+ useEffect(() => {
147
+ if (projectPath) {
148
+ mcpFetchInstalled(projectPath).catch(() => {});
149
+ runMcpDiagnostics(projectPath).catch(() => {});
150
+ fetchSkillsCatalog(projectPath).catch(() => {});
151
+ }
152
+ void refreshLibraries();
153
+ }, [projectPath, mcpFetchInstalled, runMcpDiagnostics, fetchSkillsCatalog, refreshLibraries]);
154
+
155
+ // ── Handlers ──
156
+ const handleRemoveMcp = useCallback(
157
+ (entry: { name: string; scope: string }) => {
158
+ void (async () => {
159
+ const confirmed = await confirm({
160
+ title: '移除 MCP 项目实例',
161
+ message: `确认从当前项目移除 MCP 实例「${entry.name}」?全局模板不会被删除。`,
162
+ confirmLabel: '移除实例',
163
+ cancelLabel: '取消',
164
+ variant: 'danger',
165
+ });
166
+ if (!confirmed) return;
167
+ uninstallMcpServer('', entry.name, entry.scope, projectPath ?? undefined)
168
+ .then(() => {
169
+ setApplyNotice('MCP 实例已移除;正在运行的数字员工需要重启后才会卸载该能力。');
170
+ })
171
+ .catch(() => {});
172
+ })();
173
+ },
174
+ [uninstallMcpServer, projectPath]
175
+ );
176
+
177
+ const handleRemoveSkill = useCallback(
178
+ (skill: { id: string; name: string }) => {
179
+ void (async () => {
180
+ const confirmed = await confirm({
181
+ title: '禁用 Skill',
182
+ message: `确认从当前团队禁用 Skill「${skill.name}」?全局 Skill 仍会保留。`,
183
+ confirmLabel: '禁用',
184
+ cancelLabel: '取消',
185
+ variant: 'danger',
186
+ });
187
+ if (!confirmed) return;
188
+ deleteSkill({ skillId: skill.id, projectPath: projectPath ?? undefined })
189
+ .then(() => {
190
+ setApplyNotice('Skill 已禁用;正在运行的数字员工需要重启后才会卸载该能力。');
191
+ })
192
+ .catch(() => {});
193
+ })();
194
+ },
195
+ [deleteSkill, projectPath]
196
+ );
197
+
198
+ const handleMcpTemplateAdded = useCallback(
199
+ (entry: McpLibraryEntry) => {
200
+ setAddingMcp(false);
201
+ setMcpLibrary((prev) => {
202
+ const next = prev.filter((item) => item.id !== entry.id);
203
+ return [entry, ...next];
204
+ });
205
+ setSelectedMcpTemplate(entry);
206
+ void refreshLibraries();
207
+ },
208
+ [refreshLibraries]
209
+ );
210
+
211
+ const handleMcpInstanceAdded = useCallback(() => {
212
+ setSelectedMcpTemplate(null);
213
+ if (projectPath) {
214
+ mcpFetchInstalled(projectPath).catch(() => {});
215
+ runMcpDiagnostics(projectPath).catch(() => {});
216
+ }
217
+ void refreshLibraries();
218
+ }, [projectPath, mcpFetchInstalled, runMcpDiagnostics, refreshLibraries]);
219
+
220
+ const handleImportMcpTemplates = useCallback(() => {
221
+ void (async () => {
222
+ if (!api.mcpRegistry?.libraryImport) {
223
+ setLibraryError('MCP 模板导入 API 不可用');
224
+ return;
225
+ }
226
+ setImportingMcpTemplates(true);
227
+ setLibraryError(null);
228
+ try {
229
+ const result = await api.mcpRegistry.libraryImport({
230
+ projectPath: projectPath ?? undefined,
231
+ });
232
+ await refreshLibraries();
233
+ setApplyNotice(
234
+ `已从现有 MCP 配置导入模板:新增 ${result.imported.length} 个,跳过 ${result.skipped.length} 个。`
235
+ );
236
+ } catch (err) {
237
+ setLibraryError(err instanceof Error ? err.message : '导入现有 MCP 配置失败');
238
+ } finally {
239
+ setImportingMcpTemplates(false);
240
+ }
241
+ })();
242
+ }, [projectPath, refreshLibraries]);
243
+
244
+ const handleSkillAdded = useCallback(() => {
245
+ setAddingSkill(false);
246
+ if (projectPath) {
247
+ fetchSkillsCatalog(projectPath).catch(() => {});
248
+ }
249
+ void refreshLibraries();
250
+ }, [projectPath, fetchSkillsCatalog, refreshLibraries]);
251
+
252
+ const enableSkillFromLibrary = useCallback(
253
+ async (skill: SkillCatalogItem): Promise<void> => {
254
+ if (!projectPath) return;
255
+ setEnablingSkillId(skill.id);
256
+ try {
257
+ await fetchSkillDetail(skill.id);
258
+ const detail =
259
+ useStore.getState().skillsDetailsById[skill.id] ?? skillDetailsById[skill.id];
260
+ const sourceDir = detail?.item.skillDir ?? skill.skillDir;
261
+ const preview = await previewSkillImport({
262
+ sourceDir,
263
+ scope: 'project',
264
+ rootKind: skill.rootKind,
265
+ projectPath,
266
+ folderName: skill.folderName,
267
+ });
268
+ await applySkillImport({
269
+ sourceDir,
270
+ scope: 'project',
271
+ rootKind: skill.rootKind,
272
+ projectPath,
273
+ folderName: skill.folderName,
274
+ reviewPlanId: preview.planId,
275
+ });
276
+ await fetchSkillsCatalog(projectPath);
277
+ setApplyNotice('Skill 已启用;正在运行的数字员工需要重启后才会加载新能力。');
278
+ } finally {
279
+ setEnablingSkillId(null);
280
+ }
281
+ },
282
+ [
283
+ applySkillImport,
284
+ fetchSkillDetail,
285
+ fetchSkillsCatalog,
286
+ previewSkillImport,
287
+ projectPath,
288
+ skillDetailsById,
289
+ ]
290
+ );
291
+
292
+ if (!projectPath) {
293
+ return (
294
+ <div className="flex items-center gap-2 px-1 py-2 text-xs text-[var(--color-text-muted)]">
295
+ <Wrench size={12} />
296
+ <span>需要关联项目目录才能管理能力</span>
297
+ </div>
298
+ );
299
+ }
300
+
301
+ return (
302
+ <div className="flex flex-col gap-4 px-1 py-2">
303
+ {libraryError ? (
304
+ <div className="rounded-md border border-amber-500/30 bg-amber-500/10 px-2 py-1.5 text-xs text-amber-300">
305
+ {libraryError}
306
+ </div>
307
+ ) : null}
308
+
309
+ {applyNotice ? (
310
+ <div className="rounded-md border border-blue-500/30 bg-blue-500/10 px-2 py-1.5 text-xs text-blue-300">
311
+ {applyNotice}
312
+ </div>
313
+ ) : null}
314
+
315
+ <div className="flex items-center justify-between gap-2">
316
+ <div className="text-[11px] text-[var(--color-text-muted)]">
317
+ 当前项目管理 MCP 实例;全局库只提供可复用模板。先导入/新建模板,再添加为项目实例。
318
+ </div>
319
+ <div className="flex items-center gap-1">
320
+ <Button
321
+ variant="ghost"
322
+ size="sm"
323
+ className="h-6 px-2 text-[11px]"
324
+ disabled={importingMcpTemplates}
325
+ onClick={handleImportMcpTemplates}
326
+ >
327
+ {importingMcpTemplates ? '导入中...' : '导入现有 MCP'}
328
+ </Button>
329
+ <Button
330
+ variant="ghost"
331
+ size="sm"
332
+ className="h-6 px-2 text-[11px]"
333
+ onClick={() => void refreshLibraries()}
334
+ >
335
+ <RefreshCw className={libraryLoading ? 'mr-1 size-3 animate-spin' : 'mr-1 size-3'} />
336
+ 刷新
337
+ </Button>
338
+ </div>
339
+ </div>
340
+
341
+ {/* MCP capabilities */}
342
+ <div className="flex flex-col gap-2 rounded-lg border border-[var(--color-border-subtle)] bg-white/[0.015] p-2">
343
+ <div className="flex items-center justify-between gap-2">
344
+ <div className="flex items-center gap-1.5 text-[11px] font-medium text-[var(--color-text-muted)]">
345
+ <span>MCP 实例</span>
346
+ <span className="text-[10px]">
347
+ 已配置实例 {mcpServers.length} · 模板 {mcpLibrary.length}
348
+ </span>
349
+ </div>
350
+ <Button
351
+ variant="ghost"
352
+ size="sm"
353
+ className="h-6 px-2 text-[11px]"
354
+ onClick={() => setAddingMcp(true)}
355
+ >
356
+ <Plus size={10} className="mr-1" />
357
+ 新建模板
358
+ </Button>
359
+ </div>
360
+
361
+ <div className="flex flex-wrap items-center gap-1.5">
362
+ {mcpServers.map((entry) => (
363
+ <McpChip
364
+ key={entry.name}
365
+ entry={entry}
366
+ diagnostic={diagnosticByName[entry.name]}
367
+ onRemove={handleRemoveMcp}
368
+ />
369
+ ))}
370
+ {mcpServers.length === 0 ? (
371
+ <span className="text-xs text-[var(--color-text-muted)]">
372
+ 当前项目还没有配置 MCP 实例。
373
+ </span>
374
+ ) : null}
375
+ </div>
376
+
377
+ {addingMcp ? (
378
+ <AddMcpInline onAdded={handleMcpTemplateAdded} onCancel={() => setAddingMcp(false)} />
379
+ ) : null}
380
+
381
+ <div className="space-y-1.5">
382
+ <div className="text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">
383
+ 从模板添加
384
+ </div>
385
+ <Input
386
+ value={mcpSearch}
387
+ onChange={(event) => setMcpSearch(event.target.value)}
388
+ placeholder="搜索 MCP 模板..."
389
+ className="h-7 text-xs"
390
+ />
391
+ {availableMcpLibrary.length > 0 ? (
392
+ <div className="flex flex-col gap-1.5">
393
+ {visibleMcpLibrary.map((entry) => (
394
+ <div
395
+ key={entry.id}
396
+ className="flex items-center justify-between gap-2 rounded-md border border-[var(--color-border-subtle)] px-2 py-1.5"
397
+ >
398
+ <div className="min-w-0">
399
+ <div className="truncate text-xs text-[var(--color-text)]">{entry.name}</div>
400
+ <div className="truncate text-[10px] text-[var(--color-text-muted)]">
401
+ {summarizeMcp(entry)}
402
+ </div>
403
+ </div>
404
+ <Button
405
+ variant="outline"
406
+ size="sm"
407
+ className="h-6 px-2 text-[11px]"
408
+ onClick={() => setSelectedMcpTemplate(entry)}
409
+ >
410
+ 从模板添加
411
+ </Button>
412
+ </div>
413
+ ))}
414
+ {availableMcpLibrary.length > 6 ? (
415
+ <Button
416
+ variant="ghost"
417
+ size="sm"
418
+ className="h-6 justify-start px-2 text-[11px]"
419
+ onClick={() => setShowAllMcp((value) => !value)}
420
+ >
421
+ {showAllMcp ? '收起' : `显示全部 ${availableMcpLibrary.length} 个模板`}
422
+ </Button>
423
+ ) : null}
424
+ </div>
425
+ ) : (
426
+ <div className="flex flex-col gap-2 rounded-md border border-dashed border-[var(--color-border)] px-2 py-2 text-xs text-[var(--color-text-muted)]">
427
+ <span>全局 MCP 模板库暂无可添加项。可以从现有 MCP 配置导入,或新建模板。</span>
428
+ <div className="flex flex-wrap gap-2">
429
+ <Button
430
+ variant="outline"
431
+ size="sm"
432
+ className="h-6 px-2 text-[11px]"
433
+ disabled={importingMcpTemplates}
434
+ onClick={handleImportMcpTemplates}
435
+ >
436
+ {importingMcpTemplates ? '导入中...' : '导入现有 MCP'}
437
+ </Button>
438
+ <Button
439
+ variant="ghost"
440
+ size="sm"
441
+ className="h-6 px-2 text-[11px]"
442
+ onClick={() => setAddingMcp(true)}
443
+ >
444
+ 新建模板
445
+ </Button>
446
+ </div>
447
+ </div>
448
+ )}
449
+ </div>
450
+ </div>
451
+
452
+ <McpLibraryEnableDialog
453
+ open={Boolean(selectedMcpTemplate)}
454
+ entry={selectedMcpTemplate}
455
+ projectPath={projectPath}
456
+ installedServers={mcpServers}
457
+ harnessType={harnessType}
458
+ onClose={() => setSelectedMcpTemplate(null)}
459
+ onEnabled={handleMcpInstanceAdded}
460
+ />
461
+
462
+ {/* Skill capabilities */}
463
+ <div className="flex flex-col gap-2 rounded-lg border border-[var(--color-border-subtle)] bg-white/[0.015] p-2">
464
+ <div className="flex items-center justify-between gap-2">
465
+ <div className="flex items-center gap-1.5 text-[11px] font-medium text-[var(--color-text-muted)]">
466
+ <span>Skill 能力</span>
467
+ <span className="text-[10px]">
468
+ 已启用 {skills.length} · 全局 {userSkills.length}
469
+ </span>
470
+ </div>
471
+ <Button
472
+ variant="ghost"
473
+ size="sm"
474
+ className="h-6 px-2 text-[11px]"
475
+ onClick={() => setAddingSkill(true)}
476
+ >
477
+ <Plus size={10} className="mr-1" />
478
+ 新建 Skill
479
+ </Button>
480
+ </div>
481
+
482
+ <div className="flex flex-wrap items-center gap-1.5">
483
+ {skills.map((skill) => (
484
+ <SkillChip key={skill.id} skill={skill} onRemove={handleRemoveSkill} />
485
+ ))}
486
+ {skills.length === 0 ? (
487
+ <span className="text-xs text-[var(--color-text-muted)]">
488
+ 当前团队还没有启用 Skill。
489
+ </span>
490
+ ) : null}
491
+ </div>
492
+
493
+ {addingSkill ? (
494
+ <AddSkillInline
495
+ projectPath={projectPath}
496
+ projectLabel={teamName}
497
+ onAdded={handleSkillAdded}
498
+ onCancel={() => setAddingSkill(false)}
499
+ />
500
+ ) : null}
501
+
502
+ <div className="space-y-1.5">
503
+ <div className="text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">
504
+ 从全局 Skill 启用
505
+ </div>
506
+ <Input
507
+ value={skillSearch}
508
+ onChange={(event) => setSkillSearch(event.target.value)}
509
+ placeholder="搜索 Skill..."
510
+ className="h-7 text-xs"
511
+ />
512
+ {availableUserSkills.length > 0 ? (
513
+ <div className="flex flex-col gap-1.5">
514
+ {visibleUserSkills.map((skill) => (
515
+ <div
516
+ key={skill.id}
517
+ className="flex items-center justify-between gap-2 rounded-md border border-[var(--color-border-subtle)] px-2 py-1.5"
518
+ >
519
+ <div className="min-w-0">
520
+ <div className="truncate text-xs text-[var(--color-text)]">{skill.name}</div>
521
+ <div className="truncate text-[10px] text-[var(--color-text-muted)]">
522
+ {skill.description || skill.folderName}
523
+ </div>
524
+ </div>
525
+ <Button
526
+ variant="outline"
527
+ size="sm"
528
+ className="h-6 px-2 text-[11px]"
529
+ disabled={enablingSkillId === skill.id}
530
+ onClick={() => void enableSkillFromLibrary(skill)}
531
+ >
532
+ {enablingSkillId === skill.id ? '启用中...' : '启用'}
533
+ </Button>
534
+ </div>
535
+ ))}
536
+ {availableUserSkills.length > 6 ? (
537
+ <Button
538
+ variant="ghost"
539
+ size="sm"
540
+ className="h-6 justify-start px-2 text-[11px]"
541
+ onClick={() => setShowAllSkills((value) => !value)}
542
+ >
543
+ {showAllSkills ? '收起' : `显示全部 ${availableUserSkills.length} 个 Skill`}
544
+ </Button>
545
+ ) : null}
546
+ </div>
547
+ ) : (
548
+ <div className="rounded-md border border-dashed border-[var(--color-border)] px-2 py-2 text-xs text-[var(--color-text-muted)]">
549
+ 全局 Skill 暂无可启用项。可以先创建用户级 Skill,再在团队中启用。
550
+ </div>
551
+ )}
552
+ </div>
553
+ </div>
554
+ </div>
555
+ );
556
+ };
@@ -4,20 +4,11 @@
4
4
  * Global catalog caches are in extensionsSlice (Zustand).
5
5
  */
6
6
 
7
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
7
+ import { useCallback, useMemo, useState } from 'react';
8
8
 
9
- import { api } from '@renderer/api';
9
+ import type { PluginCapability, PluginFilters, PluginSortField } from '@shared/types/extensions';
10
10
 
11
- import type {
12
- McpCatalogItem,
13
- McpSearchResult,
14
- PluginCapability,
15
- PluginFilters,
16
- PluginSortField,
17
- } from '@shared/types/extensions';
18
-
19
- export type ExtensionsSubTab = 'plugins' | 'mcp-servers' | 'skills' | 'env-vars';
20
- export type SkillsSortState = 'name-asc' | 'recent-desc';
11
+ export type ExtensionsSubTab = 'plugins';
21
12
 
22
13
  interface PluginSortState {
23
14
  field: PluginSortField;
@@ -43,90 +34,6 @@ export function useExtensionsTabState() {
43
34
  });
44
35
  const [selectedPluginId, setSelectedPluginId] = useState<string | null>(null);
45
36
 
46
- // ── MCP search (per-tab, calls API directly) ──
47
- const [mcpSearchQuery, setMcpSearchQuery] = useState('');
48
- const [mcpSearchResults, setMcpSearchResults] = useState<McpCatalogItem[]>([]);
49
- const [mcpSearchLoading, setMcpSearchLoading] = useState(false);
50
- const [mcpSearchWarnings, setMcpSearchWarnings] = useState<string[]>([]);
51
- const [selectedMcpServerId, setSelectedMcpServerId] = useState<string | null>(null);
52
-
53
- // ── Skills browse ──
54
- const [skillsSearchQuery, setSkillsSearchQuery] = useState('');
55
- const [skillsInstalledOnly] = useState(false);
56
- const [skillsSort, setSkillsSort] = useState<SkillsSortState>('name-asc');
57
- const [selectedSkillId, setSelectedSkillId] = useState<string | null>(null);
58
-
59
- // ── Debounced MCP search ──
60
- const searchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
61
- const mcpSearchRequestSeqRef = useRef(0);
62
-
63
- // Cleanup timer on unmount
64
- useEffect(() => {
65
- return () => {
66
- if (searchTimerRef.current) {
67
- clearTimeout(searchTimerRef.current);
68
- }
69
- mcpSearchRequestSeqRef.current += 1;
70
- };
71
- }, []);
72
-
73
- useEffect(() => {
74
- if (activeSubTab !== 'plugins' && selectedPluginId !== null) {
75
- setSelectedPluginId(null);
76
- }
77
- if (activeSubTab !== 'mcp-servers' && selectedMcpServerId !== null) {
78
- setSelectedMcpServerId(null);
79
- }
80
- if (activeSubTab !== 'skills' && selectedSkillId !== null) {
81
- setSelectedSkillId(null);
82
- }
83
- }, [activeSubTab, selectedMcpServerId, selectedPluginId, selectedSkillId]);
84
-
85
- const mcpSearch = useCallback((query: string) => {
86
- setMcpSearchQuery(query);
87
- const requestId = ++mcpSearchRequestSeqRef.current;
88
-
89
- if (searchTimerRef.current) {
90
- clearTimeout(searchTimerRef.current);
91
- }
92
-
93
- if (!query.trim()) {
94
- setMcpSearchResults([]);
95
- setMcpSearchWarnings([]);
96
- setMcpSearchLoading(false);
97
- return;
98
- }
99
-
100
- setMcpSearchLoading(true);
101
-
102
- searchTimerRef.current = setTimeout(() => {
103
- if (!api.mcpRegistry) {
104
- if (mcpSearchRequestSeqRef.current === requestId) {
105
- setMcpSearchLoading(false);
106
- }
107
- return;
108
- }
109
-
110
- void api.mcpRegistry.search(query).then(
111
- (result: McpSearchResult) => {
112
- if (mcpSearchRequestSeqRef.current !== requestId) {
113
- return;
114
- }
115
- setMcpSearchResults(result.servers);
116
- setMcpSearchWarnings(result.warnings);
117
- setMcpSearchLoading(false);
118
- },
119
- () => {
120
- if (mcpSearchRequestSeqRef.current !== requestId) {
121
- return;
122
- }
123
- setMcpSearchLoading(false);
124
- setMcpSearchWarnings(['Search failed']);
125
- }
126
- );
127
- }, 300);
128
- }, []);
129
-
130
37
  // ── Plugin filter helpers ──
131
38
  const updatePluginSearch = useCallback((search: string) => {
132
39
  setPluginFilters((prev) => ({ ...prev, search }));
@@ -184,23 +91,5 @@ export function useExtensionsTabState() {
184
91
  toggleInstalledOnly,
185
92
  clearFilters,
186
93
  hasActiveFilters,
187
-
188
- // MCP
189
- mcpSearchQuery,
190
- mcpSearch,
191
- mcpSearchResults,
192
- mcpSearchLoading,
193
- mcpSearchWarnings,
194
- selectedMcpServerId,
195
- setSelectedMcpServerId,
196
-
197
- // Skills
198
- skillsSearchQuery,
199
- setSkillsSearchQuery,
200
- skillsInstalledOnly,
201
- skillsSort,
202
- setSkillsSort,
203
- selectedSkillId,
204
- setSelectedSkillId,
205
94
  };
206
95
  }