mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-13 02:41:50 +08:00
feat: Add comprehensive tests for contentPattern and glob pattern matching
- Implemented final verification tests for contentPattern to validate behavior with empty strings, dangerous patterns, and normal patterns. - Created glob pattern matching tests to verify regex conversion and matching functionality. - Developed infinite loop risk tests using Worker threads to isolate potential blocking operations. - Introduced optimized contentPattern tests to validate improvements in the findMatches function. - Added verification tests to assess the effectiveness of contentPattern optimizations. - Conducted safety tests for contentPattern to identify edge cases and potential vulnerabilities. - Implemented unrestricted loop tests to analyze infinite loop risks without match limits. - Developed tests for zero-width pattern detection logic to ensure proper handling of dangerous regex patterns.
This commit is contained in:
208
.claude/commands/team/coordinate.md
Normal file
208
.claude/commands/team/coordinate.md
Normal file
@@ -0,0 +1,208 @@
|
||||
---
|
||||
name: coordinate
|
||||
description: Team coordinator - 需求澄清、MVP路线图、创建持久化agent team、跨阶段协调plan/execute/test/review
|
||||
argument-hint: "[--team-name=NAME] \"task description\""
|
||||
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*)
|
||||
group: team
|
||||
---
|
||||
|
||||
# Team Coordinate Command (/team:coordinate)
|
||||
|
||||
纯调度协调器。需求澄清 → 创建 Team → 创建任务链 → 协调消息 → 持久循环。具体工作逻辑由各 teammate 调用自己的 skill 完成。
|
||||
|
||||
## 消息总线
|
||||
|
||||
所有 teammate 在 SendMessage 的**同时**必须调用 `mcp__ccw-tools__team_msg` 记录消息,实现持久化和用户可观测:
|
||||
|
||||
```javascript
|
||||
// 记录消息(每个 teammate 发 SendMessage 前调用)
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_ready", summary: "Plan就绪: 3个task", ref: ".workflow/.team-plan/auth-team/plan.json" })
|
||||
|
||||
// Coordinator 查看全部消息
|
||||
mcp__ccw-tools__team_msg({ operation: "list", team: teamName })
|
||||
|
||||
// 按角色过滤
|
||||
mcp__ccw-tools__team_msg({ operation: "list", team: teamName, from: "tester", last: 5 })
|
||||
|
||||
// 查看团队状态
|
||||
mcp__ccw-tools__team_msg({ operation: "status", team: teamName })
|
||||
|
||||
// 读取特定消息
|
||||
mcp__ccw-tools__team_msg({ operation: "read", team: teamName, id: "MSG-003" })
|
||||
```
|
||||
|
||||
**日志位置**: `.workflow/.team-msg/{team-name}/messages.jsonl`
|
||||
**消息类型**: `plan_ready | plan_approved | plan_revision | task_unblocked | impl_complete | impl_progress | test_result | review_result | fix_required | error | shutdown`
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/team:coordinate "实现用户认证模块"
|
||||
/team:coordinate --team-name=auth-team "实现JWT刷新令牌"
|
||||
```
|
||||
|
||||
## Pipeline
|
||||
|
||||
```
|
||||
需求 → [PLAN: planner] → coordinator 审批 → [IMPL: executor] → [TEST + REVIEW: tester] → 汇报 → 等待新需求/关闭
|
||||
```
|
||||
|
||||
## Execution
|
||||
|
||||
### Phase 1: 需求澄清
|
||||
|
||||
解析 `$ARGUMENTS` 获取 `--team-name` 和任务描述。使用 AskUserQuestion 收集:
|
||||
- MVP 范围(最小可行 / 功能完整 / 全面实现)
|
||||
- 关键约束(向后兼容 / 遵循模式 / 测试覆盖 / 性能敏感)
|
||||
|
||||
简单任务可跳过澄清。
|
||||
|
||||
### Phase 2: 创建 Team + Spawn 3 Teammates
|
||||
|
||||
```javascript
|
||||
TeamCreate({ team_name: teamName })
|
||||
```
|
||||
|
||||
**Spawn 时只传角色和需求,工作细节由 skill 定义**:
|
||||
|
||||
```javascript
|
||||
// Planner
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "planner",
|
||||
mode: "plan",
|
||||
prompt: `你是 team "${teamName}" 的 PLANNER。
|
||||
|
||||
当你收到 PLAN 任务时,调用 Skill(skill="team:plan") 执行规划工作。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
复杂度: ${complexity}
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录:
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "planner", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到分配给你的 PLAN-* 任务
|
||||
2. Skill(skill="team:plan") 执行探索和规划
|
||||
3. team_msg log + SendMessage 将 plan 摘要发给 coordinator
|
||||
4. 等待 coordinator 审批或修改反馈
|
||||
5. 审批通过 → TaskUpdate completed → 检查下一个 PLAN 任务`
|
||||
})
|
||||
|
||||
// Executor
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "executor",
|
||||
prompt: `你是 team "${teamName}" 的 EXECUTOR。
|
||||
|
||||
当你收到 IMPL 任务时,调用 Skill(skill="team:execute") 执行代码实现。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录:
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "executor", to: "coordinator", type: "<type>", summary: "<摘要>" })
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到未阻塞的 IMPL-* 任务
|
||||
2. Skill(skill="team:execute") 执行实现
|
||||
3. team_msg log + SendMessage 报告完成状态和变更文件
|
||||
4. TaskUpdate completed → 检查下一个 IMPL 任务`
|
||||
})
|
||||
|
||||
// Tester (同时处理 TEST 和 REVIEW)
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "tester",
|
||||
prompt: `你是 team "${teamName}" 的 TESTER,同时负责测试和审查。
|
||||
|
||||
- 收到 TEST-* 任务 → 调用 Skill(skill="team:test") 执行测试修复循环
|
||||
- 收到 REVIEW-* 任务 → 调用 Skill(skill="team:review") 执行代码审查
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录:
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: "${teamName}", from: "tester", to: "coordinator", type: "<type>", summary: "<摘要>" })
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到未阻塞的 TEST-* 或 REVIEW-* 任务
|
||||
2. 根据任务类型调用对应 Skill
|
||||
3. team_msg log + SendMessage 报告结果给 coordinator
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
```
|
||||
|
||||
### Phase 3: 创建任务链
|
||||
|
||||
```javascript
|
||||
// PLAN-001 → IMPL-001 → TEST-001 + REVIEW-001
|
||||
TaskCreate({ subject: "PLAN-001: 探索和规划实现", description: `${taskDescription}\n\n写入: .workflow/.team-plan/${teamName}/`, activeForm: "规划中" })
|
||||
TaskUpdate({ taskId: planId, owner: "planner" })
|
||||
|
||||
TaskCreate({ subject: "IMPL-001: 实现已批准的计划", description: `${taskDescription}\n\nPlan: .workflow/.team-plan/${teamName}/plan.json`, activeForm: "实现中" })
|
||||
TaskUpdate({ taskId: implId, owner: "executor", addBlockedBy: [planId] })
|
||||
|
||||
TaskCreate({ subject: "TEST-001: 测试修复循环", description: `${taskDescription}`, activeForm: "测试中" })
|
||||
TaskUpdate({ taskId: testId, owner: "tester", addBlockedBy: [implId] })
|
||||
|
||||
TaskCreate({ subject: "REVIEW-001: 代码审查与需求验证", description: `${taskDescription}\n\nPlan: .workflow/.team-plan/${teamName}/plan.json`, activeForm: "审查中" })
|
||||
TaskUpdate({ taskId: reviewId, owner: "tester", addBlockedBy: [implId] })
|
||||
```
|
||||
|
||||
### Phase 4: 协调主循环
|
||||
|
||||
接收 teammate 消息,根据内容做调度决策。**每次做出决策前先 `team_msg list` 查看最近消息,每次做出决策后 `team_msg log` 记录**:
|
||||
|
||||
| 收到消息 | 操作 |
|
||||
|----------|------|
|
||||
| Planner: plan 就绪 | 读取 plan → 审批/修改 → team_msg log(plan_approved/plan_revision) → TaskUpdate + SendMessage |
|
||||
| Executor: 实现完成 | team_msg log(task_unblocked) → TaskUpdate IMPL completed → SendMessage 通知 tester |
|
||||
| Tester: 测试结果 >= 95% | team_msg log(test_result) → TaskUpdate TEST completed |
|
||||
| Tester: 测试结果 < 95% 且迭代 > 5 | team_msg log(error) → 上报用户 |
|
||||
| Tester: 审查无 critical | team_msg log(review_result) → TaskUpdate REVIEW completed |
|
||||
| Tester: 审查发现 critical | team_msg log(fix_required) → TaskCreate IMPL-fix → 分配 executor |
|
||||
| 所有任务 completed | → Phase 5 |
|
||||
|
||||
**用户可随时查看团队状态**:
|
||||
```bash
|
||||
# 用户在任意时刻调用查看
|
||||
mcp__ccw-tools__team_msg({ operation: "status", team: teamName })
|
||||
mcp__ccw-tools__team_msg({ operation: "list", team: teamName, last: 10 })
|
||||
```
|
||||
|
||||
### Phase 5: 汇报 + 持久循环
|
||||
|
||||
汇总变更文件、测试通过率、审查结果报告用户。
|
||||
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "当前需求已完成。下一步:",
|
||||
header: "Next",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "新需求", description: "提交新需求给当前团队" },
|
||||
{ label: "关闭团队", description: "关闭所有 teammate 并清理" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
// 新需求 → 回到 Phase 1(复用 team,新建 PLAN/IMPL/TEST/REVIEW 任务)
|
||||
// 关闭 → shutdown_request 给每个 teammate → TeamDelete()
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
| 场景 | 处理 |
|
||||
|------|------|
|
||||
| Teammate 无响应 | 发追踪消息,2次无响应 → 重新 spawn |
|
||||
| Plan 被拒 3+ 次 | Coordinator 自行规划 |
|
||||
| 测试卡在 <80% 超 5 次迭代 | 上报用户 |
|
||||
| Review 发现 critical | 创建 IMPL-fix 任务给 executor |
|
||||
359
.claude/commands/team/execute.md
Normal file
359
.claude/commands/team/execute.md
Normal file
@@ -0,0 +1,359 @@
|
||||
---
|
||||
name: execute
|
||||
description: Team executor - 实现已批准的计划、编写代码、报告进度
|
||||
argument-hint: ""
|
||||
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*)
|
||||
group: team
|
||||
---
|
||||
|
||||
# Team Execute Command (/team:execute)
|
||||
|
||||
## Overview
|
||||
|
||||
Team executor role command. Operates as a teammate within an Agent Team, responsible for implementing approved plans by writing code, self-validating, and reporting progress to the coordinator.
|
||||
|
||||
**Core capabilities:**
|
||||
- Task discovery from shared team task list (IMPL-* tasks)
|
||||
- Plan loading and task decomposition
|
||||
- Code implementation following plan modification points
|
||||
- Self-validation: syntax checks, acceptance criteria verification
|
||||
- Progress reporting to coordinator
|
||||
- Sub-agent delegation for complex tasks
|
||||
|
||||
## Role Definition
|
||||
|
||||
**Name**: `executor`
|
||||
**Responsibility**: Load plan → Implement code → Self-validate → Report completion
|
||||
**Communication**: SendMessage to coordinator only
|
||||
|
||||
## 消息总线
|
||||
|
||||
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
|
||||
|
||||
```javascript
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "<type>", summary: "<摘要>" })
|
||||
```
|
||||
|
||||
### 支持的 Message Types
|
||||
|
||||
| Type | 方向 | 触发时机 | 说明 |
|
||||
|------|------|----------|------|
|
||||
| `impl_progress` | executor → coordinator | 完成一个 batch/子任务 | 报告当前进度百分比和完成的子任务 |
|
||||
| `impl_complete` | executor → coordinator | 全部实现完成 | 附带变更文件列表和 acceptance 状态 |
|
||||
| `error` | executor → coordinator | 遇到阻塞问题 | Plan 文件缺失、文件冲突、子代理失败等 |
|
||||
|
||||
### 调用示例
|
||||
|
||||
```javascript
|
||||
// 进度更新
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_progress", summary: "Batch 1/3 完成: auth middleware 已实现", data: { batch: 1, total: 3, files: ["src/middleware/auth.ts"] } })
|
||||
|
||||
// 实现完成
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "impl_complete", summary: "IMPL-001完成: 5个文件变更, acceptance全部满足", data: { changedFiles: 5, syntaxClean: true } })
|
||||
|
||||
// 错误上报
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "executor", to: "coordinator", type: "error", summary: "plan.json路径无效, 无法加载实现计划" })
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Phase 1: Task & Plan Loading
|
||||
├─ TaskList to find unblocked IMPL-* tasks assigned to me
|
||||
├─ TaskGet to read full task details
|
||||
├─ TaskUpdate to mark in_progress
|
||||
└─ Load plan.json from plan path in task description
|
||||
|
||||
Phase 2: Task Grouping
|
||||
├─ Extract depends_on from plan tasks
|
||||
├─ Independent tasks → parallel batch
|
||||
└─ Dependent tasks → sequential batches
|
||||
|
||||
Phase 3: Code Implementation
|
||||
├─ For each task in plan:
|
||||
│ ├─ Read modification points
|
||||
│ ├─ Read reference patterns
|
||||
│ ├─ Implement changes (Edit/Write)
|
||||
│ ├─ Complex tasks → code-developer sub-agent
|
||||
│ └─ Simple tasks → direct file editing
|
||||
└─ SendMessage progress updates for complex tasks
|
||||
|
||||
Phase 4: Self-Validation
|
||||
├─ Syntax check (tsc --noEmit for TypeScript)
|
||||
├─ Verify acceptance criteria from plan
|
||||
├─ Run affected unit tests (if identifiable)
|
||||
└─ Fix any immediate issues
|
||||
|
||||
Phase 5: Completion Report
|
||||
├─ Compile changed files list
|
||||
├─ Summarize acceptance criteria status
|
||||
├─ SendMessage report to coordinator
|
||||
├─ Mark IMPL task completed
|
||||
└─ Check TaskList for next IMPL task
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Task & Plan Loading
|
||||
|
||||
```javascript
|
||||
// Find my assigned IMPL tasks
|
||||
const tasks = TaskList()
|
||||
const myImplTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('IMPL-') &&
|
||||
t.owner === 'executor' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0 // Not blocked
|
||||
)
|
||||
|
||||
if (myImplTasks.length === 0) {
|
||||
// No tasks available, idle
|
||||
return
|
||||
}
|
||||
|
||||
// Pick first available task (lowest ID)
|
||||
const task = TaskGet({ taskId: myImplTasks[0].id })
|
||||
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||
|
||||
// Extract plan path from task description
|
||||
const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/)
|
||||
const planPath = planPathMatch ? planPathMatch[0] : null
|
||||
|
||||
if (!planPath) {
|
||||
SendMessage({
|
||||
type: "message",
|
||||
recipient: "coordinator",
|
||||
content: `Cannot find plan.json path in task description for ${task.subject}. Please provide plan location.`,
|
||||
summary: "Plan path not found"
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const plan = JSON.parse(Read(planPath))
|
||||
```
|
||||
|
||||
### Phase 2: Task Grouping
|
||||
|
||||
```javascript
|
||||
// Extract dependencies and group tasks
|
||||
function extractDependencies(planTasks) {
|
||||
const taskIdToIndex = {}
|
||||
planTasks.forEach((t, i) => { taskIdToIndex[t.id] = i })
|
||||
|
||||
return planTasks.map((task, i) => {
|
||||
const deps = (task.depends_on || [])
|
||||
.map(depId => taskIdToIndex[depId])
|
||||
.filter(idx => idx !== undefined && idx < i)
|
||||
return { ...task, taskIndex: i, dependencies: deps }
|
||||
})
|
||||
}
|
||||
|
||||
function createBatches(planTasks) {
|
||||
const tasksWithDeps = extractDependencies(planTasks)
|
||||
const processed = new Set()
|
||||
const batches = []
|
||||
|
||||
// Phase 1: Independent tasks → single parallel batch
|
||||
const independent = tasksWithDeps.filter(t => t.dependencies.length === 0)
|
||||
if (independent.length > 0) {
|
||||
independent.forEach(t => processed.add(t.taskIndex))
|
||||
batches.push({ type: 'parallel', tasks: independent })
|
||||
}
|
||||
|
||||
// Phase 2+: Dependent tasks in order
|
||||
let remaining = tasksWithDeps.filter(t => !processed.has(t.taskIndex))
|
||||
while (remaining.length > 0) {
|
||||
const ready = remaining.filter(t => t.dependencies.every(d => processed.has(d)))
|
||||
if (ready.length === 0) break // circular dependency guard
|
||||
ready.forEach(t => processed.add(t.taskIndex))
|
||||
batches.push({ type: ready.length > 1 ? 'parallel' : 'sequential', tasks: ready })
|
||||
remaining = remaining.filter(t => !processed.has(t.taskIndex))
|
||||
}
|
||||
|
||||
return batches
|
||||
}
|
||||
|
||||
const batches = createBatches(plan.tasks)
|
||||
```
|
||||
|
||||
### Phase 3: Code Implementation
|
||||
|
||||
```javascript
|
||||
// Unified Task Prompt Builder (from lite-execute)
|
||||
function buildExecutionPrompt(planTask) {
|
||||
return `
|
||||
## ${planTask.title}
|
||||
|
||||
**Scope**: \`${planTask.scope}\` | **Action**: ${planTask.action || 'implement'}
|
||||
|
||||
### Modification Points
|
||||
${(planTask.modification_points || []).map(p => `- **${p.file}** → \`${p.target}\`: ${p.change}`).join('\n')}
|
||||
|
||||
### How to do it
|
||||
${planTask.description}
|
||||
|
||||
${(planTask.implementation || []).map(step => `- ${step}`).join('\n')}
|
||||
|
||||
### Reference
|
||||
- Pattern: ${planTask.reference?.pattern || 'N/A'}
|
||||
- Files: ${planTask.reference?.files?.join(', ') || 'N/A'}
|
||||
|
||||
### Done when
|
||||
${(planTask.acceptance || []).map(c => `- [ ] ${c}`).join('\n')}
|
||||
`
|
||||
}
|
||||
|
||||
// Execute each batch
|
||||
const changedFiles = []
|
||||
const previousResults = []
|
||||
|
||||
for (const batch of batches) {
|
||||
if (batch.tasks.length === 1 && isSimpleTask(batch.tasks[0])) {
|
||||
// Simple task: direct implementation
|
||||
const t = batch.tasks[0]
|
||||
// Read target files, apply modifications using Edit/Write
|
||||
for (const mp of (t.modification_points || [])) {
|
||||
const content = Read(mp.file)
|
||||
// Apply change based on modification point description
|
||||
Edit({ file_path: mp.file, old_string: "...", new_string: "..." })
|
||||
changedFiles.push(mp.file)
|
||||
}
|
||||
} else {
|
||||
// Complex task(s): delegate to code-developer sub-agent
|
||||
const prompt = batch.tasks.map(buildExecutionPrompt).join('\n\n---\n')
|
||||
|
||||
Task({
|
||||
subagent_type: "code-developer",
|
||||
run_in_background: false,
|
||||
description: batch.tasks.map(t => t.title).join(' | '),
|
||||
prompt: `## Goal
|
||||
${plan.summary}
|
||||
|
||||
## Tasks
|
||||
${prompt}
|
||||
|
||||
## Context
|
||||
### Project Guidelines
|
||||
@.workflow/project-guidelines.json
|
||||
|
||||
Complete each task according to its "Done when" checklist.`
|
||||
})
|
||||
|
||||
// Collect changed files from sub-agent results
|
||||
batch.tasks.forEach(t => {
|
||||
(t.modification_points || []).forEach(mp => changedFiles.push(mp.file))
|
||||
})
|
||||
}
|
||||
|
||||
previousResults.push({
|
||||
batchType: batch.type,
|
||||
tasks: batch.tasks.map(t => t.title),
|
||||
status: 'completed'
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Self-Validation
|
||||
|
||||
```javascript
|
||||
// Step 1: Syntax check
|
||||
const syntaxResult = Bash(`tsc --noEmit 2>&1 || true`)
|
||||
const hasSyntaxErrors = syntaxResult.includes('error TS')
|
||||
|
||||
if (hasSyntaxErrors) {
|
||||
// Attempt to fix syntax errors
|
||||
// Parse error locations, apply fixes
|
||||
console.log('Syntax errors detected, attempting fix...')
|
||||
}
|
||||
|
||||
// Step 2: Verify acceptance criteria
|
||||
const acceptanceStatus = plan.tasks.map(t => ({
|
||||
title: t.title,
|
||||
criteria: (t.acceptance || []).map(c => ({
|
||||
criterion: c,
|
||||
met: true // Evaluate based on implementation
|
||||
}))
|
||||
}))
|
||||
|
||||
// Step 3: Run affected tests (if identifiable)
|
||||
const testFiles = changedFiles
|
||||
.map(f => f.replace(/\/src\//, '/tests/').replace(/\.(ts|js)$/, '.test.$1'))
|
||||
.filter(f => Bash(`test -f ${f} && echo exists || true`).includes('exists'))
|
||||
|
||||
if (testFiles.length > 0) {
|
||||
const testResult = Bash(`npx jest ${testFiles.join(' ')} --passWithNoTests 2>&1 || true`)
|
||||
// Parse test results
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Completion Report
|
||||
|
||||
```javascript
|
||||
// Compile report
|
||||
const report = {
|
||||
task: task.subject,
|
||||
changedFiles: [...new Set(changedFiles)],
|
||||
newFiles: changedFiles.filter(f => /* detect new files */),
|
||||
acceptanceStatus: acceptanceStatus,
|
||||
syntaxClean: !hasSyntaxErrors,
|
||||
testsPassed: testFiles.length > 0 ? testResult.includes('passed') : 'N/A'
|
||||
}
|
||||
|
||||
// Send to coordinator
|
||||
SendMessage({
|
||||
type: "message",
|
||||
recipient: "coordinator",
|
||||
content: `## Implementation Complete
|
||||
|
||||
**Task**: ${task.subject}
|
||||
|
||||
### Changed Files
|
||||
${report.changedFiles.map(f => `- ${f}`).join('\n')}
|
||||
|
||||
### Acceptance Criteria
|
||||
${acceptanceStatus.map(t => `**${t.title}**: ${t.criteria.every(c => c.met) ? 'All met' : 'Partial'}`).join('\n')}
|
||||
|
||||
### Validation
|
||||
- Syntax: ${report.syntaxClean ? 'Clean' : 'Has errors (attempted fix)'}
|
||||
- Tests: ${report.testsPassed}
|
||||
|
||||
Implementation is ready for testing and review.`,
|
||||
summary: `IMPL complete: ${report.changedFiles.length} files changed`
|
||||
})
|
||||
|
||||
// Mark task completed
|
||||
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
|
||||
// Check for next IMPL task
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('IMPL-') &&
|
||||
t.owner === 'executor' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
if (nextTasks.length > 0) {
|
||||
// Continue with next task → back to Phase 1
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
```javascript
|
||||
function isSimpleTask(task) {
|
||||
return (task.modification_points || []).length <= 2 &&
|
||||
!task.code_skeleton &&
|
||||
(task.risks || []).length === 0
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Scenario | Resolution |
|
||||
|----------|------------|
|
||||
| Plan file not found | Notify coordinator, request plan location |
|
||||
| Syntax errors after implementation | Attempt auto-fix, report remaining errors |
|
||||
| Sub-agent failure | Retry once, then attempt direct implementation |
|
||||
| File conflict / merge issue | Notify coordinator, request guidance |
|
||||
| Test failures in self-validation | Report in completion message, let tester handle |
|
||||
| Circular dependencies in plan | Execute in plan order, ignore dependency chain |
|
||||
373
.claude/commands/team/plan.md
Normal file
373
.claude/commands/team/plan.md
Normal file
@@ -0,0 +1,373 @@
|
||||
---
|
||||
name: plan
|
||||
description: Team planner - 多角度代码探索、结构化实现规划、提交coordinator审批
|
||||
argument-hint: ""
|
||||
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*), Task(*)
|
||||
group: team
|
||||
---
|
||||
|
||||
# Team Plan Command (/team:plan)
|
||||
|
||||
## Overview
|
||||
|
||||
Team planner role command. Operates as a teammate within an Agent Team, responsible for multi-angle code exploration and structured implementation planning. Submits plans to the coordinator for approval.
|
||||
|
||||
**Core capabilities:**
|
||||
- Task discovery from shared team task list
|
||||
- Multi-angle codebase exploration (architecture/security/performance/bugfix/feature)
|
||||
- Complexity-adaptive planning (Low → direct, Medium/High → agent-assisted)
|
||||
- Structured plan.json generation following schema
|
||||
- Plan submission and revision cycle with coordinator
|
||||
|
||||
## Role Definition
|
||||
|
||||
**Name**: `planner`
|
||||
**Responsibility**: Code exploration → Implementation planning → Coordinator approval
|
||||
**Communication**: SendMessage to coordinator only
|
||||
|
||||
## 消息总线
|
||||
|
||||
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
|
||||
|
||||
```javascript
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })
|
||||
```
|
||||
|
||||
### 支持的 Message Types
|
||||
|
||||
| Type | 方向 | 触发时机 | 说明 |
|
||||
|------|------|----------|------|
|
||||
| `plan_ready` | planner → coordinator | Plan 生成完成 | 附带 plan.json 路径和任务数摘要 |
|
||||
| `plan_revision` | planner → coordinator | Plan 修订后重新提交 | 说明修改内容 |
|
||||
| `impl_progress` | planner → coordinator | 探索阶段进展更新 | 可选,长时间探索时使用 |
|
||||
| `error` | planner → coordinator | 遇到不可恢复错误 | 探索失败、schema缺失等 |
|
||||
|
||||
### 调用示例
|
||||
|
||||
```javascript
|
||||
// Plan 就绪
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_ready", summary: "Plan就绪: 3个task, Medium复杂度", ref: ".workflow/.team-plan/auth-impl-2026-02-09/plan.json" })
|
||||
|
||||
// Plan 修订
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "plan_revision", summary: "已按反馈拆分task-2为两个子任务" })
|
||||
|
||||
// 错误上报
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "planner", to: "coordinator", type: "error", summary: "plan-json-schema.json 未找到, 使用默认结构" })
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Phase 1: Task Discovery
|
||||
├─ Read team config to identify coordinator
|
||||
├─ TaskList to find PLAN-* tasks assigned to me
|
||||
├─ TaskGet to read full task details
|
||||
└─ TaskUpdate to mark in_progress
|
||||
|
||||
Phase 2: Multi-Angle Exploration
|
||||
├─ Complexity assessment (Low/Medium/High)
|
||||
├─ Angle selection based on task type
|
||||
├─ Semantic search via mcp__ace-tool__search_context
|
||||
├─ Pattern search via Grep/Glob
|
||||
├─ Complex tasks: cli-explore-agent sub-agents
|
||||
└─ Write exploration results to session folder
|
||||
|
||||
Phase 3: Plan Generation
|
||||
├─ Read plan-json-schema.json for structure reference
|
||||
├─ Low complexity → Direct Claude planning
|
||||
├─ Medium/High → cli-lite-planning-agent
|
||||
└─ Output: plan.json
|
||||
|
||||
Phase 4: Submit for Approval
|
||||
├─ SendMessage plan summary to coordinator
|
||||
├─ Wait for approve/revision feedback
|
||||
└─ If revision → update plan → resubmit
|
||||
|
||||
Phase 5: Idle & Next Task
|
||||
├─ Mark current task completed
|
||||
├─ TaskList to check for new PLAN tasks
|
||||
└─ No tasks → idle (wait for coordinator assignment)
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Read team config
|
||||
const teamConfig = JSON.parse(Read(`~/.claude/teams/${teamName}/config.json`))
|
||||
|
||||
// Find my assigned PLAN tasks
|
||||
const tasks = TaskList()
|
||||
const myPlanTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('PLAN-') &&
|
||||
t.owner === 'planner' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
if (myPlanTasks.length === 0) {
|
||||
// No tasks available, idle
|
||||
return
|
||||
}
|
||||
|
||||
// Pick first available task (lowest ID)
|
||||
const task = TaskGet({ taskId: myPlanTasks[0].id })
|
||||
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||
```
|
||||
|
||||
### Phase 2: Multi-Angle Exploration
|
||||
|
||||
```javascript
|
||||
// Session setup
|
||||
const taskSlug = task.subject.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 40)
|
||||
const dateStr = new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString().substring(0, 10)
|
||||
const sessionFolder = `.workflow/.team-plan/${taskSlug}-${dateStr}`
|
||||
Bash(`mkdir -p ${sessionFolder}`)
|
||||
|
||||
// Complexity assessment
|
||||
function assessComplexity(desc) {
|
||||
let score = 0
|
||||
if (/refactor|architect|restructure|模块|系统/.test(desc)) score += 2
|
||||
if (/multiple|多个|across|跨/.test(desc)) score += 2
|
||||
if (/integrate|集成|api|database/.test(desc)) score += 1
|
||||
if (/security|安全|performance|性能/.test(desc)) score += 1
|
||||
return score >= 4 ? 'High' : score >= 2 ? 'Medium' : 'Low'
|
||||
}
|
||||
|
||||
const complexity = assessComplexity(task.description)
|
||||
|
||||
// Angle selection
|
||||
const ANGLE_PRESETS = {
|
||||
architecture: ['architecture', 'dependencies', 'modularity', 'integration-points'],
|
||||
security: ['security', 'auth-patterns', 'dataflow', 'validation'],
|
||||
performance: ['performance', 'bottlenecks', 'caching', 'data-access'],
|
||||
bugfix: ['error-handling', 'dataflow', 'state-management', 'edge-cases'],
|
||||
feature: ['patterns', 'integration-points', 'testing', 'dependencies']
|
||||
}
|
||||
|
||||
function selectAngles(desc, count) {
|
||||
const text = desc.toLowerCase()
|
||||
let preset = 'feature'
|
||||
if (/refactor|architect|restructure|modular/.test(text)) preset = 'architecture'
|
||||
else if (/security|auth|permission|access/.test(text)) preset = 'security'
|
||||
else if (/performance|slow|optimi|cache/.test(text)) preset = 'performance'
|
||||
else if (/fix|bug|error|issue|broken/.test(text)) preset = 'bugfix'
|
||||
return ANGLE_PRESETS[preset].slice(0, count)
|
||||
}
|
||||
|
||||
const angleCount = complexity === 'High' ? 4 : (complexity === 'Medium' ? 3 : 1)
|
||||
const selectedAngles = selectAngles(task.description, angleCount)
|
||||
|
||||
// Execute exploration
|
||||
// Low complexity: direct search with mcp__ace-tool__search_context + Grep/Glob
|
||||
// Medium/High: launch cli-explore-agent sub-agents in parallel
|
||||
|
||||
if (complexity === 'Low') {
|
||||
// Direct exploration
|
||||
const results = mcp__ace-tool__search_context({
|
||||
project_root_path: projectRoot,
|
||||
query: task.description
|
||||
})
|
||||
// Write single exploration file
|
||||
Write(`${sessionFolder}/exploration-${selectedAngles[0]}.json`, JSON.stringify({
|
||||
project_structure: "...",
|
||||
relevant_files: [],
|
||||
patterns: [],
|
||||
dependencies: [],
|
||||
integration_points: [],
|
||||
constraints: [],
|
||||
clarification_needs: [],
|
||||
_metadata: { exploration_angle: selectedAngles[0] }
|
||||
}, null, 2))
|
||||
} else {
|
||||
// Launch parallel cli-explore-agent for each angle
|
||||
selectedAngles.forEach((angle, index) => {
|
||||
Task({
|
||||
subagent_type: "cli-explore-agent",
|
||||
run_in_background: false,
|
||||
description: `Explore: ${angle}`,
|
||||
prompt: `
|
||||
## Task Objective
|
||||
Execute **${angle}** exploration for task planning context.
|
||||
|
||||
## Output Location
|
||||
**Session Folder**: ${sessionFolder}
|
||||
**Output File**: ${sessionFolder}/exploration-${angle}.json
|
||||
|
||||
## Assigned Context
|
||||
- **Exploration Angle**: ${angle}
|
||||
- **Task Description**: ${task.description}
|
||||
- **Exploration Index**: ${index + 1} of ${selectedAngles.length}
|
||||
|
||||
## MANDATORY FIRST STEPS
|
||||
1. Run: rg -l "{relevant_keyword}" --type ts (locate relevant files)
|
||||
2. Execute: cat ~/.ccw/workflows/cli-templates/schemas/explore-json-schema.json (get output schema)
|
||||
3. Read: .workflow/project-tech.json (if exists - technology stack)
|
||||
|
||||
## Expected Output
|
||||
Write JSON to: ${sessionFolder}/exploration-${angle}.json
|
||||
Follow explore-json-schema.json structure with ${angle}-focused findings.
|
||||
`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Build explorations manifest
|
||||
const explorationManifest = {
|
||||
session_id: `${taskSlug}-${dateStr}`,
|
||||
task_description: task.description,
|
||||
complexity: complexity,
|
||||
exploration_count: selectedAngles.length,
|
||||
explorations: selectedAngles.map(angle => ({
|
||||
angle: angle,
|
||||
file: `exploration-${angle}.json`,
|
||||
path: `${sessionFolder}/exploration-${angle}.json`
|
||||
}))
|
||||
}
|
||||
Write(`${sessionFolder}/explorations-manifest.json`, JSON.stringify(explorationManifest, null, 2))
|
||||
```
|
||||
|
||||
### Phase 3: Plan Generation
|
||||
|
||||
```javascript
|
||||
// Read schema reference
|
||||
const schema = Bash(`cat ~/.ccw/workflows/cli-templates/schemas/plan-json-schema.json`)
|
||||
|
||||
if (complexity === 'Low') {
|
||||
// Direct Claude planning
|
||||
// Read all exploration files
|
||||
explorationManifest.explorations.forEach(exp => {
|
||||
const data = Read(exp.path)
|
||||
// Incorporate findings into plan
|
||||
})
|
||||
|
||||
// Generate plan following schema
|
||||
const plan = {
|
||||
summary: "...",
|
||||
approach: "...",
|
||||
tasks: [/* structured tasks with dependencies, modification points, acceptance criteria */],
|
||||
estimated_time: "...",
|
||||
recommended_execution: "Agent",
|
||||
complexity: "Low",
|
||||
_metadata: {
|
||||
timestamp: new Date().toISOString(),
|
||||
source: "team-planner",
|
||||
planning_mode: "direct"
|
||||
}
|
||||
}
|
||||
Write(`${sessionFolder}/plan.json`, JSON.stringify(plan, null, 2))
|
||||
} else {
|
||||
// Use cli-lite-planning-agent for Medium/High
|
||||
Task({
|
||||
subagent_type: "cli-lite-planning-agent",
|
||||
run_in_background: false,
|
||||
description: "Generate detailed implementation plan",
|
||||
prompt: `
|
||||
Generate implementation plan and write plan.json.
|
||||
|
||||
## Output Location
|
||||
**Session Folder**: ${sessionFolder}
|
||||
**Output Files**:
|
||||
- ${sessionFolder}/planning-context.md
|
||||
- ${sessionFolder}/plan.json
|
||||
|
||||
## Output Schema Reference
|
||||
Execute: cat ~/.ccw/workflows/cli-templates/schemas/plan-json-schema.json
|
||||
|
||||
## Task Description
|
||||
${task.description}
|
||||
|
||||
## Multi-Angle Exploration Context
|
||||
${explorationManifest.explorations.map(exp => `### Exploration: ${exp.angle}
|
||||
Path: ${exp.path}`).join('\n\n')}
|
||||
|
||||
## Complexity Level
|
||||
${complexity}
|
||||
|
||||
## Requirements
|
||||
Generate plan.json following schema. Key constraints:
|
||||
- tasks: 2-7 structured tasks (group by feature/module, NOT by file)
|
||||
- Each task: id, title, scope, modification_points, implementation, acceptance, depends_on
|
||||
- Prefer parallel tasks (minimize depends_on)
|
||||
`
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Submit for Approval
|
||||
|
||||
```javascript
|
||||
// Read generated plan
|
||||
const plan = JSON.parse(Read(`${sessionFolder}/plan.json`))
|
||||
|
||||
// Send plan summary to coordinator
|
||||
SendMessage({
|
||||
type: "message",
|
||||
recipient: "coordinator", // team lead
|
||||
content: `## Plan Ready for Review
|
||||
|
||||
**Task**: ${task.subject}
|
||||
**Complexity**: ${complexity}
|
||||
**Tasks**: ${plan.tasks.length}
|
||||
|
||||
### Task Summary
|
||||
${plan.tasks.map((t, i) => `${i+1}. ${t.title} (${t.scope || 'N/A'})`).join('\n')}
|
||||
|
||||
### Approach
|
||||
${plan.approach}
|
||||
|
||||
### Plan Location
|
||||
${sessionFolder}/plan.json
|
||||
|
||||
Please review and approve or request revisions.`,
|
||||
summary: `Plan ready: ${plan.tasks.length} tasks`
|
||||
})
|
||||
|
||||
// Wait for coordinator response
|
||||
// If approved → mark task completed
|
||||
// If revision requested → update plan based on feedback → resubmit
|
||||
```
|
||||
|
||||
### Phase 5: After Approval
|
||||
|
||||
```javascript
|
||||
// Mark PLAN task as completed
|
||||
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
|
||||
// Check for more PLAN tasks
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('PLAN-') &&
|
||||
t.owner === 'planner' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
if (nextTasks.length > 0) {
|
||||
// Continue with next PLAN task → back to Phase 1
|
||||
} else {
|
||||
// No more tasks, idle
|
||||
// Will be woken by coordinator message for new assignments
|
||||
}
|
||||
```
|
||||
|
||||
## Session Files
|
||||
|
||||
```
|
||||
.workflow/.team-plan/{task-slug}-{YYYY-MM-DD}/
|
||||
├── exploration-{angle1}.json # Per-angle exploration results
|
||||
├── exploration-{angle2}.json
|
||||
├── explorations-manifest.json # Exploration index
|
||||
├── planning-context.md # Evidence + understanding (Medium/High)
|
||||
└── plan.json # Implementation plan
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Scenario | Resolution |
|
||||
|----------|------------|
|
||||
| Exploration agent failure | Skip exploration, plan from task description only |
|
||||
| Planning agent failure | Fallback to direct Claude planning |
|
||||
| Plan rejected 3+ times | Notify coordinator, suggest alternative approach |
|
||||
| No PLAN tasks available | Idle, wait for coordinator assignment |
|
||||
| Schema file not found | Use basic plan structure without schema validation |
|
||||
383
.claude/commands/team/review.md
Normal file
383
.claude/commands/team/review.md
Normal file
@@ -0,0 +1,383 @@
|
||||
---
|
||||
name: review
|
||||
description: Team reviewer - 代码质量/安全/架构审查、需求验证、发现报告给coordinator
|
||||
argument-hint: ""
|
||||
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Bash(*), Glob(*), Grep(*), Task(*)
|
||||
group: team
|
||||
---
|
||||
|
||||
# Team Review Command (/team:review)
|
||||
|
||||
## Overview
|
||||
|
||||
Team reviewer role command. Operates as a teammate within an Agent Team (typically handled by the tester), responsible for multi-dimensional code review and requirement verification. Reports findings to the coordinator with severity classification.
|
||||
|
||||
**Core capabilities:**
|
||||
- Task discovery from shared team task list (REVIEW-* tasks)
|
||||
- Multi-dimensional review: quality, security, architecture, requirement verification
|
||||
- Pattern-based security scanning with Grep
|
||||
- Acceptance criteria verification against plan
|
||||
- Severity-classified findings (critical/high/medium/low)
|
||||
- Optional CLI-assisted deep analysis (Gemini/Qwen)
|
||||
|
||||
## Role Definition
|
||||
|
||||
**Name**: `tester` (same teammate handles both TEST and REVIEW tasks)
|
||||
**Responsibility**: Review code changes → Verify requirements → Report findings
|
||||
**Communication**: SendMessage to coordinator only
|
||||
|
||||
## 消息总线
|
||||
|
||||
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
|
||||
|
||||
```javascript
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "<type>", summary: "<摘要>" })
|
||||
```
|
||||
|
||||
### 支持的 Message Types
|
||||
|
||||
| Type | 方向 | 触发时机 | 说明 |
|
||||
|------|------|----------|------|
|
||||
| `review_result` | tester → coordinator | 审查完成 | 附带 verdict(APPROVE/CONDITIONAL/BLOCK)和发现统计 |
|
||||
| `fix_required` | tester → coordinator | 发现 critical issues | 需要创建 IMPL-fix 任务给 executor |
|
||||
| `error` | tester → coordinator | 审查无法完成 | Plan 缺失、变更文件无法读取等 |
|
||||
|
||||
### 调用示例
|
||||
|
||||
```javascript
|
||||
// 审查通过
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "review_result", summary: "REVIEW-001: APPROVE, 2 medium + 1 low findings", data: { verdict: "APPROVE", critical: 0, high: 0, medium: 2, low: 1 } })
|
||||
|
||||
// 审查有条件通过
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "review_result", summary: "REVIEW-001: CONDITIONAL, 4 high severity findings需关注", data: { verdict: "CONDITIONAL", critical: 0, high: 4, medium: 3, low: 2 } })
|
||||
|
||||
// 发现 critical 问题
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "fix_required", summary: "发现eval()使用和硬编码密码, 需立即修复", data: { critical: 2, details: ["eval() in auth.ts:42", "hardcoded password in config.ts:15"] } })
|
||||
|
||||
// 错误上报
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "error", summary: "plan.json未找到, 无法进行需求验证" })
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Phase 1: Task Discovery
|
||||
├─ TaskList to find unblocked REVIEW-* tasks assigned to me
|
||||
├─ TaskGet to read full task details
|
||||
└─ TaskUpdate to mark in_progress
|
||||
|
||||
Phase 2: Review Context Loading
|
||||
├─ Read plan.json (requirements + acceptance criteria)
|
||||
├─ git diff to get changed files
|
||||
├─ Read test results (if available)
|
||||
└─ Read changed file contents
|
||||
|
||||
Phase 3: Multi-Dimensional Review
|
||||
├─ Quality: code style, maintainability, @ts-ignore/any usage
|
||||
├─ Security: eval/exec/innerHTML/hardcoded secrets (Grep patterns)
|
||||
├─ Architecture: layering compliance, modularity, tech debt
|
||||
├─ Requirement Verification: plan acceptance criteria vs implementation
|
||||
└─ Optional: CLI deep analysis (Gemini for security, Qwen for architecture)
|
||||
|
||||
Phase 4: Finding Summary
|
||||
├─ Classify by severity: critical/high/medium/low
|
||||
├─ Generate actionable recommendations
|
||||
└─ Determine overall verdict
|
||||
|
||||
Phase 5: Report to Coordinator
|
||||
├─ SendMessage with review findings
|
||||
├─ No critical issues → mark REVIEW task completed
|
||||
└─ Critical issues → flag for immediate attention
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Find my assigned REVIEW tasks
|
||||
const tasks = TaskList()
|
||||
const myReviewTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('REVIEW-') &&
|
||||
t.owner === 'tester' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
if (myReviewTasks.length === 0) return // idle
|
||||
|
||||
const task = TaskGet({ taskId: myReviewTasks[0].id })
|
||||
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||
```
|
||||
|
||||
### Phase 2: Review Context Loading
|
||||
|
||||
```javascript
|
||||
// Load plan for acceptance criteria
|
||||
const planPathMatch = task.description.match(/\.workflow\/\.team-plan\/[^\s]+\/plan\.json/)
|
||||
let plan = null
|
||||
if (planPathMatch) {
|
||||
try { plan = JSON.parse(Read(planPathMatch[0])) } catch {}
|
||||
}
|
||||
|
||||
// Get changed files via git
|
||||
const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`)
|
||||
.split('\n')
|
||||
.filter(f => f.trim() && !f.startsWith('.'))
|
||||
|
||||
// Read changed file contents for review
|
||||
const fileContents = {}
|
||||
for (const file of changedFiles.slice(0, 20)) { // limit to 20 files
|
||||
try { fileContents[file] = Read(file) } catch {}
|
||||
}
|
||||
|
||||
// Load test results if available
|
||||
let testResults = null
|
||||
const testSummary = tasks.find(t => t.subject.startsWith('TEST-') && t.status === 'completed')
|
||||
```
|
||||
|
||||
### Phase 3: Multi-Dimensional Review
|
||||
|
||||
```javascript
|
||||
const findings = {
|
||||
critical: [],
|
||||
high: [],
|
||||
medium: [],
|
||||
low: []
|
||||
}
|
||||
|
||||
// --- Quality Review ---
|
||||
function reviewQuality(files) {
|
||||
const issues = []
|
||||
|
||||
// Check for @ts-ignore, @ts-expect-error, any type
|
||||
const tsIgnore = Grep({ pattern: '@ts-ignore|@ts-expect-error', glob: '*.{ts,tsx}', output_mode: 'content' })
|
||||
if (tsIgnore) issues.push({ type: 'quality', detail: '@ts-ignore/@ts-expect-error usage detected', severity: 'medium' })
|
||||
|
||||
const anyType = Grep({ pattern: ': any[^A-Z]|as any', glob: '*.{ts,tsx}', output_mode: 'content' })
|
||||
if (anyType) issues.push({ type: 'quality', detail: 'Untyped `any` usage detected', severity: 'medium' })
|
||||
|
||||
// Check for console.log left in production code
|
||||
const consoleLogs = Grep({ pattern: 'console\\.log', glob: '*.{ts,tsx,js,jsx}', path: 'src/', output_mode: 'content' })
|
||||
if (consoleLogs) issues.push({ type: 'quality', detail: 'console.log found in source code', severity: 'low' })
|
||||
|
||||
// Check for empty catch blocks
|
||||
const emptyCatch = Grep({ pattern: 'catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content', multiline: true })
|
||||
if (emptyCatch) issues.push({ type: 'quality', detail: 'Empty catch blocks detected', severity: 'high' })
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
// --- Security Review ---
|
||||
function reviewSecurity(files) {
|
||||
const issues = []
|
||||
|
||||
// Dangerous functions
|
||||
const dangerousFns = Grep({ pattern: '\\beval\\b|\\bexec\\b|innerHTML|dangerouslySetInnerHTML', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' })
|
||||
if (dangerousFns) issues.push({ type: 'security', detail: 'Dangerous function usage: eval/exec/innerHTML', severity: 'critical' })
|
||||
|
||||
// Hardcoded secrets
|
||||
const secrets = Grep({ pattern: 'password\\s*=\\s*["\']|secret\\s*=\\s*["\']|api_key\\s*=\\s*["\']', glob: '*.{ts,tsx,js,jsx,py}', output_mode: 'content', '-i': true })
|
||||
if (secrets) issues.push({ type: 'security', detail: 'Hardcoded secrets/passwords detected', severity: 'critical' })
|
||||
|
||||
// SQL injection risk
|
||||
const sqlInjection = Grep({ pattern: 'query\\s*\\(\\s*`|execute\\s*\\(\\s*`|\\$\\{.*\\}.*(?:SELECT|INSERT|UPDATE|DELETE)', glob: '*.{ts,js,py}', output_mode: 'content', '-i': true })
|
||||
if (sqlInjection) issues.push({ type: 'security', detail: 'Potential SQL injection via template literals', severity: 'critical' })
|
||||
|
||||
// XSS via user input
|
||||
const xssRisk = Grep({ pattern: 'document\\.write|window\\.location\\s*=', glob: '*.{ts,tsx,js,jsx}', output_mode: 'content' })
|
||||
if (xssRisk) issues.push({ type: 'security', detail: 'Potential XSS vectors detected', severity: 'high' })
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
// --- Architecture Review ---
|
||||
function reviewArchitecture(files) {
|
||||
const issues = []
|
||||
|
||||
// Circular dependency indicators
|
||||
// Check for imports that may create cycles
|
||||
for (const [file, content] of Object.entries(fileContents)) {
|
||||
const imports = content.match(/from\s+['"]([^'"]+)['"]/g) || []
|
||||
// Basic heuristic: component importing from parent directory
|
||||
const parentImports = imports.filter(i => i.includes('../..'))
|
||||
if (parentImports.length > 2) {
|
||||
issues.push({ type: 'architecture', detail: `${file}: excessive parent directory imports (possible layering violation)`, severity: 'medium' })
|
||||
}
|
||||
}
|
||||
|
||||
// Large file detection
|
||||
for (const [file, content] of Object.entries(fileContents)) {
|
||||
const lines = content.split('\n').length
|
||||
if (lines > 500) {
|
||||
issues.push({ type: 'architecture', detail: `${file}: ${lines} lines - consider splitting`, severity: 'low' })
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
// --- Requirement Verification ---
|
||||
function verifyRequirements(plan) {
|
||||
const issues = []
|
||||
if (!plan) {
|
||||
issues.push({ type: 'requirement', detail: 'No plan found for requirement verification', severity: 'medium' })
|
||||
return issues
|
||||
}
|
||||
|
||||
for (const planTask of plan.tasks) {
|
||||
for (const criterion of (planTask.acceptance || [])) {
|
||||
// Check if criterion appears to be met
|
||||
// This is a heuristic check - look for relevant code in changed files
|
||||
const keywords = criterion.toLowerCase().split(/\s+/).filter(w => w.length > 4)
|
||||
const hasEvidence = keywords.some(kw =>
|
||||
Object.values(fileContents).some(content => content.toLowerCase().includes(kw))
|
||||
)
|
||||
|
||||
if (!hasEvidence) {
|
||||
issues.push({
|
||||
type: 'requirement',
|
||||
detail: `Acceptance criterion may not be met: "${criterion}" (task: ${planTask.title})`,
|
||||
severity: 'high'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
// Execute all review dimensions
|
||||
const qualityIssues = reviewQuality(changedFiles)
|
||||
const securityIssues = reviewSecurity(changedFiles)
|
||||
const architectureIssues = reviewArchitecture(changedFiles)
|
||||
const requirementIssues = plan ? verifyRequirements(plan) : []
|
||||
|
||||
// Classify into severity buckets
|
||||
const allIssues = [...qualityIssues, ...securityIssues, ...architectureIssues, ...requirementIssues]
|
||||
allIssues.forEach(issue => {
|
||||
findings[issue.severity].push(issue)
|
||||
})
|
||||
```
|
||||
|
||||
### Phase 4: Finding Summary
|
||||
|
||||
```javascript
|
||||
const totalIssues = Object.values(findings).flat().length
|
||||
const hasCritical = findings.critical.length > 0
|
||||
|
||||
const verdict = hasCritical
|
||||
? 'BLOCK - Critical issues must be resolved'
|
||||
: findings.high.length > 3
|
||||
? 'CONDITIONAL - High severity issues should be addressed'
|
||||
: 'APPROVE - No blocking issues found'
|
||||
|
||||
const recommendations = []
|
||||
if (hasCritical) {
|
||||
recommendations.push('Fix all critical security issues before merging')
|
||||
}
|
||||
if (findings.high.length > 0) {
|
||||
recommendations.push('Address high severity issues in a follow-up')
|
||||
}
|
||||
if (findings.medium.length > 3) {
|
||||
recommendations.push('Consider refactoring to reduce medium severity issues')
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Report to Coordinator
|
||||
|
||||
```javascript
|
||||
SendMessage({
|
||||
type: "message",
|
||||
recipient: "coordinator",
|
||||
content: `## Code Review Report
|
||||
|
||||
**Task**: ${task.subject}
|
||||
**Verdict**: ${verdict}
|
||||
**Files Reviewed**: ${changedFiles.length}
|
||||
**Total Findings**: ${totalIssues}
|
||||
|
||||
### Finding Summary
|
||||
- Critical: ${findings.critical.length}
|
||||
- High: ${findings.high.length}
|
||||
- Medium: ${findings.medium.length}
|
||||
- Low: ${findings.low.length}
|
||||
|
||||
${findings.critical.length > 0 ? `### Critical Issues
|
||||
${findings.critical.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')}
|
||||
` : ''}
|
||||
${findings.high.length > 0 ? `### High Severity
|
||||
${findings.high.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')}
|
||||
` : ''}
|
||||
${findings.medium.length > 0 ? `### Medium Severity
|
||||
${findings.medium.map(f => `- [${f.type.toUpperCase()}] ${f.detail}`).join('\n')}
|
||||
` : ''}
|
||||
### Recommendations
|
||||
${recommendations.map(r => `- ${r}`).join('\n')}
|
||||
|
||||
${plan ? `### Requirement Verification
|
||||
${plan.tasks.map(t => `- **${t.title}**: ${requirementIssues.filter(i => i.detail.includes(t.title)).length === 0 ? 'Criteria met' : 'Needs verification'}`).join('\n')}
|
||||
` : ''}`,
|
||||
summary: `Review: ${verdict.split(' - ')[0]} (${totalIssues} findings)`
|
||||
})
|
||||
|
||||
// Mark task based on verdict
|
||||
if (!hasCritical) {
|
||||
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
} else {
|
||||
// Keep in_progress, coordinator needs to create fix tasks
|
||||
SendMessage({
|
||||
type: "message",
|
||||
recipient: "coordinator",
|
||||
content: `Critical issues found in review. Recommend creating IMPL-fix tasks for executor to address: ${findings.critical.map(f => f.detail).join('; ')}`,
|
||||
summary: "Critical issues need fix tasks"
|
||||
})
|
||||
}
|
||||
|
||||
// Check for next REVIEW task
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('REVIEW-') &&
|
||||
t.owner === 'tester' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
if (nextTasks.length > 0) {
|
||||
// Continue with next task
|
||||
}
|
||||
```
|
||||
|
||||
## Optional: CLI Deep Analysis
|
||||
|
||||
For complex reviews, the tester can invoke CLI tools for deeper analysis:
|
||||
|
||||
```bash
|
||||
# Security deep analysis (Gemini)
|
||||
ccw cli -p "
|
||||
PURPOSE: Deep security audit of implementation changes
|
||||
TASK: Scan for OWASP Top 10 vulnerabilities, injection flaws, auth bypass vectors
|
||||
CONTEXT: @src/**/*.{ts,tsx,js,jsx}
|
||||
EXPECTED: Security findings with severity, file:line references, remediation
|
||||
CONSTRAINTS: Focus on changed files only
|
||||
" --tool gemini --mode analysis
|
||||
|
||||
# Architecture deep analysis (Qwen)
|
||||
ccw cli -p "
|
||||
PURPOSE: Architecture compliance review
|
||||
TASK: Evaluate layering, modularity, separation of concerns
|
||||
CONTEXT: @src/**/*
|
||||
EXPECTED: Architecture assessment with recommendations
|
||||
CONSTRAINTS: Focus on changed modules
|
||||
" --tool qwen --mode analysis
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Scenario | Resolution |
|
||||
|----------|------------|
|
||||
| Plan file not found | Review without requirement verification, note in report |
|
||||
| No changed files detected | Report to coordinator, may need manual file list |
|
||||
| Grep pattern errors | Skip specific check, continue with remaining |
|
||||
| CLI analysis timeout | Report partial results, note incomplete analysis |
|
||||
| Too many files to review (> 50) | Focus on source files, skip generated/vendor files |
|
||||
| Cannot determine file content | Skip file, note in report |
|
||||
391
.claude/commands/team/test.md
Normal file
391
.claude/commands/team/test.md
Normal file
@@ -0,0 +1,391 @@
|
||||
---
|
||||
name: test
|
||||
description: Team tester - 自适应测试修复循环、渐进式测试、报告结果给coordinator
|
||||
argument-hint: ""
|
||||
allowed-tools: SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*)
|
||||
group: team
|
||||
---
|
||||
|
||||
# Team Test Command (/team:test)
|
||||
|
||||
## Overview
|
||||
|
||||
Team tester role command. Operates as a teammate within an Agent Team, responsible for test execution with adaptive fix cycles and progressive testing. Reports results to the coordinator.
|
||||
|
||||
**Core capabilities:**
|
||||
- Task discovery from shared team task list (TEST-* tasks)
|
||||
- Test framework auto-detection (jest/vitest/pytest/mocha)
|
||||
- Adaptive strategy engine: conservative → aggressive → surgical
|
||||
- Progressive testing: affected tests during iterations, full suite for final validation
|
||||
- Fix cycle with max iterations and quality gate (>= 95% pass rate)
|
||||
- Structured result reporting to coordinator
|
||||
|
||||
## Role Definition
|
||||
|
||||
**Name**: `tester`
|
||||
**Responsibility**: Run tests → Fix cycle → Report results
|
||||
**Communication**: SendMessage to coordinator only
|
||||
|
||||
## 消息总线
|
||||
|
||||
每次 SendMessage **前**,必须调用 `mcp__ccw-tools__team_msg` 记录消息:
|
||||
|
||||
```javascript
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "<type>", summary: "<摘要>" })
|
||||
```
|
||||
|
||||
### 支持的 Message Types
|
||||
|
||||
| Type | 方向 | 触发时机 | 说明 |
|
||||
|------|------|----------|------|
|
||||
| `test_result` | tester → coordinator | 测试循环结束(通过或达到最大迭代) | 附带 pass rate、迭代次数、剩余失败 |
|
||||
| `impl_progress` | tester → coordinator | 修复循环中间进度 | 可选,长时间修复时使用(如迭代>5) |
|
||||
| `fix_required` | tester → coordinator | 测试发现需要 executor 修复的问题 | 超出 tester 修复能力的架构/设计问题 |
|
||||
| `error` | tester → coordinator | 测试框架不可用或测试执行崩溃 | 命令未找到、超时、环境问题等 |
|
||||
|
||||
### 调用示例
|
||||
|
||||
```javascript
|
||||
// 测试通过
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "test_result", summary: "TEST-001通过: 98% pass rate, 3次迭代", data: { passRate: 98, iterations: 3, total: 42, passed: 41, failed: 1 } })
|
||||
|
||||
// 测试未达标
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "test_result", summary: "TEST-001未达标: 82% pass rate, 10次迭代已用完", data: { passRate: 82, iterations: 10, criticalFailures: 2 } })
|
||||
|
||||
// 需要 executor 修复
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "fix_required", summary: "数据库连接池配置导致集成测试全部失败, 需executor修复" })
|
||||
|
||||
// 错误上报
|
||||
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "tester", to: "coordinator", type: "error", summary: "jest命令未找到, 请确认测试框架已安装" })
|
||||
```
|
||||
|
||||
## Execution Process
|
||||
|
||||
```
|
||||
Phase 1: Task Discovery
|
||||
├─ TaskList to find unblocked TEST-* tasks assigned to me
|
||||
├─ TaskGet to read full task details
|
||||
└─ TaskUpdate to mark in_progress
|
||||
|
||||
Phase 2: Test Framework Detection
|
||||
├─ Detect framework: jest/vitest/pytest/mocha
|
||||
├─ Identify test command from package.json/pyproject.toml
|
||||
└─ Locate affected test files based on changed files
|
||||
|
||||
Phase 3: Test Execution & Fix Cycle (max 10 iterations)
|
||||
├─ Strategy Engine:
|
||||
│ ├─ Iteration 1-2: Conservative (single targeted fix)
|
||||
│ ├─ Pass rate > 80% + similar failures: Aggressive (batch fix)
|
||||
│ └─ Regression detected (drop > 10%): Surgical (minimal + rollback)
|
||||
├─ Progressive Testing:
|
||||
│ ├─ Iterations: run affected tests only
|
||||
│ └─ Final: full test suite validation
|
||||
└─ Quality Gate: pass rate >= 95%
|
||||
|
||||
Phase 4: Result Analysis
|
||||
├─ Calculate final pass rate
|
||||
├─ Classify failure severity (critical/high/medium/low)
|
||||
└─ Generate test summary
|
||||
|
||||
Phase 5: Report to Coordinator
|
||||
├─ SendMessage with test results
|
||||
├─ >= 95%: mark TEST task completed
|
||||
└─ < 95% after max iterations: report needs intervention
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Find my assigned TEST tasks
|
||||
const tasks = TaskList()
|
||||
const myTestTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('TEST-') &&
|
||||
t.owner === 'tester' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
if (myTestTasks.length === 0) return // idle
|
||||
|
||||
const task = TaskGet({ taskId: myTestTasks[0].id })
|
||||
TaskUpdate({ taskId: task.id, status: 'in_progress' })
|
||||
```
|
||||
|
||||
### Phase 2: Test Framework Detection
|
||||
|
||||
```javascript
|
||||
// Detect test framework
|
||||
function detectTestFramework() {
|
||||
// Check package.json
|
||||
try {
|
||||
const pkg = JSON.parse(Read('package.json'))
|
||||
const deps = { ...pkg.dependencies, ...pkg.devDependencies }
|
||||
if (deps.vitest) return { framework: 'vitest', command: 'npx vitest run' }
|
||||
if (deps.jest) return { framework: 'jest', command: 'npx jest' }
|
||||
if (deps.mocha) return { framework: 'mocha', command: 'npx mocha' }
|
||||
} catch {}
|
||||
|
||||
// Check pyproject.toml / pytest
|
||||
try {
|
||||
const pyproject = Read('pyproject.toml')
|
||||
if (pyproject.includes('pytest')) return { framework: 'pytest', command: 'pytest' }
|
||||
} catch {}
|
||||
|
||||
// Fallback
|
||||
return { framework: 'unknown', command: 'npm test' }
|
||||
}
|
||||
|
||||
const testConfig = detectTestFramework()
|
||||
|
||||
// Locate affected test files
|
||||
function findAffectedTests(changedFiles) {
|
||||
const testFiles = []
|
||||
for (const file of changedFiles) {
|
||||
// Convention: src/foo.ts → tests/foo.test.ts or __tests__/foo.test.ts
|
||||
const testVariants = [
|
||||
file.replace(/\/src\//, '/tests/').replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'),
|
||||
file.replace(/\/src\//, '/__tests__/').replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'),
|
||||
file.replace(/\.(ts|js|tsx|jsx)$/, '.test.$1'),
|
||||
file.replace(/\.(ts|js|tsx|jsx)$/, '.spec.$1')
|
||||
]
|
||||
for (const variant of testVariants) {
|
||||
const exists = Bash(`test -f "${variant}" && echo exists || true`)
|
||||
if (exists.includes('exists')) testFiles.push(variant)
|
||||
}
|
||||
}
|
||||
return [...new Set(testFiles)]
|
||||
}
|
||||
|
||||
// Extract changed files from task description or git diff
|
||||
const changedFiles = Bash(`git diff --name-only HEAD~1 2>/dev/null || git diff --name-only --cached`).split('\n').filter(Boolean)
|
||||
const affectedTests = findAffectedTests(changedFiles)
|
||||
```
|
||||
|
||||
### Phase 3: Test Execution & Fix Cycle
|
||||
|
||||
```javascript
|
||||
const MAX_ITERATIONS = 10
|
||||
const PASS_RATE_TARGET = 95
|
||||
|
||||
let currentPassRate = 0
|
||||
let previousPassRate = 0
|
||||
const iterationHistory = []
|
||||
|
||||
for (let iteration = 1; iteration <= MAX_ITERATIONS; iteration++) {
|
||||
// Strategy selection
|
||||
const strategy = selectStrategy(iteration, currentPassRate, previousPassRate, iterationHistory)
|
||||
|
||||
// Determine test scope
|
||||
const isFullSuite = iteration === MAX_ITERATIONS || currentPassRate >= PASS_RATE_TARGET
|
||||
const testCommand = isFullSuite
|
||||
? testConfig.command
|
||||
: `${testConfig.command} ${affectedTests.join(' ')}`
|
||||
|
||||
// Run tests
|
||||
const testOutput = Bash(`${testCommand} 2>&1 || true`, { timeout: 300000 })
|
||||
|
||||
// Parse results
|
||||
const results = parseTestResults(testOutput, testConfig.framework)
|
||||
previousPassRate = currentPassRate
|
||||
currentPassRate = results.passRate
|
||||
|
||||
// Record iteration
|
||||
iterationHistory.push({
|
||||
iteration,
|
||||
pass_rate: currentPassRate,
|
||||
strategy: strategy,
|
||||
failed_tests: results.failedTests,
|
||||
total: results.total,
|
||||
passed: results.passed
|
||||
})
|
||||
|
||||
// Quality gate check
|
||||
if (currentPassRate >= PASS_RATE_TARGET) {
|
||||
if (!isFullSuite) {
|
||||
// Run full suite for final validation
|
||||
const fullOutput = Bash(`${testConfig.command} 2>&1 || true`, { timeout: 300000 })
|
||||
const fullResults = parseTestResults(fullOutput, testConfig.framework)
|
||||
currentPassRate = fullResults.passRate
|
||||
if (currentPassRate >= PASS_RATE_TARGET) break
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (iteration >= MAX_ITERATIONS) break
|
||||
|
||||
// Apply fixes based on strategy
|
||||
applyFixes(results.failedTests, strategy, testOutput)
|
||||
}
|
||||
|
||||
// Strategy Engine
|
||||
function selectStrategy(iteration, passRate, prevPassRate, history) {
|
||||
// Regression detection
|
||||
if (prevPassRate > 0 && passRate < prevPassRate - 10) return 'surgical'
|
||||
|
||||
// Iteration-based default
|
||||
if (iteration <= 2) return 'conservative'
|
||||
|
||||
// Pattern-based upgrade
|
||||
if (passRate > 80) {
|
||||
// Check if failures are similar (same test files, same error patterns)
|
||||
const recentFailures = history.slice(-2).flatMap(h => h.failed_tests)
|
||||
const uniqueFailures = [...new Set(recentFailures)]
|
||||
if (uniqueFailures.length <= recentFailures.length * 0.6) return 'aggressive'
|
||||
}
|
||||
|
||||
return 'conservative'
|
||||
}
|
||||
|
||||
// Fix application
|
||||
function applyFixes(failedTests, strategy, testOutput) {
|
||||
switch (strategy) {
|
||||
case 'conservative':
|
||||
// Fix one failure at a time
|
||||
// Read failing test, understand error, apply targeted fix
|
||||
if (failedTests.length > 0) {
|
||||
const target = failedTests[0]
|
||||
// Analyze error message from testOutput
|
||||
// Read source file and test file
|
||||
// Apply minimal fix
|
||||
}
|
||||
break
|
||||
|
||||
case 'aggressive':
|
||||
// Batch fix similar failures
|
||||
// Group by error pattern
|
||||
// Apply fixes to all related failures
|
||||
break
|
||||
|
||||
case 'surgical':
|
||||
// Minimal changes, consider rollback
|
||||
// Only fix the most critical failure
|
||||
// Verify no regression
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Test result parser
|
||||
function parseTestResults(output, framework) {
|
||||
let passed = 0, failed = 0, total = 0, failedTests = []
|
||||
|
||||
if (framework === 'jest' || framework === 'vitest') {
|
||||
const passMatch = output.match(/(\d+) passed/)
|
||||
const failMatch = output.match(/(\d+) failed/)
|
||||
passed = passMatch ? parseInt(passMatch[1]) : 0
|
||||
failed = failMatch ? parseInt(failMatch[1]) : 0
|
||||
total = passed + failed
|
||||
|
||||
// Extract failed test names
|
||||
const failPattern = /FAIL\s+(.+)/g
|
||||
let m
|
||||
while ((m = failPattern.exec(output)) !== null) failedTests.push(m[1].trim())
|
||||
} else if (framework === 'pytest') {
|
||||
const summaryMatch = output.match(/(\d+) passed.*?(\d+) failed/)
|
||||
if (summaryMatch) {
|
||||
passed = parseInt(summaryMatch[1])
|
||||
failed = parseInt(summaryMatch[2])
|
||||
}
|
||||
total = passed + failed
|
||||
}
|
||||
|
||||
return {
|
||||
passed, failed, total,
|
||||
passRate: total > 0 ? Math.round((passed / total) * 100) : 100,
|
||||
failedTests
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Result Analysis
|
||||
|
||||
```javascript
|
||||
// Classify failure severity
|
||||
function classifyFailures(failedTests, testOutput) {
|
||||
return failedTests.map(test => {
|
||||
const testLower = test.toLowerCase()
|
||||
let severity = 'low'
|
||||
if (/auth|security|permission|login|password/.test(testLower)) severity = 'critical'
|
||||
else if (/core|main|primary|data|state/.test(testLower)) severity = 'high'
|
||||
else if (/edge|flaky|timeout|env/.test(testLower)) severity = 'low'
|
||||
else severity = 'medium'
|
||||
return { test, severity }
|
||||
})
|
||||
}
|
||||
|
||||
const classifiedFailures = classifyFailures(
|
||||
iterationHistory[iterationHistory.length - 1]?.failed_tests || [],
|
||||
'' // last test output
|
||||
)
|
||||
|
||||
const hasCriticalFailures = classifiedFailures.some(f => f.severity === 'critical')
|
||||
```
|
||||
|
||||
### Phase 5: Report to Coordinator
|
||||
|
||||
```javascript
|
||||
const finalIteration = iterationHistory[iterationHistory.length - 1]
|
||||
const success = currentPassRate >= PASS_RATE_TARGET
|
||||
|
||||
SendMessage({
|
||||
type: "message",
|
||||
recipient: "coordinator",
|
||||
content: `## Test Results
|
||||
|
||||
**Task**: ${task.subject}
|
||||
**Status**: ${success ? 'PASSED' : 'NEEDS ATTENTION'}
|
||||
|
||||
### Summary
|
||||
- **Pass Rate**: ${currentPassRate}% (target: ${PASS_RATE_TARGET}%)
|
||||
- **Iterations**: ${iterationHistory.length}/${MAX_ITERATIONS}
|
||||
- **Total Tests**: ${finalIteration?.total || 0}
|
||||
- **Passed**: ${finalIteration?.passed || 0}
|
||||
- **Failed**: ${finalIteration?.total - finalIteration?.passed || 0}
|
||||
|
||||
### Strategy History
|
||||
${iterationHistory.map(h => `- Iteration ${h.iteration}: ${h.strategy} → ${h.pass_rate}%`).join('\n')}
|
||||
|
||||
${!success ? `### Remaining Failures
|
||||
${classifiedFailures.map(f => `- [${f.severity.toUpperCase()}] ${f.test}`).join('\n')}
|
||||
|
||||
${hasCriticalFailures ? '**CRITICAL failures detected - immediate attention required**' : ''}` : '### All tests passing'}`,
|
||||
summary: `Tests: ${currentPassRate}% pass rate (${iterationHistory.length} iterations)`
|
||||
})
|
||||
|
||||
if (success) {
|
||||
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
} else {
|
||||
// Keep in_progress, coordinator decides next steps
|
||||
SendMessage({
|
||||
type: "message",
|
||||
recipient: "coordinator",
|
||||
content: `Test pass rate ${currentPassRate}% is below ${PASS_RATE_TARGET}% after ${MAX_ITERATIONS} iterations. Need coordinator decision on next steps.`,
|
||||
summary: "Test target not met, need guidance"
|
||||
})
|
||||
}
|
||||
|
||||
// Check for next TEST task
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('TEST-') &&
|
||||
t.owner === 'tester' &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
if (nextTasks.length > 0) {
|
||||
// Continue with next task
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Scenario | Resolution |
|
||||
|----------|------------|
|
||||
| Test command not found | Detect framework, try alternatives (npm test, pytest, etc.) |
|
||||
| Test execution timeout | Reduce test scope, retry with affected tests only |
|
||||
| Regression detected (pass rate drops > 10%) | Switch to surgical strategy, consider rollback |
|
||||
| Stuck tests (same failure 3+ iterations) | Report to coordinator, suggest different approach |
|
||||
| Max iterations reached < 95% | Report failure details, let coordinator decide |
|
||||
| No test files found | Report to coordinator, suggest test generation needed |
|
||||
Reference in New Issue
Block a user