@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
@@ -0,0 +1,218 @@
1
+ /**
2
+ * societyStore 测试 —— Zustand 数据层(TDD 先行)。
3
+ *
4
+ * 注入一个 mock API(不触网络),断言:loadAll 聚合四类数据并管理 loading/error;
5
+ * 每个 mutation(注册/发布/自荐/选派/交付/社交)调用对应 API 后**只刷新受影响切片**。
6
+ */
7
+ import { describe, expect, it, vi } from 'vitest';
8
+
9
+ import { createSocietyStore } from './societyStore';
10
+ import type { SocietyApiClient } from './societyApi';
11
+
12
+ function mockApi(overrides: Partial<SocietyApiClient> = {}): SocietyApiClient {
13
+ return {
14
+ listWorkers: vi.fn().mockResolvedValue([]),
15
+ registerWorker: vi.fn().mockResolvedValue({}),
16
+ getWorker: vi.fn().mockResolvedValue(null),
17
+ listOpenNeeds: vi.fn().mockResolvedValue([]),
18
+ listActiveNeeds: vi.fn().mockResolvedValue([]),
19
+ listAllNeeds: vi.fn().mockResolvedValue([]),
20
+ publishNeed: vi.fn().mockResolvedValue({}),
21
+ volunteer: vi.fn().mockResolvedValue({}),
22
+ selectAssignee: vi.fn().mockResolvedValue({}),
23
+ startNeed: vi.fn().mockResolvedValue({}),
24
+ deliverNeed: vi.fn().mockResolvedValue({}),
25
+ acceptDelivery: vi.fn().mockResolvedValue({}),
26
+ cancelNeed: vi.fn().mockResolvedValue({}),
27
+ listRelationships: vi.fn().mockResolvedValue([]),
28
+ sendMessage: vi.fn().mockResolvedValue({}),
29
+ getFeed: vi.fn().mockResolvedValue([]),
30
+ runAutonomyTick: vi.fn().mockResolvedValue({ ok: true, applied: 0 }),
31
+ autoSelectPending: vi.fn().mockResolvedValue({ ok: true, selected: 0 }),
32
+ ...overrides,
33
+ };
34
+ }
35
+
36
+ describe('createSocietyStore', () => {
37
+ it('loadAll aggregates workers, open needs, relationships, and feed', async () => {
38
+ const api = mockApi({
39
+ listWorkers: vi.fn().mockResolvedValue([{ workerId: 'a' }]),
40
+ listOpenNeeds: vi.fn().mockResolvedValue([{ needId: 'n1' }]),
41
+ listActiveNeeds: vi.fn().mockResolvedValue([{ needId: 'a1', status: 'assigned' }]),
42
+ listRelationships: vi.fn().mockResolvedValue([{ fromWorker: 'a' }]),
43
+ getFeed: vi.fn().mockResolvedValue([{ id: 'm1' }]),
44
+ });
45
+ const store = createSocietyStore(api);
46
+ await store.getState().loadAll();
47
+ const s = store.getState();
48
+ expect(s.workers).toEqual([{ workerId: 'a' }]);
49
+ expect(s.openNeeds).toEqual([{ needId: 'n1' }]);
50
+ expect(s.activeNeeds).toEqual([{ needId: 'a1', status: 'assigned' }]);
51
+ expect(s.relationships).toEqual([{ fromWorker: 'a' }]);
52
+ expect(s.feed).toEqual([{ id: 'm1' }]);
53
+ expect(s.loading).toBe(false);
54
+ expect(s.error).toBeNull();
55
+ });
56
+
57
+ it('refresh reloads all data (loadAllInto alias)', async () => {
58
+ // refresh 是 loadAll 的公开别名(两者都调 loadAllInto),此前无测试调用 → funcs 覆盖缺口。
59
+ const api = mockApi({
60
+ listWorkers: vi.fn().mockResolvedValue([{ workerId: 'a' }]),
61
+ });
62
+ const store = createSocietyStore(api);
63
+ await store.getState().refresh();
64
+ expect(store.getState().workers).toEqual([{ workerId: 'a' }]);
65
+ expect(store.getState().loading).toBe(false);
66
+ });
67
+
68
+ it('sets error and clears loading when loadAll fails', async () => {
69
+ const api = mockApi({ listWorkers: vi.fn().mockRejectedValue(new Error('down')) });
70
+ const store = createSocietyStore(api);
71
+ await store.getState().loadAll();
72
+ const s = store.getState();
73
+ expect(s.error).toBe('down');
74
+ expect(s.loading).toBe(false);
75
+ });
76
+
77
+ it('registerWorker calls the API then reloads only workers (not needs/feed)', async () => {
78
+ const api = mockApi({ listWorkers: vi.fn().mockResolvedValue([{ workerId: 'dev' }]) });
79
+ const store = createSocietyStore(api);
80
+ await store.getState().registerWorker({ workerId: 'dev', name: 'Dev', capabilities: 'code' });
81
+ expect(api.registerWorker).toHaveBeenCalledWith({
82
+ workerId: 'dev',
83
+ name: 'Dev',
84
+ capabilities: 'code',
85
+ });
86
+ expect(api.listWorkers).toHaveBeenCalledTimes(1); // targeted reload
87
+ expect(api.listOpenNeeds).not.toHaveBeenCalled(); // unaffected slice untouched
88
+ expect(api.getFeed).not.toHaveBeenCalled();
89
+ expect(store.getState().workers).toEqual([{ workerId: 'dev' }]);
90
+ });
91
+
92
+ it('publishNeed calls the API then reloads only open needs', async () => {
93
+ const api = mockApi({ listOpenNeeds: vi.fn().mockResolvedValue([{ needId: 'n1' }]) });
94
+ const store = createSocietyStore(api);
95
+ await store
96
+ .getState()
97
+ .publishNeed({ postedBy: 'u', subject: 'X', requiredCapabilities: 'code' });
98
+ expect(api.publishNeed).toHaveBeenCalledWith({
99
+ postedBy: 'u',
100
+ subject: 'X',
101
+ requiredCapabilities: 'code',
102
+ });
103
+ expect(api.listOpenNeeds).toHaveBeenCalledTimes(1);
104
+ expect(api.listWorkers).not.toHaveBeenCalled();
105
+ expect(store.getState().openNeeds).toEqual([{ needId: 'n1' }]);
106
+ });
107
+
108
+ it('volunteer -> select -> deliver -> accept each reload open needs', async () => {
109
+ const api = mockApi();
110
+ const store = createSocietyStore(api);
111
+ const st = store.getState();
112
+ await st.volunteer('n1', 'dev');
113
+ await st.selectAssignee('n1');
114
+ await st.deliverNeed('n1', 'v1');
115
+ await st.acceptDelivery('n1');
116
+ expect(api.volunteer).toHaveBeenCalledWith('n1', 'dev');
117
+ expect(api.selectAssignee).toHaveBeenCalledWith('n1');
118
+ expect(api.deliverNeed).toHaveBeenCalledWith('n1', 'v1');
119
+ expect(api.acceptDelivery).toHaveBeenCalledWith('n1');
120
+ expect(api.listOpenNeeds).toHaveBeenCalledTimes(4);
121
+ });
122
+
123
+ it('sendMessage reloads only the feed (not workers/needs)', async () => {
124
+ const api = mockApi({ getFeed: vi.fn().mockResolvedValue([{ id: 'm1' }]) });
125
+ const store = createSocietyStore(api);
126
+ await store.getState().sendMessage('a', 'b', 'hi');
127
+ expect(api.sendMessage).toHaveBeenCalledWith('a', 'b', 'hi');
128
+ expect(api.getFeed).toHaveBeenCalledTimes(1);
129
+ expect(api.listWorkers).not.toHaveBeenCalled();
130
+ expect(api.listOpenNeeds).not.toHaveBeenCalled();
131
+ expect(store.getState().feed).toEqual([{ id: 'm1' }]);
132
+ });
133
+
134
+ it('runAutonomyTick calls the API then reloads open needs and feed (autonomy changes both)', async () => {
135
+ const api = mockApi({
136
+ runAutonomyTick: vi.fn().mockResolvedValue({ ok: true, applied: 3 }),
137
+ listOpenNeeds: vi.fn().mockResolvedValue([{ needId: 'n1' }]),
138
+ getFeed: vi.fn().mockResolvedValue([{ id: 'm1' }]),
139
+ });
140
+ const store = createSocietyStore(api);
141
+ await store.getState().runAutonomyTick();
142
+ expect(api.runAutonomyTick).toHaveBeenCalledTimes(1);
143
+ expect(api.listOpenNeeds).toHaveBeenCalledTimes(1); // 自荐改变需求的自荐者
144
+ expect(api.getFeed).toHaveBeenCalledTimes(1); // 自荐产生社交消息
145
+ expect(api.listWorkers).not.toHaveBeenCalled(); // 花名册不受影响
146
+ });
147
+
148
+ it('autoSelectPending calls the API then reloads open needs and feed', async () => {
149
+ const api = mockApi({
150
+ autoSelectPending: vi.fn().mockResolvedValue({ ok: true, selected: 2 }),
151
+ listOpenNeeds: vi.fn().mockResolvedValue([{ needId: 'n1' }]),
152
+ getFeed: vi.fn().mockResolvedValue([{ id: 'm1' }]),
153
+ });
154
+ const store = createSocietyStore(api);
155
+ await store.getState().autoSelectPending();
156
+ expect(api.autoSelectPending).toHaveBeenCalledTimes(1);
157
+ expect(api.listOpenNeeds).toHaveBeenCalledTimes(1); // 选派改变需求状态
158
+ expect(api.getFeed).toHaveBeenCalledTimes(1); // 选派产生通知消息
159
+ });
160
+
161
+ it('startNeed and cancelNeed each reload BOTH open and active needs (graph stays in sync)', async () => {
162
+ // reloadNeeds(旧名 reloadOpenNeeds)同时刷新 open + active:画布据 activeNeeds 渲染
163
+ // worker→task 锚点。锁住「需求生命周期 mutation 后两切片都刷新」——否则 cancel 一个
164
+ // assigned 需求(assigned→cancelled 合法,见 iter-9)会让画布 activeNeeds 留陈旧节点,
165
+ // 或诱导后人把 reload「优化」成只刷 open(函数名曾误导,本轮已正名)。
166
+ const api = mockApi({
167
+ listOpenNeeds: vi.fn().mockResolvedValue([{ needId: 'n1' }]),
168
+ listActiveNeeds: vi.fn().mockResolvedValue([{ needId: 'a1', status: 'in_progress' }]),
169
+ });
170
+ const store = createSocietyStore(api);
171
+
172
+ await store.getState().startNeed('n1', 'dev');
173
+ expect(api.startNeed).toHaveBeenCalledWith('n1', 'dev');
174
+ expect(api.listOpenNeeds).toHaveBeenCalledTimes(1);
175
+ expect(api.listActiveNeeds).toHaveBeenCalledTimes(1); // ← 关键:active 也刷新
176
+
177
+ await store.getState().cancelNeed('n1');
178
+ expect(api.cancelNeed).toHaveBeenCalledWith('n1');
179
+ expect(api.listOpenNeeds).toHaveBeenCalledTimes(2);
180
+ expect(api.listActiveNeeds).toHaveBeenCalledTimes(2); // ← cancel 同样双切片刷新
181
+ });
182
+
183
+ it('sets error (and preserves prior data, leaves loading untouched) when a mutation fails', async () => {
184
+ // iter-3 标记的缺口:此前只测了 loadAll 失败;mutation 失败的 error 契约未覆盖。
185
+ // mutate 捕获异常 → 写 error;不触碰数据切片(既有数据保留);mutations 不动 loading。
186
+ const api = mockApi({
187
+ listWorkers: vi.fn().mockResolvedValue([{ workerId: 'a' }]),
188
+ registerWorker: vi.fn().mockRejectedValue(new Error('already exists')),
189
+ });
190
+ const store = createSocietyStore(api);
191
+ await store.getState().loadAll(); // 先装好数据
192
+ expect(store.getState().workers).toEqual([{ workerId: 'a' }]);
193
+
194
+ await store.getState().registerWorker({ workerId: 'a', name: 'A', capabilities: '' });
195
+
196
+ const s = store.getState();
197
+ expect(s.error).toBe('already exists'); // 失败 → 写 error(mutate 的 catch 分支)
198
+ expect(s.loading).toBe(false); // mutation 不动 loading(loadAll 已结束为 false)
199
+ expect(s.workers).toEqual([{ workerId: 'a' }]); // 既有数据未被清空
200
+ });
201
+
202
+ it('stringifies a non-Error thrown value into the error state (loadAll and mutations)', async () => {
203
+ // store 的 catch 兜底 `e instanceof Error ? e.message : String(e)`:非 Error 抛值(裸字符串/
204
+ // 数字等)须 String() 化——error 状态恒为 string 是 UI 渲染前提(否则 React 渲染 number/对象报错)。
205
+ // 现有 loadAll/mutation 失败用例都用 new Error(真臂 e.message);本测补两处 false 臂 String(e)。
206
+ const api = mockApi({
207
+ listWorkers: vi.fn().mockRejectedValue('network down'), // 非 Error(裸字符串)
208
+ registerWorker: vi.fn().mockRejectedValue(42), // 非 Error(数字)
209
+ });
210
+ const store = createSocietyStore(api);
211
+
212
+ await store.getState().loadAll();
213
+ expect(store.getState().error).toBe('network down'); // String('network down') → L63 兜底
214
+
215
+ await store.getState().registerWorker({ workerId: 'x', name: 'X' });
216
+ expect(store.getState().error).toBe('42'); // String(42) → L96 兜底
217
+ });
218
+ });
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Worker Society — 前端状态层(Zustand)。
3
+ *
4
+ * 把 SocietyApiClient 聚合成一个可被 React 组件订阅的 store:workers / 公告板需求 /
5
+ * 关系图 / 活动流,外加 loading/error。每个 mutation(注册/发布/自荐/选派/交付/社交)
6
+ * 调用 API 后**只刷新受影响切片**,避免全量重拉。
7
+ *
8
+ * api 可注入(测试用 mock;生产用 createSocietyApi())。store 是 factory 形式,便于
9
+ * 在测试里每次拿到干净实例。
10
+ */
11
+ import { create } from 'zustand';
12
+
13
+ import {
14
+ createSocietyApi,
15
+ type PublishNeedInput,
16
+ type RegisterWorkerInput,
17
+ type SocietyApiClient,
18
+ } from './societyApi';
19
+ import type { PublishedNeed, Relationship, WorkerProfile } from '../core/domain/models/society';
20
+ import type { SocialMessageRecord } from '../main/infrastructure/crossTeamMessageGateway';
21
+
22
+ export interface SocietyStoreState {
23
+ workers: WorkerProfile[];
24
+ openNeeds: PublishedNeed[];
25
+ /** 仍在生命周期内的需求(open/assigned/in_progress/delivered)—— 画布据此渲染 worker 跑向任务。 */
26
+ activeNeeds: PublishedNeed[];
27
+ relationships: Relationship[];
28
+ feed: SocialMessageRecord[];
29
+ loading: boolean;
30
+ error: string | null;
31
+
32
+ loadAll(): Promise<void>;
33
+ refresh(): Promise<void>;
34
+ registerWorker(input: RegisterWorkerInput): Promise<WorkerProfile | undefined>;
35
+ publishNeed(input: PublishNeedInput): Promise<PublishedNeed | undefined>;
36
+ volunteer(needId: string, workerId: string): Promise<unknown>;
37
+ selectAssignee(needId: string): Promise<unknown>;
38
+ startNeed(needId: string, workerId: string): Promise<unknown>;
39
+ deliverNeed(needId: string, result: string): Promise<unknown>;
40
+ acceptDelivery(needId: string): Promise<unknown>;
41
+ cancelNeed(needId: string): Promise<unknown>;
42
+ sendMessage(fromWorker: string, toWorker: string, text: string): Promise<unknown>;
43
+ runAutonomyTick(): Promise<{ ok: boolean; applied: number } | undefined>;
44
+ autoSelectPending(): Promise<{ ok: boolean; selected: number } | undefined>;
45
+ }
46
+
47
+ /** 拉取四类数据并写入 store;统一管理 loading/error。 */
48
+ async function loadAllInto(
49
+ set: (partial: Partial<SocietyStoreState>) => void,
50
+ api: SocietyApiClient
51
+ ): Promise<void> {
52
+ set({ loading: true, error: null });
53
+ try {
54
+ const [workers, openNeeds, activeNeeds, relationships, feed] = await Promise.all([
55
+ api.listWorkers(),
56
+ api.listOpenNeeds(),
57
+ api.listActiveNeeds(),
58
+ api.listRelationships(),
59
+ api.getFeed(),
60
+ ]);
61
+ set({ workers, openNeeds, activeNeeds, relationships, feed, loading: false });
62
+ } catch (e) {
63
+ set({ loading: false, error: e instanceof Error ? e.message : String(e) });
64
+ }
65
+ }
66
+
67
+ /**
68
+ * 创建一个 society store(默认指向同源 /api/society/*)。
69
+ * @param api 注入的 API 客户端;省略则用 createSocietyApi()。
70
+ */
71
+ export function createSocietyStore(api: SocietyApiClient = createSocietyApi()) {
72
+ const useSocietyStore = create<SocietyStoreState>((set) => {
73
+ // 局部刷新:只重拉受影响的切片,避免全量 loadAll。
74
+ const reloadWorkers = async (): Promise<void> => set({ workers: await api.listWorkers() });
75
+ // 需求切片刷新:open(看板的待办)与 active(画布的生命周期)同源,一起刷新,
76
+ // 这样任意状态流转(自荐/选派/执行/交付)后两处视图都同步。
77
+ const reloadNeeds = async (): Promise<void> => {
78
+ const [openNeeds, activeNeeds] = await Promise.all([
79
+ api.listOpenNeeds(),
80
+ api.listActiveNeeds(),
81
+ ]);
82
+ set({ openNeeds, activeNeeds });
83
+ };
84
+ const reloadFeed = async (): Promise<void> => set({ feed: await api.getFeed() });
85
+
86
+ // 统一 mutation:调命令 → 捕获错误 → 仅刷受影响切片 → 回传命令结果(供调用方给反馈)。
87
+ const mutate = async <T>(
88
+ run: () => Promise<T>,
89
+ after: () => Promise<void>
90
+ ): Promise<T | undefined> => {
91
+ set({ error: null });
92
+ try {
93
+ const result = await run();
94
+ await after();
95
+ return result;
96
+ } catch (e) {
97
+ set({ error: e instanceof Error ? e.message : String(e) });
98
+ return undefined;
99
+ }
100
+ };
101
+
102
+ return {
103
+ workers: [],
104
+ openNeeds: [],
105
+ activeNeeds: [],
106
+ relationships: [],
107
+ feed: [],
108
+ loading: false,
109
+ error: null,
110
+
111
+ loadAll: () => loadAllInto(set, api),
112
+ refresh: () => loadAllInto(set, api),
113
+
114
+ registerWorker: (input) => mutate(() => api.registerWorker(input), reloadWorkers),
115
+ publishNeed: (input) => mutate(() => api.publishNeed(input), reloadNeeds),
116
+ volunteer: (needId, workerId) => mutate(() => api.volunteer(needId, workerId), reloadNeeds),
117
+ selectAssignee: (needId) => mutate(() => api.selectAssignee(needId), reloadNeeds),
118
+ startNeed: (needId, workerId) => mutate(() => api.startNeed(needId, workerId), reloadNeeds),
119
+ deliverNeed: (needId, result) => mutate(() => api.deliverNeed(needId, result), reloadNeeds),
120
+ acceptDelivery: (needId) => mutate(() => api.acceptDelivery(needId), reloadNeeds),
121
+ cancelNeed: (needId) => mutate(() => api.cancelNeed(needId), reloadNeeds),
122
+ sendMessage: (fromWorker, toWorker, text) =>
123
+ mutate(() => api.sendMessage(fromWorker, toWorker, text), reloadFeed),
124
+ runAutonomyTick: () =>
125
+ mutate(
126
+ () => api.runAutonomyTick(),
127
+ // 自治自荐同时改变「需求的自荐者」与「社交活动流」。
128
+ async () => {
129
+ await reloadNeeds();
130
+ await reloadFeed();
131
+ }
132
+ ),
133
+ autoSelectPending: () =>
134
+ mutate(
135
+ () => api.autoSelectPending(),
136
+ // 选派改变需求状态并产生通知消息。
137
+ async () => {
138
+ await reloadNeeds();
139
+ await reloadFeed();
140
+ }
141
+ ),
142
+ };
143
+ });
144
+
145
+ return useSocietyStore;
146
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * societyViewUtils 测试 —— 前端纯可视化逻辑。
3
+ *
4
+ * 图谱引擎不暴露节点尺寸/边宽/透明度字段,故历史上的 workerNodeRadius/edgeWidth/
5
+ * trustOpacity 与看板用的 topWorkersByReputation/activeWorkers/sortNeedsByLifecycle
6
+ * 在「纯图谱」改造后无消费者,已随实现一并删除(见 docs §15 迭代 #6)。本文件只覆盖
7
+ * 当前仍被消费的纯映射:状态标签、声誉档色、状态区分色、头像稳定分配。
8
+ * 纯函数,无 React/DOM 依赖,易测。
9
+ */
10
+ import { describe, expect, it } from 'vitest';
11
+
12
+ import type { PublishedNeed } from '../core/domain/models/society';
13
+ import {
14
+ NEED_STATUS_LABEL,
15
+ needStatusColor,
16
+ pickAvatarUrl,
17
+ reputationColor,
18
+ } from './societyViewUtils';
19
+
20
+ describe('NEED_STATUS_LABEL', () => {
21
+ it('provides a non-empty Chinese label for every need status', () => {
22
+ const statuses: PublishedNeed['status'][] = [
23
+ 'open',
24
+ 'assigned',
25
+ 'in_progress',
26
+ 'delivered',
27
+ 'closed',
28
+ 'expired',
29
+ 'cancelled',
30
+ ];
31
+ for (const s of statuses) {
32
+ expect(typeof NEED_STATUS_LABEL[s]).toBe('string');
33
+ expect(NEED_STATUS_LABEL[s].length).toBeGreaterThan(0);
34
+ }
35
+ });
36
+ });
37
+
38
+ describe('reputationColor', () => {
39
+ it('is green for high, amber for mid, red for low', () => {
40
+ expect(reputationColor(80)).toBe('#16a34a');
41
+ expect(reputationColor(50)).toBe('#d97706');
42
+ expect(reputationColor(20)).toBe('#dc2626');
43
+ });
44
+ });
45
+
46
+ describe('needStatusColor', () => {
47
+ it('assigns a distinct color per status', () => {
48
+ const statuses: PublishedNeed['status'][] = [
49
+ 'open',
50
+ 'assigned',
51
+ 'in_progress',
52
+ 'delivered',
53
+ 'closed',
54
+ 'expired',
55
+ 'cancelled',
56
+ ];
57
+ const colors = statuses.map(needStatusColor);
58
+ expect(new Set(colors).size).toBe(statuses.length); // all distinct
59
+ });
60
+ });
61
+
62
+ describe('pickAvatarUrl (stable sprite assignment)', () => {
63
+ const urls = ['a.png', 'b.png', 'c.png', 'd.png', 'e.png'];
64
+ it('is deterministic — same id always maps to the same url', () => {
65
+ expect(pickAvatarUrl('frontend', urls)).toBe(pickAvatarUrl('frontend', urls));
66
+ });
67
+ it('always returns an in-range url for any id', () => {
68
+ for (const id of ['a', 'b', 'c', 'x', 'y', 'z', 'long-id-123']) {
69
+ expect(urls).toContain(pickAvatarUrl(id, urls));
70
+ }
71
+ });
72
+ it('returns undefined for an empty url set', () => {
73
+ expect(pickAvatarUrl('anyone', [])).toBeUndefined();
74
+ });
75
+ it('distributes distinct ids across the set (not all collapsed to one)', () => {
76
+ const picks = new Set(
77
+ ['w1', 'w2', 'w3', 'w4', 'w5', 'w6', 'w7', 'w8'].map((id) => pickAvatarUrl(id, urls))
78
+ );
79
+ expect(picks.size).toBeGreaterThan(1);
80
+ });
81
+ });
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Worker Society — 前端纯可视化逻辑。
3
+ *
4
+ * 图谱(@claude-teams/agent-graph)自身负责节点尺寸/边样式/粒子:其 GraphNode/GraphEdge
5
+ * 契约不暴露 size/radius/width/opacity 字段,故「声誉=节点大小、协作=边宽、信任=边透明度」
6
+ * 无法经图谱编码——这些信息改由 SocietyNodeOverlay 弹卡呈现(声誉色/状态徽章)。
7
+ * 旧的 workerNodeRadius/edgeWidth/trustOpacity 及看板用的排行榜/排序映射在「纯图谱」改造后
8
+ * 已无消费者(见 docs §15 迭代 #6),一并删除,杜绝死代码。
9
+ *
10
+ * 本文件只保留当前真正被消费的纯映射:
11
+ * - NEED_STATUS_LABEL:需求状态中文标签(弹卡/徽章单一来源);
12
+ * - reputationColor / needStatusColor:声誉档 / 需求状态 → 区分色(弹卡用);
13
+ * - pickAvatarUrl:把 workerId 稳定映射到内置头像列表(图谱用)。
14
+ * 纯函数,无 React/DOM/network 副作用,便于单测与复用。
15
+ */
16
+ import type { NeedStatus, PublishedNeed } from '../core/domain/models/society';
17
+
18
+ /** 需求生命周期状态的中文标签(弹卡/状态徽章共用,单一来源)。 */
19
+ export const NEED_STATUS_LABEL: Record<NeedStatus, string> = {
20
+ open: '招募中',
21
+ assigned: '已选派',
22
+ in_progress: '执行中',
23
+ delivered: '待审核',
24
+ closed: '已完结',
25
+ expired: '已过期',
26
+ cancelled: '已取消',
27
+ };
28
+
29
+ /** 声誉档位颜色:高=绿、中=琥珀、低=红。 */
30
+ export function reputationColor(reputation: number): string {
31
+ if (reputation >= 70) return '#16a34a';
32
+ if (reputation >= 40) return '#d97706';
33
+ return '#dc2626';
34
+ }
35
+
36
+ /** 需求生命周期状态 → 区分色。 */
37
+ export function needStatusColor(status: PublishedNeed['status']): string {
38
+ switch (status) {
39
+ case 'open':
40
+ return '#2563eb';
41
+ case 'assigned':
42
+ return '#7c3aed';
43
+ case 'in_progress':
44
+ return '#d97706';
45
+ case 'delivered':
46
+ return '#0891b2';
47
+ case 'closed':
48
+ return '#16a34a';
49
+ case 'expired':
50
+ return '#9ca3af';
51
+ case 'cancelled':
52
+ return '#dc2626';
53
+ default:
54
+ return '#9ca3af';
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 把一个 id 稳定映射到 url 列表中的某一项(确定性哈希:同 id 永远同一项)。
60
+ * 空列表返回 undefined。供 SocietyGraph 给每个 worker 稳定分配头像(复用 hermit
61
+ * 自带的 participant-avatars 目录)。
62
+ */
63
+ export function pickAvatarUrl(seed: string, urls: readonly string[]): string | undefined {
64
+ if (urls.length === 0) return undefined;
65
+ let h = 0;
66
+ for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;
67
+ return urls[h % urls.length];
68
+ }
@@ -7,6 +7,9 @@
7
7
 
8
8
  import type { CcAgentType } from '@shared/types/ccConnect';
9
9
  import type {
10
+ CapabilityCommandPromptRequest,
11
+ CapabilityPackExportRequest,
12
+ CapabilityPackImportRequest,
10
13
  McpCustomInstallRequest,
11
14
  McpLibraryImportRequest,
12
15
  McpLibraryUpsertRequest,
@@ -18,6 +21,7 @@ import type {
18
21
  SkillWatcherEvent,
19
22
  } from '@shared/types/extensions';
20
23
 
24
+ import { CapabilityPackLoaderService } from '@main/services/extensions/capability-packs/CapabilityPackLoaderService';
21
25
  import { PluginCatalogService } from '@main/services/extensions/catalog/PluginCatalogService';
22
26
  import { ExtensionFacadeService } from '@main/services/extensions/ExtensionFacadeService';
23
27
  import { McpLibraryService } from '@main/services/extensions/library/McpLibraryService';
@@ -52,6 +56,7 @@ function wrapHandler<T>(handler: () => Promise<T>): Promise<IpcResult<T>> {
52
56
 
53
57
  let facade: ExtensionFacadeService | null = null;
54
58
  let mcpLibrary: McpLibraryService | null = null;
59
+ let capabilityPacks: CapabilityPackLoaderService | null = null;
55
60
  let skillsCatalog: SkillsCatalogService | null = null;
56
61
  let skillsMutation: SkillsMutationService | null = null;
57
62
  let skillsWatcher: SkillsWatcherService | null = null;
@@ -71,6 +76,11 @@ function getMcpLibrary(): McpLibraryService {
71
76
  return mcpLibrary;
72
77
  }
73
78
 
79
+ function getCapabilityPacks(): CapabilityPackLoaderService {
80
+ if (!capabilityPacks) capabilityPacks = new CapabilityPackLoaderService();
81
+ return capabilityPacks;
82
+ }
83
+
74
84
  function getSkillsCatalog(): SkillsCatalogService {
75
85
  if (!skillsCatalog) skillsCatalog = new SkillsCatalogService();
76
86
  return skillsCatalog;
@@ -114,6 +124,10 @@ const channels = {
114
124
  MCP_INSTALL: 'extensions:mcp:install',
115
125
  MCP_INSTALL_CUSTOM: 'extensions:mcp:installCustom',
116
126
  MCP_UNINSTALL: 'extensions:mcp:uninstall',
127
+ CAPABILITY_PACKS_LIST: 'extensions:capabilityPacks:list',
128
+ CAPABILITY_PACKS_IMPORT: 'extensions:capabilityPacks:import',
129
+ CAPABILITY_PACKS_EXPORT: 'extensions:capabilityPacks:export',
130
+ CAPABILITY_PACKS_COMMAND_PROMPT: 'extensions:capabilityPacks:commandPrompt',
117
131
  SKILLS_LIST: 'extensions:skills:list',
118
132
  SKILLS_GET_DETAIL: 'extensions:skills:getDetail',
119
133
  SKILLS_UPSERT: 'extensions:skills:upsert',
@@ -249,6 +263,19 @@ export const extensionHandlers = {
249
263
  mcpLibraryImport: (request: McpLibraryImportRequest) =>
250
264
  wrapHandler(() => getMcpLibrary().importFromLive(request)),
251
265
 
266
+ // ── Capability Packs ──
267
+
268
+ capabilityPacksList: () => wrapHandler(() => getCapabilityPacks().list()),
269
+
270
+ capabilityPacksImport: (request: CapabilityPackImportRequest) =>
271
+ wrapHandler(() => getCapabilityPacks().importPack(request)),
272
+
273
+ capabilityPacksExport: (request: CapabilityPackExportRequest) =>
274
+ wrapHandler(() => getCapabilityPacks().exportPack(request)),
275
+
276
+ capabilityPacksCommandPrompt: (request: CapabilityCommandPromptRequest) =>
277
+ wrapHandler(() => getCapabilityPacks().getCommandPrompt(request)),
278
+
252
279
  // ── Skills ──
253
280
 
254
281
  skillsList: (projectPath?: string) => wrapHandler(() => getSkillsCatalog().list(projectPath)),