diff --git a/.claude/skills/team-frontend/SKILL.md b/.claude/skills/team-frontend/SKILL.md new file mode 100644 index 00000000..66be1f90 --- /dev/null +++ b/.claude/skills/team-frontend/SKILL.md @@ -0,0 +1,433 @@ +--- +name: team-frontend +description: Unified team skill for frontend development team. All roles invoke this skill with --role arg. Built-in ui-ux-pro-max design intelligence. Triggers on "team frontend". +allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), WebFetch(*), WebSearch(*) +--- + +# Team Frontend Development + +全栈前端开发团队,内置 ui-ux-pro-max 设计智能。具备需求分析、设计系统生成、前端实现、质量保证的完整能力。All team members invoke this skill with `--role=xxx` to route to role-specific execution. + +## Architecture Overview + +``` +┌──────────────────────────────────────────────────┐ +│ Skill(skill="team-frontend", args="--role=xxx") │ +└───────────────────┬──────────────────────────────┘ + │ Role Router + ┌───────┬───────┼───────┬───────┐ + ↓ ↓ ↓ ↓ ↓ +┌──────────┐┌──────────┐┌──────────┐┌──────────┐┌──────────┐ +│coordinator││ analyst ││ architect││ developer││ qa │ +│ roles/ ││ roles/ ││ roles/ ││ roles/ ││ roles/ │ +└──────────┘└──────────┘└──────────┘└──────────┘└──────────┘ +``` + +## Command Architecture + +Each role is organized as a folder with a `role.md` orchestrator and optional `commands/` for delegation: + +``` +roles/ +├── coordinator/ +│ ├── role.md +│ └── commands/ +├── analyst/ +│ ├── role.md +│ └── commands/ +│ └── design-intelligence.md +├── architect/ +│ ├── role.md +│ └── commands/ +├── developer/ +│ ├── role.md +│ └── commands/ +└── qa/ + ├── role.md + └── commands/ + └── pre-delivery-checklist.md +``` + +## Role Router + +### Input Parsing + +Parse `$ARGUMENTS` to extract `--role`: + +```javascript +const args = "$ARGUMENTS" +const roleMatch = args.match(/--role[=\s]+(\w+)/) + +if (!roleMatch) { + throw new Error("Missing --role argument. Available roles: coordinator, analyst, architect, developer, qa") +} + +const role = roleMatch[1] +const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "frontend" +``` + +### Role Dispatch + +```javascript +const VALID_ROLES = { + "coordinator": { file: "roles/coordinator/role.md", prefix: null }, + "analyst": { file: "roles/analyst/role.md", prefix: "ANALYZE" }, + "architect": { file: "roles/architect/role.md", prefix: "ARCH" }, + "developer": { file: "roles/developer/role.md", prefix: "DEV" }, + "qa": { file: "roles/qa/role.md", prefix: "QA" } +} + +if (!VALID_ROLES[role]) { + throw new Error(`Unknown role: ${role}. Available: ${Object.keys(VALID_ROLES).join(', ')}`) +} + +// Read and execute role-specific logic +Read(VALID_ROLES[role].file) +// → Execute the 5-phase process defined in that file +``` + +### Available Roles + +| Role | Task Prefix | Responsibility | Role File | +|------|-------------|----------------|-----------| +| `coordinator` | N/A | 需求澄清、行业识别、流水线编排、进度监控、GC循环控制 | [roles/coordinator/role.md](roles/coordinator/role.md) | +| `analyst` | ANALYZE-* | 需求分析、调用 ui-ux-pro-max 获取设计智能、行业推理规则匹配 | [roles/analyst/role.md](roles/analyst/role.md) | +| `architect` | ARCH-* | 消费设计智能、定义设计令牌系统、组件架构、技术选型 | [roles/architect/role.md](roles/architect/role.md) | +| `developer` | DEV-* | 消费架构产出、实现前端组件/页面代码 | [roles/developer/role.md](roles/developer/role.md) | +| `qa` | QA-* | 代码审查、可访问性检查、行业反模式检查、Pre-Delivery验证 | [roles/qa/role.md](roles/qa/role.md) | + +## Shared Infrastructure + +### Role Isolation Rules + +**核心原则**: 每个角色仅能执行自己职责范围内的工作。 + +#### Output Tagging(强制) + +所有角色的输出必须带 `[role_name]` 标识前缀: + +```javascript +SendMessage({ + content: `## [${role}] ...`, + summary: `[${role}] ...` +}) + +mcp__ccw-tools__team_msg({ + summary: `[${role}] ...` +}) +``` + +#### Coordinator 隔离 + +| 允许 | 禁止 | +|------|------| +| 需求澄清 (AskUserQuestion) | ❌ 直接编写/修改代码 | +| 创建任务链 (TaskCreate) | ❌ 调用实现类 subagent | +| 分发任务给 worker | ❌ 直接执行分析/测试/审查 | +| 监控进度 (消息总线) | ❌ 绕过 worker 自行完成任务 | +| 汇报结果给用户 | ❌ 修改源代码或产物文件 | + +#### Worker 隔离 + +| 允许 | 禁止 | +|------|------| +| 处理自己前缀的任务 | ❌ 处理其他角色前缀的任务 | +| SendMessage 给 coordinator | ❌ 直接与其他 worker 通信 | +| 使用 Toolbox 中声明的工具 | ❌ 为其他角色创建任务 (TaskCreate) | + +### Message Bus (All Roles) + +Every SendMessage **before**, must call `mcp__ccw-tools__team_msg` to log: + +```javascript +mcp__ccw-tools__team_msg({ + operation: "log", + team: teamName, + from: role, + to: "coordinator", + type: "", + summary: `[${role}] `, + ref: "" +}) +``` + +**Message types by role**: + +| Role | Types | +|------|-------| +| coordinator | `task_unblocked`, `sync_checkpoint`, `fix_required`, `error`, `shutdown` | +| analyst | `analyze_ready`, `analyze_progress`, `error` | +| architect | `arch_ready`, `arch_revision`, `arch_progress`, `error` | +| developer | `dev_complete`, `dev_progress`, `error` | +| qa | `qa_passed`, `qa_result`, `fix_required`, `error` | + +### CLI Fallback + +当 `mcp__ccw-tools__team_msg` MCP 不可用时: + +```javascript +Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --type "" --summary "" --json`) +``` + +### Task Lifecycle (All Worker Roles) + +```javascript +// Standard task lifecycle every worker role follows +// Phase 1: Discovery +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) && + t.owner === role && + t.status === 'pending' && + t.blockedBy.length === 0 +) +if (myTasks.length === 0) return // idle +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) + +// Phase 2-4: Role-specific (see roles/{role}/role.md) + +// Phase 5: Report + Loop — 所有输出必须带 [role] 标识 +mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: role, to: "coordinator", type: "...", summary: `[${role}] ...` }) +SendMessage({ type: "message", recipient: "coordinator", content: `## [${role}] ...`, summary: `[${role}] ...` }) +TaskUpdate({ taskId: task.id, status: 'completed' }) +// Check for next task → back to Phase 1 +``` + +## Pipeline Architecture + +### Three Pipeline Modes + +``` +page (单页面 - CP-1 线性): + ANALYZE-001 → ARCH-001 → DEV-001 → QA-001 + +feature (多组件特性 - CP-1 + CP-2 + CP-8): + ANALYZE-001 → ARCH-001(tokens+structure) → QA-001(architecture-review) + → DEV-001(components) → QA-002(code-review) + +system (完整前端系统 - CP-1 + CP-2 + CP-8 + CP-9 双轨): + ANALYZE-001 → ARCH-001(tokens) → QA-001(token-review) + → [ARCH-002(components) ∥ DEV-001(tokens)](并行, blockedBy QA-001) + → QA-002(component-review) → DEV-002(components) → QA-003(final) +``` + +### Generator-Critic Loop (CP-2) + +developer ↔ qa 循环,确保代码质量和设计合规: + +``` +┌──────────┐ DEV artifact ┌──────────┐ +│ developer│ ──────────────────────→ │ qa │ +│(Generator)│ │ (Critic) │ +│ │ ←────────────────────── │ │ +└──────────┘ QA feedback └──────────┘ + (max 2 rounds) + +Convergence: qa.score >= 8 && qa.critical_count === 0 +``` + +### Consulting Pattern (CP-8) + +developer 可向 analyst 咨询设计决策: + +``` +developer → coordinator: "需要设计决策咨询" +coordinator → analyst: 创建 ANALYZE-consult 任务 +analyst → coordinator: 设计建议 +coordinator → developer: 转发建议 +``` + +### Shared Memory + +```json +{ + "design_intelligence": {}, + "design_token_registry": { + "colors": {}, "typography": {}, "spacing": {}, "shadows": {} + }, + "component_inventory": [], + "style_decisions": [], + "qa_history": [], + "industry_context": {} +} +``` + +每个角色在 Phase 2 读取,Phase 5 写入自己负责的字段。 + +## Session Directory + +``` +.workflow/.team/FE-{slug}-{YYYY-MM-DD}/ +├── team-session.json # Session state +├── shared-memory.json # Cross-role accumulated knowledge +├── analysis/ # Analyst output +│ ├── design-intelligence.json +│ └── requirements.md +├── architecture/ # Architect output +│ ├── design-tokens.json +│ ├── component-specs/ +│ │ └── {component-name}.md +│ └── project-structure.md +├── qa/ # QA output +│ └── audit-{NNN}.md +└── build/ # Developer output + ├── token-files/ + └── component-files/ +``` + +## ui-ux-pro-max Integration + +### Design Intelligence Engine + +analyst 角色通过 Skill 调用 ui-ux-pro-max 获取行业设计智能: + +```javascript +// 生成完整设计系统推荐 +Skill(skill="ui-ux-pro-max", args="${industry} ${keywords} --design-system") + +// 领域搜索(UX 指南、排版、色彩等) +Skill(skill="ui-ux-pro-max", args="${query} --domain ${domain}") + +// 技术栈指南 +Skill(skill="ui-ux-pro-max", args="${query} --stack ${stack}") + +// 持久化设计系统(跨会话复用) +Skill(skill="ui-ux-pro-max", args="${query} --design-system --persist -p ${projectName}") +``` + +### Installation + +``` +/plugin install ui-ux-pro-max@ui-ux-pro-max-skill +``` + +### Fallback Strategy + +若 ui-ux-pro-max skill 未安装,降级为 LLM 通用设计知识。 + +### Supported Domains & Stacks + +- **Domains**: product, style, typography, color, landing, chart, ux, web +- **Stacks**: html-tailwind, react, nextjs, vue, svelte, shadcn, swiftui, react-native, flutter + +## Coordinator Spawn Template + +When coordinator creates teammates: + +```javascript +TeamCreate({ team_name: teamName }) + +// Analyst +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "analyst", + prompt: `你是 team "${teamName}" 的 ANALYST。 +当你收到 ANALYZE-* 任务时,调用 Skill(skill="team-frontend", args="--role=analyst") 执行。 +当前需求: ${taskDescription} +约束: ${constraints} +Session: ${sessionFolder} + +## 角色准则(强制) +- 你只能处理 ANALYZE-* 前缀的任务 +- 所有输出必须带 [analyst] 标识前缀 +- 仅与 coordinator 通信 + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 ANALYZE-* 任务 +2. Skill(skill="team-frontend", args="--role=analyst") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Architect +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "architect", + prompt: `你是 team "${teamName}" 的 ARCHITECT。 +当你收到 ARCH-* 任务时,调用 Skill(skill="team-frontend", args="--role=architect") 执行。 +当前需求: ${taskDescription} +Session: ${sessionFolder} + +## 角色准则(强制) +- 你只能处理 ARCH-* 前缀的任务 +- 所有输出必须带 [architect] 标识前缀 +- 仅与 coordinator 通信 + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 ARCH-* 任务 +2. Skill(skill="team-frontend", args="--role=architect") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// Developer +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "developer", + prompt: `你是 team "${teamName}" 的 DEVELOPER。 +当你收到 DEV-* 任务时,调用 Skill(skill="team-frontend", args="--role=developer") 执行。 +当前需求: ${taskDescription} +Session: ${sessionFolder} + +## 角色准则(强制) +- 你只能处理 DEV-* 前缀的任务 +- 所有输出必须带 [developer] 标识前缀 +- 仅与 coordinator 通信 + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 DEV-* 任务 +2. Skill(skill="team-frontend", args="--role=developer") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) + +// QA +Task({ + subagent_type: "general-purpose", + team_name: teamName, + name: "qa", + prompt: `你是 team "${teamName}" 的 QA (质量保证)。 +当你收到 QA-* 任务时,调用 Skill(skill="team-frontend", args="--role=qa") 执行。 +当前需求: ${taskDescription} +Session: ${sessionFolder} + +## 角色准则(强制) +- 你只能处理 QA-* 前缀的任务 +- 所有输出必须带 [qa] 标识前缀 +- 仅与 coordinator 通信 + +## 消息总线(必须) +每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。 + +工作流程: +1. TaskList → 找到 QA-* 任务 +2. Skill(skill="team-frontend", args="--role=qa") 执行 +3. team_msg log + SendMessage 结果给 coordinator +4. TaskUpdate completed → 检查下一个任务` +}) +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Unknown --role value | Error with available role list | +| Missing --role arg | Error with usage hint | +| Role file not found | Error with expected path (roles/{name}/role.md) | +| QA score < 6 超过 2 轮 GC | Coordinator 上报用户 | +| 双轨同步失败 | 回退到单轨顺序执行 | +| ui-ux-pro-max skill 未安装 | 降级为 LLM 通用设计知识,提示安装命令 | +| DEV 找不到设计文件 | 等待 Sync Point 或上报 | diff --git a/.claude/skills/team-frontend/roles/analyst/commands/design-intelligence.md b/.claude/skills/team-frontend/roles/analyst/commands/design-intelligence.md new file mode 100644 index 00000000..efb30ebb --- /dev/null +++ b/.claude/skills/team-frontend/roles/analyst/commands/design-intelligence.md @@ -0,0 +1,150 @@ +# Command: design-intelligence + +> 通过 Skill 调用 ui-ux-pro-max 获取行业设计智能,生成 design-intelligence.json。 + +## When to Use + +- Phase 3 of analyst role: Core design intelligence retrieval +- ui-ux-pro-max skill 已安装 + +## Strategy + +### Delegation Mode + +**Mode**: Skill invocation +**Skill**: `ui-ux-pro-max` + +## Execution Steps + +### Step 1: 调用 ui-ux-pro-max 生成设计系统 + +analyst 在 subagent 中通过 Skill 调用 ui-ux-pro-max,获取完整设计系统推荐: + +```javascript +// 核心调用:生成完整设计系统 +// ui-ux-pro-max 的 Step 2 会自动执行 search.py --design-system +Task({ + subagent_type: "general-purpose", + run_in_background: false, + description: "Retrieve design intelligence via ui-ux-pro-max skill", + prompt: `调用 ui-ux-pro-max skill 获取设计系统推荐。 + +## 需求 +- 产品类型/行业: ${industry} +- 关键词: ${keywords} +- 技术栈: ${detectedStack} + +## 执行步骤 + +### 1. 生成设计系统(必须) +Skill(skill="ui-ux-pro-max", args="${industry} ${keywords} --design-system") + +### 2. 补充 UX 指南 +Skill(skill="ui-ux-pro-max", args="accessibility animation responsive --domain ux") + +### 3. 获取技术栈指南 +Skill(skill="ui-ux-pro-max", args="${keywords} --stack ${detectedStack}") + +## 输出 +将所有结果整合写入: ${sessionFolder}/analysis/design-intelligence-raw.md + +包含: +- 设计系统推荐(pattern, style, colors, typography, effects, anti-patterns) +- UX 最佳实践 +- 技术栈指南 +- 行业反模式列表 +` +}) +``` + +### Step 2: 解析 Skill 输出 + +```javascript +// 读取 ui-ux-pro-max 的原始输出 +let rawOutput = '' +try { + rawOutput = Read(`${sessionFolder}/analysis/design-intelligence-raw.md`) +} catch {} + +// 解析为结构化 design-intelligence.json +const designIntelligence = { + _source: "ui-ux-pro-max-skill", + _generated_at: new Date().toISOString(), + industry: industry, + detected_stack: detectedStack, + design_system: parseDesignSystem(rawOutput), + ux_guidelines: parseUxGuidelines(rawOutput), + stack_guidelines: parseStackGuidelines(rawOutput), + recommendations: { + style: null, + color_palette: null, + typography: null, + anti_patterns: parseAntiPatterns(rawOutput), + must_have: industryConfig?.mustHave || [] + } +} + +Write(`${sessionFolder}/analysis/design-intelligence.json`, JSON.stringify(designIntelligence, null, 2)) +``` + +### Step 3: Fallback + +```javascript +// 若 ui-ux-pro-max skill 不可用(未安装),降级为 LLM 通用设计知识 +// analyst 在 Phase 3 中直接基于 LLM 知识生成设计推荐 +if (!skillAvailable) { + return { + _source: "llm-general-knowledge", + _fallback: true, + note: "ui-ux-pro-max skill not installed. Install via: /plugin install ui-ux-pro-max@ui-ux-pro-max-skill" + } +} +``` + +## Skill 调用参考 + +ui-ux-pro-max 支持的调用方式: + +| 用途 | 调用 | +|------|------| +| 完整设计系统 | `Skill(skill="ui-ux-pro-max", args=" --design-system")` | +| 持久化设计系统 | `Skill(skill="ui-ux-pro-max", args=" --design-system --persist -p ")` | +| 领域搜索 | `Skill(skill="ui-ux-pro-max", args=" --domain ")` | +| 技术栈指南 | `Skill(skill="ui-ux-pro-max", args=" --stack ")` | + +可用领域: product, style, typography, color, landing, chart, ux, web +可用技术栈: html-tailwind, react, nextjs, vue, svelte, shadcn, swiftui, react-native, flutter + +## Output Format + +```json +{ + "_source": "ui-ux-pro-max-skill", + "design_system": { + "pattern": "...", + "style": "...", + "colors": { "primary": "...", "secondary": "...", "cta": "..." }, + "typography": { "heading": "...", "body": "..." }, + "effects": "...", + "anti_patterns": [] + }, + "ux_guidelines": [], + "stack_guidelines": {}, + "recommendations": { + "style": null, + "color_palette": null, + "typography": null, + "anti_patterns": [], + "must_have": [] + } +} +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| ui-ux-pro-max skill not installed | Fallback to LLM general knowledge, suggest install command | +| Skill execution error | Retry once, then fallback | +| Partial output | Use available data, fill gaps with defaults | +| Timeout | Use partial results, log warning | diff --git a/.claude/skills/team-frontend/roles/analyst/role.md b/.claude/skills/team-frontend/roles/analyst/role.md new file mode 100644 index 00000000..1f9cee60 --- /dev/null +++ b/.claude/skills/team-frontend/roles/analyst/role.md @@ -0,0 +1,361 @@ +# Role: analyst + +需求分析师。调用 ui-ux-pro-max 搜索引擎获取行业设计智能,分析需求、匹配行业推理规则、生成 design-intelligence.json 供下游角色消费。 + +## Role Identity + +- **Name**: `analyst` +- **Task Prefix**: `ANALYZE-*` +- **Responsibility**: Read-only analysis + design intelligence retrieval +- **Communication**: SendMessage to coordinator only +- **Output Tag**: `[analyst]` + +## Role Boundaries + +### MUST + +- 仅处理 `ANALYZE-*` 前缀的任务 +- 所有输出必须带 `[analyst]` 标识 +- 仅通过 SendMessage 与 coordinator 通信 +- 严格在需求分析和设计智能检索范围内工作 + +### MUST NOT + +- ❌ 执行架构设计、代码实现、质量审查等其他角色职责 +- ❌ 直接与其他 worker 角色通信 +- ❌ 为其他角色创建任务 +- ❌ 修改源代码文件 + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `analyze_ready` | analyst → coordinator | Analysis complete | 设计智能已就绪,下游可消费 | +| `analyze_progress` | analyst → coordinator | Partial progress | 分析进度更新 | +| `error` | analyst → coordinator | Analysis failure | 分析失败或工具不可用 | + +## Toolbox + +### Available Tools + +| Tool | Purpose | +|------|---------| +| Read, Glob, Grep | 读取项目文件、搜索现有代码模式 | +| Bash (search.py) | 调用 ui-ux-pro-max 搜索引擎 | +| WebSearch, WebFetch | 竞品参考、设计趋势搜索 | +| Task (cli-explore-agent) | 深度代码库探索 | + +### Subagent Capabilities + +| Agent Type | Purpose | +|------------|---------| +| `cli-explore-agent` | 探索现有代码库的设计模式和组件结构 | + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('ANALYZE-') && + t.owner === 'analyst' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Context Loading + +```javascript +// Extract session folder from task description +const sessionMatch = task.description.match(/Session:\s*([^\n]+)/) +const sessionFolder = sessionMatch ? sessionMatch[1].trim() : null + +// Extract industry context +const industryMatch = task.description.match(/Industry:\s*([^\n]+)/) +const industry = industryMatch ? industryMatch[1].trim() : 'SaaS/科技' + +// Load shared memory +let sharedMemory = {} +try { + sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) +} catch {} + +// Load session info +let session = {} +try { + session = JSON.parse(Read(`${sessionFolder}/team-session.json`)) +} catch {} + +// Detect existing design system in project +const existingTokenFiles = Glob({ pattern: '**/*token*.*' }) +const existingCssVars = Glob({ pattern: '**/*.css' }) + +// Detect tech stack +const packageJsonExists = Glob({ pattern: 'package.json' }) +let detectedStack = 'html-tailwind' +if (packageJsonExists.length > 0) { + try { + const pkg = JSON.parse(Read('package.json')) + const deps = { ...pkg.dependencies, ...pkg.devDependencies } + if (deps['next']) detectedStack = 'nextjs' + else if (deps['react']) detectedStack = 'react' + else if (deps['vue']) detectedStack = 'vue' + else if (deps['svelte']) detectedStack = 'svelte' + if (deps['@shadcn/ui'] || deps['shadcn-ui']) detectedStack = 'shadcn' + } catch {} +} +``` + +### Phase 3: Core Analysis — Design Intelligence Retrieval + +This is the key integration point with ui-ux-pro-max. 通过 Skill 调用获取设计智能。 + +详细执行策略见: [commands/design-intelligence.md](commands/design-intelligence.md) + +#### Step 1: 通过 Skill 调用 ui-ux-pro-max + +```javascript +const taskDesc = task.description.replace(/Session:.*\n?/g, '').replace(/Industry:.*\n?/g, '').trim() +const keywords = taskDesc.split(/\s+/).slice(0, 5).join(' ') + +// 通过 subagent 调用 ui-ux-pro-max skill 获取完整设计智能 +// ui-ux-pro-max 内部会自动执行 search.py --design-system +Task({ + subagent_type: "general-purpose", + run_in_background: false, + description: "Retrieve design intelligence via ui-ux-pro-max skill", + prompt: `调用 ui-ux-pro-max skill 获取设计系统推荐。 + +## 需求 +- 产品类型/行业: ${industry} +- 关键词: ${keywords} +- 技术栈: ${detectedStack} + +## 执行步骤 + +### 1. 生成设计系统(必须) +Skill(skill="ui-ux-pro-max", args="${industry} ${keywords} --design-system") + +### 2. 补充 UX 指南 +Skill(skill="ui-ux-pro-max", args="accessibility animation responsive --domain ux") + +### 3. 获取技术栈指南 +Skill(skill="ui-ux-pro-max", args="${keywords} --stack ${detectedStack}") + +## 输出 +将所有结果整合写入: ${sessionFolder}/analysis/design-intelligence-raw.md + +包含: +- 设计系统推荐(pattern, style, colors, typography, effects, anti-patterns) +- UX 最佳实践 +- 技术栈指南 +- 行业反模式列表 +` +}) + +// 读取 skill 输出 +let designSystemRaw = '' +try { + designSystemRaw = Read(`${sessionFolder}/analysis/design-intelligence-raw.md`) +} catch { + // Skill 输出不可用,将在 Step 3 使用 fallback +} + +const uiproAvailable = designSystemRaw.length > 0 +``` + +#### Step 2: Fallback — LLM 通用设计知识 + +```javascript +// 若 ui-ux-pro-max skill 不可用(未安装或执行失败),降级为 LLM 通用知识 +if (!uiproAvailable) { + // analyst 直接基于 LLM 知识生成设计推荐 + // 不需要外部工具,但质量低于 ui-ux-pro-max 的数据驱动推荐 + designSystemRaw = null +} +``` + +#### Step 3: Analyze Existing Codebase + +```javascript +// Explore existing design patterns in the project +let existingPatterns = {} + +if (existingTokenFiles.length > 0 || existingCssVars.length > 0) { + Task({ + subagent_type: "cli-explore-agent", + run_in_background: false, + description: "Explore existing design system", + prompt: `Analyze the existing design system in this project: +- Token files: ${existingTokenFiles.slice(0, 5).join(', ')} +- CSS files: ${existingCssVars.slice(0, 5).join(', ')} + +Find: color palette, typography scale, spacing system, component patterns. +Output as JSON: { colors, typography, spacing, components, patterns }` + }) +} +``` + +#### Step 4: Competitive Reference (optional) + +```javascript +// Quick web search for design inspiration if needed +if (industry !== '其他') { + try { + const webResults = WebSearch({ query: `${industry} web design trends 2025 best practices` }) + // Extract relevant insights + } catch {} +} +``` + +### Phase 4: Synthesis & Output + +```javascript +// Compile design intelligence +// 若 Skill 调用成功,解析 raw output;否则使用 LLM fallback +const designIntelligence = { + _source: uiproAvailable ? "ui-ux-pro-max-skill" : "llm-general-knowledge", + _generated_at: new Date().toISOString(), + industry: industry, + detected_stack: detectedStack, + + // From ui-ux-pro-max skill (or LLM fallback) + design_system: uiproAvailable ? parseDesignSystem(designSystemRaw) : generateFallbackDesignSystem(industry, taskDesc), + ux_guidelines: uiproAvailable ? parseUxGuidelines(designSystemRaw) : [], + stack_guidelines: uiproAvailable ? parseStackGuidelines(designSystemRaw) : {}, + + // From codebase analysis + existing_patterns: existingPatterns, + existing_tokens: existingTokenFiles, + + // Synthesized recommendations + recommendations: { + style: null, // Recommended UI style + color_palette: null, // Recommended colors + typography: null, // Recommended font pairing + anti_patterns: uiproAvailable ? parseAntiPatterns(designSystemRaw) : [], + must_have: session.industry_config?.mustHave || [] + } +} + +// Write design intelligence for downstream consumption +Write(`${sessionFolder}/analysis/design-intelligence.json`, JSON.stringify(designIntelligence, null, 2)) + +// Write human-readable requirements summary +Write(`${sessionFolder}/analysis/requirements.md`, `# Requirements Analysis + +## Task +${taskDesc} + +## Industry Context +- **Industry**: ${industry} +- **Detected Stack**: ${detectedStack} +- **Design Intelligence Source**: ${designIntelligence._source} + +## Design System Recommendations +${designSystemRaw || '(Using LLM general knowledge — install ui-ux-pro-max for data-driven recommendations)'} + +## Existing Patterns Found +${JSON.stringify(existingPatterns, null, 2)} + +## Anti-Patterns to Avoid +${designIntelligence.recommendations.anti_patterns.map(p => \`- ❌ \${p}\`).join('\\n') || 'None specified'} + +## Must-Have Requirements +${designIntelligence.recommendations.must_have.map(m => \`- ✅ \${m}\`).join('\\n') || 'Standard requirements'} +`) + +// Update shared memory +sharedMemory.design_intelligence = designIntelligence +sharedMemory.industry_context = { industry, config: session.industry_config } +Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2)) + +const resultStatus = 'complete' +const resultSummary = `Design intelligence generated (source: ${designIntelligence._source}), stack: ${detectedStack}, industry: ${industry}` +const resultDetails = `Files:\n- ${sessionFolder}/analysis/design-intelligence.json\n- ${sessionFolder}/analysis/requirements.md` +``` + +#### Fallback: LLM General Knowledge + +```javascript +function generateFallbackDesignSystem(industry, taskDesc) { + // When ui-ux-pro-max skill is not installed, use LLM general knowledge + // Install: /plugin install ui-ux-pro-max@ui-ux-pro-max-skill + return { + _fallback: true, + note: "Generated from LLM general knowledge. Install ui-ux-pro-max skill for data-driven recommendations.", + colors: { primary: "#1976d2", secondary: "#dc004e", background: "#ffffff" }, + typography: { heading: ["Inter", "system-ui"], body: ["Inter", "system-ui"] }, + style: "modern-minimal" + } +} +``` + +### Phase 5: Report to Coordinator + +```javascript +mcp__ccw-tools__team_msg({ + operation: "log", + team: teamName, + from: "analyst", + to: "coordinator", + type: "analyze_ready", + summary: `[analyst] ANALYZE complete: ${task.subject}`, + ref: `${sessionFolder}/analysis/design-intelligence.json` +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## [analyst] Analysis Results + +**Task**: ${task.subject} +**Status**: ${resultStatus} + +### Summary +${resultSummary} + +### Design Intelligence +- **Source**: ${designIntelligence._source} +- **Industry**: ${industry} +- **Stack**: ${detectedStack} +- **Anti-patterns**: ${designIntelligence.recommendations.anti_patterns.length} identified + +### Output Files +${resultDetails}`, + summary: `[analyst] ANALYZE complete` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next task (e.g., ANALYZE-consult from CP-8) +const nextTasks = TaskList().filter(t => + t.subject.startsWith('ANALYZE-') && + t.owner === 'analyst' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (nextTasks.length > 0) { + // Continue with next task → back to Phase 1 +} +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No ANALYZE-* tasks available | Idle, wait for coordinator | +| ui-ux-pro-max not found | Fallback to LLM general knowledge, log warning | +| search.py execution error | Retry once, then fallback | +| Python not available | Fallback to LLM general knowledge | +| Session folder not found | Notify coordinator, request location | +| Web search fails | Skip competitive reference, continue | +| Critical issue beyond scope | SendMessage fix_required to coordinator | diff --git a/.claude/skills/team-frontend/roles/architect/role.md b/.claude/skills/team-frontend/roles/architect/role.md new file mode 100644 index 00000000..2ada632e --- /dev/null +++ b/.claude/skills/team-frontend/roles/architect/role.md @@ -0,0 +1,408 @@ +# Role: architect + +前端架构师。消费 design-intelligence.json,定义设计令牌系统、组件架构、项目结构、技术选型。设计令牌值优先使用 ui-ux-pro-max 推荐值。 + +## Role Identity + +- **Name**: `architect` +- **Task Prefix**: `ARCH-*` +- **Responsibility**: Code generation (architecture artifacts) +- **Communication**: SendMessage to coordinator only +- **Output Tag**: `[architect]` + +## Role Boundaries + +### MUST + +- 仅处理 `ARCH-*` 前缀的任务 +- 所有输出必须带 `[architect]` 标识 +- 仅通过 SendMessage 与 coordinator 通信 +- 严格在架构设计和令牌定义范围内工作 + +### MUST NOT + +- ❌ 执行需求分析、代码实现、质量审查等其他角色职责 +- ❌ 直接与其他 worker 角色通信 +- ❌ 为其他角色创建任务 +- ❌ 实现具体组件代码(仅定义规格) + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `arch_ready` | architect → coordinator | Architecture complete | 架构产出就绪,下游可消费 | +| `arch_revision` | architect → coordinator | Revision after QA feedback | 架构修订完成 | +| `arch_progress` | architect → coordinator | Partial progress | 架构进度更新 | +| `error` | architect → coordinator | Architecture failure | 架构设计失败 | + +## Toolbox + +### Available Tools + +| Tool | Purpose | +|------|---------| +| Read, Write, Edit | 读写架构产物文件 | +| Glob, Grep | 搜索项目结构和模式 | +| Task (code-developer) | 复杂架构文件生成委派 | + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('ARCH-') && + t.owner === 'architect' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Context Loading + +```javascript +// Extract session folder +const sessionMatch = task.description.match(/Session:\s*([^\n]+)/) +const sessionFolder = sessionMatch ? sessionMatch[1].trim() : null + +// Extract scope (tokens / components / full) +const scopeMatch = task.description.match(/Scope:\s*([^\n]+)/) +const scope = scopeMatch ? scopeMatch[1].trim() : 'full' + +// Load design intelligence from analyst +let designIntel = {} +try { + designIntel = JSON.parse(Read(`${sessionFolder}/analysis/design-intelligence.json`)) +} catch { + // No design intelligence available — use defaults +} + +// Load shared memory +let sharedMemory = {} +try { + sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) +} catch {} + +// Load existing project structure +const projectFiles = Glob({ pattern: 'src/**/*' }) +const hasExistingProject = projectFiles.length > 0 +``` + +### Phase 3: Architecture Design + +#### Step 1: Design Token System (scope: tokens or full) + +```javascript +if (scope === 'tokens' || scope === 'full') { + const recommended = designIntel.design_system || {} + + const designTokens = { + "$schema": "https://design-tokens.github.io/community-group/format/", + "color": { + "primary": { + "$type": "color", + "$value": { + "light": recommended.colors?.primary || "#1976d2", + "dark": recommended.colors?.primary_dark || "#90caf9" + } + }, + "secondary": { + "$type": "color", + "$value": { + "light": recommended.colors?.secondary || "#dc004e", + "dark": recommended.colors?.secondary_dark || "#f48fb1" + } + }, + "background": { + "$type": "color", + "$value": { "light": recommended.colors?.background || "#ffffff", "dark": "#121212" } + }, + "surface": { + "$type": "color", + "$value": { "light": "#f5f5f5", "dark": "#1e1e1e" } + }, + "text": { + "$type": "color", + "$value": { "light": "#1a1a1a", "dark": "#e0e0e0" } + }, + "cta": { + "$type": "color", + "$value": recommended.colors?.cta || "#F97316" + } + }, + "typography": { + "font-family": { + "heading": { + "$type": "fontFamily", + "$value": recommended.typography?.heading || ["Inter", "system-ui", "sans-serif"] + }, + "body": { + "$type": "fontFamily", + "$value": recommended.typography?.body || ["Inter", "system-ui", "sans-serif"] + }, + "mono": { + "$type": "fontFamily", + "$value": ["JetBrains Mono", "Fira Code", "monospace"] + } + }, + "font-size": { + "xs": { "$type": "dimension", "$value": "0.75rem" }, + "sm": { "$type": "dimension", "$value": "0.875rem" }, + "base": { "$type": "dimension", "$value": "1rem" }, + "lg": { "$type": "dimension", "$value": "1.125rem" }, + "xl": { "$type": "dimension", "$value": "1.25rem" }, + "2xl": { "$type": "dimension", "$value": "1.5rem" }, + "3xl": { "$type": "dimension", "$value": "2rem" } + } + }, + "spacing": { + "xs": { "$type": "dimension", "$value": "0.25rem" }, + "sm": { "$type": "dimension", "$value": "0.5rem" }, + "md": { "$type": "dimension", "$value": "1rem" }, + "lg": { "$type": "dimension", "$value": "1.5rem" }, + "xl": { "$type": "dimension", "$value": "2rem" }, + "2xl": { "$type": "dimension", "$value": "3rem" } + }, + "border-radius": { + "sm": { "$type": "dimension", "$value": "0.25rem" }, + "md": { "$type": "dimension", "$value": "0.5rem" }, + "lg": { "$type": "dimension", "$value": "1rem" }, + "full": { "$type": "dimension", "$value": "9999px" } + }, + "shadow": { + "sm": { "$type": "shadow", "$value": "0 1px 2px rgba(0,0,0,0.05)" }, + "md": { "$type": "shadow", "$value": "0 4px 6px rgba(0,0,0,0.1)" }, + "lg": { "$type": "shadow", "$value": "0 10px 15px rgba(0,0,0,0.1)" } + }, + "transition": { + "fast": { "$type": "duration", "$value": "150ms" }, + "normal": { "$type": "duration", "$value": "200ms" }, + "slow": { "$type": "duration", "$value": "300ms" } + } + } + + Write(`${sessionFolder}/architecture/design-tokens.json`, JSON.stringify(designTokens, null, 2)) +} +``` + +#### Step 2: Component Architecture (scope: components or full) + +```javascript +if (scope === 'components' || scope === 'full') { + const taskDesc = task.description.replace(/Session:.*\n?/g, '').replace(/Scope:.*\n?/g, '').trim() + const antiPatterns = designIntel.recommendations?.anti_patterns || [] + const styleHints = designIntel.design_system?.css_keywords || '' + + // Analyze requirements and define component specs + // Each component spec includes: props, variants, accessibility, implementation hints + Bash(`mkdir -p "${sessionFolder}/architecture/component-specs"`) + + // Generate component spec template with design intelligence hints + const componentSpecTemplate = `# Component: {name} + +## Design Reference +- **Style**: ${designIntel.design_system?.style || 'modern-minimal'} +- **Stack**: ${designIntel.detected_stack || 'react'} + +## Props +| Prop | Type | Default | Description | +|------|------|---------|-------------| + +## Variants +| Variant | Description | +|---------|-------------| + +## Accessibility +- Role: +- Keyboard: +- ARIA: +- Contrast: 4.5:1 minimum + +## Implementation Hints +${styleHints ? `- CSS Keywords: ${styleHints}` : ''} +${antiPatterns.length > 0 ? `\n## Anti-Patterns to AVOID\n${antiPatterns.map(p => '- ❌ ' + p).join('\n')}` : ''} +` + + // Write component specs based on task requirements + // (Actual component list derived from task description analysis) +} +``` + +#### Step 3: Project Structure + +```javascript +if (scope === 'full' || !hasExistingProject) { + const stack = designIntel.detected_stack || 'react' + + const projectStructure = { + stack: stack, + structure: getStackStructure(stack), + conventions: { + naming: "kebab-case for files, PascalCase for components", + imports: "absolute imports via @/ alias", + styling: stack === 'html-tailwind' ? 'Tailwind CSS' : 'CSS Modules + design tokens', + testing: "co-located test files (*.test.tsx)" + } + } + + Write(`${sessionFolder}/architecture/project-structure.md`, `# Project Structure + +## Stack: ${stack} + +## Directory Layout +\`\`\` +${projectStructure.structure} +\`\`\` + +## Conventions +${Object.entries(projectStructure.conventions).map(([k, v]) => `- **${k}**: ${v}`).join('\n')} +`) +} + +function getStackStructure(stack) { + const structures = { + 'react': `src/ +├── components/ # Reusable UI components +│ ├── ui/ # Primitive components (Button, Input, etc.) +│ └── layout/ # Layout components (Header, Footer, etc.) +├── pages/ # Page-level components +├── hooks/ # Custom React hooks +├── styles/ # Global styles + design tokens +│ ├── tokens.css # CSS custom properties from design tokens +│ └── global.css # Global resets and base styles +├── utils/ # Utility functions +└── types/ # TypeScript type definitions`, + 'nextjs': `app/ +├── (routes)/ # Route groups +├── components/ # Shared components +│ ├── ui/ # Primitive components +│ └── layout/ # Layout components +├── lib/ # Utility functions +├── styles/ # Global styles + design tokens +│ ├── tokens.css +│ └── globals.css +└── types/ # TypeScript types`, + 'vue': `src/ +├── components/ # Vue components +│ ├── ui/ # Primitive components +│ └── layout/ # Layout components +├── views/ # Page views +├── composables/ # Vue composables +├── styles/ # Global styles + design tokens +└── types/ # TypeScript types`, + 'html-tailwind': `src/ +├── components/ # HTML partials +├── pages/ # HTML pages +├── styles/ # Tailwind config + custom CSS +│ └── tailwind.config.js +└── assets/ # Static assets` + } + return structures[stack] || structures['react'] +} +``` + +### Phase 4: Self-Validation + +```javascript +// Validate architecture artifacts +const validationResults = { issues: [] } + +// Check design tokens JSON validity +try { + JSON.parse(Read(`${sessionFolder}/architecture/design-tokens.json`)) +} catch (e) { + validationResults.issues.push({ severity: 'critical', message: 'design-tokens.json is invalid JSON' }) +} + +// Check color contrast (basic) +// Check that all required token categories exist +const requiredCategories = ['color', 'typography', 'spacing'] +const tokens = JSON.parse(Read(`${sessionFolder}/architecture/design-tokens.json`)) +for (const cat of requiredCategories) { + if (!tokens[cat]) { + validationResults.issues.push({ severity: 'high', message: `Missing token category: ${cat}` }) + } +} + +// Check anti-pattern compliance +const antiPatterns = designIntel.recommendations?.anti_patterns || [] +// Verify token values don't violate industry anti-patterns + +// Update shared memory with architecture output +sharedMemory.design_token_registry = tokens +Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2)) + +const resultStatus = validationResults.issues.length === 0 ? 'complete' : 'complete_with_warnings' +const resultSummary = `Architecture artifacts generated for scope: ${scope}. ${validationResults.issues.length} issues found.` +const resultDetails = `Files:\n- ${sessionFolder}/architecture/design-tokens.json\n- ${sessionFolder}/architecture/project-structure.md\n- ${sessionFolder}/architecture/component-specs/` +``` + +### Phase 5: Report to Coordinator + +```javascript +mcp__ccw-tools__team_msg({ + operation: "log", + team: teamName, + from: "architect", + to: "coordinator", + type: "arch_ready", + summary: `[architect] ARCH complete: ${task.subject}`, + ref: `${sessionFolder}/architecture/design-tokens.json` +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## [architect] Architecture Results + +**Task**: ${task.subject} +**Status**: ${resultStatus} +**Scope**: ${scope} + +### Summary +${resultSummary} + +### Design Tokens +- Colors: ${Object.keys(tokens.color || {}).length} tokens +- Typography: ${Object.keys(tokens.typography || {}).length} categories +- Spacing: ${Object.keys(tokens.spacing || {}).length} scales +- Source: ${designIntel._source || 'defaults'} + +### Output Files +${resultDetails} + +${validationResults.issues.length > 0 ? `### Warnings\n${validationResults.issues.map(i => `- [${i.severity}] ${i.message}`).join('\n')}` : ''}`, + summary: `[architect] ARCH complete` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next task +const nextTasks = TaskList().filter(t => + t.subject.startsWith('ARCH-') && + t.owner === 'architect' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (nextTasks.length > 0) { + // Continue with next task → back to Phase 1 +} +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No ARCH-* tasks available | Idle, wait for coordinator | +| design-intelligence.json not found | Use default token values, log warning | +| Session folder not found | Notify coordinator, request location | +| Token validation fails | Report issues, continue with warnings | +| Sub-agent failure | Retry once, fallback to direct execution | +| Critical issue beyond scope | SendMessage error to coordinator | diff --git a/.claude/skills/team-frontend/roles/coordinator/role.md b/.claude/skills/team-frontend/roles/coordinator/role.md new file mode 100644 index 00000000..4a6885f4 --- /dev/null +++ b/.claude/skills/team-frontend/roles/coordinator/role.md @@ -0,0 +1,312 @@ +# Role: coordinator + +Frontend team coordinator. Orchestrates pipeline: requirement clarification → industry identification → team creation → task chain → dispatch → monitoring → reporting. Manages Generator-Critic loops between developer and qa, consulting pattern between developer and analyst. + +## Role Identity + +- **Name**: `coordinator` +- **Task Prefix**: N/A (coordinator creates tasks, doesn't receive them) +- **Responsibility**: Orchestration +- **Communication**: SendMessage to all teammates +- **Output Tag**: `[coordinator]` + +## Role Boundaries + +### MUST + +- 所有输出(SendMessage、team_msg、日志)必须带 `[coordinator]` 标识 +- 仅负责需求澄清、任务创建/分发、进度监控、结果汇报 +- 通过 TaskCreate 创建任务并分配给 worker 角色 +- 通过消息总线监控 worker 进度并路由消息 + +### MUST NOT + +- ❌ **直接执行任何业务任务**(代码编写、分析、测试、审查等) +- ❌ 直接调用 code-developer、cli-explore-agent 等实现类 subagent +- ❌ 直接修改源代码或生成产物文件 +- ❌ 绕过 worker 角色自行完成应委派的工作 +- ❌ 在输出中省略 `[coordinator]` 标识 + +> **核心原则**: coordinator 是指挥者,不是执行者。所有实际工作必须通过 TaskCreate 委派给 worker 角色。 + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `task_unblocked` | coordinator → any | Dependency resolved | Notify worker of available task | +| `sync_checkpoint` | coordinator → all | QA passed at sync point | Design artifacts stable for consumption | +| `fix_required` | coordinator → developer | QA found issues | Create DEV-fix task | +| `error` | coordinator → all | Critical system error | Escalation to user | +| `shutdown` | coordinator → all | Team being dissolved | Clean shutdown signal | + +## Execution + +### Phase 0: Session Resume Check + +```javascript +const args = "$ARGUMENTS" +const isResume = /--resume|--continue/.test(args) + +if (isResume) { + const sessionDirs = Glob({ pattern: '.workflow/.team/FE-*/team-session.json' }) + const resumable = sessionDirs.map(f => { + try { + const session = JSON.parse(Read(f)) + if (session.status === 'active' || session.status === 'paused') return session + } catch {} + return null + }).filter(Boolean) + + if (resumable.length === 1) { + var resumedSession = resumable[0] + } else if (resumable.length > 1) { + AskUserQuestion({ questions: [{ question: "检测到多个可恢复的会话,请选择:", header: "Resume", multiSelect: false, + options: resumable.slice(0, 4).map(s => ({ label: s.session_id, description: `${s.topic} (${s.current_phase}, ${s.status})` })) + }]}) + var resumedSession = resumable.find(s => s.session_id === userChoice) + } + + if (resumedSession) { + const teamName = resumedSession.team_name + const sessionFolder = `.workflow/.team/${resumedSession.session_id}` + TeamCreate({ team_name: teamName }) + // Spawn workers, create remaining tasks, jump to Phase 4 + } +} +``` + +### Phase 1: Requirement Clarification + +```javascript +const args = "$ARGUMENTS" +const teamNameMatch = args.match(/--team-name[=\s]+([\w-]+)/) +const teamName = teamNameMatch ? teamNameMatch[1] : `frontend-${Date.now().toString(36)}` +const taskDescription = args.replace(/--team-name[=\s]+[\w-]+/, '').replace(/--role[=\s]+\w+/, '').replace(/--resume|--continue/, '').trim() +``` + +Assess scope, industry, and select pipeline: + +```javascript +AskUserQuestion({ + questions: [ + { + question: "前端开发范围:", + header: "Scope", + multiSelect: false, + options: [ + { label: "单页面", description: "设计并实现一个独立页面/组件" }, + { label: "多组件特性", description: "多组件 + 设计令牌 + 交互逻辑" }, + { label: "完整前端系统", description: "从零构建完整前端(令牌 + 组件库 + 页面)" } + ] + }, + { + question: "产品行业/类型:", + header: "Industry", + multiSelect: false, + options: [ + { label: "SaaS/科技", description: "SaaS、开发工具、AI 产品" }, + { label: "电商/零售", description: "电商、奢侈品、市场平台" }, + { label: "医疗/金融", description: "医疗、银行、保险(高合规要求)" }, + { label: "其他", description: "手动输入行业关键词" } + ] + } + ] +}) + +// Map scope to pipeline +const pipelineMap = { + '单页面': 'page', + '多组件特性': 'feature', + '完整前端系统': 'system' +} +const pipeline = pipelineMap[scopeChoice] + +// Industry-based audit strictness +const industryConfig = { + 'SaaS/科技': { strictness: 'standard', mustHave: [] }, + '电商/零售': { strictness: 'standard', mustHave: ['responsive', 'performance'] }, + '医疗/金融': { strictness: 'strict', mustHave: ['wcag-aaa', 'high-contrast', 'security-first'] }, + '其他': { strictness: 'standard', mustHave: [] } +} +const industry = industryConfig[industryChoice] +``` + +Design constraints: + +```javascript +AskUserQuestion({ + questions: [{ + question: "设计约束:", + header: "Constraint", + multiSelect: true, + options: [ + { label: "现有设计系统", description: "必须兼容现有设计令牌和组件" }, + { label: "WCAG AA", description: "必须满足 WCAG 2.1 AA 可访问性标准" }, + { label: "响应式", description: "必须支持 mobile/tablet/desktop" }, + { label: "暗色模式", description: "必须支持 light/dark 主题切换" } + ] + }] +}) +``` + +### Phase 2: Create Team + Session + Spawn Teammates + +```javascript +// Create session directory +const slug = taskDescription.replace(/[^a-zA-Z0-9\u4e00-\u9fff]+/g, '-').slice(0, 30) +const date = new Date().toISOString().slice(0, 10) +const sessionId = `FE-${slug}-${date}` +const sessionFolder = `.workflow/.team/${sessionId}` +Bash(`mkdir -p "${sessionFolder}/analysis" "${sessionFolder}/architecture" "${sessionFolder}/qa" "${sessionFolder}/build"`) + +// Initialize session +Write(`${sessionFolder}/team-session.json`, JSON.stringify({ + session_id: sessionId, + team_name: teamName, + topic: taskDescription, + pipeline: pipeline, + industry: industryChoice, + industry_config: industry, + constraints: constraintChoices, + status: 'active', + current_phase: 'init', + created_at: new Date().toISOString() +}, null, 2)) + +// Initialize shared memory +Write(`${sessionFolder}/shared-memory.json`, JSON.stringify({ + design_intelligence: {}, + design_token_registry: { colors: {}, typography: {}, spacing: {}, shadows: {} }, + component_inventory: [], + style_decisions: [], + qa_history: [], + industry_context: { industry: industryChoice, config: industry } +}, null, 2)) + +// Create team and spawn workers +TeamCreate({ team_name: teamName }) +// → Spawn analyst, architect, developer, qa (see SKILL.md Coordinator Spawn Template) +``` + +### Phase 3: Create Task Chain + +Based on selected pipeline: + +```javascript +if (pipeline === 'page') { + // CP-1 Linear: ANALYZE → ARCH → DEV → QA + TaskCreate({ subject: "ANALYZE-001: 需求分析与设计智能获取", description: `${taskDescription}\nSession: ${sessionFolder}\nIndustry: ${industryChoice}`, owner: "analyst" }) + TaskCreate({ subject: "ARCH-001: 页面架构与设计令牌", description: `${taskDescription}\nSession: ${sessionFolder}`, owner: "architect", addBlockedBy: ["ANALYZE-001"] }) + TaskCreate({ subject: "DEV-001: 页面实现", description: `${taskDescription}\nSession: ${sessionFolder}`, owner: "developer", addBlockedBy: ["ARCH-001"] }) + TaskCreate({ subject: "QA-001: 代码审查与质量验证", description: `${taskDescription}\nSession: ${sessionFolder}`, owner: "qa", addBlockedBy: ["DEV-001"] }) +} + +if (pipeline === 'feature') { + // CP-1 + CP-2: ANALYZE → ARCH → QA(arch) → DEV → QA(code) + TaskCreate({ subject: "ANALYZE-001: 需求分析与设计智能获取", description: `${taskDescription}\nSession: ${sessionFolder}\nIndustry: ${industryChoice}`, owner: "analyst" }) + TaskCreate({ subject: "ARCH-001: 设计令牌+组件架构", description: `${taskDescription}\nSession: ${sessionFolder}`, owner: "architect", addBlockedBy: ["ANALYZE-001"] }) + TaskCreate({ subject: "QA-001: 架构审查", description: `审查 ARCH-001 产出\nSession: ${sessionFolder}\nType: architecture-review`, owner: "qa", addBlockedBy: ["ARCH-001"] }) + TaskCreate({ subject: "DEV-001: 组件实现", description: `${taskDescription}\nSession: ${sessionFolder}`, owner: "developer", addBlockedBy: ["QA-001"] }) + TaskCreate({ subject: "QA-002: 代码审查", description: `审查 DEV-001 产出\nSession: ${sessionFolder}\nType: code-review`, owner: "qa", addBlockedBy: ["DEV-001"] }) +} + +if (pipeline === 'system') { + // CP-1 + CP-2 + CP-9 Dual-Track + TaskCreate({ subject: "ANALYZE-001: 需求分析与设计智能获取", description: `${taskDescription}\nSession: ${sessionFolder}\nIndustry: ${industryChoice}`, owner: "analyst" }) + TaskCreate({ subject: "ARCH-001: 设计令牌系统", description: `${taskDescription}\nSession: ${sessionFolder}\nScope: tokens`, owner: "architect", addBlockedBy: ["ANALYZE-001"] }) + TaskCreate({ subject: "QA-001: 令牌审查", description: `审查 ARCH-001 令牌系统\nSession: ${sessionFolder}\nType: token-review`, owner: "qa", addBlockedBy: ["ARCH-001"] }) + // Dual-track after QA-001 + TaskCreate({ subject: "ARCH-002: 组件架构设计", description: `${taskDescription}\nSession: ${sessionFolder}\nScope: components`, owner: "architect", addBlockedBy: ["QA-001"] }) + TaskCreate({ subject: "DEV-001: 令牌实现", description: `实现设计令牌\nSession: ${sessionFolder}\nScope: tokens`, owner: "developer", addBlockedBy: ["QA-001"] }) + // Sync point 2 + TaskCreate({ subject: "QA-002: 组件架构审查", description: `审查 ARCH-002 组件架构\nSession: ${sessionFolder}\nType: component-review`, owner: "qa", addBlockedBy: ["ARCH-002"] }) + TaskCreate({ subject: "DEV-002: 组件实现", description: `${taskDescription}\nSession: ${sessionFolder}\nScope: components`, owner: "developer", addBlockedBy: ["QA-002", "DEV-001"] }) + TaskCreate({ subject: "QA-003: 最终质量验证", description: `最终审查\nSession: ${sessionFolder}\nType: final`, owner: "qa", addBlockedBy: ["DEV-002"] }) +} +``` + +### Phase 4: Coordination Loop + +Receive teammate messages, dispatch based on content. +**Before each decision**: `team_msg list` to check recent messages. +**After each decision**: `team_msg log` to record. + +| Received Message | Action | +|-----------------|--------| +| analyst: `analyze_ready` | team_msg log → TaskUpdate ANALYZE completed → unblock ARCH | +| architect: `arch_ready` | team_msg log → TaskUpdate ARCH completed → unblock QA/DEV | +| developer: `dev_complete` | team_msg log → TaskUpdate DEV completed → unblock QA | +| qa: `qa_passed` | team_msg log → TaskUpdate QA completed → unblock next stage | +| qa: `fix_required` | Create DEV-fix task → notify developer (CP-2 GC loop) | +| developer: consult request | Create ANALYZE-consult task → notify analyst (CP-8) | +| Worker: `error` | Assess severity → retry or escalate to user | +| All tasks completed | → Phase 5 | + +#### GC Loop Control (CP-2) + +```javascript +let gcRound = 0 +const MAX_GC_ROUNDS = 2 + +// When QA sends fix_required +if (qaMessage.type === 'fix_required' && gcRound < MAX_GC_ROUNDS) { + gcRound++ + // Create fix task for developer + TaskCreate({ + subject: `DEV-fix-${gcRound}: 修复 QA 发现的问题`, + description: `${qaMessage.issues}\nSession: ${sessionFolder}\nGC Round: ${gcRound}`, + owner: "developer" + }) + // Re-queue QA after fix + TaskCreate({ + subject: `QA-recheck-${gcRound}: 复查修复`, + description: `复查 DEV-fix-${gcRound}\nSession: ${sessionFolder}`, + owner: "qa", + addBlockedBy: [`DEV-fix-${gcRound}`] + }) +} else if (gcRound >= MAX_GC_ROUNDS) { + // Escalate to user + AskUserQuestion({ questions: [{ question: `QA 审查 ${MAX_GC_ROUNDS} 轮后仍有问题,如何处理?`, header: "GC Escalation", multiSelect: false, + options: [ + { label: "接受当前状态", description: "跳过剩余问题,继续下一阶段" }, + { label: "手动介入", description: "暂停流水线,手动修复" } + ] + }]}) +} +``` + +### Phase 5: Report + Persist + +Summarize results. Update session status. + +```javascript +// Update session +const session = JSON.parse(Read(`${sessionFolder}/team-session.json`)) +session.status = 'completed' +session.completed_at = new Date().toISOString() +Write(`${sessionFolder}/team-session.json`, JSON.stringify(session, null, 2)) + +AskUserQuestion({ + questions: [{ + question: "当前需求已完成。下一步:", + header: "Next", + multiSelect: false, + options: [ + { label: "新需求", description: "提交新需求给当前团队" }, + { label: "关闭团队", description: "关闭所有 teammate 并清理" } + ] + }] +}) +// 新需求 → 回到 Phase 1 +// 关闭 → shutdown → TeamDelete() +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| Teammate unresponsive | Send follow-up, 2x → respawn | +| QA rejected 3+ times | Escalate to user | +| Dual-track sync failure | Fallback to single-track sequential | +| ui-ux-pro-max unavailable | Continue with LLM general knowledge | +| DEV can't find design files | Wait for sync point or escalate | diff --git a/.claude/skills/team-frontend/roles/developer/role.md b/.claude/skills/team-frontend/roles/developer/role.md new file mode 100644 index 00000000..4c406e51 --- /dev/null +++ b/.claude/skills/team-frontend/roles/developer/role.md @@ -0,0 +1,348 @@ +# Role: developer + +前端开发者。消费架构产出,实现前端组件/页面代码。代码生成时引用 design-intelligence.json 的 Implementation Checklist 和技术栈指南,遵循 Anti-Patterns 约束。 + +## Role Identity + +- **Name**: `developer` +- **Task Prefix**: `DEV-*` +- **Responsibility**: Code generation +- **Communication**: SendMessage to coordinator only +- **Output Tag**: `[developer]` + +## Role Boundaries + +### MUST + +- 仅处理 `DEV-*` 前缀的任务 +- 所有输出必须带 `[developer]` 标识 +- 仅通过 SendMessage 与 coordinator 通信 +- 严格在前端代码实现范围内工作 + +### MUST NOT + +- ❌ 执行需求分析、架构设计、质量审查等其他角色职责 +- ❌ 直接与其他 worker 角色通信 +- ❌ 为其他角色创建任务 +- ❌ 修改设计令牌定义(仅消费) + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `dev_complete` | developer → coordinator | Implementation complete | 代码实现完成 | +| `dev_progress` | developer → coordinator | Partial progress | 实现进度更新 | +| `error` | developer → coordinator | Implementation failure | 实现失败 | + +## Toolbox + +### Available Tools + +| Tool | Purpose | +|------|---------| +| Read, Write, Edit | 读写源代码文件 | +| Bash | 运行构建命令、安装依赖、格式化 | +| Glob, Grep | 搜索项目文件和代码模式 | +| Task (code-developer) | 复杂组件实现委派 | + +### Subagent Capabilities + +| Agent Type | Purpose | +|------------|---------| +| `code-developer` | 复杂组件/页面的代码实现 | + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('DEV-') && + t.owner === 'developer' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Context Loading + +```javascript +// Extract session folder and scope +const sessionMatch = task.description.match(/Session:\s*([^\n]+)/) +const sessionFolder = sessionMatch ? sessionMatch[1].trim() : null + +const scopeMatch = task.description.match(/Scope:\s*([^\n]+)/) +const scope = scopeMatch ? scopeMatch[1].trim() : 'full' + +// Load design intelligence +let designIntel = {} +try { + designIntel = JSON.parse(Read(`${sessionFolder}/analysis/design-intelligence.json`)) +} catch {} + +// Load design tokens +let designTokens = {} +try { + designTokens = JSON.parse(Read(`${sessionFolder}/architecture/design-tokens.json`)) +} catch {} + +// Load project structure +let projectStructure = '' +try { + projectStructure = Read(`${sessionFolder}/architecture/project-structure.md`) +} catch {} + +// Load component specs (if available) +let componentSpecs = [] +try { + const specFiles = Glob({ pattern: `${sessionFolder}/architecture/component-specs/*.md` }) + componentSpecs = specFiles.map(f => ({ name: f, content: Read(f) })) +} catch {} + +// Load shared memory +let sharedMemory = {} +try { + sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) +} catch {} + +// Detect stack +const detectedStack = designIntel.detected_stack || 'react' +``` + +### Phase 3: Code Implementation + +#### Step 1: Generate Design Token CSS + +```javascript +if (scope === 'tokens' || scope === 'full') { + // Convert design-tokens.json to CSS custom properties + let cssVars = ':root {\n' + + // Colors + if (designTokens.color) { + for (const [name, token] of Object.entries(designTokens.color)) { + const value = typeof token.$value === 'object' ? token.$value.light : token.$value + cssVars += ` --color-${name}: ${value};\n` + } + } + + // Typography + if (designTokens.typography?.['font-family']) { + for (const [name, token] of Object.entries(designTokens.typography['font-family'])) { + const value = Array.isArray(token.$value) ? token.$value.join(', ') : token.$value + cssVars += ` --font-${name}: ${value};\n` + } + } + if (designTokens.typography?.['font-size']) { + for (const [name, token] of Object.entries(designTokens.typography['font-size'])) { + cssVars += ` --text-${name}: ${token.$value};\n` + } + } + + // Spacing + if (designTokens.spacing) { + for (const [name, token] of Object.entries(designTokens.spacing)) { + cssVars += ` --space-${name}: ${token.$value};\n` + } + } + + // Border radius + if (designTokens['border-radius']) { + for (const [name, token] of Object.entries(designTokens['border-radius'])) { + cssVars += ` --radius-${name}: ${token.$value};\n` + } + } + + // Shadows + if (designTokens.shadow) { + for (const [name, token] of Object.entries(designTokens.shadow)) { + cssVars += ` --shadow-${name}: ${token.$value};\n` + } + } + + // Transitions + if (designTokens.transition) { + for (const [name, token] of Object.entries(designTokens.transition)) { + cssVars += ` --duration-${name}: ${token.$value};\n` + } + } + + cssVars += '}\n' + + // Dark mode + if (designTokens.color) { + cssVars += '\n@media (prefers-color-scheme: dark) {\n :root {\n' + for (const [name, token] of Object.entries(designTokens.color)) { + if (typeof token.$value === 'object' && token.$value.dark) { + cssVars += ` --color-${name}: ${token.$value.dark};\n` + } + } + cssVars += ' }\n}\n' + } + + // Write token CSS + Bash(`mkdir -p src/styles`) + Write('src/styles/tokens.css', cssVars) +} +``` + +#### Step 2: Implement Components + +```javascript +if (scope === 'components' || scope === 'full') { + const taskDesc = task.description.replace(/Session:.*\n?/g, '').replace(/Scope:.*\n?/g, '').trim() + const antiPatterns = designIntel.recommendations?.anti_patterns || [] + const stackGuidelines = designIntel.stack_guidelines || {} + const implementationChecklist = designIntel.design_system?.implementation_checklist || [] + + // Delegate to code-developer for complex implementation + Task({ + subagent_type: "code-developer", + run_in_background: false, + description: `Implement frontend components: ${taskDesc}`, + prompt: `## Goal +${taskDesc} + +## Tech Stack +${detectedStack} + +## Design Tokens +Import from: src/styles/tokens.css +Use CSS custom properties (var(--color-primary), var(--space-md), etc.) + +## Component Specs +${componentSpecs.map(s => s.content).join('\n\n---\n\n')} + +## Stack-Specific Guidelines +${JSON.stringify(stackGuidelines, null, 2)} + +## Implementation Checklist (MUST verify each item) +${implementationChecklist.map(item => `- [ ] ${item}`).join('\n') || '- [ ] Semantic HTML\n- [ ] Keyboard accessible\n- [ ] Responsive layout\n- [ ] Dark mode support'} + +## Anti-Patterns to AVOID +${antiPatterns.map(p => `- ❌ ${p}`).join('\n') || 'None specified'} + +## Coding Standards +- Use design token CSS variables, never hardcode colors/spacing +- All interactive elements must have cursor: pointer +- Transitions: 150-300ms (use var(--duration-normal)) +- Text contrast: minimum 4.5:1 ratio +- Include focus-visible styles for keyboard navigation +- Support prefers-reduced-motion +- Responsive: mobile-first with md/lg breakpoints +- No emoji as functional icons +` + }) +} +``` + +### Phase 4: Self-Validation + +```javascript +// Pre-delivery self-check +const implementedFiles = Glob({ pattern: 'src/**/*.{tsx,jsx,vue,svelte,html,css}' }) +const selfCheckResults = { passed: [], failed: [] } + +for (const file of implementedFiles.slice(0, 20)) { + try { + const content = Read(file) + + // Check: no hardcoded colors (hex outside tokens.css) + if (file !== 'src/styles/tokens.css' && /#[0-9a-fA-F]{3,8}/.test(content)) { + selfCheckResults.failed.push({ file, check: 'hardcoded-color', message: 'Found hardcoded color value' }) + } + + // Check: cursor-pointer on interactive elements + if (/button| ({ path: f, status: 'implemented' })) +Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2)) + +const resultStatus = selfCheckResults.failed.length === 0 ? 'complete' : 'complete_with_warnings' +const resultSummary = `Implemented ${implementedFiles.length} files. Self-check: ${selfCheckResults.failed.length} issues.` +const resultDetails = `Files:\n${implementedFiles.map(f => `- ${f}`).join('\n')}\n\n${selfCheckResults.failed.length > 0 ? `Self-check issues:\n${selfCheckResults.failed.map(f => `- [${f.check}] ${f.file}: ${f.message}`).join('\n')}` : 'All self-checks passed.'}` +``` + +### Phase 5: Report to Coordinator + +```javascript +mcp__ccw-tools__team_msg({ + operation: "log", + team: teamName, + from: "developer", + to: "coordinator", + type: "dev_complete", + summary: `[developer] DEV complete: ${task.subject}` +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## [developer] Implementation Results + +**Task**: ${task.subject} +**Status**: ${resultStatus} +**Scope**: ${scope} + +### Summary +${resultSummary} + +### Details +${resultDetails}`, + summary: `[developer] DEV complete` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next task +const nextTasks = TaskList().filter(t => + t.subject.startsWith('DEV-') && + t.owner === 'developer' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (nextTasks.length > 0) { + // Continue with next task → back to Phase 1 +} +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No DEV-* tasks available | Idle, wait for coordinator | +| design-tokens.json not found | Notify coordinator, request architecture output | +| design-intelligence.json not found | Use default implementation guidelines | +| Sub-agent failure | Retry once, fallback to direct implementation | +| Build/compile errors | Attempt auto-fix, report remaining issues | +| Critical issue beyond scope | SendMessage error to coordinator | diff --git a/.claude/skills/team-frontend/roles/qa/commands/pre-delivery-checklist.md b/.claude/skills/team-frontend/roles/qa/commands/pre-delivery-checklist.md new file mode 100644 index 00000000..4015dead --- /dev/null +++ b/.claude/skills/team-frontend/roles/qa/commands/pre-delivery-checklist.md @@ -0,0 +1,116 @@ +# Command: pre-delivery-checklist + +> 最终交付前的 CSS 级别精准检查清单,融合 ui-ux-pro-max Pre-Delivery Checklist 和 ux-guidelines.csv 规则。 + +## When to Use + +- Phase 3 of qa role, Dimension 5: Pre-Delivery +- Final review type (`reviewType === 'final'` or `reviewType === 'code-review'`) + +## Strategy + +### Delegation Mode + +**Mode**: Direct (inline pattern matching) + +## Checklist Items + +### Accessibility + +| # | Check | Pattern | Severity | Do | Don't | +|---|-------|---------|----------|-----|-------| +| 1 | Images have alt text | `500ms or <100ms transitions | +| 9 | Loading states | Async ops without loading indicator | MEDIUM | Show skeleton/spinner during fetch | Leave blank screen while loading | +| 10 | Error states | Async ops without error handling | HIGH | Show user-friendly error message | Silently fail or show raw error | + +### Design Compliance + +| # | Check | Pattern | Severity | Do | Don't | +|---|-------|---------|----------|-----|-------| +| 11 | No hardcoded colors | Hex values outside tokens.css | HIGH | Use var(--color-*) tokens | Hardcode #hex values | +| 12 | No hardcoded spacing | px values for margin/padding | MEDIUM | Use var(--space-*) tokens | Hardcode pixel values | +| 13 | No emoji as icons | Unicode emoji in UI | HIGH | Use proper SVG/icon library | Use emoji for functional icons | +| 14 | Dark mode support | No prefers-color-scheme | MEDIUM | Support light/dark themes | Design for light mode only | + +### Layout + +| # | Check | Pattern | Severity | Do | Don't | +|---|-------|---------|----------|-----|-------| +| 15 | Responsive breakpoints | No md:/lg:/@media | MEDIUM | Mobile-first responsive design | Desktop-only layout | +| 16 | No horizontal scroll | Fixed widths > viewport | HIGH | Use relative/fluid widths | Set fixed pixel widths on containers | + +## Execution + +```javascript +function runPreDeliveryChecklist(fileContents) { + const results = { passed: 0, failed: 0, items: [] } + + const checks = [ + { id: 1, check: "Images have alt text", test: (c) => /]*alt=/.test(c), severity: 'CRITICAL' }, + { id: 7, check: "cursor-pointer on clickable", test: (c) => /button|onClick/.test(c) && !/cursor-pointer/.test(c), severity: 'MEDIUM' }, + { id: 11, check: "No hardcoded colors", test: (c, f) => f !== 'src/styles/tokens.css' && /#[0-9a-fA-F]{6}/.test(c), severity: 'HIGH' }, + { id: 13, check: "No emoji as icons", test: (c) => /[\u{1F300}-\u{1F9FF}]/u.test(c), severity: 'HIGH' }, + { id: 14, check: "Dark mode support", test: (c) => !/prefers-color-scheme|dark:|\.dark/.test(c), severity: 'MEDIUM', global: true }, + { id: 15, check: "Responsive breakpoints", test: (c) => !/md:|lg:|@media.*min-width/.test(c), severity: 'MEDIUM', global: true } + ] + + // Per-file checks + for (const [file, content] of Object.entries(fileContents)) { + for (const check of checks.filter(c => !c.global)) { + if (check.test(content, file)) { + results.failed++ + results.items.push({ ...check, file, status: 'FAIL' }) + } else { + results.passed++ + results.items.push({ ...check, file, status: 'PASS' }) + } + } + } + + // Global checks (across all content) + const allContent = Object.values(fileContents).join('\n') + for (const check of checks.filter(c => c.global)) { + if (check.test(allContent)) { + results.failed++ + results.items.push({ ...check, file: 'global', status: 'FAIL' }) + } else { + results.passed++ + results.items.push({ ...check, file: 'global', status: 'PASS' }) + } + } + + return results +} +``` + +## Output Format + +``` +## Pre-Delivery Checklist Results +- Passed: X / Y +- Failed: Z + +### Failed Items +- [CRITICAL] #1 Images have alt text — src/components/Hero.tsx +- [HIGH] #11 No hardcoded colors — src/styles/custom.css +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No files to check | Report empty checklist, score 10/10 | +| File read error | Skip file, note in report | +| Regex error | Skip check, note in report | diff --git a/.claude/skills/team-frontend/roles/qa/role.md b/.claude/skills/team-frontend/roles/qa/role.md new file mode 100644 index 00000000..08e62908 --- /dev/null +++ b/.claude/skills/team-frontend/roles/qa/role.md @@ -0,0 +1,491 @@ +# Role: qa + +质量保证工程师。融合 ux-guidelines.csv 的 Do/Don't 规则、Pre-Delivery Checklist、行业反模式库,执行 5 维度代码审查。从概念级审查升级为 CSS 级别精准审查。 + +## Role Identity + +- **Name**: `qa` +- **Task Prefix**: `QA-*` +- **Responsibility**: Read-only analysis (code review + quality audit) +- **Communication**: SendMessage to coordinator only +- **Output Tag**: `[qa]` + +## Role Boundaries + +### MUST + +- 仅处理 `QA-*` 前缀的任务 +- 所有输出必须带 `[qa]` 标识 +- 仅通过 SendMessage 与 coordinator 通信 +- 严格在质量审查范围内工作 + +### MUST NOT + +- ❌ 执行需求分析、架构设计、代码实现等其他角色职责 +- ❌ 直接与其他 worker 角色通信 +- ❌ 为其他角色创建任务 +- ❌ 直接修改源代码(仅报告问题) + +## Message Types + +| Type | Direction | Trigger | Description | +|------|-----------|---------|-------------| +| `qa_passed` | qa → coordinator | All checks passed | 审查通过,可进入下一阶段 | +| `qa_result` | qa → coordinator | Review complete with findings | 审查完成,有发现需处理 | +| `fix_required` | qa → coordinator | Critical issues found | 发现严重问题,需修复 (triggers CP-2 GC loop) | +| `error` | qa → coordinator | Review failure | 审查过程失败 | + +## Toolbox + +### Available Tools + +| Tool | Purpose | +|------|---------| +| Read, Glob, Grep | 读取代码文件、搜索模式 | +| Bash (read-only) | 运行 lint/type-check 等只读检查命令 | + +## 5-Dimension Audit Framework + +| Dimension | Weight | Source | Focus | +|-----------|--------|--------|-------| +| Code Quality | 0.20 | Standard code review | 代码结构、命名、可维护性 | +| Accessibility | 0.25 | ux-guidelines.csv accessibility rules | WCAG 合规、键盘导航、屏幕阅读器 | +| Design Compliance | 0.20 | design-intelligence.json anti-patterns | 行业反模式检查、设计令牌使用 | +| UX Best Practices | 0.20 | ux-guidelines.csv Do/Don't rules | 交互模式、响应式、动画 | +| Pre-Delivery | 0.15 | ui-ux-pro-max Pre-Delivery Checklist | 最终交付检查清单 | + +## Execution (5-Phase) + +### Phase 1: Task Discovery + +```javascript +const tasks = TaskList() +const myTasks = tasks.filter(t => + t.subject.startsWith('QA-') && + t.owner === 'qa' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (myTasks.length === 0) return // idle + +const task = TaskGet({ taskId: myTasks[0].id }) +TaskUpdate({ taskId: task.id, status: 'in_progress' }) +``` + +### Phase 2: Context Loading + +```javascript +// Extract session folder and review type +const sessionMatch = task.description.match(/Session:\s*([^\n]+)/) +const sessionFolder = sessionMatch ? sessionMatch[1].trim() : null + +const typeMatch = task.description.match(/Type:\s*([^\n]+)/) +const reviewType = typeMatch ? typeMatch[1].trim() : 'code-review' +// Types: architecture-review, token-review, component-review, code-review, final + +// Load design intelligence +let designIntel = {} +try { + designIntel = JSON.parse(Read(`${sessionFolder}/analysis/design-intelligence.json`)) +} catch {} + +// Load design tokens +let designTokens = {} +try { + designTokens = JSON.parse(Read(`${sessionFolder}/architecture/design-tokens.json`)) +} catch {} + +// Load shared memory for industry context +let sharedMemory = {} +try { + sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) +} catch {} + +const industryContext = sharedMemory.industry_context || {} +const antiPatterns = designIntel.recommendations?.anti_patterns || [] +const mustHave = designIntel.recommendations?.must_have || [] + +// Determine audit strictness from industry +const strictness = industryContext.config?.strictness || 'standard' + +// Collect files to review based on review type +let filesToReview = [] +if (reviewType === 'architecture-review' || reviewType === 'token-review') { + filesToReview = Glob({ pattern: `${sessionFolder}/architecture/**/*` }) +} else if (reviewType === 'component-review') { + filesToReview = Glob({ pattern: `${sessionFolder}/architecture/component-specs/**/*` }) +} else { + // code-review or final: review implemented source files + filesToReview = Glob({ pattern: 'src/**/*.{tsx,jsx,vue,svelte,html,css}' }) +} + +// Read file contents +const fileContents = {} +for (const file of filesToReview.slice(0, 30)) { + try { fileContents[file] = Read(file) } catch {} +} +``` + +### Phase 3: 5-Dimension Audit + +```javascript +const audit = { + score: 0, + dimensions: {}, + issues: [], + passed: [], + critical_count: 0 +} + +// ═══════════════════════════════════════════ +// Dimension 1: Code Quality (weight: 0.20) +// ═══════════════════════════════════════════ +const codeQuality = { score: 10, issues: [] } + +for (const [file, content] of Object.entries(fileContents)) { + // Check: consistent naming conventions + // Check: no unused imports/variables + // Check: reasonable file length (< 300 lines) + if (content.split('\n').length > 300) { + codeQuality.issues.push({ file, severity: 'MEDIUM', message: 'File exceeds 300 lines, consider splitting' }) + codeQuality.score -= 1 + } + + // Check: no console.log in production code + if (/console\.(log|debug)/.test(content) && !/\.test\.|\.spec\./.test(file)) { + codeQuality.issues.push({ file, severity: 'LOW', message: 'console.log found in production code' }) + codeQuality.score -= 0.5 + } + + // Check: proper error handling + if (/catch\s*\(\s*\)\s*\{[\s]*\}/.test(content)) { + codeQuality.issues.push({ file, severity: 'HIGH', message: 'Empty catch block found' }) + codeQuality.score -= 2 + } +} + +audit.dimensions.code_quality = { weight: 0.20, score: Math.max(0, codeQuality.score), issues: codeQuality.issues } + +// ═══════════════════════════════════════════ +// Dimension 2: Accessibility (weight: 0.25) +// ═══════════════════════════════════════════ +const accessibility = { score: 10, issues: [] } + +for (const [file, content] of Object.entries(fileContents)) { + if (!/\.(tsx|jsx|vue|svelte|html)$/.test(file)) continue + + // Check: images have alt text + if (/]*alt=/.test(content)) { + accessibility.issues.push({ file, severity: 'CRITICAL', message: 'Image missing alt attribute', do: 'Always provide alt text', dont: 'Leave alt empty for decorative images without role="presentation"' }) + accessibility.score -= 3 + } + + // Check: form inputs have labels + if (/ or aria-label', dont: 'Rely on placeholder as label' }) + accessibility.score -= 2 + } + + // Check: buttons have accessible text + if (/]*>\s* parseInt(h[2])) || [] + for (let i = 1; i < headings.length; i++) { + if (headings[i] - headings[i-1] > 1) { + accessibility.issues.push({ file, severity: 'MEDIUM', message: `Heading level skipped: h${headings[i-1]} → h${headings[i]}` }) + accessibility.score -= 1 + } + } + } + + // Check: color contrast (basic — flag hardcoded light colors on light bg) + // Check: focus-visible styles + if (/button| 0 && (ms < 100 || ms > 500)) { + uxPractices.issues.push({ file, severity: 'LOW', message: `Transition duration ${ms}ms outside recommended range (150-300ms)` }) + uxPractices.score -= 0.5 + } + } + + // Check: responsive breakpoints + if (/className|class=/.test(content) && !/md:|lg:|@media/.test(content) && /\.(tsx|jsx|vue|html)$/.test(file)) { + uxPractices.issues.push({ file, severity: 'MEDIUM', message: 'No responsive breakpoints detected', do: 'Use mobile-first responsive design', dont: 'Design for desktop only' }) + uxPractices.score -= 1 + } + + // Check: loading states for async operations + if (/fetch|axios|useSWR|useQuery/.test(content) && !/loading|isLoading|skeleton|spinner/.test(content)) { + uxPractices.issues.push({ file, severity: 'MEDIUM', message: 'Async operation without loading state', do: 'Show loading indicator during data fetching', dont: 'Leave blank screen while loading' }) + uxPractices.score -= 1 + } + + // Check: error states + if (/fetch|axios|useSWR|useQuery/.test(content) && !/error|isError|catch/.test(content)) { + uxPractices.issues.push({ file, severity: 'HIGH', message: 'Async operation without error handling', do: 'Show user-friendly error message', dont: 'Silently fail or show raw error' }) + uxPractices.score -= 2 + } +} + +audit.dimensions.ux_practices = { weight: 0.20, score: Math.max(0, uxPractices.score), issues: uxPractices.issues } + +// ═══════════════════════════════════════════ +// Dimension 5: Pre-Delivery Checklist (weight: 0.15) +// ═══════════════════════════════════════════ +const preDelivery = { score: 10, issues: [] } + +// Only run full pre-delivery on final review +if (reviewType === 'final' || reviewType === 'code-review') { + const allContent = Object.values(fileContents).join('\n') + + const checklist = [ + { check: "No emojis as functional icons", test: () => /[\u{1F300}-\u{1F9FF}]/u.test(allContent), severity: 'HIGH' }, + { check: "cursor-pointer on clickable", test: () => /button|onClick/.test(allContent) && !/cursor-pointer/.test(allContent), severity: 'MEDIUM' }, + { check: "Transitions 150-300ms", test: () => { const m = allContent.match(/duration[:-]\s*(\d+)/g); return m?.some(d => { const v = parseInt(d.match(/\d+/)[0]); return v > 0 && (v < 100 || v > 500) }) }, severity: 'LOW' }, + { check: "Focus states visible", test: () => /button|input| /animation|@keyframes/.test(allContent) && !/prefers-reduced-motion/.test(allContent), severity: 'MEDIUM' }, + { check: "Responsive breakpoints", test: () => !/md:|lg:|@media.*min-width/.test(allContent), severity: 'MEDIUM' }, + { check: "No hardcoded colors", test: () => { const nonToken = Object.entries(fileContents).filter(([f]) => f !== 'src/styles/tokens.css'); return nonToken.some(([,c]) => /#[0-9a-fA-F]{6}/.test(c)) }, severity: 'HIGH' }, + { check: "Dark mode support", test: () => !/prefers-color-scheme|dark:|\.dark/.test(allContent), severity: 'MEDIUM' } + ] + + for (const item of checklist) { + try { + if (item.test()) { + preDelivery.issues.push({ check: item.check, severity: item.severity, message: `Pre-delivery check failed: ${item.check}` }) + preDelivery.score -= (item.severity === 'HIGH' ? 2 : item.severity === 'MEDIUM' ? 1 : 0.5) + } + } catch {} + } +} + +audit.dimensions.pre_delivery = { weight: 0.15, score: Math.max(0, preDelivery.score), issues: preDelivery.issues } +``` + +### Phase 4: Score Calculation & Report + +```javascript +// Calculate weighted score +audit.score = Object.values(audit.dimensions).reduce((sum, dim) => { + return sum + (dim.score * dim.weight) +}, 0) + +// Collect all issues +audit.issues = Object.values(audit.dimensions).flatMap(dim => dim.issues) +audit.critical_count = audit.issues.filter(i => i.severity === 'CRITICAL').length +audit.passed = Object.entries(audit.dimensions) + .filter(([, dim]) => dim.issues.length === 0) + .map(([name]) => name) + +// Determine verdict +let verdict = 'PASSED' +if (audit.score < 6 || audit.critical_count > 0) { + verdict = 'FIX_REQUIRED' +} else if (audit.score < 8) { + verdict = 'PASSED_WITH_WARNINGS' +} + +// Write audit report +const auditIndex = Glob({ pattern: `${sessionFolder}/qa/audit-*.md` }).length + 1 +const auditFile = `${sessionFolder}/qa/audit-${String(auditIndex).padStart(3, '0')}.md` + +Write(auditFile, `# QA Audit Report #${auditIndex} + +## Summary +- **Review Type**: ${reviewType} +- **Verdict**: ${verdict} +- **Score**: ${audit.score.toFixed(1)} / 10 +- **Critical Issues**: ${audit.critical_count} +- **Total Issues**: ${audit.issues.length} +- **Strictness**: ${strictness} + +## Dimension Scores + +| Dimension | Weight | Score | Issues | +|-----------|--------|-------|--------| +| Code Quality | 0.20 | ${audit.dimensions.code_quality.score.toFixed(1)} | ${audit.dimensions.code_quality.issues.length} | +| Accessibility | 0.25 | ${audit.dimensions.accessibility.score.toFixed(1)} | ${audit.dimensions.accessibility.issues.length} | +| Design Compliance | 0.20 | ${audit.dimensions.design_compliance.score.toFixed(1)} | ${audit.dimensions.design_compliance.issues.length} | +| UX Best Practices | 0.20 | ${audit.dimensions.ux_practices.score.toFixed(1)} | ${audit.dimensions.ux_practices.issues.length} | +| Pre-Delivery | 0.15 | ${audit.dimensions.pre_delivery.score.toFixed(1)} | ${audit.dimensions.pre_delivery.issues.length} | + +## Issues + +${audit.issues.map(i => `### [${i.severity}] ${i.message} +- **File**: ${i.file || i.check || 'N/A'} +${i.do ? `- ✅ **Do**: ${i.do}` : ''} +${i.dont ? `- ❌ **Don't**: ${i.dont}` : ''} +`).join('\n')} + +## Passed Dimensions +${audit.passed.map(p => `- ✅ ${p}`).join('\n') || 'None — all dimensions have issues'} +`) + +// Update shared memory +sharedMemory.qa_history = sharedMemory.qa_history || [] +sharedMemory.qa_history.push({ + audit_index: auditIndex, + review_type: reviewType, + verdict: verdict, + score: audit.score, + critical_count: audit.critical_count, + total_issues: audit.issues.length, + timestamp: new Date().toISOString() +}) +Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2)) + +const resultStatus = verdict +const resultSummary = `Score: ${audit.score.toFixed(1)}/10, Verdict: ${verdict}, ${audit.issues.length} issues (${audit.critical_count} critical)` +const resultDetails = `Report: ${auditFile}` +``` + +### Phase 5: Report to Coordinator + +```javascript +const msgType = verdict === 'FIX_REQUIRED' ? 'fix_required' : verdict === 'PASSED' ? 'qa_passed' : 'qa_result' + +mcp__ccw-tools__team_msg({ + operation: "log", + team: teamName, + from: "qa", + to: "coordinator", + type: msgType, + summary: `[qa] QA ${verdict}: ${task.subject} (${audit.score.toFixed(1)}/10)`, + ref: auditFile +}) + +SendMessage({ + type: "message", + recipient: "coordinator", + content: `## [qa] QA Results + +**Task**: ${task.subject} +**Verdict**: ${verdict} +**Score**: ${audit.score.toFixed(1)} / 10 + +### Dimension Summary +${Object.entries(audit.dimensions).map(([name, dim]) => + `- **${name}**: ${dim.score.toFixed(1)}/10 (${dim.issues.length} issues)` +).join('\n')} + +### Critical Issues +${audit.issues.filter(i => i.severity === 'CRITICAL').map(i => `- ❌ ${i.message} (${i.file || i.check})`).join('\n') || 'None'} + +### High Priority Issues +${audit.issues.filter(i => i.severity === 'HIGH').map(i => `- ⚠️ ${i.message} (${i.file || i.check})`).join('\n') || 'None'} + +### Report +${resultDetails}`, + summary: `[qa] QA ${verdict} (${audit.score.toFixed(1)}/10)` +}) + +TaskUpdate({ taskId: task.id, status: 'completed' }) + +// Check for next task +const nextTasks = TaskList().filter(t => + t.subject.startsWith('QA-') && + t.owner === 'qa' && + t.status === 'pending' && + t.blockedBy.length === 0 +) + +if (nextTasks.length > 0) { + // Continue with next task → back to Phase 1 +} +``` + +## Error Handling + +| Scenario | Resolution | +|----------|------------| +| No QA-* tasks available | Idle, wait for coordinator | +| design-intelligence.json not found | Skip design compliance dimension, adjust weights | +| No files to review | Report empty review, notify coordinator | +| Session folder not found | Notify coordinator, request location | +| Critical issue beyond scope | SendMessage error to coordinator | diff --git a/.claude/skills/team-frontend/specs/team-config.json b/.claude/skills/team-frontend/specs/team-config.json new file mode 100644 index 00000000..d8096e8f --- /dev/null +++ b/.claude/skills/team-frontend/specs/team-config.json @@ -0,0 +1,84 @@ +{ + "team_name": "frontend", + "skill_name": "team-frontend", + "team_display_name": "Frontend Development", + "description": "全栈前端开发团队,内置 ui-ux-pro-max 设计智能。具备需求分析、设计系统生成、前端实现、质量保证的完整能力。", + "pipeline_type": "adaptive", + "roles": [ + { + "name": "coordinator", + "task_prefix": null, + "responsibility_type": "Orchestration", + "description": "需求澄清、行业识别、流水线编排、进度监控、GC循环控制", + "message_types": ["task_unblocked", "sync_checkpoint", "fix_required", "error", "shutdown"] + }, + { + "name": "analyst", + "task_prefix": "ANALYZE", + "responsibility_type": "Read-only analysis", + "description": "需求分析、调用 ui-ux-pro-max 获取设计智能、行业推理规则匹配", + "message_types": ["analyze_ready", "analyze_progress", "error"] + }, + { + "name": "architect", + "task_prefix": "ARCH", + "responsibility_type": "Code generation", + "description": "消费设计智能、定义设计令牌系统、组件架构、技术选型", + "message_types": ["arch_ready", "arch_revision", "arch_progress", "error"] + }, + { + "name": "developer", + "task_prefix": "DEV", + "responsibility_type": "Code generation", + "description": "消费架构产出、实现前端组件/页面代码", + "message_types": ["dev_complete", "dev_progress", "error"] + }, + { + "name": "qa", + "task_prefix": "QA", + "responsibility_type": "Read-only analysis", + "description": "5维度代码审查:代码质量、可访问性、设计合规、UX最佳实践、Pre-Delivery验证", + "message_types": ["qa_passed", "qa_result", "fix_required", "error"] + } + ], + "pipelines": { + "page": { + "description": "单页面开发:分析→架构→开发→质检", + "task_chain": ["ANALYZE-001", "ARCH-001", "DEV-001", "QA-001"], + "collaboration_patterns": ["CP-1", "CP-2"], + "complexity": "low" + }, + "feature": { + "description": "多组件特性:分析→架构→质检→开发→质检", + "task_chain": ["ANALYZE-001", "ARCH-001", "QA-001", "DEV-001", "QA-002"], + "collaboration_patterns": ["CP-1", "CP-2", "CP-8"], + "complexity": "medium" + }, + "system": { + "description": "完整前端系统:分析→架构→[开发令牌 ∥ 架构组件]→质检→开发组件→最终质检", + "task_chain": ["ANALYZE-001", "ARCH-001", "QA-001", "ARCH-002||DEV-001", "QA-002", "DEV-002", "QA-003"], + "sync_points": ["QA-001", "QA-002"], + "collaboration_patterns": ["CP-1", "CP-2", "CP-8", "CP-9"], + "complexity": "high" + } + }, + "uipro_config": { + "search_paths": [ + "G:/github_lib/ui-ux-pro-max-skill/src/ui-ux-pro-max", + "../ui-ux-pro-max-skill/src/ui-ux-pro-max", + "./ui-ux-pro-max" + ], + "fallback": "LLM general design knowledge", + "data_domains": ["product", "style", "typography", "color", "landing", "chart", "ux", "web"], + "supported_stacks": ["html-tailwind", "react", "nextjs", "vue", "svelte", "shadcn"] + }, + "shared_memory_schema": { + "design_intelligence": {}, + "design_token_registry": {}, + "component_inventory": [], + "style_decisions": [], + "qa_history": [], + "industry_context": {} + }, + "generated_at": "2026-02-17T00:00:00+08:00" +}