Files
zclaw_openfang/docs/superpowers/plans/2026-03-21-phase2-domain-reorganization.md
iven 0d4fa96b82
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
refactor: 统一项目名称从OpenFang到ZCLAW
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括:
- 配置文件中的项目名称
- 代码注释和文档引用
- 环境变量和路径
- 类型定义和接口名称
- 测试用例和模拟数据

同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
2026-03-27 07:36:03 +08:00

26 KiB
Raw Blame History

ZCLAW 架构优化 - Phase 2: 领域重组 实施计划

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 按领域重组代码结构,迁移到 Valtio 状态管理,引入 XState 状态机

Architecture: 创建 domains/ 目录,按业务领域组织代码,使用 Valtio 替代 Zustand使用 XState 管理 Hands 状态

Tech Stack: TypeScript, Valtio, XState, React

Spec Reference: docs/superpowers/specs/2026-03-21-architecture-optimization-design.md

Duration: 4 周 (16 人日)


File Structure

New Files

desktop/src/
├── domains/
│   ├── chat/
│   │   ├── index.ts              # 导出入口
│   │   ├── store.ts              # Valtio store
│   │   ├── types.ts              # 类型定义
│   │   ├── api.ts                # API 调用
│   │   └── hooks.ts              # React hooks
│   ├── hands/
│   │   ├── index.ts              # 导出入口
│   │   ├── store.ts              # Valtio store
│   │   ├── machine.ts            # XState 状态机
│   │   ├── types.ts              # 类型定义
│   │   └── hooks.ts              # React hooks
│   ├── intelligence/
│   │   ├── index.ts              # 导出入口
│   │   ├── client.ts             # 统一客户端
│   │   ├── cache.ts              # 缓存策略
│   │   └── types.ts              # 类型定义
│   └── skills/
│       ├── index.ts              # 导出入口
│       ├── store.ts              # Valtio store
│       └── types.ts              # 类型定义
└── shared/
    ├── index.ts                  # 导出入口
    ├── error-handling.ts         # 统一错误处理
    ├── logging.ts                # 统一日志
    └── types.ts                  # 共享类型

Modified Files

desktop/
├── package.json                  # 添加 Valtio, XState 依赖
├── src/store/chatStore.ts        # 重导出 domains/chat
├── src/store/handStore.ts        # 重导出 domains/hands
└── src/components/               # 更新导入路径

Chunk 1: 依赖安装和目录结构

Task 1.1: 安装 Valtio 和 XState

Files:

  • Modify: desktop/package.json

  • Step 1: 安装 Valtio

Run:

cd g:/ZClaw_zclaw/desktop && pnpm add valtio

Expected: valtio 安装成功

  • Step 2: 安装 XState

Run:

cd g:/ZClaw_zclaw/desktop && pnpm add xstate @xstate/react

Expected: xstate 和 @xstate/react 安装成功

  • Step 3: 验证安装

Run:

cd g:/ZClaw_zclaw/desktop && pnpm list valtio xstate @xstate/react

Expected: 显示已安装版本

  • Step 4: 提交依赖更新
cd g:/ZClaw_zclaw && git add desktop/package.json desktop/pnpm-lock.yaml
git commit -m "$(cat <<'EOF'
feat(deps): add Valtio and XState for Phase 2

- Add valtio for Proxy-based state management
- Add xstate and @xstate/react for state machines

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Task 1.2: 创建领域目录结构

Files:

  • Create: desktop/src/domains/chat/ directory

  • Create: desktop/src/domains/hands/ directory

  • Create: desktop/src/domains/intelligence/ directory

  • Create: desktop/src/domains/skills/ directory

  • Create: desktop/src/shared/ directory

  • Step 1: 创建目录

Run:

cd g:/ZClaw_zclaw/desktop/src && mkdir -p domains/chat domains/hands domains/intelligence domains/skills shared
  • Step 2: 提交目录结构
cd g:/ZClaw_zclaw && git add desktop/src/domains desktop/src/shared
git commit -m "$(cat <<'EOF'
refactor: create domains directory structure

- Create domains/chat for chat system
- Create domains/hands for automation
- Create domains/intelligence for AI layer
- Create domains/skills for skill system
- Create shared for common utilities

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Chunk 2: Chat Domain 迁移

Task 2.1: 创建 Chat Domain 类型定义

Files:

  • Create: desktop/src/domains/chat/types.ts

  • Step 1: 提取类型定义

Create desktop/src/domains/chat/types.ts:

/**
 * Chat Domain Types
 *
 * Core types for the chat system.
 */

export interface MessageFile {
  name: string;
  path?: string;
  size?: number;
  type?: string;
}

export interface CodeBlock {
  language?: string;
  filename?: string;
  content?: string;
}

export interface Message {
  id: string;
  role: 'user' | 'assistant' | 'tool' | 'hand' | 'workflow';
  content: string;
  timestamp: Date;
  runId?: string;
  streaming?: boolean;
  toolName?: string;
  toolInput?: string;
  toolOutput?: string;
  error?: string;
  handName?: string;
  handStatus?: string;
  handResult?: unknown;
  workflowId?: string;
  workflowStep?: string;
  workflowStatus?: string;
  workflowResult?: unknown;
  files?: MessageFile[];
  codeBlocks?: CodeBlock[];
}

export interface Conversation {
  id: string;
  title: string;
  messages: Message[];
  sessionKey: string | null;
  agentId: string | null;
  createdAt: Date;
  updatedAt: Date;
}

export interface Agent {
  id: string;
  name: string;
  icon: string;
  color: string;
  lastMessage: string;
  time: string;
}

export interface AgentProfileLike {
  id: string;
  name: string;
  nickname?: string;
  role?: string;
}

export interface ChatState {
  messages: Message[];
  conversations: Conversation[];
  currentConversationId: string | null;
  agents: Agent[];
  currentAgent: Agent | null;
  isStreaming: boolean;
  currentModel: string;
  sessionKey: string | null;
}
  • Step 2: 提交类型定义
cd g:/ZClaw_zclaw && git add desktop/src/domains/chat/types.ts
git commit -m "$(cat <<'EOF'
refactor(chat): extract chat domain types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Task 2.2: 创建 Valtio Chat Store

Files:

  • Create: desktop/src/domains/chat/store.ts

  • Step 1: 创建 Valtio Store

Create desktop/src/domains/chat/store.ts:

/**
 * Chat Domain Store
 *
 * Valtio-based state management for chat.
 * Replaces Zustand for better performance with fine-grained reactivity.
 */
import { proxy, subscribe } from 'valtio';
import type { Message, Conversation, Agent, AgentProfileLike, ChatState } from './types';

// Default agent
const DEFAULT_AGENT: Agent = {
  id: '1',
  name: 'ZCLAW',
  icon: '🦞',
  color: 'bg-gradient-to-br from-orange-500 to-red-500',
  lastMessage: '发送消息开始对话',
  time: '',
};

// Helper functions
function generateConvId(): string {
  return `conv_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
}

function deriveTitle(messages: Message[]): string {
  const firstUser = messages.find(m => m.role === 'user');
  if (firstUser) {
    const text = firstUser.content.trim();
    return text.length > 30 ? text.slice(0, 30) + '...' : text;
  }
  return '新对话';
}

export function toChatAgent(profile: AgentProfileLike): Agent {
  return {
    id: profile.id,
    name: profile.name,
    icon: profile.nickname?.slice(0, 1) || '🦞',
    color: 'bg-gradient-to-br from-orange-500 to-red-500',
    lastMessage: profile.role || '新分身',
    time: '',
  };
}

// State interface with actions
interface ChatStore extends ChatState {
  // Actions
  addMessage: (message: Message) => void;
  updateMessage: (id: string, updates: Partial<Message>) => void;
  setCurrentAgent: (agent: Agent) => void;
  syncAgents: (profiles: AgentProfileLike[]) => void;
  setCurrentModel: (model: string) => void;
  newConversation: () => void;
  switchConversation: (id: string) => void;
  deleteConversation: (id: string) => void;
  clearMessages: () => void;
}

// Create proxy state
export const chatStore = proxy<ChatStore>({
  // Initial state
  messages: [],
  conversations: [],
  currentConversationId: null,
  agents: [DEFAULT_AGENT],
  currentAgent: DEFAULT_AGENT,
  isStreaming: false,
  currentModel: 'glm-5',
  sessionKey: null,

  // Actions
  addMessage: (message: Message) => {
    chatStore.messages.push(message);
  },

  updateMessage: (id: string, updates: Partial<Message>) => {
    const msg = chatStore.messages.find(m => m.id === id);
    if (msg) {
      Object.assign(msg, updates);
    }
  },

  setCurrentAgent: (agent: Agent) => {
    chatStore.currentAgent = agent;
  },

  syncAgents: (profiles: AgentProfileLike[]) => {
    if (profiles.length === 0) {
      chatStore.agents = [DEFAULT_AGENT];
    } else {
      chatStore.agents = profiles.map(toChatAgent);
    }
  },

  setCurrentModel: (model: string) => {
    chatStore.currentModel = model;
  },

  newConversation: () => {
    // Save current conversation if has messages
    if (chatStore.messages.length > 0) {
      const conversation: Conversation = {
        id: chatStore.currentConversationId || generateConvId(),
        title: deriveTitle(chatStore.messages),
        messages: [...chatStore.messages],
        sessionKey: chatStore.sessionKey,
        agentId: chatStore.currentAgent?.id || null,
        createdAt: new Date(),
        updatedAt: new Date(),
      };
      chatStore.conversations.unshift(conversation);
    }

    // Reset for new conversation
    chatStore.messages = [];
    chatStore.sessionKey = null;
    chatStore.isStreaming = false;
    chatStore.currentConversationId = null;
  },

  switchConversation: (id: string) => {
    const conv = chatStore.conversations.find(c => c.id === id);
    if (conv) {
      // Save current first
      if (chatStore.messages.length > 0) {
        const currentConv: Conversation = {
          id: chatStore.currentConversationId || generateConvId(),
          title: deriveTitle(chatStore.messages),
          messages: [...chatStore.messages],
          sessionKey: chatStore.sessionKey,
          agentId: chatStore.currentAgent?.id || null,
          createdAt: new Date(),
          updatedAt: new Date(),
        };
        const existingIndex = chatStore.conversations.findIndex(
          c => c.id === chatStore.currentConversationId
        );
        if (existingIndex >= 0) {
          chatStore.conversations[existingIndex] = currentConv;
        } else {
          chatStore.conversations.unshift(currentConv);
        }
      }

      // Switch to new
      chatStore.messages = [...conv.messages];
      chatStore.sessionKey = conv.sessionKey;
      chatStore.currentConversationId = conv.id;
    }
  },

  deleteConversation: (id: string) => {
    const index = chatStore.conversations.findIndex(c => c.id === id);
    if (index >= 0) {
      chatStore.conversations.splice(index, 1);

      // If deleting current, clear messages
      if (chatStore.currentConversationId === id) {
        chatStore.messages = [];
        chatStore.sessionKey = null;
        chatStore.currentConversationId = null;
      }
    }
  },

  clearMessages: () => {
    chatStore.messages = [];
  },
});

// Optional: Subscribe to changes for debugging
if (import.meta.env.DEV) {
  subscribe(chatStore, (ops) => {
    console.log('[ChatStore] Changes:', ops);
  });
}
  • Step 2: 提交 Valtio Store
cd g:/ZClaw_zclaw && git add desktop/src/domains/chat/store.ts
git commit -m "$(cat <<'EOF'
refactor(chat): create Valtio-based chat store

- Replace Zustand with Valtio for fine-grained reactivity
- Implement core actions: addMessage, updateMessage, etc.
- Add conversation management: new, switch, delete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Task 2.3: 创建 Chat Domain Hooks

Files:

  • Create: desktop/src/domains/chat/hooks.ts

  • Step 1: 创建 React Hooks

Create desktop/src/domains/chat/hooks.ts:

/**
 * Chat Domain Hooks
 *
 * React hooks for accessing chat state with Valtio.
 */
import { useSnapshot } from 'valtio';
import { chatStore } from './store';
import type { Message, Agent, Conversation } from './types';

/**
 * Hook to access the full chat state.
 * Only re-renders when accessed properties change.
 */
export function useChatState() {
  return useSnapshot(chatStore);
}

/**
 * Hook to access messages only.
 * Only re-renders when messages change.
 */
export function useMessages(): readonly Message[] {
  const { messages } = useSnapshot(chatStore);
  return messages;
}

/**
 * Hook to access streaming state.
 * Only re-renders when isStreaming changes.
 */
export function useIsStreaming(): boolean {
  const { isStreaming } = useSnapshot(chatStore);
  return isStreaming;
}

/**
 * Hook to access current agent.
 */
export function useCurrentAgent(): Agent | null {
  const { currentAgent } = useSnapshot(chatStore);
  return currentAgent;
}

/**
 * Hook to access conversations.
 */
export function useConversations(): readonly Conversation[] {
  const { conversations } = useSnapshot(chatStore);
  return conversations;
}

/**
 * Hook to access chat actions.
 * Returns the store directly for calling actions.
 */
export function useChatActions() {
  return chatStore;
}
  • Step 2: 创建 Domain Index

Create desktop/src/domains/chat/index.ts:

/**
 * Chat Domain
 *
 * Public API for the chat system.
 */

// Types
export type {
  Message,
  MessageFile,
  CodeBlock,
  Conversation,
  Agent,
  AgentProfileLike,
  ChatState,
} from './types';

// Store
export { chatStore, toChatAgent } from './store';

// Hooks
export {
  useChatState,
  useMessages,
  useIsStreaming,
  useCurrentAgent,
  useConversations,
  useChatActions,
} from './hooks';
  • Step 3: 提交 Hooks 和 Index
cd g:/ZClaw_zclaw && git add desktop/src/domains/chat/hooks.ts desktop/src/domains/chat/index.ts
git commit -m "$(cat <<'EOF'
refactor(chat): add chat domain hooks and public API

- Add useChatState, useMessages, useIsStreaming hooks
- Export types, store, and hooks from domain index

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Chunk 3: Hands Domain 迁移

Task 3.1: 创建 Hands Domain 类型定义

Files:

  • Create: desktop/src/domains/hands/types.ts

  • Step 1: 创建类型定义

Create desktop/src/domains/hands/types.ts:

/**
 * Hands Domain Types
 *
 * Core types for the automation/hands system.
 */

export interface HandRequirement {
  description: string;
  met: boolean;
  details?: string;
}

export interface Hand {
  id: string;
  name: string;
  description: string;
  status: HandStatus;
  currentRunId?: string;
  requirements_met?: boolean;
  category?: string;
  icon?: string;
  provider?: string;
  model?: string;
  requirements?: HandRequirement[];
  tools?: string[];
  metrics?: string[];
  toolCount?: number;
  metricCount?: number;
}

export type HandStatus =
  | 'idle'
  | 'running'
  | 'needs_approval'
  | 'error'
  | 'unavailable'
  | 'setup_needed';

export interface HandRun {
  runId: string;
  status: string;
  startedAt: string;
  completedAt?: string;
  result?: unknown;
  error?: string;
}

export interface Trigger {
  id: string;
  type: string;
  enabled: boolean;
}

export interface ApprovalRequest {
  id: string;
  handName: string;
  action: string;
  params: Record<string, unknown>;
  createdAt: Date;
}

export interface HandsState {
  hands: Hand[];
  runs: Record<string, HandRun>;
  triggers: Trigger[];
  approvalQueue: ApprovalRequest[];
  isLoading: boolean;
  error: string | null;
}

// XState Events
export type HandsEvent =
  | { type: 'START'; handId: string }
  | { type: 'APPROVE'; requestId: string }
  | { type: 'REJECT'; requestId: string }
  | { type: 'COMPLETE'; runId: string; result: unknown }
  | { type: 'ERROR'; runId: string; error: string }
  | { type: 'RESET' };
  • Step 2: 提交类型定义
cd g:/ZClaw_zclaw && git add desktop/src/domains/hands/types.ts
git commit -m "$(cat <<'EOF'
refactor(hands): extract hands domain types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Task 3.2: 创建 XState 状态机

Files:

  • Create: desktop/src/domains/hands/machine.ts

  • Step 1: 创建状态机

Create desktop/src/domains/hands/machine.ts:

/**
 * Hands State Machine
 *
 * XState machine for managing hand execution lifecycle.
 */
import { setup, assign } from 'xstate';
import type { HandStatus, HandsEvent, Hand } from './types';

export interface HandContext {
  handId: string;
  handName: string;
  runId: string | null;
  error: string | null;
  result: unknown;
}

export type HandState =
  | { value: 'idle'; context: HandContext }
  | { value: 'running'; context: HandContext }
  | { value: 'needs_approval'; context: HandContext }
  | { value: 'success'; context: HandContext }
  | { value: 'error'; context: HandContext };

export const handMachine = setup({
  types: {
    context: {} as HandContext,
    events: {} as HandsEvent,
  },
  actions: {
    setRunId: assign({
      runId: (_, params: { runId: string }) => params.runId,
    }),
    setError: assign({
      error: (_, params: { error: string }) => params.error,
    }),
    setResult: assign({
      result: (_, params: { result: unknown }) => params.result,
    }),
    clearError: assign({
      error: null,
    }),
  },
}).createMachine({
  id: 'hand',
  initial: 'idle',
  context: {
    handId: '',
    handName: '',
    runId: null,
    error: null,
    result: null,
  },
  states: {
    idle: {
      on: {
        START: {
          target: 'running',
          actions: {
            type: 'setRunId',
            params: ({ event }) => ({ runId: `run_${Date.now()}` }),
          },
        },
      },
    },
    running: {
      on: {
        APPROVE: 'needs_approval',
        COMPLETE: {
          target: 'success',
          actions: {
            type: 'setResult',
            params: ({ event }) => ({ result: event.result }),
          },
        },
        ERROR: {
          target: 'error',
          actions: {
            type: 'setError',
            params: ({ event }) => ({ error: event.error }),
          },
        },
      },
    },
    needs_approval: {
      on: {
        APPROVE: 'running',
        REJECT: 'idle',
      },
    },
    success: {
      on: {
        RESET: {
          target: 'idle',
          actions: 'clearError',
        },
      },
    },
    error: {
      on: {
        RESET: {
          target: 'idle',
          actions: 'clearError',
        },
        START: 'running',
      },
    },
  },
});
  • Step 2: 提交状态机
cd g:/ZClaw_zclaw && git add desktop/src/domains/hands/machine.ts
git commit -m "$(cat <<'EOF'
refactor(hands): create XState machine for hand execution

- Define states: idle, running, needs_approval, success, error
- Define events: START, APPROVE, REJECT, COMPLETE, ERROR, RESET
- Add context for tracking runId, error, result

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Task 3.3: 创建 Hands Valtio Store

Files:

  • Create: desktop/src/domains/hands/store.ts

  • Create: desktop/src/domains/hands/hooks.ts

  • Create: desktop/src/domains/hands/index.ts

  • Step 1: 创建 Store

Create desktop/src/domains/hands/store.ts:

/**
 * Hands Domain Store
 *
 * Valtio-based state management for hands/automation.
 */
import { proxy } from 'valtio';
import type { Hand, HandRun, Trigger, ApprovalRequest, HandsState } from './types';

interface HandsStore extends HandsState {
  // Actions
  setHands: (hands: Hand[]) => void;
  updateHand: (id: string, updates: Partial<Hand>) => void;
  addRun: (run: HandRun) => void;
  updateRun: (runId: string, updates: Partial<HandRun>) => void;
  setTriggers: (triggers: Trigger[]) => void;
  addApproval: (request: ApprovalRequest) => void;
  removeApproval: (id: string) => void;
  setLoading: (loading: boolean) => void;
  setError: (error: string | null) => void;
}

export const handsStore = proxy<HandsStore>({
  // Initial state
  hands: [],
  runs: {},
  triggers: [],
  approvalQueue: [],
  isLoading: false,
  error: null,

  // Actions
  setHands: (hands: Hand[]) => {
    handsStore.hands = hands;
  },

  updateHand: (id: string, updates: Partial<Hand>) => {
    const hand = handsStore.hands.find(h => h.id === id);
    if (hand) {
      Object.assign(hand, updates);
    }
  },

  addRun: (run: HandRun) => {
    handsStore.runs[run.runId] = run;
  },

  updateRun: (runId: string, updates: Partial<HandRun>) => {
    if (handsStore.runs[runId]) {
      Object.assign(handsStore.runs[runId], updates);
    }
  },

  setTriggers: (triggers: Trigger[]) => {
    handsStore.triggers = triggers;
  },

  addApproval: (request: ApprovalRequest) => {
    handsStore.approvalQueue.push(request);
  },

  removeApproval: (id: string) => {
    const index = handsStore.approvalQueue.findIndex(a => a.id === id);
    if (index >= 0) {
      handsStore.approvalQueue.splice(index, 1);
    }
  },

  setLoading: (loading: boolean) => {
    handsStore.isLoading = loading;
  },

  setError: (error: string | null) => {
    handsStore.error = error;
  },
});
  • Step 2: 创建 Hooks

Create desktop/src/domains/hands/hooks.ts:

/**
 * Hands Domain Hooks
 */
import { useSnapshot } from 'valtio';
import { handsStore } from './store';

export function useHandsState() {
  return useSnapshot(handsStore);
}

export function useHands() {
  const { hands } = useSnapshot(handsStore);
  return hands;
}

export function useApprovalQueue() {
  const { approvalQueue } = useSnapshot(handsStore);
  return approvalQueue;
}

export function useHandsActions() {
  return handsStore;
}
  • Step 3: 创建 Index

Create desktop/src/domains/hands/index.ts:

/**
 * Hands Domain
 */
export * from './types';
export { handMachine } from './machine';
export { handsStore } from './store';
export { useHandsState, useHands, useApprovalQueue, useHandsActions } from './hooks';
  • Step 4: 提交 Hands Domain
cd g:/ZClaw_zclaw && git add desktop/src/domains/hands/
git commit -m "$(cat <<'EOF'
refactor(hands): complete hands domain migration

- Add Valtio store for hands state
- Add React hooks for hands access
- Export all from domain index

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Chunk 4: Shared Module 提取

Task 4.1: 创建共享错误处理

Files:

  • Create: desktop/src/shared/error-handling.ts

  • Create: desktop/src/shared/types.ts

  • Create: desktop/src/shared/index.ts

  • Step 1: 创建错误处理工具

Create desktop/src/shared/error-handling.ts:

/**
 * Shared Error Handling
 *
 * Unified error handling utilities.
 */

export class AppError extends Error {
  constructor(
    message: string,
    public code: string,
    public cause?: Error
  ) {
    super(message);
    this.name = 'AppError';
  }
}

export function isError(error: unknown): error is Error {
  return error instanceof Error;
}

export function getErrorMessage(error: unknown): string {
  if (isError(error)) {
    return error.message;
  }
  return String(error);
}

export function wrapError(error: unknown, code: string): AppError {
  if (error instanceof AppError) {
    return error;
  }
  return new AppError(getErrorMessage(error), code, isError(error) ? error : undefined);
}
  • Step 2: 创建共享类型

Create desktop/src/shared/types.ts:

/**
 * Shared Types
 */

export type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

export type AsyncResult<T, E = Error> = Promise<Result<T, E>>;

export interface PaginatedResponse<T> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
}
  • Step 3: 创建 Index

Create desktop/src/shared/index.ts:

/**
 * Shared Module
 */
export * from './error-handling';
export * from './types';
  • Step 4: 提交共享模块
cd g:/ZClaw_zclaw && git add desktop/src/shared/
git commit -m "$(cat <<'EOF'
refactor(shared): create shared module

- Add AppError class for unified error handling
- Add Result type for functional error handling
- Add PaginatedResponse type

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Chunk 5: 集成和验证

Task 5.1: 创建向后兼容层

Files:

  • Modify: desktop/src/store/chatStore.ts

  • Step 1: 更新旧 Store 重导出

Update desktop/src/store/chatStore.ts to re-export from domain:

/**
 * Chat Store - Backward Compatibility Layer
 *
 * This file re-exports from the new domains/chat module.
 * Import from '@/domains/chat' for new code.
 */
export * from '../domains/chat';
  • Step 2: 提交兼容层
cd g:/ZClaw_zclaw && git add desktop/src/store/chatStore.ts
git commit -m "$(cat <<'EOF'
refactor(chat): add backward compatibility layer

- Re-export from domains/chat for backward compatibility
- Maintains existing import paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Task 5.2: 运行测试验证

  • Step 1: 运行 Chat Store 测试

Run:

cd g:/ZClaw_zclaw/desktop && pnpm test tests/store/chatStore.test.ts

Expected: Tests pass with new Valtio store

  • Step 2: 运行所有测试

Run:

cd g:/ZClaw_zclaw/desktop && pnpm test

Expected: No new test failures


Task 5.3: 更新文档

  • Step 1: 创建 Phase 2 变更日志

Create docs/changelogs/2026-03-21-phase2-domain-reorganization.md


Verification Checklist

Domain Structure

  • domains/chat/ created with types, store, hooks
  • domains/hands/ created with types, machine, store, hooks
  • shared/ created with error-handling, types

State Management

  • Valtio installed and configured
  • Chat store migrated to Valtio
  • Hands store migrated to Valtio
  • XState machine created for hands

Compatibility

  • Backward compatibility layer in place
  • Existing imports still work
  • Tests passing

Next Steps (Phase 3)

  • Valtio 性能优化
  • XState 状态机完整集成
  • Intelligence 缓存增强
  • 组件迁移到新 Hooks