@yancyyu/openhermit 1.6.41 → 1.6.43

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 (281) hide show
  1. package/README.md +98 -89
  2. package/bin/hermit.mjs +96 -0
  3. package/dist-renderer/assets/{ProjectEditorOverlay-Br0X83Jf.js → ProjectEditorOverlay-C98qSs7-.js} +1 -1
  4. package/dist-renderer/assets/{TeamGraphOverlay-DHMTbZPZ.js → TeamGraphOverlay-CsBbZwcL.js} +1 -1
  5. package/dist-renderer/assets/{_basePickBy-DzIiX7yH.js → _basePickBy-ZOyLWjMK.js} +1 -1
  6. package/dist-renderer/assets/{_baseUniq-6hZuzTLU.js → _baseUniq-DBb726rt.js} +1 -1
  7. package/dist-renderer/assets/{arc-CXgO6fx_.js → arc-CdiTaR_R.js} +1 -1
  8. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DKWgtDHr.js → architectureDiagram-VXUJARFQ-Cz3sc5TH.js} +1 -1
  9. package/dist-renderer/assets/{blockDiagram-VD42YOAC-DOMUcC40.js → blockDiagram-VD42YOAC-DE4c-KJ3.js} +1 -1
  10. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B_k2L7qX.js → c4Diagram-YG6GDRKO-CmTMDTrV.js} +1 -1
  11. package/dist-renderer/assets/channel-KTpqi9eT.js +1 -0
  12. package/dist-renderer/assets/{chunk-4BX2VUAB-BeD_ccFy.js → chunk-4BX2VUAB-rhHy3tFl.js} +1 -1
  13. package/dist-renderer/assets/{chunk-55IACEB6-ClZfkA5w.js → chunk-55IACEB6-fLZBzuo_.js} +1 -1
  14. package/dist-renderer/assets/{chunk-B4BG7PRW-5XluxXsn.js → chunk-B4BG7PRW-DOzxQhim.js} +1 -1
  15. package/dist-renderer/assets/{chunk-DI55MBZ5-BzIjjNVm.js → chunk-DI55MBZ5-COQCcXC5.js} +1 -1
  16. package/dist-renderer/assets/{chunk-FMBD7UC4-HgH3MK_H.js → chunk-FMBD7UC4-IKU9U_Y4.js} +1 -1
  17. package/dist-renderer/assets/{chunk-QN33PNHL-WeC5T3Ba.js → chunk-QN33PNHL-D6WV154X.js} +1 -1
  18. package/dist-renderer/assets/{chunk-QZHKN3VN-Cu1ApHfW.js → chunk-QZHKN3VN-D90_2DQp.js} +1 -1
  19. package/dist-renderer/assets/{chunk-TZMSLE5B-BOhlynJM.js → chunk-TZMSLE5B-BQEil57G.js} +1 -1
  20. package/dist-renderer/assets/classDiagram-2ON5EDUG-lpzulY5X.js +1 -0
  21. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-lpzulY5X.js +1 -0
  22. package/dist-renderer/assets/clone-CriGymY9.js +1 -0
  23. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DGZSihDQ.js → cose-bilkent-S5V4N54A-6WiK6U2P.js} +1 -1
  24. package/dist-renderer/assets/{dagre-6UL2VRFP-CnxwCbku.js → dagre-6UL2VRFP-DF4MMuTn.js} +1 -1
  25. package/dist-renderer/assets/{diagram-PSM6KHXK-DsIhoxdI.js → diagram-PSM6KHXK-CcF1eZ7E.js} +1 -1
  26. package/dist-renderer/assets/{diagram-QEK2KX5R-Cmh9KUF5.js → diagram-QEK2KX5R-DYlOVPQB.js} +1 -1
  27. package/dist-renderer/assets/{diagram-S2PKOQOG-CKxV456A.js → diagram-S2PKOQOG-BHXWsZOP.js} +1 -1
  28. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-EnvYjOjc.js → erDiagram-Q2GNP2WA-GjmuBx8d.js} +1 -1
  29. package/dist-renderer/assets/{flowDiagram-NV44I4VS-BmNeWY_A.js → flowDiagram-NV44I4VS-BuS7YVHk.js} +1 -1
  30. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-D30fyK-u.js → ganttDiagram-JELNMOA3-3Teu5tAa.js} +1 -1
  31. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-CrUNiYg1.js → gitGraphDiagram-V2S2FVAM-BiLdCYu5.js} +1 -1
  32. package/dist-renderer/assets/{graph-CY1gTfTb.js → graph-CDP_R8ct.js} +1 -1
  33. package/dist-renderer/assets/{index-CaEbzwAU.js → index-BSZdT-g-.js} +1 -1
  34. package/dist-renderer/assets/{index-D5K-SjBG.js → index-BhWvMqsz.js} +1 -1
  35. package/dist-renderer/assets/{index-9_hO4N1e.js → index-C2_AupSj.js} +1 -1
  36. package/dist-renderer/assets/{index-59r209c1.js → index-C5ujiwAR.js} +580 -588
  37. package/dist-renderer/assets/index-CIS2CTK9.css +1 -0
  38. package/dist-renderer/assets/{index-DMR9B1UP.js → index-CVNjLwkq.js} +1 -1
  39. package/dist-renderer/assets/{index-BC2hXmg_.js → index-CwG3se0q.js} +1 -1
  40. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-By_XUlcD.js → infoDiagram-HS3SLOUP-DLHUFo72.js} +1 -1
  41. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BM1LJE9m.js → journeyDiagram-XKPGCS4Q-BE07RpJD.js} +1 -1
  42. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-DHIW3aTA.js → kanban-definition-3W4ZIXB7-DDHZy4NB.js} +1 -1
  43. package/dist-renderer/assets/{layout-DAKiL_Mo.js → layout-5nA5wUxO.js} +1 -1
  44. package/dist-renderer/assets/{linear-DwOaRYea.js → linear-BtF1i2qN.js} +1 -1
  45. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-b7bJ2cha.js → mindmap-definition-VGOIOE7T-Z1Ui9Sqy.js} +1 -1
  46. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-DxyL9Zr2.js → pieDiagram-ADFJNKIX-LCjxckWv.js} +1 -1
  47. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CR33pHlF.js → quadrantDiagram-AYHSOK5B-BOwKjSco.js} +1 -1
  48. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-BAiSRSlh.js → requirementDiagram-UZGBJVZJ-pChP8Znd.js} +1 -1
  49. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8JmDjoa.js → sankeyDiagram-TZEHDZUN-DifZ2qpo.js} +1 -1
  50. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-c1d0Wi1m.js → sequenceDiagram-WL72ISMW-CJg-WYyY.js} +1 -1
  51. package/dist-renderer/assets/{splashScene-D0YB9uxm.js → splashScene-94xWCzLA.js} +1 -1
  52. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-nT8BiH2O.js → stateDiagram-FKZM4ZOC-DWHOoFdv.js} +1 -1
  53. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-CGYZOoMb.js +1 -0
  54. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-DpoRepUA.js → timeline-definition-IT6M3QCI-CPgokIo8.js} +1 -1
  55. package/dist-renderer/assets/{treemap-GDKQZRPO-C41UJeIH.js → treemap-GDKQZRPO-DAVqSR9L.js} +1 -1
  56. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-KMjGARKN.js → xychartDiagram-PRI3JC2R-CCOcGbrD.js} +1 -1
  57. package/dist-renderer/chat-community-qr.jpg +0 -0
  58. package/dist-renderer/fonts/Agave-Bold.ttf +0 -0
  59. package/dist-renderer/fonts/Agave-Regular.ttf +0 -0
  60. package/dist-renderer/icon.png +0 -0
  61. package/dist-renderer/icon.rar +0 -0
  62. package/dist-renderer/index.html +3 -3
  63. package/package.json +21 -26
  64. package/src/features/worker-society/core/application/WorkerSocietyService.test.ts +802 -0
  65. package/src/features/worker-society/core/application/WorkerSocietyService.ts +428 -0
  66. package/src/features/worker-society/core/application/fakes.ts +101 -0
  67. package/src/features/worker-society/core/application/ports.ts +70 -0
  68. package/src/features/worker-society/core/domain/models/society.ts +141 -0
  69. package/src/features/worker-society/core/domain/policies/societyPolicies.test.ts +739 -0
  70. package/src/features/worker-society/core/domain/policies/societyPolicies.ts +496 -0
  71. package/src/features/worker-society/main/adapters/input/societyMcp.test.ts +317 -0
  72. package/src/features/worker-society/main/adapters/input/societyMcp.ts +257 -0
  73. package/src/features/worker-society/main/adapters/input/societyRoutes.test.ts +695 -0
  74. package/src/features/worker-society/main/adapters/input/societyRoutes.ts +194 -0
  75. package/src/features/worker-society/main/composition/societyComposition.test.ts +74 -0
  76. package/src/features/worker-society/main/composition/societyComposition.ts +70 -0
  77. package/src/features/worker-society/main/composition/workerSocietyPlugin.test.ts +69 -0
  78. package/src/features/worker-society/main/composition/workerSocietyPlugin.ts +67 -0
  79. package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.test.ts +132 -0
  80. package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.ts +84 -0
  81. package/src/features/worker-society/main/infrastructure/fsStores.test.ts +216 -0
  82. package/src/features/worker-society/main/infrastructure/fsStores.ts +113 -0
  83. package/src/features/worker-society/main/infrastructure/mergingProfileStore.test.ts +195 -0
  84. package/src/features/worker-society/main/infrastructure/mergingProfileStore.ts +96 -0
  85. package/src/features/worker-society/renderer/SocietyGraph.tsx +166 -0
  86. package/src/features/worker-society/renderer/SocietyNodeLabels.tsx +139 -0
  87. package/src/features/worker-society/renderer/SocietyNodeOverlay.tsx +339 -0
  88. package/src/features/worker-society/renderer/SocietyView.tsx +437 -0
  89. package/src/features/worker-society/renderer/index.ts +11 -0
  90. package/src/features/worker-society/renderer/societyApi.test.ts +259 -0
  91. package/src/features/worker-society/renderer/societyApi.ts +144 -0
  92. package/src/features/worker-society/renderer/societyGraphAdapter.test.ts +321 -0
  93. package/src/features/worker-society/renderer/societyGraphAdapter.ts +240 -0
  94. package/src/features/worker-society/renderer/societyOverlayActions.test.ts +57 -0
  95. package/src/features/worker-society/renderer/societyOverlayActions.ts +49 -0
  96. package/src/features/worker-society/renderer/societyStore.test.ts +218 -0
  97. package/src/features/worker-society/renderer/societyStore.ts +146 -0
  98. package/src/features/worker-society/renderer/societyViewUtils.test.ts +81 -0
  99. package/src/features/worker-society/renderer/societyViewUtils.ts +68 -0
  100. package/src/main/ipc/extensions.ts +27 -0
  101. package/src/main/server.ts +1731 -539
  102. package/src/main/services/ccConnect/CcConnectBridge.ts +26 -11
  103. package/src/main/services/ccConnect/CcConnectClient.ts +9 -2
  104. package/src/main/services/ccConnect/workDirReconcile.test.ts +57 -0
  105. package/src/main/services/ccConnect/workDirReconcile.ts +36 -0
  106. package/src/main/services/direct-cli/DirectCliSessionManager.test.ts +397 -0
  107. package/src/main/services/direct-cli/DirectCliSessionManager.ts +508 -0
  108. package/src/main/services/direct-cli/DirectCliSessionStore.test.ts +79 -0
  109. package/src/main/services/direct-cli/DirectCliSessionStore.ts +97 -0
  110. package/src/main/services/direct-cli/__tests__/directCliMessageId.test.ts +40 -0
  111. package/src/main/services/direct-cli/directCliMessageId.ts +21 -0
  112. package/src/main/services/direct-cli/index.ts +17 -0
  113. package/src/main/services/extensions/capability-packs/CapabilityPackLoaderService.ts +637 -0
  114. package/src/main/services/extensions/catalog/PluginCatalogService.ts +2 -2
  115. package/src/main/services/loop-assets/LoopAssetsScannerService.ts +657 -0
  116. package/src/main/services/runtime/providerAwareCliEnv.ts +33 -5
  117. package/src/main/services/session-intelligence/LocalSessionScanner.ts +156 -71
  118. package/src/main/services/session-intelligence/SessionUsageParser.ts +103 -8
  119. package/src/main/services/session-intelligence/UsageTelemetryService.ts +11 -0
  120. package/src/main/services/session-intelligence/__tests__/teamSessionListMapper.test.ts +104 -0
  121. package/src/main/services/session-intelligence/teamSessionListMapper.ts +78 -0
  122. package/src/main/services/system-manager/AdminLoopInitializer.ts +95 -0
  123. package/src/main/services/system-manager/BuiltinWorkflowSeeder.ts +744 -0
  124. package/src/main/services/system-manager/SystemManagerConfigService.ts +19 -1
  125. package/src/main/services/system-manager/WorkflowPromptService.ts +58 -5
  126. package/src/main/services/system-manager/__tests__/AdminLoopInitializer.test.ts +129 -0
  127. package/src/main/services/system-manager/__tests__/SystemManagerConfigService.test.ts +60 -0
  128. package/src/main/services/teams-mvp/CollaborationBoardService.ts +2 -0
  129. package/src/main/services/teams-mvp/OpsRunbookContext.ts +60 -0
  130. package/src/main/services/teams-mvp/TaskDispatchService.test.ts +305 -0
  131. package/src/main/services/teams-mvp/TaskDispatchService.ts +250 -131
  132. package/src/main/services/teams-mvp/TeamProvisioningService.ts +12 -2
  133. package/src/main/services/teams-mvp/TeamWorkspaceService.test.ts +207 -0
  134. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +104 -51
  135. package/src/main/services/teams-mvp/index.ts +6 -0
  136. package/src/main/utils/externalPlatformSessionRouting.ts +92 -0
  137. package/src/main/utils/toolApprovalRules.ts +151 -0
  138. package/src/renderer/App.tsx +24 -89
  139. package/src/renderer/api/httpClient.ts +132 -52
  140. package/src/renderer/api/providers.ts +5 -16
  141. package/src/renderer/components/chat/CommunityChatView.tsx +81 -0
  142. package/src/renderer/components/dashboard/DashboardView.tsx +130 -84
  143. package/src/renderer/components/extensions/ExtensionStoreView.tsx +39 -5
  144. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +2 -1
  145. package/src/renderer/components/extensions/capability-packs/CapabilityPacksPanel.tsx +170 -0
  146. package/src/renderer/components/layout/PaneContent.tsx +10 -2
  147. package/src/renderer/components/layout/SortableTab.tsx +4 -0
  148. package/src/renderer/components/layout/TabBarActions.tsx +13 -16
  149. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +4 -135
  150. package/src/renderer/components/schedules/SchedulesView.tsx +22 -14
  151. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +7 -6
  152. package/src/renderer/components/settings/SettingsTabs.tsx +24 -21
  153. package/src/renderer/components/settings/SettingsView.tsx +22 -13
  154. package/src/renderer/components/settings/components/SettingRow.tsx +13 -5
  155. package/src/renderer/components/settings/components/SettingsSectionCard.tsx +53 -0
  156. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +10 -6
  157. package/src/renderer/components/settings/components/SettingsSelect.tsx +12 -9
  158. package/src/renderer/components/settings/components/SettingsToggle.tsx +6 -5
  159. package/src/renderer/components/settings/components/index.ts +1 -0
  160. package/src/renderer/components/settings/sections/AdvancedSection.tsx +78 -59
  161. package/src/renderer/components/settings/sections/CliStatusSection.tsx +32 -44
  162. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  163. package/src/renderer/components/settings/sections/GeneralSection.tsx +216 -186
  164. package/src/renderer/components/settings/sections/PlatformsSection.tsx +25 -17
  165. package/src/renderer/components/settings/sections/TaskBusSection.tsx +63 -22
  166. package/src/renderer/components/sidebar/SidebarSessions.tsx +120 -80
  167. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +1 -1
  168. package/src/renderer/components/splash/splashScene.ts +6 -2
  169. package/src/renderer/components/system-manager/SystemManagerView.tsx +169 -255
  170. package/src/renderer/components/tasks/TasksView.tsx +63 -37
  171. package/src/renderer/components/team/CcSessionsSection.tsx +124 -89
  172. package/src/renderer/components/team/HarnessBrandLogos.tsx +318 -0
  173. package/src/renderer/components/team/HarnessSelect.tsx +25 -26
  174. package/src/renderer/components/team/TeamDetailView.tsx +137 -153
  175. package/src/renderer/components/team/TeamEmptyState.tsx +9 -37
  176. package/src/renderer/components/team/TeamListView.tsx +144 -30
  177. package/src/renderer/components/team/__tests__/CcSessionsSection.hasLocalFile.test.tsx +128 -0
  178. package/src/renderer/components/team/activity/ActivityItem.tsx +21 -9
  179. package/src/renderer/components/team/activity/ActivityTimeline.tsx +2 -2
  180. package/src/renderer/components/team/dialogs/AdvancedCliSection.tsx +1 -1
  181. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +13 -10
  182. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +189 -57
  183. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +9 -157
  184. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +19 -15
  185. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +48 -10
  186. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +11 -12
  187. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +39 -37
  188. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +434 -64
  189. package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +12 -10
  190. package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +2 -2
  191. package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.bindProject.test.tsx +399 -0
  192. package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.chineseRepro.test.tsx +253 -0
  193. package/src/renderer/components/team/dialogs/platformAllowUtils.ts +91 -0
  194. package/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +1 -1
  195. package/src/renderer/components/team/kanban/KanbanTaskCard.test.tsx +41 -0
  196. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +41 -86
  197. package/src/renderer/components/team/loop-console/LoopCommandComposer.tsx +310 -0
  198. package/src/renderer/components/team/loop-console/LoopConsolePanel.tsx +372 -0
  199. package/src/renderer/components/team/loop-console/loopSendIntent.test.ts +85 -0
  200. package/src/renderer/components/team/loop-console/loopSendIntent.ts +221 -0
  201. package/src/renderer/components/team/loop-console/useLeadSessionToolActivity.ts +74 -0
  202. package/src/renderer/components/team/loop-console/useLoopCommandSuggestions.ts +165 -0
  203. package/src/renderer/components/team/loop-console/useLoopConsoleController.ts +266 -0
  204. package/src/renderer/components/team/members/LeadModelRow.test.tsx +1 -1
  205. package/src/renderer/components/team/members/LeadModelRow.tsx +5 -3
  206. package/src/renderer/components/team/members/MemberDetailDialog.tsx +11 -0
  207. package/src/renderer/components/team/members/MemberDetailStats.tsx +13 -3
  208. package/src/renderer/components/team/members/MemberDraftRow.test.tsx +1 -1
  209. package/src/renderer/components/team/members/MemberDraftRow.tsx +1 -1
  210. package/src/renderer/components/team/members/MemberMessagesTab.tsx +2 -2
  211. package/src/renderer/components/team/members/MemberStatsTab.tsx +1 -1
  212. package/src/renderer/components/team/messages/MessageComposer.tsx +150 -44
  213. package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +2 -2
  214. package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -28
  215. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +6 -6
  216. package/src/renderer/components/team/taskLogs/ExactTaskLogCard.tsx +2 -2
  217. package/src/renderer/components/team/taskLogs/TaskLogStreamSection.tsx +1 -1
  218. package/src/renderer/components/terminal/TerminalPanel.tsx +2 -3
  219. package/src/renderer/components/ui/MentionableTextarea.tsx +5 -1
  220. package/src/renderer/constants/teamColors.ts +5 -5
  221. package/src/renderer/hooks/useExtensionsTabState.ts +1 -1
  222. package/src/renderer/hooks/useMentionDetection.ts +5 -1
  223. package/src/renderer/hooks/useProjectWorkflowCommands.ts +57 -0
  224. package/src/renderer/hooks/useTeamSuggestions.ts +2 -0
  225. package/src/renderer/index.css +19 -2
  226. package/src/renderer/main.tsx +7 -1
  227. package/src/renderer/store/index.ts +18 -1
  228. package/src/renderer/store/slices/extensionsSlice.ts +83 -0
  229. package/src/renderer/store/slices/tabSlice.ts +61 -0
  230. package/src/renderer/store/slices/teamSlice.ts +138 -9
  231. package/src/renderer/types/mention.ts +8 -0
  232. package/src/renderer/types/tabs.ts +3 -1
  233. package/src/renderer/utils/__tests__/bindProjectSlug.test.ts +69 -0
  234. package/src/renderer/utils/__tests__/groupTransformer.test.ts +148 -0
  235. package/src/renderer/utils/__tests__/initialRoute.test.ts +101 -0
  236. package/src/renderer/utils/__tests__/leadToolActivity.test.ts +124 -0
  237. package/src/renderer/utils/__tests__/mergeTeamMessages.test.ts +81 -0
  238. package/src/renderer/utils/__tests__/teamMessageFiltering.test.ts +213 -0
  239. package/src/renderer/utils/__tests__/teamMessageKey.test.ts +75 -0
  240. package/src/renderer/utils/__tests__/workflowCommandExecution.test.ts +173 -0
  241. package/src/renderer/utils/__tests__/workflowCommandSuggestions.test.ts +59 -0
  242. package/src/renderer/utils/bindProjectSlug.ts +57 -0
  243. package/src/renderer/utils/capabilityCommandExecution.ts +113 -0
  244. package/src/renderer/utils/initialRoute.ts +89 -0
  245. package/src/renderer/utils/leadToolActivity.ts +117 -0
  246. package/src/renderer/utils/loopShortcutSuggestions.ts +106 -0
  247. package/src/renderer/utils/mentionSuggestions.ts +1 -1
  248. package/src/renderer/utils/slashCommandRegistry.ts +231 -0
  249. package/src/renderer/utils/teamMentionDirective.ts +31 -0
  250. package/src/renderer/utils/workflowCommandExecution.ts +96 -0
  251. package/src/renderer/utils/workflowCommandSuggestions.ts +49 -0
  252. package/src/shared/types/api.ts +79 -4
  253. package/src/shared/types/ccConnect.ts +1 -0
  254. package/src/shared/types/extensions/api.ts +19 -0
  255. package/src/shared/types/extensions/capabilityPack.ts +118 -0
  256. package/src/shared/types/extensions/index.ts +29 -1
  257. package/src/shared/types/index.ts +6 -0
  258. package/src/shared/types/loopAssets.ts +54 -0
  259. package/src/shared/types/providers.ts +0 -16
  260. package/src/shared/types/systemManager.ts +26 -1
  261. package/src/shared/types/team.ts +43 -3
  262. package/src/shared/types/terminal.ts +2 -36
  263. package/src/shared/types/worker.test.ts +28 -0
  264. package/src/shared/types/worker.ts +3 -0
  265. package/src/shared/utils/__tests__/effortLevels.test.ts +88 -0
  266. package/src/shared/utils/__tests__/providerBackend.test.ts +88 -0
  267. package/src/shared/utils/__tests__/providerLaunchArgs.test.ts +220 -0
  268. package/src/shared/utils/claudeStreamJson.test.ts +187 -0
  269. package/src/shared/utils/claudeStreamJson.ts +153 -0
  270. package/src/shared/utils/providerLaunchArgs.ts +217 -0
  271. package/src/shared/utils/slashCommands.ts +10 -0
  272. package/src/types/node-pty.d.ts +8 -0
  273. package/dist-renderer/assets/channel-D0XS_akr.js +0 -1
  274. package/dist-renderer/assets/classDiagram-2ON5EDUG-D13Ffs0U.js +0 -1
  275. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-D13Ffs0U.js +0 -1
  276. package/dist-renderer/assets/clone-B1ZrxI1D.js +0 -1
  277. package/dist-renderer/assets/index-iyjkpSus.css +0 -32
  278. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Dmibmlso.js +0 -1
  279. package/src/main/services/system-manager/SystemManagerPtyService.ts +0 -233
  280. package/src/renderer/components/common/TerminalPane.tsx +0 -213
  281. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +0 -292
@@ -1,26 +1,36 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import { useCallback, useEffect, useMemo, useState } from 'react';
2
2
 
3
- import '@xterm/xterm/css/xterm.css';
4
-
5
- import { FitAddon } from '@xterm/addon-fit';
6
- import { WebLinksAddon } from '@xterm/addon-web-links';
7
- import { Terminal } from '@xterm/xterm';
8
3
  import { api } from '@renderer/api';
9
4
  import { useStore } from '@renderer/store';
10
5
  import { Button } from '@renderer/components/ui/button';
11
- import { Input } from '@renderer/components/ui/input';
12
- import { SYSTEM_MANAGER_DISPLAY_NAME } from '@shared/types/team';
6
+ import {
7
+ buildCapabilityPackCommandSuggestions,
8
+ collectSlashSuggestionAliases,
9
+ } from '@renderer/utils/slashCommandRegistry';
10
+ import { buildWorkflowCommandSuggestion } from '@renderer/utils/workflowCommandSuggestions';
11
+ import { SYSTEM_MANAGER_DISPLAY_NAME, SYSTEM_MANAGER_TEAM_NAME } from '@shared/types/team';
13
12
  import type {
14
13
  SystemManagerConfig,
15
14
  SystemManagerStatus,
16
15
  WorkflowPromptSummary,
17
16
  } from '@shared/types/systemManager';
18
- import { Loader2, RefreshCw, TerminalSquare } from 'lucide-react';
17
+ import { Settings2, TerminalSquare } from 'lucide-react';
18
+
19
+ import type { MentionSuggestion } from '@renderer/types/mention';
19
20
 
20
- import { FolderBrowser } from './FolderBrowser';
21
+ import { LoopConsolePanel } from '../team/loop-console/LoopConsolePanel';
22
+ import { RuntimeConfigDialog } from '../team/dialogs/RuntimeConfigDialog';
23
+
24
+ import type {
25
+ CcSession,
26
+ ResolvedTeamMember,
27
+ TeamTaskWithKanban,
28
+ TeamViewSnapshot,
29
+ } from '@shared/types';
21
30
 
22
31
  interface SystemManagerViewProps {
23
32
  isPaneFocused?: boolean;
33
+ isActive?: boolean;
24
34
  }
25
35
 
26
36
  function formatPathForTitle(pathValue: string): string {
@@ -34,110 +44,52 @@ function joinPath(basePath: string, childPath: string): string {
34
44
  return `${trimmedBase}/${childPath}`;
35
45
  }
36
46
 
47
+ const EMPTY_ADMIN_TASKS: TeamTaskWithKanban[] = [];
48
+ const EMPTY_CAPABILITY_PACKS = [] as const;
49
+ const NOOP_FETCH_CAPABILITY_PACKS = () => Promise.resolve();
50
+
51
+ function buildAdminLoopMember(teamData: TeamViewSnapshot | null): ResolvedTeamMember[] {
52
+ const lead = teamData?.members[0];
53
+ return [
54
+ {
55
+ name: lead?.name ?? SYSTEM_MANAGER_DISPLAY_NAME,
56
+ agentId: lead?.agentId,
57
+ status: teamData?.isAlive ? 'active' : 'idle',
58
+ currentTaskId: null,
59
+ taskCount: teamData?.tasks.length ?? 0,
60
+ lastActiveAt: null,
61
+ messageCount: 0,
62
+ color: 'slate',
63
+ agentType: 'admin-loop',
64
+ role: 'Workspace loop manager',
65
+ workflow: lead?.workflow,
66
+ providerId: lead?.providerId,
67
+ model: lead?.model,
68
+ effort: lead?.effort,
69
+ cwd: teamData?.config.projectPath,
70
+ gitBranch: lead?.gitBranch,
71
+ runtimeAdvisory: lead?.runtimeAdvisory,
72
+ },
73
+ ];
74
+ }
75
+
37
76
  export const SystemManagerView = ({
38
77
  isPaneFocused: _isPaneFocused = false,
78
+ isActive: _isActive = true,
39
79
  }: SystemManagerViewProps): React.JSX.Element => {
40
- const terminalHostRef = useRef<HTMLDivElement | null>(null);
41
- const terminalRef = useRef<Terminal | null>(null);
42
- const fitAddonRef = useRef<FitAddon | null>(null);
43
- const ptyIdRef = useRef<string | null>(null);
44
- const autoStartedRef = useRef(false);
45
- const startClaudeRef = useRef<((workDirOverride?: string) => Promise<void>) | null>(null);
46
-
47
80
  const [status, setStatus] = useState<SystemManagerStatus | null>(null);
48
- const [config, setConfig] = useState<SystemManagerConfig | null>(null);
49
- const [workDirInput, setWorkDirInput] = useState('');
50
81
  const [workflowPrompts, setWorkflowPrompts] = useState<WorkflowPromptSummary[]>([]);
51
82
  const [warnings, setWarnings] = useState<string[]>([]);
52
83
  const [loading, setLoading] = useState(true);
53
- const [starting, setStarting] = useState(false);
54
- const [running, setRunning] = useState(false);
84
+ const [adminTeamData, setAdminTeamData] = useState<TeamViewSnapshot | null>(null);
85
+ const [adminSessions, setAdminSessions] = useState<CcSession[]>([]);
86
+ const [pendingRepliesByMember, setPendingRepliesByMember] = useState<Record<string, number>>({});
87
+ const [bindingDialogOpen, setBindingDialogOpen] = useState(false);
55
88
  const [error, setError] = useState<string | null>(null);
56
- const fetchTeams = useStore((state) => state.fetchTeams);
57
-
58
- const writeTerminalLine = useCallback((line: string) => {
59
- terminalRef.current?.writeln(`\x1b[90m${line}\x1b[0m`);
60
- }, []);
61
-
62
- const fitTerminal = useCallback(() => {
63
- try {
64
- fitAddonRef.current?.fit();
65
- if (ptyIdRef.current && terminalRef.current) {
66
- api.terminal.resize(ptyIdRef.current, terminalRef.current.cols, terminalRef.current.rows);
67
- }
68
- } catch {
69
- // xterm fit can throw when the element is not measurable yet.
70
- }
71
- }, []);
72
-
73
- useEffect(() => {
74
- const host = terminalHostRef.current;
75
- if (!host) return;
76
-
77
- const term = new Terminal({
78
- cursorBlink: true,
79
- convertEol: true,
80
- fontFamily: 'JetBrains Mono, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
81
- fontSize: 13,
82
- lineHeight: 1.28,
83
- theme: {
84
- background: 'var(--color-surface)',
85
- foreground: 'var(--color-text)',
86
- cursor: 'var(--color-text)',
87
- selectionBackground: 'var(--color-border-emphasis)',
88
- black: 'var(--color-surface-sidebar)',
89
- red: '#f87171',
90
- green: '#86efac',
91
- yellow: '#fde68a',
92
- blue: 'var(--color-accent)',
93
- magenta: '#d8b4fe',
94
- cyan: '#818cf8',
95
- white: 'var(--color-text)',
96
- },
97
- });
98
- const fitAddon = new FitAddon();
99
- term.loadAddon(fitAddon);
100
- term.loadAddon(new WebLinksAddon());
101
- term.open(host);
102
- terminalRef.current = term;
103
- fitAddonRef.current = fitAddon;
104
- fitTerminal();
105
- term.writeln('\x1b[90m# Hermit 控制台 · 本地 Claude Code PTY\x1b[0m');
106
- term.writeln('\x1b[90m# 点击 Start Claude 等价于在当前目录运行 claude\x1b[0m');
107
- term.writeln('');
108
-
109
- const dataDispose = api.terminal.onData((_event, ptyId, data) => {
110
- if (ptyId === ptyIdRef.current) term.write(data);
111
- });
112
- const exitDispose = api.terminal.onExit((_event, ptyId, exitCode) => {
113
- if (ptyId === ptyIdRef.current) {
114
- setRunning(false);
115
- ptyIdRef.current = null;
116
- term.writeln(`\r\n\x1b[90m[claude exited with code ${exitCode}]\x1b[0m`);
117
- }
118
- });
119
- const inputDispose = term.onData((data) => {
120
- if (ptyIdRef.current) api.terminal.write(ptyIdRef.current, data);
121
- });
122
- const resizeObserver = new ResizeObserver(() => fitTerminal());
123
- resizeObserver.observe(host);
124
-
125
- return () => {
126
- dataDispose();
127
- exitDispose();
128
- inputDispose.dispose();
129
- resizeObserver.disconnect();
130
- if (ptyIdRef.current) {
131
- void api.terminal.kill(ptyIdRef.current).catch(() => {
132
- // Component is unmounting; there is no safe UI surface for this lifecycle error.
133
- });
134
- }
135
- term.dispose();
136
- terminalRef.current = null;
137
- fitAddonRef.current = null;
138
- ptyIdRef.current = null;
139
- };
140
- }, [fitTerminal]);
89
+ const capabilityPacks = useStore((state) => state.capabilityPacks ?? EMPTY_CAPABILITY_PACKS);
90
+ const fetchCapabilityPacks = useStore(
91
+ (state) => state.fetchCapabilityPacks ?? NOOP_FETCH_CAPABILITY_PACKS
92
+ );
141
93
 
142
94
  const load = useCallback(async (): Promise<SystemManagerConfig | null> => {
143
95
  setLoading(true);
@@ -146,33 +98,46 @@ export const SystemManagerView = ({
146
98
  const [nextStatus, nextConfig] = await Promise.all([
147
99
  api.systemManager.getStatus(),
148
100
  api.systemManager.getConfig(),
101
+ fetchCapabilityPacks().then(() => undefined),
102
+ ]);
103
+ await api.teams.ensureSystemManager();
104
+ const [nextTeamData, nextSessions] = await Promise.all([
105
+ api.teams.getData(SYSTEM_MANAGER_TEAM_NAME),
106
+ api.teams.getTeamSessions(SYSTEM_MANAGER_TEAM_NAME),
149
107
  ]);
150
108
  setStatus(nextStatus);
151
- setConfig(nextConfig);
152
- setWorkDirInput(nextConfig.selectedWorkDir);
109
+ setAdminTeamData(nextTeamData);
110
+ setAdminSessions(nextSessions);
153
111
  const candidateFolders = [
112
+ nextStatus.globalHermitWorkflowFolder,
113
+ joinPath(nextStatus.adminWorkDir, '.claude/commands'),
154
114
  nextConfig.workflowFolder,
155
- joinPath(nextConfig.selectedWorkDir, 'workflows'),
115
+ joinPath(nextStatus.adminWorkDir, 'workflows'),
156
116
  ].filter((folder): folder is string => Boolean(folder));
157
- let loadedWorkflow = false;
117
+ const seenFolders = new Set<string>();
118
+ const seenPrompts = new Set<string>();
119
+ const nextPrompts: WorkflowPromptSummary[] = [];
120
+ const nextWarnings: string[] = [];
158
121
  for (const folder of candidateFolders) {
122
+ if (seenFolders.has(folder)) continue;
123
+ seenFolders.add(folder);
159
124
  try {
160
125
  const workflowResult = await api.systemManager.listWorkflowPrompts(folder);
161
- setConfig((current) =>
162
- current ? { ...current, workflowFolder: workflowResult.folder } : current
163
- );
164
- setWorkflowPrompts(workflowResult.prompts);
165
- setWarnings(workflowResult.warnings);
166
- loadedWorkflow = true;
167
- break;
126
+ for (const prompt of workflowResult.prompts) {
127
+ const basename = prompt.filename.replace(/\.[^.]+$/, '');
128
+ const key = prompt.commandName ?? basename;
129
+ if (seenPrompts.has(key) || seenPrompts.has(basename)) continue;
130
+ seenPrompts.add(key);
131
+ seenPrompts.add(basename);
132
+ nextPrompts.push(prompt);
133
+ }
134
+ nextWarnings.push(...workflowResult.warnings);
168
135
  } catch {
169
136
  // Common commands are optional; missing folders should not interrupt opening the console.
170
137
  }
171
138
  }
172
- if (!loadedWorkflow) {
173
- setWorkflowPrompts([]);
174
- setWarnings([]);
175
- }
139
+ setWorkflowPrompts(nextPrompts);
140
+ setWarnings(nextWarnings);
176
141
  return nextConfig;
177
142
  } catch (err) {
178
143
  setError(err instanceof Error ? err.message : String(err));
@@ -180,101 +145,23 @@ export const SystemManagerView = ({
180
145
  } finally {
181
146
  setLoading(false);
182
147
  }
183
- }, []);
184
-
185
- const stopClaude = useCallback(async (): Promise<boolean> => {
186
- const ptyId = ptyIdRef.current;
187
- if (!ptyId) {
188
- setRunning(false);
189
- return true;
190
- }
191
-
192
- try {
193
- await api.terminal.kill(ptyId);
194
- if (ptyIdRef.current === ptyId) {
195
- ptyIdRef.current = null;
196
- setRunning(false);
197
- writeTerminalLine('[stopped]');
198
- }
199
- return true;
200
- } catch (err) {
201
- const message = err instanceof Error ? err.message : String(err);
202
- setError(message);
203
- writeTerminalLine(`[failed to stop claude] ${message}`);
204
- return false;
205
- }
206
- }, [writeTerminalLine]);
207
-
208
- const startClaude = useCallback(async (workDirOverride?: string) => {
209
- if (starting) return;
210
- setStarting(true);
211
- setError(null);
212
- try {
213
- const stopped = await stopClaude();
214
- if (!stopped) return;
215
- const nextConfig = await api.systemManager.updateConfig({
216
- selectedWorkDir: workDirOverride ?? workDirInput,
217
- });
218
- setConfig(nextConfig);
219
- void fetchTeams();
220
- terminalRef.current?.clear();
221
- writeTerminalLine(`# cd ${nextConfig.selectedWorkDir}`);
222
- writeTerminalLine('$ claude');
223
- const ptyId = await api.terminal.spawn({
224
- cwd: nextConfig.selectedWorkDir,
225
- cols: terminalRef.current?.cols ?? 120,
226
- rows: terminalRef.current?.rows ?? 34,
227
- });
228
- ptyIdRef.current = ptyId;
229
- setRunning(true);
230
- terminalRef.current?.focus();
231
- } catch (err) {
232
- const message = err instanceof Error ? err.message : String(err);
233
- setError(message);
234
- writeTerminalLine(`[failed to start claude] ${message}`);
235
- } finally {
236
- setStarting(false);
237
- }
238
- }, [fetchTeams, starting, stopClaude, workDirInput, writeTerminalLine]);
239
-
240
- useEffect(() => {
241
- startClaudeRef.current = startClaude;
242
- }, [startClaude]);
148
+ }, [fetchCapabilityPacks]);
243
149
 
244
150
  useEffect(() => {
245
- void load().then((nextConfig) => {
246
- if (nextConfig && !autoStartedRef.current) {
247
- autoStartedRef.current = true;
248
- void startClaudeRef.current?.(nextConfig.selectedWorkDir);
249
- }
250
- });
151
+ void load();
251
152
  }, [load]);
252
153
 
253
- const refreshConsole = useCallback(async () => {
254
- await load();
255
- await startClaude(workDirInput || undefined);
256
- }, [load, startClaude, workDirInput]);
257
-
258
- const runWorkflowPrompt = useCallback(
259
- async (prompt: WorkflowPromptSummary) => {
260
- if (!config?.workflowFolder) return;
261
- if (!ptyIdRef.current) {
262
- await startClaude();
263
- }
264
- const ptyId = ptyIdRef.current;
265
- if (!ptyId) return;
266
- const result = await api.systemManager.readWorkflowPrompt(config.workflowFolder, prompt.id);
267
- writeTerminalLine(`$ # workflow: ${prompt.label}`);
268
- api.terminal.write(ptyId, `${result.content}\r`);
269
- },
270
- [config?.workflowFolder, startClaude, writeTerminalLine]
271
- );
272
-
273
- const titlePath = useMemo(
274
- () =>
275
- formatPathForTitle(config?.selectedWorkDir ?? (workDirInput || status?.defaultWorkDir || '')),
276
- [config?.selectedWorkDir, status?.defaultWorkDir, workDirInput]
277
- );
154
+ const adminWorkflowCommandSuggestions = useMemo(() => {
155
+ const workflowSuggestions = [...workflowPrompts]
156
+ .sort((a, b) => (a.order ?? 999) - (b.order ?? 999))
157
+ .map((prompt) => buildWorkflowCommandSuggestion(prompt, 'admin-workflow'));
158
+ const packSuggestions = buildCapabilityPackCommandSuggestions(capabilityPacks, 'admin-loop', {
159
+ forceNamespacedAliases: collectSlashSuggestionAliases(workflowSuggestions),
160
+ });
161
+ return [...workflowSuggestions, ...packSuggestions];
162
+ }, [capabilityPacks, workflowPrompts]);
163
+ const adminMembers = useMemo(() => buildAdminLoopMember(adminTeamData), [adminTeamData]);
164
+ const adminTasks = adminTeamData?.tasks ?? EMPTY_ADMIN_TASKS;
278
165
 
279
166
  return (
280
167
  <div className="flex size-full flex-col bg-[var(--color-surface)] p-4 text-[var(--color-text)]">
@@ -289,63 +176,90 @@ export const SystemManagerView = ({
289
176
  <TerminalSquare size={14} className="text-[var(--color-text-muted)]" />
290
177
  {SYSTEM_MANAGER_DISPLAY_NAME}
291
178
  </div>
292
- <Input
293
- value={workDirInput}
294
- onChange={(event) => setWorkDirInput(event.target.value)}
295
- onKeyDown={(e) => { if (e.key === 'Enter') void refreshConsole(); }}
296
- className="h-8 min-w-[220px] flex-1 border-[var(--color-border)] bg-[var(--color-surface)] font-mono text-xs text-[var(--color-text)]"
297
- placeholder={titlePath || '工作目录'}
298
- />
299
- <FolderBrowser value={workDirInput} onChange={setWorkDirInput} />
300
- <div className="shrink-0 text-[11px] text-[var(--color-text-muted)]">
301
- {running ? 'claude running' : (status?.localStatus ?? 'starting')}
179
+ <div className="ml-auto shrink-0 text-[11px] text-[var(--color-text-muted)]">
180
+ {status?.localStatus ?? 'ready'}
302
181
  </div>
303
182
  <Button
304
183
  size="sm"
305
184
  variant="outline"
306
185
  className="h-8 shrink-0 border-[var(--color-border)]"
307
- disabled={starting}
308
- onClick={() => void refreshConsole()}
186
+ onClick={() => setBindingDialogOpen(true)}
309
187
  >
310
- {starting ? <Loader2 size={13} className="animate-spin" /> : <RefreshCw size={13} />}
311
- 刷新
188
+ <Settings2 size={13} />
189
+ 运行时
312
190
  </Button>
313
191
  </div>
314
192
 
315
- <div className="min-h-0 flex-1 bg-[var(--color-surface)] p-2">
316
- <div
317
- ref={terminalHostRef}
318
- className="size-full overflow-hidden rounded-lg bg-[var(--color-surface)]"
319
- />
320
- </div>
193
+ <div className="min-h-0 flex-1 overflow-y-auto bg-[var(--color-surface)] p-4">
194
+ <div className="mx-auto flex w-full max-w-5xl flex-col gap-4">
195
+ <div className="rounded-xl border border-indigo-500/20 bg-[var(--color-surface-raised)] p-4 shadow-sm">
196
+ <div className="flex flex-wrap items-start justify-between gap-3">
197
+ <div className="min-w-0">
198
+ <div className="font-mono text-sm text-[var(--color-text)]">helm 指令台</div>
199
+ <p className="mt-2 max-w-3xl text-sm leading-6 text-[var(--color-text-secondary)]">
200
+ 全局巡检、诊断、复盘、治理和改进提案。团队消息、runtime 注入和派单在 Team Loop
201
+ 指令台。
202
+ </p>
203
+ </div>
204
+ <span className="rounded-full border border-[var(--color-border-subtle)] px-2 py-0.5 text-[10px] text-[var(--color-text-muted)]">
205
+ {workflowPrompts.length} workflows
206
+ </span>
207
+ </div>
208
+ <div className="mt-3 grid gap-2 text-[11px] text-[var(--color-text-muted)] sm:grid-cols-3">
209
+ <div className="rounded-lg border border-[var(--color-border-subtle)] bg-[var(--color-surface)] px-3 py-2">
210
+ 作用域:{formatPathForTitle(status?.adminWorkDir ?? '—')}
211
+ </div>
212
+ <div className="rounded-lg border border-[var(--color-border-subtle)] bg-[var(--color-surface)] px-3 py-2">
213
+ 命令源:全局 Hermit / 团队 `.claude/commands` / workflows
214
+ </div>
215
+ <div className="rounded-lg border border-[var(--color-border-subtle)] bg-[var(--color-surface)] px-3 py-2">
216
+ 默认边界:只读/报告/提案优先
217
+ </div>
218
+ </div>
219
+ </div>
321
220
 
322
- {(workflowPrompts.length || warnings.length || error || loading) && (
323
- <div className="border-t border-[var(--color-border)] bg-[var(--color-surface)] px-4 py-3">
324
- {workflowPrompts.length ? (
325
- <div className="flex flex-wrap gap-2">
326
- {workflowPrompts.map((prompt) => (
327
- <button
328
- key={prompt.id}
329
- type="button"
330
- className="rounded-full border border-[var(--color-border)] bg-[var(--color-surface-raised)] px-3 py-1 font-mono text-[11px] text-[var(--color-text-secondary)] hover:border-[var(--color-border-emphasis)] hover:text-[var(--color-text)]"
331
- disabled={starting}
332
- onClick={() => void runWorkflowPrompt(prompt)}
333
- >
334
- {prompt.label}
335
- </button>
336
- ))}
221
+ {(warnings.length || error || loading) && (
222
+ <div className="rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-raised)] px-3 py-2 text-xs">
223
+ {warnings.length ? (
224
+ <div className="text-amber-300">{warnings.join(';')}</div>
225
+ ) : null}
226
+ {error ? <div className="text-red-300">{error}</div> : null}
227
+ {loading ? (
228
+ <div className="text-[var(--color-text-muted)]">加载 Helm Loop 配置中...</div>
229
+ ) : null}
230
+ </div>
231
+ )}
232
+
233
+ <LoopConsolePanel
234
+ teamName={SYSTEM_MANAGER_TEAM_NAME}
235
+ members={adminMembers}
236
+ tasks={adminTasks}
237
+ isTeamAlive={adminTeamData?.isAlive}
238
+ isProvisioning={loading}
239
+ currentLeadSessionId={adminTeamData?.config.leadSessionId}
240
+ leadProjectPath={adminTeamData?.config.projectPath}
241
+ sessions={adminSessions}
242
+ commandSuggestions={adminWorkflowCommandSuggestions}
243
+ slashCommandMode="session"
244
+ pendingRepliesByMember={pendingRepliesByMember}
245
+ onPendingReplyChange={setPendingRepliesByMember}
246
+ />
247
+
248
+ {!workflowPrompts.length && !loading ? (
249
+ <div className="rounded-xl border border-dashed border-[var(--color-border)] bg-[var(--color-surface)] px-4 py-5 text-center text-sm text-[var(--color-text-muted)]">
250
+ 当前没有可用 Helm Loop workflow。Hermit 默认命令会预装到
251
+ `~/.claude/commands/hermit`;团队自定义命令可添加到 `.claude/commands` 或
252
+ workflows。
337
253
  </div>
338
- ) : null}
339
- {warnings.length ? (
340
- <div className="mt-2 text-xs text-amber-300">{warnings.join(';')}</div>
341
- ) : null}
342
- {error ? <div className="mt-2 text-xs text-red-300">{error}</div> : null}
343
- {loading ? (
344
- <div className="mt-2 text-xs text-[var(--color-text-muted)]">加载控制台配置中...</div>
345
254
  ) : null}
346
255
  </div>
347
- )}
256
+ </div>
348
257
  </div>
258
+ <RuntimeConfigDialog
259
+ open={bindingDialogOpen}
260
+ teamName={SYSTEM_MANAGER_TEAM_NAME}
261
+ onClose={() => setBindingDialogOpen(false)}
262
+ />
349
263
  </div>
350
264
  );
351
265
  };
@@ -3,7 +3,14 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { cn } from '@renderer/lib/utils';
4
4
  import { useStore } from '@renderer/store';
5
5
  import { deriveTaskDisplayId } from '@shared/utils/taskIdentity';
6
- import { CheckCircle2, ClipboardList, PlayCircle, Calendar, Columns3, RefreshCw } from 'lucide-react';
6
+ import {
7
+ CheckCircle2,
8
+ ClipboardList,
9
+ PlayCircle,
10
+ Calendar,
11
+ Columns3,
12
+ RefreshCw,
13
+ } from 'lucide-react';
7
14
  import { useShallow } from 'zustand/react/shallow';
8
15
 
9
16
  import { KanbanColumn } from '../team/kanban/KanbanColumn';
@@ -15,7 +22,7 @@ type TasksSubTab = 'overview' | 'schedules';
15
22
  type OverviewStatus = Extract<TeamTaskStatus, 'pending' | 'in_progress' | 'completed'>;
16
23
 
17
24
  const SUB_TABS: { id: TasksSubTab; label: string; icon: React.ReactNode }[] = [
18
- { id: 'overview', label: '任务总览', icon: <Columns3 size={13} /> },
25
+ { id: 'overview', label: 'Loop 任务总览', icon: <Columns3 size={13} /> },
19
26
  { id: 'schedules', label: '定时任务', icon: <Calendar size={13} /> },
20
27
  ];
21
28
 
@@ -174,7 +181,7 @@ const TaskOverviewPool = (): React.JSX.Element => {
174
181
  if (globalTasksLoading && !globalTasksInitialized) {
175
182
  return (
176
183
  <div className="flex h-full items-center justify-center text-sm text-[var(--color-text-muted)]">
177
- 加载团队任务…
184
+ 加载 Loop 任务…
178
185
  </div>
179
186
  );
180
187
  }
@@ -191,7 +198,7 @@ const TaskOverviewPool = (): React.JSX.Element => {
191
198
  onChange={(e) => setTeamFilter(e.target.value)}
192
199
  className={selectCls}
193
200
  >
194
- <option value="all">全部团队</option>
201
+ <option value="all">全部 Loop workspace</option>
195
202
  {teamOptions.map(([teamName, displayName]) => (
196
203
  <option key={teamName} value={teamName}>
197
204
  {displayName}
@@ -242,38 +249,51 @@ const TaskOverviewPool = (): React.JSX.Element => {
242
249
  </button>
243
250
  </div>
244
251
 
245
- {/* Kanban columns reuse team kanban styling */}
246
- <div className="w-full min-w-0 max-w-full overflow-x-auto overflow-y-hidden pb-6">
247
- <div className="grid min-w-[900px] grid-cols-3 items-start gap-3">
248
- {COLUMNS.map((column) => {
249
- const tasks = grouped.get(column.id) ?? [];
250
- return (
251
- <KanbanColumn
252
- key={column.id}
253
- title={column.title}
254
- count={tasks.length}
255
- icon={column.icon}
256
- headerBg={column.headerBg}
257
- bodyBg={column.bodyBg}
258
- >
259
- {tasks.length === 0 ? (
260
- <div className="py-6 text-center text-[11px] text-[var(--color-text-muted)] opacity-40">
261
- No tasks
262
- </div>
263
- ) : (
264
- tasks.map((task) => (
265
- <GlobalOverviewTaskCard
266
- key={`${task.teamName}:${task.id}`}
267
- task={task}
268
- onOpen={() => openGlobalTaskDetail(task.teamName, task.id)}
269
- />
270
- ))
271
- )}
272
- </KanbanColumn>
273
- );
274
- })}
252
+ {overviewTasks.length === 0 ? (
253
+ // Genuinely empty task bus (not just hidden by filters) — show an
254
+ // explanatory empty state so a fresh user / QA reader doesn't mistake
255
+ // the light content for a rendering bug (QA F-3).
256
+ <div className="flex h-full flex-col items-center justify-center gap-2 text-center">
257
+ <ClipboardList size={28} className="text-[var(--color-text-muted)] opacity-30" />
258
+ <p className="text-sm text-[var(--color-text-muted)]">暂无 Loop 任务</p>
259
+ <p className="max-w-sm text-[11px] leading-5 text-[var(--color-text-muted)] opacity-60">
260
+ 各团队看板上 pending / in_progress / completed 的任务会自动汇总到这里。
261
+ </p>
275
262
  </div>
276
- </div>
263
+ ) : (
264
+ /* Kanban columns — reuse team kanban styling */
265
+ <div className="w-full min-w-0 max-w-full overflow-x-auto overflow-y-hidden pb-6">
266
+ <div className="grid min-w-[900px] grid-cols-3 items-start gap-3">
267
+ {COLUMNS.map((column) => {
268
+ const tasks = grouped.get(column.id) ?? [];
269
+ return (
270
+ <KanbanColumn
271
+ key={column.id}
272
+ title={column.title}
273
+ count={tasks.length}
274
+ icon={column.icon}
275
+ headerBg={column.headerBg}
276
+ bodyBg={column.bodyBg}
277
+ >
278
+ {tasks.length === 0 ? (
279
+ <div className="py-6 text-center text-[11px] text-[var(--color-text-muted)] opacity-40">
280
+ No tasks
281
+ </div>
282
+ ) : (
283
+ tasks.map((task) => (
284
+ <GlobalOverviewTaskCard
285
+ key={`${task.teamName}:${task.id}`}
286
+ task={task}
287
+ onOpen={() => openGlobalTaskDetail(task.teamName, task.id)}
288
+ />
289
+ ))
290
+ )}
291
+ </KanbanColumn>
292
+ );
293
+ })}
294
+ </div>
295
+ </div>
296
+ )}
277
297
  </div>
278
298
  );
279
299
  };
@@ -302,7 +322,10 @@ const GlobalOverviewTaskCard = ({
302
322
  <span className="mt-0.5 shrink-0 text-[9px] tabular-nums text-[var(--color-text-muted)] opacity-50">
303
323
  #{task.displayId ?? deriveTaskDisplayId(task.id)}
304
324
  </span>
305
- <h5 className="min-w-0 flex-1 line-clamp-2 text-[11px] font-medium" style={{ color: 'var(--color-text)' }}>
325
+ <h5
326
+ className="line-clamp-2 min-w-0 flex-1 text-[11px] font-medium"
327
+ style={{ color: 'var(--color-text)' }}
328
+ >
306
329
  {task.subject}
307
330
  </h5>
308
331
  </div>
@@ -311,7 +334,10 @@ const GlobalOverviewTaskCard = ({
311
334
  {dispatchFrom} → {dispatchTo}
312
335
  </span>
313
336
  ) : null}
314
- <div className="mt-1.5 flex items-center gap-1.5 text-[10px]" style={{ color: 'var(--color-text-muted)' }}>
337
+ <div
338
+ className="mt-1.5 flex items-center gap-1.5 text-[10px]"
339
+ style={{ color: 'var(--color-text-muted)' }}
340
+ >
315
341
  <span>{task.teamDisplayName}</span>
316
342
  <span style={{ opacity: 0.3 }}>·</span>
317
343
  <span>{ownerLabel}</span>