refactor: 移除 Team 和 Swarm 协作功能
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
功能论证结论:Team(团队)和 Swarm(协作)为零后端支持的 纯前端 localStorage 空壳,Pipeline 系统已完全覆盖其全部能力。 删除 16 个文件,约 7,950 行代码: - 5 个组件:TeamCollaborationView, TeamOrchestrator, TeamList, DevQALoop, SwarmDashboard - 1 个 Store:teamStore.ts - 3 个 Client/库:team-client.ts, useTeamEvents.ts, agent-swarm.ts - 1 个类型文件:team.ts - 4 个测试文件 - 1 个文档(归档 swarm-coordination.md) 修改 4 个文件: - Sidebar.tsx:移除"团队"和"协作"导航项 - App.tsx:移除 team/swarm 视图路由 - types/index.ts:移除 team 类型导出 - chatStore.ts:移除 dispatchSwarmTask 方法 更新 CHANGELOG.md 和功能文档 README.md
This commit is contained in:
@@ -4,7 +4,6 @@ import type { AgentStreamDelta } from '../lib/gateway-client';
|
||||
import { getClient } from './connectionStore';
|
||||
import { intelligenceClient } from '../lib/intelligence-client';
|
||||
import { getMemoryExtractor } from '../lib/memory-extractor';
|
||||
import { getAgentSwarm } from '../lib/agent-swarm';
|
||||
import { getSkillDiscovery } from '../lib/skill-discovery';
|
||||
import { useOfflineStore, isOffline } from './offlineStore';
|
||||
import { useConnectionStore } from './connectionStore';
|
||||
@@ -98,7 +97,6 @@ interface ChatState {
|
||||
newConversation: () => void;
|
||||
switchConversation: (id: string) => void;
|
||||
deleteConversation: (id: string) => void;
|
||||
dispatchSwarmTask: (description: string, style?: 'sequential' | 'parallel' | 'debate') => Promise<string | null>;
|
||||
searchSkills: (query: string) => { results: Array<{ id: string; name: string; description: string }>; totalAvailable: number };
|
||||
}
|
||||
|
||||
@@ -545,39 +543,6 @@ export const useChatStore = create<ChatState>()(
|
||||
}
|
||||
},
|
||||
|
||||
dispatchSwarmTask: async (description: string, style?: 'sequential' | 'parallel' | 'debate') => {
|
||||
try {
|
||||
const swarm = getAgentSwarm();
|
||||
const task = swarm.createTask(description, {
|
||||
communicationStyle: style || 'parallel',
|
||||
});
|
||||
|
||||
// Set up executor that uses the connected client
|
||||
swarm.setExecutor(async (agentId: string, prompt: string, context?: string) => {
|
||||
const client = getClient();
|
||||
const fullPrompt = context ? `${context}\n\n${prompt}` : prompt;
|
||||
const result = await client.chat(fullPrompt, { agentId: agentId.startsWith('clone_') ? undefined : agentId });
|
||||
return result?.response || '(无响应)';
|
||||
});
|
||||
|
||||
const result = await swarm.execute(task);
|
||||
|
||||
// Add swarm result as assistant message
|
||||
const swarmMsg: Message = {
|
||||
id: `swarm_${Date.now()}`,
|
||||
role: 'assistant',
|
||||
content: result.summary || '协作任务完成',
|
||||
timestamp: new Date(),
|
||||
};
|
||||
get().addMessage(swarmMsg);
|
||||
|
||||
return result.task.id;
|
||||
} catch (err) {
|
||||
log.warn('Swarm dispatch failed:', err);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
searchSkills: (query: string) => {
|
||||
const discovery = getSkillDiscovery();
|
||||
const result = discovery.searchSkills(query);
|
||||
|
||||
@@ -1,608 +0,0 @@
|
||||
/**
|
||||
* Team Store - Multi-Agent Team Collaboration State Management
|
||||
*
|
||||
* Manages team orchestration, task assignment, Dev↔QA loops,
|
||||
* and real-time collaboration state.
|
||||
*
|
||||
* @module store/teamStore
|
||||
*/
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type {
|
||||
Team,
|
||||
TeamMember,
|
||||
TeamTask,
|
||||
TeamTaskStatus,
|
||||
TeamMemberRole,
|
||||
DevQALoop,
|
||||
DevQALoopState,
|
||||
CreateTeamRequest,
|
||||
AddTeamTaskRequest,
|
||||
TeamMetrics,
|
||||
CollaborationEvent,
|
||||
ReviewFeedback,
|
||||
TaskDeliverable,
|
||||
} from '../types/team';
|
||||
// === Store State ===
|
||||
|
||||
interface TeamStoreState {
|
||||
// Data
|
||||
teams: Team[];
|
||||
activeTeam: Team | null;
|
||||
metrics: TeamMetrics | null;
|
||||
|
||||
// UI State
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
selectedTaskId: string | null;
|
||||
selectedMemberId: string | null;
|
||||
|
||||
// Real-time events
|
||||
recentEvents: CollaborationEvent[];
|
||||
|
||||
// Actions - Team Management
|
||||
loadTeams: () => Promise<void>;
|
||||
createTeam: (request: CreateTeamRequest) => Promise<Team | null>;
|
||||
deleteTeam: (teamId: string) => Promise<boolean>;
|
||||
setActiveTeam: (team: Team | null) => void;
|
||||
|
||||
// Actions - Member Management
|
||||
addMember: (teamId: string, agentId: string, role: TeamMemberRole) => Promise<TeamMember | null>;
|
||||
removeMember: (teamId: string, memberId: string) => Promise<boolean>;
|
||||
updateMemberRole: (teamId: string, memberId: string, role: TeamMemberRole) => Promise<boolean>;
|
||||
|
||||
// Actions - Task Management
|
||||
addTask: (request: AddTeamTaskRequest) => Promise<TeamTask | null>;
|
||||
updateTaskStatus: (teamId: string, taskId: string, status: TeamTaskStatus) => Promise<boolean>;
|
||||
assignTask: (teamId: string, taskId: string, memberId: string) => Promise<boolean>;
|
||||
submitDeliverable: (teamId: string, taskId: string, deliverable: TaskDeliverable) => Promise<boolean>;
|
||||
|
||||
// Actions - Dev↔QA Loop
|
||||
startDevQALoop: (teamId: string, taskId: string, developerId: string, reviewerId: string) => Promise<DevQALoop | null>;
|
||||
submitReview: (teamId: string, loopId: string, feedback: Omit<ReviewFeedback, 'reviewedAt' | 'reviewerId'>) => Promise<boolean>;
|
||||
updateLoopState: (teamId: string, loopId: string, state: DevQALoopState) => Promise<boolean>;
|
||||
|
||||
// Actions - Events
|
||||
addEvent: (event: CollaborationEvent) => void;
|
||||
clearEvents: () => void;
|
||||
|
||||
// Actions - UI
|
||||
setSelectedTask: (taskId: string | null) => void;
|
||||
setSelectedMember: (memberId: string | null) => void;
|
||||
clearError: () => void;
|
||||
}
|
||||
|
||||
// === Helper Functions ===
|
||||
|
||||
const generateId = () => `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
const calculateMetrics = (team: Team): TeamMetrics => {
|
||||
const completedTasks = team.tasks.filter(t => t.status === 'completed');
|
||||
const totalTasks = team.tasks.length;
|
||||
const reviewedTasks = completedTasks.filter(t => t.reviewFeedback);
|
||||
|
||||
const avgCompletionTime = completedTasks.length > 0
|
||||
? completedTasks.reduce((sum, t) => {
|
||||
if (t.startedAt && t.completedAt) {
|
||||
return sum + (new Date(t.completedAt).getTime() - new Date(t.startedAt).getTime());
|
||||
}
|
||||
return sum;
|
||||
}, 0) / completedTasks.length
|
||||
: 0;
|
||||
|
||||
const approvedReviews = reviewedTasks.filter(t => t.reviewFeedback?.verdict === 'approved');
|
||||
const passRate = reviewedTasks.length > 0
|
||||
? (approvedReviews.length / reviewedTasks.length) * 100
|
||||
: 0;
|
||||
|
||||
const totalIterations = team.activeLoops.reduce((sum, loop) => sum + loop.iterationCount, 0);
|
||||
const avgIterations = team.activeLoops.length > 0
|
||||
? totalIterations / team.activeLoops.length
|
||||
: 0;
|
||||
|
||||
const escalations = team.activeLoops.filter(loop => loop.state === 'escalated').length;
|
||||
|
||||
const efficiency = totalTasks > 0
|
||||
? Math.min(100, (completedTasks.length / totalTasks) * 100 * (passRate / 100))
|
||||
: 0;
|
||||
|
||||
return {
|
||||
tasksCompleted: completedTasks.length,
|
||||
avgCompletionTime,
|
||||
passRate,
|
||||
avgIterations,
|
||||
escalations,
|
||||
efficiency,
|
||||
};
|
||||
};
|
||||
|
||||
// === Store Implementation ===
|
||||
|
||||
export const useTeamStore = create<TeamStoreState>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
// Initial State
|
||||
teams: [],
|
||||
activeTeam: null,
|
||||
metrics: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
selectedTaskId: null,
|
||||
selectedMemberId: null,
|
||||
recentEvents: [],
|
||||
|
||||
// Team Management
|
||||
loadTeams: async () => {
|
||||
set({ isLoading: true, error: null });
|
||||
try {
|
||||
// For now, load from localStorage until API is available
|
||||
// Note: persist middleware stores data as { state: { teams: [...] }, version: ... }
|
||||
const stored = localStorage.getItem('zclaw-teams');
|
||||
let teams: Team[] = [];
|
||||
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored);
|
||||
// Handle persist middleware format
|
||||
if (parsed?.state?.teams && Array.isArray(parsed.state.teams)) {
|
||||
teams = parsed.state.teams;
|
||||
} else if (Array.isArray(parsed)) {
|
||||
// Direct array format (legacy)
|
||||
teams = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
set({ teams, isLoading: false });
|
||||
} catch (error) {
|
||||
console.error('[TeamStore] Failed to load teams:', error);
|
||||
set({ teams: [], isLoading: false });
|
||||
}
|
||||
},
|
||||
|
||||
createTeam: async (request: CreateTeamRequest) => {
|
||||
set({ isLoading: true, error: null });
|
||||
try {
|
||||
const now = new Date().toISOString();
|
||||
const members: TeamMember[] = request.memberAgents.map((m) => ({
|
||||
id: generateId(),
|
||||
agentId: m.agentId,
|
||||
name: `Agent-${m.agentId.slice(0, 4)}`,
|
||||
role: m.role,
|
||||
skills: [],
|
||||
workload: 0,
|
||||
status: 'idle',
|
||||
maxConcurrentTasks: m.role === 'orchestrator' ? 5 : 2,
|
||||
currentTasks: [],
|
||||
}));
|
||||
|
||||
const team: Team = {
|
||||
id: generateId(),
|
||||
name: request.name,
|
||||
description: request.description,
|
||||
members,
|
||||
tasks: [],
|
||||
pattern: request.pattern,
|
||||
activeLoops: [],
|
||||
status: 'active',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = [...state.teams, team];
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return { teams, activeTeam: team, isLoading: false };
|
||||
});
|
||||
|
||||
return team;
|
||||
} catch (error) {
|
||||
set({ error: (error as Error).message, isLoading: false });
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
deleteTeam: async (teamId: string) => {
|
||||
set({ isLoading: true, error: null });
|
||||
try {
|
||||
set(state => {
|
||||
const teams = state.teams.filter(t => t.id !== teamId);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? null : state.activeTeam,
|
||||
isLoading: false
|
||||
};
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
set({ error: (error as Error).message, isLoading: false });
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
setActiveTeam: (team: Team | null) => {
|
||||
set(() => ({
|
||||
activeTeam: team,
|
||||
metrics: team ? calculateMetrics(team) : null,
|
||||
}));
|
||||
},
|
||||
|
||||
// Member Management
|
||||
addMember: async (teamId: string, agentId: string, role: TeamMemberRole) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return null;
|
||||
|
||||
const member: TeamMember = {
|
||||
id: generateId(),
|
||||
agentId,
|
||||
name: `Agent-${agentId.slice(0, 4)}`,
|
||||
role,
|
||||
skills: [],
|
||||
workload: 0,
|
||||
status: 'idle',
|
||||
maxConcurrentTasks: role === 'orchestrator' ? 5 : 2,
|
||||
currentTasks: [],
|
||||
};
|
||||
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
members: [...team.members, member],
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
};
|
||||
});
|
||||
|
||||
return member;
|
||||
},
|
||||
|
||||
removeMember: async (teamId: string, memberId: string) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return false;
|
||||
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
members: team.members.filter(m => m.id !== memberId),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
updateMemberRole: async (teamId: string, memberId: string, role: TeamMemberRole) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return false;
|
||||
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
members: team.members.map(m =>
|
||||
m.id === memberId ? { ...m, role, maxConcurrentTasks: role === 'orchestrator' ? 5 : 2 } : m
|
||||
),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Task Management
|
||||
addTask: async (request: AddTeamTaskRequest) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === request.teamId);
|
||||
if (!team) return null;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const task: TeamTask = {
|
||||
id: generateId(),
|
||||
title: request.title,
|
||||
description: request.description,
|
||||
status: request.assigneeId ? 'assigned' : 'pending',
|
||||
priority: request.priority,
|
||||
assigneeId: request.assigneeId,
|
||||
dependencies: request.dependencies || [],
|
||||
type: request.type,
|
||||
estimate: request.estimate,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
tasks: [...team.tasks, task],
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === request.teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === request.teamId ? updatedTeam : state.activeTeam,
|
||||
metrics: state.activeTeam?.id === request.teamId ? calculateMetrics(updatedTeam) : state.metrics,
|
||||
};
|
||||
});
|
||||
|
||||
return task;
|
||||
},
|
||||
|
||||
updateTaskStatus: async (teamId: string, taskId: string, status: TeamTaskStatus) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return false;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
tasks: team.tasks.map(t => {
|
||||
if (t.id !== taskId) return t;
|
||||
const updates: Partial<TeamTask> = { status, updatedAt: now };
|
||||
if (status === 'in_progress' && !t.startedAt) {
|
||||
updates.startedAt = now;
|
||||
}
|
||||
if (status === 'completed') {
|
||||
updates.completedAt = now;
|
||||
}
|
||||
return { ...t, ...updates };
|
||||
}),
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
metrics: state.activeTeam?.id === teamId ? calculateMetrics(updatedTeam) : state.metrics,
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
assignTask: async (teamId: string, taskId: string, memberId: string) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return false;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
tasks: team.tasks.map(t =>
|
||||
t.id === taskId
|
||||
? { ...t, assigneeId: memberId, status: 'assigned' as TeamTaskStatus, updatedAt: now }
|
||||
: t
|
||||
),
|
||||
members: team.members.map(m =>
|
||||
m.id === memberId
|
||||
? { ...m, currentTasks: [...m.currentTasks, taskId], workload: (m.workload + 25) }
|
||||
: m
|
||||
),
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
submitDeliverable: async (teamId: string, taskId: string, deliverable: TaskDeliverable) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return false;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
tasks: team.tasks.map(t =>
|
||||
t.id === taskId
|
||||
? { ...t, deliverable, status: 'review' as TeamTaskStatus, updatedAt: now }
|
||||
: t
|
||||
),
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Dev↔QA Loop
|
||||
startDevQALoop: async (teamId: string, taskId: string, developerId: string, reviewerId: string) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return null;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const loop: DevQALoop = {
|
||||
id: generateId(),
|
||||
developerId,
|
||||
reviewerId,
|
||||
taskId,
|
||||
state: 'developing',
|
||||
iterationCount: 0,
|
||||
maxIterations: 3,
|
||||
feedbackHistory: [],
|
||||
startedAt: now,
|
||||
lastUpdatedAt: now,
|
||||
};
|
||||
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
activeLoops: [...team.activeLoops, loop],
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
};
|
||||
});
|
||||
|
||||
return loop;
|
||||
},
|
||||
|
||||
submitReview: async (teamId: string, loopId: string, feedback: Omit<ReviewFeedback, 'reviewedAt' | 'reviewerId'>) => {
|
||||
const state = get();
|
||||
const team = state.teams.find(t => t.id === teamId);
|
||||
if (!team) return false;
|
||||
|
||||
const loop = team.activeLoops.find(l => l.id === loopId);
|
||||
if (!loop) return false;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const fullFeedback: ReviewFeedback = {
|
||||
...feedback,
|
||||
reviewedAt: now,
|
||||
reviewerId: loop.reviewerId,
|
||||
};
|
||||
|
||||
let newState: DevQALoopState;
|
||||
let newIterationCount = loop.iterationCount;
|
||||
|
||||
if (feedback.verdict === 'approved') {
|
||||
newState = 'approved';
|
||||
} else if (newIterationCount >= loop.maxIterations - 1) {
|
||||
newState = 'escalated';
|
||||
} else {
|
||||
newState = 'revising';
|
||||
newIterationCount++;
|
||||
}
|
||||
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
tasks: team.tasks.map(t =>
|
||||
t.id === loop.taskId
|
||||
? { ...t, reviewFeedback: fullFeedback, updatedAt: now }
|
||||
: t
|
||||
),
|
||||
activeLoops: team.activeLoops.map(l =>
|
||||
l.id === loopId
|
||||
? {
|
||||
...l,
|
||||
state: newState,
|
||||
iterationCount: newIterationCount,
|
||||
feedbackHistory: [...l.feedbackHistory, fullFeedback],
|
||||
lastUpdatedAt: now,
|
||||
}
|
||||
: l
|
||||
),
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
metrics: state.activeTeam?.id === teamId ? calculateMetrics(updatedTeam) : state.metrics,
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
updateLoopState: async (teamId: string, loopId: string, state: DevQALoopState) => {
|
||||
const teamStore = get();
|
||||
const team = teamStore.teams.find(t => t.id === teamId);
|
||||
if (!team) return false;
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const updatedTeam = {
|
||||
...team,
|
||||
activeLoops: team.activeLoops.map(l =>
|
||||
l.id === loopId
|
||||
? { ...l, state, lastUpdatedAt: now }
|
||||
: l
|
||||
),
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
set(state => {
|
||||
const teams = state.teams.map(t => t.id === teamId ? updatedTeam : t);
|
||||
localStorage.setItem('zclaw-teams', JSON.stringify(teams));
|
||||
return {
|
||||
teams,
|
||||
activeTeam: state.activeTeam?.id === teamId ? updatedTeam : state.activeTeam,
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// Events
|
||||
addEvent: (event: CollaborationEvent) => {
|
||||
set(state => ({
|
||||
recentEvents: [event, ...state.recentEvents].slice(0, 100),
|
||||
}));
|
||||
},
|
||||
|
||||
clearEvents: () => {
|
||||
set({ recentEvents: [] });
|
||||
},
|
||||
|
||||
// UI
|
||||
setSelectedTask: (taskId: string | null) => {
|
||||
set({ selectedTaskId: taskId });
|
||||
},
|
||||
|
||||
setSelectedMember: (memberId: string | null) => {
|
||||
set({ selectedMemberId: memberId });
|
||||
},
|
||||
|
||||
clearError: () => {
|
||||
set({ error: null });
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'zclaw-teams',
|
||||
partialize: (state) => ({
|
||||
teams: state.teams,
|
||||
activeTeam: state.activeTeam,
|
||||
}),
|
||||
},
|
||||
));
|
||||
Reference in New Issue
Block a user