@yancyyu/openhermit 1.6.28 → 1.6.29

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 (96) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-CQm6jUR1.js +52 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-Ba5njic5.js → TeamGraphOverlay-h0WDfifv.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-BvnK-OC1.js → _basePickBy-CgG_tjgX.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-DmFYXx9G.js → _baseUniq-DwPTU9lP.js} +1 -1
  5. package/dist-renderer/assets/{arc-DX4ZQFY4.js → arc-7nIrGRzY.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DfYr3vEN.js → architectureDiagram-VXUJARFQ-BYhA6Ev2.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-DuXdVeWn.js → blockDiagram-VD42YOAC-BVpZUGDg.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-Bw2nixXe.js → c4Diagram-YG6GDRKO-DsdreMQ9.js} +1 -1
  9. package/dist-renderer/assets/channel-C0SqeFU7.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-DLiNGQoE.js → chunk-4BX2VUAB-CcoAs7Jd.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-B1L_8VIF.js → chunk-55IACEB6-CGGAOoXd.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-DaZMWKGk.js → chunk-B4BG7PRW-FhpTEPvD.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-ku-dflJG.js → chunk-DI55MBZ5-DoYySbm1.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-DV-mF1dP.js → chunk-FMBD7UC4-e9l2tGHG.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-ByGcDFQ0.js → chunk-QN33PNHL-DeiXVTCy.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-7dv-Min8.js → chunk-QZHKN3VN-DC2UJLJM.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-WdXL5fTu.js → chunk-TZMSLE5B-BHFD9eZI.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-DWew1HpM.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-DWew1HpM.js +1 -0
  20. package/dist-renderer/assets/clone-Dm-k63Yr.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-CNcsvqPl.js → cose-bilkent-S5V4N54A-BdybQraU.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-DBNx4qqx.js → dagre-6UL2VRFP-DdF3pwM3.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-BfVlT6sT.js → diagram-PSM6KHXK-B9Ldd3nh.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-HvVjs0K6.js → diagram-QEK2KX5R-XEqkrbpu.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-DYb_KnWS.js → diagram-S2PKOQOG-CipwtY59.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-Ba-IgI5G.js → erDiagram-Q2GNP2WA-BB-2ISGo.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-2iDN8Kpj.js → flowDiagram-NV44I4VS-B8XmJ0u2.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-Byjf8Fa3.js → ganttDiagram-JELNMOA3-D-8XglBb.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DbKvfZ_j.js → gitGraphDiagram-V2S2FVAM-DL4ChakD.js} +1 -1
  30. package/dist-renderer/assets/{graph-Enirf-f8.js → graph-BiFNoBjP.js} +1 -1
  31. package/dist-renderer/assets/{index-AjxP_rE_.js → index-6m1ZAymG.js} +1 -1
  32. package/dist-renderer/assets/index-BhellmRb.css +1 -0
  33. package/dist-renderer/assets/{index-DY1zqsb6.js → index-BowUl0Jb.js} +540 -536
  34. package/dist-renderer/assets/{index-CtlzGepK.js → index-Dp3kJTEe.js} +1 -1
  35. package/dist-renderer/assets/{index-COZPUWJW.js → index-TOpt_T7A.js} +1 -1
  36. package/dist-renderer/assets/{index-DdhqolqE.js → index-qNBNjW4K.js} +1 -1
  37. package/dist-renderer/assets/{index-ChR1D6ZF.js → index-vAykq1H1.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D6uicwz1.js → infoDiagram-HS3SLOUP-DRIBfHDi.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DqwZsXlQ.js → journeyDiagram-XKPGCS4Q-BOMiigU4.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-fCDVhVUm.js → kanban-definition-3W4ZIXB7-DDxeyjod.js} +1 -1
  41. package/dist-renderer/assets/{layout-CPFgj98r.js → layout-DNANbrI4.js} +1 -1
  42. package/dist-renderer/assets/{linear-CYiQ7Y3M.js → linear-DxEJi1yT.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-D31dS2KE.js → mindmap-definition-VGOIOE7T-nBfGriW8.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-BOsCJfds.js → pieDiagram-ADFJNKIX-Din5j6sV.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CYTVQCfr.js → quadrantDiagram-AYHSOK5B-DMVK2BEQ.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CODCFpkt.js → requirementDiagram-UZGBJVZJ-6SC94Gg_.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-Z4ce9ZtZ.js → sankeyDiagram-TZEHDZUN-CD2gghhu.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-CmS9TxhW.js → sequenceDiagram-WL72ISMW-BnhkN7nZ.js} +1 -1
  49. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-o9k-ns3q.js → stateDiagram-FKZM4ZOC-Bn8XdYX-.js} +1 -1
  50. package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-CxHMyEt1.js → stateDiagram-v2-4FDKWEC3-1b6sI1_g.js} +1 -1
  51. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-B6T3zrde.js → timeline-definition-IT6M3QCI-CNs3RPoa.js} +1 -1
  52. package/dist-renderer/assets/treemap-GDKQZRPO-DU_yr827.js +162 -0
  53. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CleBrdqc.js → xychartDiagram-PRI3JC2R-B8o5J2f3.js} +1 -1
  54. package/dist-renderer/index.html +2 -2
  55. package/package.json +1 -1
  56. package/src/main/server.ts +699 -179
  57. package/src/main/services/session-intelligence/UsageTelemetryService.ts +33 -18
  58. package/src/main/services/teams-mvp/CollaborationBoardService.ts +310 -0
  59. package/src/main/services/teams-mvp/TaskDispatchService.ts +880 -95
  60. package/src/main/services/teams-mvp/TeamProvisioningService.ts +58 -19
  61. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +25 -2
  62. package/src/main/services/teams-mvp/index.ts +3 -0
  63. package/src/renderer/App.tsx +5 -0
  64. package/src/renderer/api/httpClient.ts +67 -0
  65. package/src/renderer/components/layout/PaneContent.tsx +2 -0
  66. package/src/renderer/components/layout/SortableTab.tsx +1 -0
  67. package/src/renderer/components/layout/TabBarActions.tsx +12 -12
  68. package/src/renderer/components/schedules/SchedulesView.tsx +54 -22
  69. package/src/renderer/components/settings/sections/AdvancedSection.tsx +1 -1
  70. package/src/renderer/components/settings/sections/TaskBusSection.tsx +129 -79
  71. package/src/renderer/components/tasks/TasksView.tsx +343 -0
  72. package/src/renderer/components/team/TeamDetailView.tsx +20 -98
  73. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +1 -1
  74. package/src/renderer/components/team/editor/EditorContextMenu.tsx +8 -23
  75. package/src/renderer/components/team/editor/EditorFileTree.tsx +0 -4
  76. package/src/renderer/components/team/editor/EditorSelectionMenu.tsx +1 -8
  77. package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +0 -10
  78. package/src/renderer/components/team/kanban/KanbanBoard.tsx +5 -1
  79. package/src/renderer/components/team/members/MemberDetailDialog.tsx +8 -33
  80. package/src/renderer/components/team/messages/MessageComposer.tsx +39 -3
  81. package/src/renderer/components/team/messages/MessagesPanel.tsx +72 -2
  82. package/src/renderer/components/team/messages/StatusBlock.tsx +2 -24
  83. package/src/renderer/components/team/schedule/ScheduleEmptyState.tsx +1 -1
  84. package/src/renderer/components/ui/MentionableTextarea.tsx +0 -1
  85. package/src/renderer/store/slices/scheduleSlice.ts +21 -0
  86. package/src/renderer/store/slices/teamSlice.ts +59 -23
  87. package/src/renderer/types/tabs.ts +1 -0
  88. package/src/shared/types/api.ts +29 -0
  89. package/src/shared/types/team.ts +104 -1
  90. package/dist-renderer/assets/ProjectEditorOverlay-A4DZTvSy.js +0 -57
  91. package/dist-renderer/assets/channel-Pre42N5O.js +0 -1
  92. package/dist-renderer/assets/classDiagram-2ON5EDUG-CdJsTJsj.js +0 -1
  93. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CdJsTJsj.js +0 -1
  94. package/dist-renderer/assets/clone-BjQBiNfj.js +0 -1
  95. package/dist-renderer/assets/index-BIOJremZ.css +0 -1
  96. package/dist-renderer/assets/treemap-GDKQZRPO-CVd5GNDw.js +0 -162
@@ -68,6 +68,7 @@ import {
68
68
  Terminal,
69
69
  Trash2,
70
70
  Loader2,
71
+ MessageSquare,
71
72
  Users,
72
73
  } from 'lucide-react';
73
74
  import { useShallow } from 'zustand/react/shallow';
@@ -1591,6 +1592,24 @@ export const TeamDetailView = ({
1591
1592
  openLaunchDialog('relaunch');
1592
1593
  }, [openLaunchDialog]);
1593
1594
 
1595
+ const handleStartCcConnectTeam = useCallback(() => {
1596
+ void (async () => {
1597
+ if (!data?.config.projectPath) {
1598
+ openLaunchDialog('launch');
1599
+ return;
1600
+ }
1601
+ await api.ccSettings.restart();
1602
+ await api.teams.launchTeam({
1603
+ teamName,
1604
+ cwd: data.config.projectPath,
1605
+ });
1606
+ window.setTimeout(() => {
1607
+ void fetchTeams();
1608
+ void selectTeam(teamName);
1609
+ }, 1500);
1610
+ })();
1611
+ }, [data?.config.projectPath, fetchTeams, openLaunchDialog, selectTeam, teamName]);
1612
+
1594
1613
  const handleLaunchDialogSubmit = useCallback(
1595
1614
  async (request: TeamLaunchRequest): Promise<void> => {
1596
1615
  await launchTeam(request);
@@ -1904,11 +1923,8 @@ export const TeamDetailView = ({
1904
1923
  pendingRepliesByMember,
1905
1924
  onPendingReplyChange: setPendingRepliesByMember,
1906
1925
  onMemberClick: handleSelectMember,
1907
- onTaskClick: handleOpenTask,
1908
- onCreateTaskFromMessage: handleCreateTaskFromMessage,
1909
1926
  onReplyToMessage: handleReplyToMessage,
1910
1927
  onRestartTeam: handleRestartTeam,
1911
- onTaskIdClick: handleTaskIdClick,
1912
1928
  inlineScrollContainerRef: contentRef,
1913
1929
  showPositionControls: false,
1914
1930
  }),
@@ -1917,12 +1933,9 @@ export const TeamDetailView = ({
1917
1933
  data?.config.leadSessionId,
1918
1934
  data?.isAlive,
1919
1935
  data?.tasks,
1920
- handleCreateTaskFromMessage,
1921
- handleOpenTask,
1922
1936
  handleReplyToMessage,
1923
1937
  handleRestartTeam,
1924
1938
  handleSelectMember,
1925
- handleTaskIdClick,
1926
1939
  pendingRepliesByMember,
1927
1940
  teamName,
1928
1941
  teamSessionIds,
@@ -2245,10 +2258,7 @@ export const TeamDetailView = ({
2245
2258
  </div>
2246
2259
 
2247
2260
  {!data.isAlive && !isTeamProvisioning ? (
2248
- <TeamOfflineStatusBanner
2249
- teamName={teamName}
2250
- onLaunch={() => openLaunchDialog('launch')}
2251
- />
2261
+ <TeamOfflineStatusBanner teamName={teamName} onLaunch={handleStartCcConnectTeam} />
2252
2262
  ) : null}
2253
2263
 
2254
2264
  <div ref={provisioningBannerRef}>
@@ -2276,16 +2286,12 @@ export const TeamDetailView = ({
2276
2286
  <TeamMemberListBridge
2277
2287
  teamName={teamName}
2278
2288
  members={membersWithLiveBranches}
2279
- memberTaskCounts={memberTaskCounts}
2280
- taskMap={taskMap}
2281
2289
  pendingRepliesByMember={pendingRepliesByMember}
2282
2290
  isTeamAlive={data.isAlive}
2283
2291
  isTeamProvisioning={isTeamProvisioning}
2284
2292
  launchParams={launchParams}
2285
2293
  onMemberClick={handleSelectMember}
2286
2294
  onSendMessage={handleSendMessageToMember}
2287
- onAssignTask={handleAssignTaskToMember}
2288
- onOpenTask={handleOpenTaskById}
2289
2295
  onRestartMember={handleRestartMember}
2290
2296
  onSkipMemberForLaunch={handleSkipMemberForLaunch}
2291
2297
  />
@@ -2412,7 +2418,6 @@ export const TeamDetailView = ({
2412
2418
  const task = data?.tasks.find((t) => t.id === taskId);
2413
2419
  await updateTaskStatus(teamName, taskId, 'pending');
2414
2420
 
2415
- // Notify assignee directly via inbox — they'll see it immediately
2416
2421
  if (task?.owner) {
2417
2422
  try {
2418
2423
  await api.teams.sendMessage(teamName, {
@@ -2425,7 +2430,6 @@ export const TeamDetailView = ({
2425
2430
  }
2426
2431
  }
2427
2432
 
2428
- // Also notify team lead so they can reassign/coordinate
2429
2433
  if (data?.isAlive) {
2430
2434
  try {
2431
2435
  const ownerSuffix = task?.owner
@@ -2556,16 +2560,7 @@ export const TeamDetailView = ({
2556
2560
  setReplyQuote(undefined);
2557
2561
  setSendDialogOpen(true);
2558
2562
  }}
2559
- onAssignTask={() => {
2560
- const name = selectedMember?.name ?? '';
2561
- closeSelectedMemberDialog();
2562
- openCreateTaskDialog('', '', name);
2563
- }}
2564
2563
  onRestartMember={handleRestartMember}
2565
- onTaskClick={(task) => {
2566
- closeSelectedMemberDialog();
2567
- setSelectedTask(task);
2568
- }}
2569
2564
  onUpdateRole={async (memberName, role) => {
2570
2565
  setUpdatingRoleLoading(true);
2571
2566
  try {
@@ -2598,22 +2593,6 @@ export const TeamDetailView = ({
2598
2593
  }}
2599
2594
  />
2600
2595
 
2601
- <CreateTaskDialog
2602
- open={createTaskDialog.open}
2603
- teamName={teamName}
2604
- members={activeMembers}
2605
- tasks={data.tasks}
2606
- isTeamAlive={data.isAlive && !isTeamProvisioning}
2607
- defaultSubject={createTaskDialog.defaultSubject}
2608
- defaultDescription={createTaskDialog.defaultDescription}
2609
- defaultOwner={createTaskDialog.defaultOwner}
2610
- defaultStartImmediately={createTaskDialog.defaultStartImmediately}
2611
- defaultChip={createTaskDialog.defaultChip}
2612
- onClose={closeCreateTaskDialog}
2613
- onSubmit={handleCreateTask}
2614
- submitting={creatingTask}
2615
- />
2616
-
2617
2596
  <EditTeamDialog
2618
2597
  open={editDialogOpen}
2619
2598
  teamName={teamName}
@@ -2806,63 +2785,6 @@ export const TeamDetailView = ({
2806
2785
  }}
2807
2786
  />
2808
2787
 
2809
- <TaskDetailDialog
2810
- open={selectedTask !== null}
2811
- task={selectedTask}
2812
- teamName={teamName}
2813
- kanbanTaskState={
2814
- selectedTask ? data?.kanbanState.tasks[selectedTask.id] : undefined
2815
- }
2816
- taskMap={taskMap}
2817
- members={activeMembers}
2818
- onClose={() => setSelectedTask(null)}
2819
- onScrollToTask={(taskId) => {
2820
- setSelectedTask(null);
2821
- const el = document.querySelector(`[data-task-id="${taskId}"]`);
2822
- if (el) {
2823
- el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
2824
- el.classList.remove('kanban-card-focus-pulse');
2825
- void (el as HTMLElement).offsetWidth;
2826
- el.classList.add('kanban-card-focus-pulse');
2827
- el.addEventListener(
2828
- 'animationend',
2829
- () => el.classList.remove('kanban-card-focus-pulse'),
2830
- { once: true }
2831
- );
2832
- }
2833
- }}
2834
- onOwnerChange={(taskId, owner) => {
2835
- void (async () => {
2836
- try {
2837
- await updateTaskOwner(teamName, taskId, owner);
2838
- } catch {
2839
- // error via store
2840
- }
2841
- })();
2842
- }}
2843
- onViewChanges={handleViewChangesForFile}
2844
- onOpenInEditor={(filePath) => {
2845
- const { revealFileInEditor } = useStore.getState();
2846
- revealFileInEditor(filePath);
2847
- }}
2848
- onDeleteTask={handleDeleteTask}
2849
- />
2850
-
2851
- <TrashDialog
2852
- open={trashOpen}
2853
- tasks={deletedTasks}
2854
- onClose={() => setTrashOpen(false)}
2855
- onRestore={(taskId) => {
2856
- void (async () => {
2857
- try {
2858
- await restoreTask(teamName, taskId);
2859
- } catch {
2860
- // error via store
2861
- }
2862
- })();
2863
- }}
2864
- />
2865
-
2866
2788
  <ChangeReviewDialog
2867
2789
  open={reviewDialogState.open}
2868
2790
  onOpenChange={(open) =>
@@ -2058,7 +2058,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
2058
2058
  ) : effectiveTeamName ? (
2059
2059
  `为团队“${effectiveTeamName}”创建自动运行计划`
2060
2060
  ) : (
2061
- '创建团队任务自动执行计划'
2061
+ '创建团队自动运行计划'
2062
2062
  );
2063
2063
 
2064
2064
  const submitLabel = isLaunchMode
@@ -16,7 +16,6 @@ import {
16
16
  FilePlus,
17
17
  FolderOpen,
18
18
  FolderPlus,
19
- ListTodo,
20
19
  MessageSquare,
21
20
  Pencil,
22
21
  Trash2,
@@ -39,8 +38,6 @@ interface EditorContextMenuProps {
39
38
  onNewFolder: (parentDir: string) => void;
40
39
  onDelete: (path: string) => void;
41
40
  onRename: (path: string) => void;
42
- /** Trigger "Create Task" with a file mention (files only, not directories) */
43
- onCreateTask?: (filePath: string) => void;
44
41
  /** Trigger "Write Teammate" with a file mention (files only, not directories) */
45
42
  onSendMessage?: (filePath: string) => void;
46
43
  }
@@ -56,7 +53,6 @@ export const EditorContextMenu = ({
56
53
  onNewFolder,
57
54
  onDelete,
58
55
  onRename,
59
- onCreateTask,
60
56
  onSendMessage,
61
57
  }: EditorContextMenuProps): React.ReactElement => {
62
58
  const [target, setTarget] = useState<TargetEntry | null>(null);
@@ -183,27 +179,16 @@ export const EditorContextMenu = ({
183
179
  )}
184
180
 
185
181
  {/* Team actions — file only */}
186
- {target && !target.isDir && (onCreateTask || onSendMessage) && (
182
+ {target && !target.isDir && onSendMessage && (
187
183
  <>
188
184
  <ContextMenu.Separator className="my-1 h-px bg-border" />
189
- {onSendMessage && (
190
- <ContextMenu.Item
191
- className="flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-xs text-text outline-none hover:bg-surface-raised focus:bg-surface-raised"
192
- onSelect={() => onSendMessage(target.path)}
193
- >
194
- <MessageSquare className="size-3.5 text-text-muted" />
195
- Write Teammate
196
- </ContextMenu.Item>
197
- )}
198
- {onCreateTask && (
199
- <ContextMenu.Item
200
- className="flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-xs text-text outline-none hover:bg-surface-raised focus:bg-surface-raised"
201
- onSelect={() => onCreateTask(target.path)}
202
- >
203
- <ListTodo className="size-3.5 text-text-muted" />
204
- Create Task
205
- </ContextMenu.Item>
206
- )}
185
+ <ContextMenu.Item
186
+ className="flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-xs text-text outline-none hover:bg-surface-raised focus:bg-surface-raised"
187
+ onSelect={() => onSendMessage(target.path)}
188
+ >
189
+ <MessageSquare className="size-3.5 text-text-muted" />
190
+ Write Teammate
191
+ </ContextMenu.Item>
207
192
  </>
208
193
  )}
209
194
  </ContextMenu.Content>
@@ -54,8 +54,6 @@ import type { FileTreeEntry, GitFileStatusType } from '@shared/types/editor';
54
54
  interface EditorFileTreeProps {
55
55
  selectedFilePath: string | null;
56
56
  onFileSelect: (filePath: string) => void;
57
- /** Trigger "Create Task" with a file mention from context menu */
58
- onCreateTask?: (filePath: string) => void;
59
57
  /** Trigger "Write Teammate" with a file mention from context menu */
60
58
  onSendMessage?: (filePath: string) => void;
61
59
  }
@@ -91,7 +89,6 @@ let fileTreeRenderCount = 0;
91
89
  export const EditorFileTree = ({
92
90
  selectedFilePath,
93
91
  onFileSelect,
94
- onCreateTask,
95
92
  onSendMessage,
96
93
  }: EditorFileTreeProps): React.ReactElement => {
97
94
  fileTreeRenderCount++;
@@ -452,7 +449,6 @@ export const EditorFileTree = ({
452
449
  onNewFolder={handleNewFolder}
453
450
  onDelete={handleDelete}
454
451
  onRename={handleRename}
455
- onCreateTask={onCreateTask}
456
452
  onSendMessage={onSendMessage}
457
453
  >
458
454
  <DndContext
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { Button } from '@renderer/components/ui/button';
9
9
  import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
10
- import { ListTodo, MessageSquare } from 'lucide-react';
10
+ import { MessageSquare } from 'lucide-react';
11
11
 
12
12
  import type { EditorSelectionInfo } from '@shared/types/editor';
13
13
 
@@ -20,7 +20,6 @@ interface EditorSelectionMenuProps {
20
20
  /** Bounding rect of the editor content container (for viewport → container conversion) */
21
21
  containerRect: DOMRect;
22
22
  onSendMessage: () => void;
23
- onCreateTask: () => void;
24
23
  }
25
24
 
26
25
  // =============================================================================
@@ -39,7 +38,6 @@ export const EditorSelectionMenu = ({
39
38
  info,
40
39
  containerRect,
41
40
  onSendMessage,
42
- onCreateTask,
43
41
  }: EditorSelectionMenuProps): React.ReactElement | null => {
44
42
  if (!info.text.trim()) return null;
45
43
 
@@ -71,11 +69,6 @@ export const EditorSelectionMenu = ({
71
69
  label="Write Teammate"
72
70
  onClick={onSendMessage}
73
71
  />
74
- <MenuButton
75
- icon={<ListTodo className="size-3.5" />}
76
- label="Create Task"
77
- onClick={onCreateTask}
78
- />
79
72
  </div>
80
73
  );
81
74
  };
@@ -628,12 +628,6 @@ export const ProjectEditorOverlay = ({
628
628
  <EditorFileTree
629
629
  selectedFilePath={activeTabId}
630
630
  onFileSelect={handleFileSelect}
631
- onCreateTask={
632
- onEditorAction
633
- ? (filePath: string) =>
634
- onEditorAction(buildFileAction('createTask', filePath, projectPath))
635
- : undefined
636
- }
637
631
  onSendMessage={
638
632
  onEditorAction
639
633
  ? (filePath: string) =>
@@ -829,10 +823,6 @@ export const ProjectEditorOverlay = ({
829
823
  onEditorAction(buildSelectionAction('sendMessage', selectionInfo));
830
824
  setSelectionInfo(null);
831
825
  }}
832
- onCreateTask={() => {
833
- onEditorAction(buildSelectionAction('createTask', selectionInfo));
834
- setSelectionInfo(null);
835
- }}
836
826
  />
837
827
  )}
838
828
  </div>
@@ -274,7 +274,11 @@ export const KanbanBoard = ({
274
274
  }: KanbanBoardProps): React.JSX.Element => {
275
275
  const boardRef = useRef<HTMLDivElement>(null);
276
276
  const scrollRestoreTimeoutsRef = useRef<number[]>([]);
277
- const [viewMode, setViewMode] = useState<KanbanViewMode>('grid');
277
+ const [viewMode, setViewMode] = useState<KanbanViewMode>('columns');
278
+
279
+ useEffect(() => {
280
+ setViewMode('columns');
281
+ }, []);
278
282
  const enableTaskSorting =
279
283
  viewMode === 'columns' && !!onColumnOrderChange && sort.field === 'manual';
280
284
 
@@ -26,7 +26,6 @@ import { type MemberActivityFilter, type MemberDetailTab } from './memberDetailT
26
26
  import { MemberLaunchDiagnosticsButton } from './MemberLaunchDiagnosticsButton';
27
27
  import { MemberMessagesTab } from './MemberMessagesTab';
28
28
  import { MemberStatsTab } from './MemberStatsTab';
29
- import { MemberTasksTab } from './MemberTasksTab';
30
29
  import { MemberWorkspaceTab } from './MemberWorkspaceTab';
31
30
 
32
31
  import type { TeamLaunchParams } from '@renderer/store/slices/teamSlice';
@@ -56,8 +55,10 @@ interface MemberDetailDialogProps {
56
55
  launchParams?: TeamLaunchParams;
57
56
  onClose: () => void;
58
57
  onSendMessage: () => void;
59
- onAssignTask: () => void;
60
- onTaskClick: (task: TeamTaskWithKanban) => void;
58
+ /** Deprecated: team tasks UI has been removed, kept for compatibility with older callers/tests. */
59
+ onAssignTask?: () => void;
60
+ /** Deprecated: team tasks UI has been removed, kept for compatibility with older callers/tests. */
61
+ onTaskClick?: (task: TeamTaskWithKanban) => void;
61
62
  onRemoveMember?: () => void;
62
63
  onRestartMember?: (memberName: string) => Promise<void> | void;
63
64
  onUpdateRole?: (memberName: string, role: string | undefined) => Promise<void> | void;
@@ -83,18 +84,12 @@ export const MemberDetailDialog = ({
83
84
  launchParams,
84
85
  onClose,
85
86
  onSendMessage,
86
- onAssignTask,
87
- onTaskClick,
88
87
  onRemoveMember,
89
88
  onRestartMember,
90
89
  onUpdateRole,
91
90
  updatingRole,
92
91
  onViewMemberChanges,
93
92
  }: MemberDetailDialogProps): React.JSX.Element | null => {
94
- const memberTasks = useMemo(
95
- () => (member ? tasks.filter((t) => t.owner === member.name) : []),
96
- [tasks, member]
97
- );
98
93
  const memberMessages = useStore((state) =>
99
94
  selectMemberMessagesForTeamMember(state, teamName, member?.name ?? null)
100
95
  );
@@ -111,17 +106,9 @@ export const MemberDetailDialog = ({
111
106
  }).length;
112
107
  }, [member, memberMessages, members, tasks, teamName]);
113
108
 
114
- const inProgressTasks = useMemo(
115
- () => memberTasks.filter((t) => t.status === 'in_progress').length,
116
- [memberTasks]
117
- );
118
-
119
- const completedTasks = useMemo(
120
- () => memberTasks.filter((t) => t.status === 'completed').length,
121
- [memberTasks]
109
+ const [activeTab, setActiveTab] = useState<MemberDetailTab>(
110
+ initialTab === 'tasks' ? 'workspace' : initialTab
122
111
  );
123
-
124
- const [activeTab, setActiveTab] = useState<MemberDetailTab>(initialTab);
125
112
  const [restarting, setRestarting] = useState(false);
126
113
  const [restartError, setRestartError] = useState<string | null>(null);
127
114
 
@@ -202,8 +189,8 @@ export const MemberDetailDialog = ({
202
189
  </DialogHeader>
203
190
 
204
191
  <MemberDetailStats
205
- totalTasks={memberTasks.length}
206
- inProgressTasks={inProgressTasks}
192
+ totalTasks={0}
193
+ inProgressTasks={0}
207
194
  totalTokens={totalTokens}
208
195
  statsLoading={statsLoading}
209
196
  statsComputedAt={memberStats?.computedAt}
@@ -217,14 +204,6 @@ export const MemberDetailDialog = ({
217
204
  className="min-w-0 overflow-hidden"
218
205
  >
219
206
  <TabsList className="w-full">
220
- <TabsTrigger value="tasks" className="flex-1 gap-1.5">
221
- Tasks
222
- {memberTasks.length > 0 && (
223
- <span className="rounded-full bg-[var(--color-surface)] px-1.5 text-[10px]">
224
- {memberTasks.length}
225
- </span>
226
- )}
227
- </TabsTrigger>
228
207
  <TabsTrigger value="workspace" className="flex-1 gap-1.5">
229
208
  <FolderOpen size={12} />
230
209
  Workspace
@@ -242,9 +221,6 @@ export const MemberDetailDialog = ({
242
221
  Stats
243
222
  </TabsTrigger>
244
223
  </TabsList>
245
- <TabsContent value="tasks">
246
- <MemberTasksTab tasks={memberTasks} onTaskClick={onTaskClick} />
247
- </TabsContent>
248
224
  <TabsContent value="workspace">
249
225
  <MemberWorkspaceTab
250
226
  teamName={teamName}
@@ -260,7 +236,6 @@ export const MemberDetailDialog = ({
260
236
  members={members}
261
237
  tasks={tasks}
262
238
  initialFilter={initialActivityFilter}
263
- onTaskClick={onTaskClick}
264
239
  />
265
240
  </TabsContent>
266
241
  <TabsContent value="stats">
@@ -69,6 +69,11 @@ interface MessageComposerProps {
69
69
  actionMode?: AgentActionMode,
70
70
  taskRefs?: TaskRef[]
71
71
  ) => void;
72
+ onDispatchTask?: (
73
+ toTeam: string,
74
+ subject: string,
75
+ description: string
76
+ ) => Promise<boolean | void> | boolean | void;
72
77
  }
73
78
 
74
79
  export const MessageComposer = ({
@@ -86,6 +91,7 @@ export const MessageComposer = ({
86
91
  onSessionChange,
87
92
  textareaRef: externalTextareaRef,
88
93
  onSend,
94
+ onDispatchTask,
89
95
  }: MessageComposerProps): React.JSX.Element => {
90
96
  const internalTextareaRef = useRef<HTMLTextAreaElement>(null);
91
97
  const textareaRef = useMemo(() => {
@@ -223,7 +229,26 @@ export const MessageComposer = ({
223
229
  ? '斜杠命令需要团队负责人在线'
224
230
  : null
225
231
  : null;
226
- const canSend =
232
+ const teamDispatch = useMemo(() => {
233
+ const match = trimmed.match(/^@([^\s]+)\s+([\s\S]+)$/);
234
+ if (!match || !onDispatchTask) return null;
235
+ const mentioned = match[1];
236
+ const subject = match[2]?.trim();
237
+ if (!mentioned || !subject) return null;
238
+ const targetTeam = teamMentionSuggestions.find((team) => {
239
+ const slug = team.id.startsWith('team:') ? team.id.slice('team:'.length) : team.id;
240
+ return slug === mentioned || team.name === mentioned;
241
+ });
242
+ const slug = targetTeam
243
+ ? targetTeam.id.startsWith('team:')
244
+ ? targetTeam.id.slice('team:'.length)
245
+ : targetTeam.id
246
+ : mentioned;
247
+ return { slug, subject };
248
+ }, [onDispatchTask, teamMentionSuggestions, trimmed]);
249
+ const canDispatchToTeam =
250
+ teamDispatch !== null && trimmed.length > 0 && trimmed.length <= MAX_TEXT_LENGTH && !sending;
251
+ const canSendRegularMessage =
227
252
  recipient.length > 0 &&
228
253
  trimmed.length > 0 &&
229
254
  trimmed.length <= MAX_TEXT_LENGTH &&
@@ -231,6 +256,7 @@ export const MessageComposer = ({
231
256
  !isProvisioning &&
232
257
  !attachmentsBlocked &&
233
258
  !slashCommandRestrictionReason;
259
+ const canSend = canDispatchToTeam || canSendRegularMessage;
234
260
 
235
261
  // Track whether we initiated a send — clear draft only on confirmed success
236
262
  const pendingSendRef = useRef(false);
@@ -238,9 +264,19 @@ export const MessageComposer = ({
238
264
  const handleSend = useCallback(() => {
239
265
  if (!canSend) return;
240
266
  dismissMentionsRef.current?.();
241
- pendingSendRef.current = true;
242
267
  const taskRefs = extractTaskRefsFromText(draft.text, taskSuggestions);
243
268
  const serialized = serializeChipsWithText(trimmed, draft.chips);
269
+
270
+ if (teamDispatch && onDispatchTask) {
271
+ void Promise.resolve(
272
+ onDispatchTask(teamDispatch.slug, teamDispatch.subject, serialized)
273
+ ).then((dispatched) => {
274
+ if (dispatched !== false) draft.clearDraft();
275
+ });
276
+ return;
277
+ }
278
+
279
+ pendingSendRef.current = true;
244
280
  onSend(
245
281
  recipient,
246
282
  serialized,
@@ -249,7 +285,7 @@ export const MessageComposer = ({
249
285
  undefined,
250
286
  taskRefs
251
287
  );
252
- }, [canSend, recipient, trimmed, onSend, draft.attachments, draft.chips, taskSuggestions]);
288
+ }, [canSend, recipient, trimmed, onSend, draft, taskSuggestions, teamDispatch, onDispatchTask]);
253
289
 
254
290
  // Clear draft only after send completes successfully (sending: true → false, no error)
255
291
  useEffect(() => {