mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
fix(team-skills): enable true parallel execution with --agent-name mechanism
Previously, parallel tasks assigned to the same role (e.g., multiple EXPLORE-* tasks with owner: 'explorer') executed serially because only one agent instance existed per role name. This adds conditional parallel agent spawning with instance-specific names (explorer-1, explorer-2) and --agent-name arg for role task discovery filtering. Affected skills: team-ultra-analyze, team-quality-assurance, team-brainstorm, team-issue. Single-task modes preserve backward compatibility with original agent names.
This commit is contained in:
@@ -39,6 +39,7 @@ if (!roleMatch) {
|
||||
|
||||
const role = roleMatch[1]
|
||||
const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "brainstorm"
|
||||
const agentName = args.match(/--agent-name[=\s]+([\w-]+)/)?.[1] || role
|
||||
```
|
||||
|
||||
### Role Dispatch
|
||||
@@ -119,7 +120,6 @@ mcp__ccw-tools__team_msg({
|
||||
const TEAM_CONFIG = {
|
||||
name: "brainstorm",
|
||||
sessionDir: ".workflow/.team/BRS-{slug}-{date}/",
|
||||
msgDir: ".workflow/.team-msg/brainstorm/",
|
||||
sharedMemory: "shared-memory.json"
|
||||
}
|
||||
```
|
||||
@@ -182,7 +182,7 @@ Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --ty
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) &&
|
||||
t.owner === role &&
|
||||
t.owner === agentName && // Use agentName (e.g., 'ideator-1') instead of role
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -245,12 +245,48 @@ IDEA → CHALLENGE → (if critique.severity >= HIGH) → IDEA-fix → CHALLENGE
|
||||
```javascript
|
||||
TeamCreate({ team_name: teamName })
|
||||
|
||||
// Ideator
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "ideator",
|
||||
prompt: `你是 team "${teamName}" 的 IDEATOR。
|
||||
// Ideator — conditional parallel spawn for Full pipeline (multiple angles)
|
||||
const isFullPipeline = selectedPipeline === 'full'
|
||||
const ideaAngles = selectedAngles || []
|
||||
|
||||
if (isFullPipeline && ideaAngles.length > 1) {
|
||||
// Full pipeline: spawn N ideators for N parallel angle tasks
|
||||
for (let i = 0; i < ideaAngles.length; i++) {
|
||||
const agentName = `ideator-${i + 1}`
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: agentName,
|
||||
prompt: `你是 team "${teamName}" 的 IDEATOR (${agentName})。
|
||||
你的 agent 名称是 "${agentName}",任务发现时用此名称匹配 owner。
|
||||
|
||||
当你收到 IDEA-* 任务时,调用 Skill(skill="team-brainstorm", args="--role=ideator --agent-name=${agentName}") 执行。
|
||||
当前话题: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 owner 为 "${agentName}" 的 IDEA-* 前缀任务
|
||||
- 所有输出(SendMessage、team_msg)必须带 [ideator] 标识前缀
|
||||
- 仅与 coordinator 通信,不得直接联系其他 worker
|
||||
- 不得使用 TaskCreate 为其他角色创建任务
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到 owner === "${agentName}" 的 IDEA-* 任务
|
||||
2. Skill(skill="team-brainstorm", args="--role=ideator --agent-name=${agentName}") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator(带 [ideator] 标识)
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Quick/Deep pipeline: single ideator
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "ideator",
|
||||
prompt: `你是 team "${teamName}" 的 IDEATOR。
|
||||
当你收到 IDEA-* 任务时,调用 Skill(skill="team-brainstorm", args="--role=ideator") 执行。
|
||||
当前话题: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
@@ -269,7 +305,8 @@ Task({
|
||||
2. Skill(skill="team-brainstorm", args="--role=ideator") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator(带 [ideator] 标识)
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Challenger
|
||||
Task({
|
||||
|
||||
@@ -185,10 +185,12 @@ TaskUpdate({ taskId: evalId, owner: "evaluator", addBlockedBy: [synthId] })
|
||||
|
||||
```javascript
|
||||
// 并行创意: IDEA-001 + IDEA-002 + IDEA-003 (no dependencies between them)
|
||||
// Each gets a distinct agent owner for true parallel execution
|
||||
const ideaAngles = selectedAngles.slice(0, 3)
|
||||
ideaAngles.forEach((angle, i) => {
|
||||
const ideatorName = ideaAngles.length > 1 ? `ideator-${i+1}` : 'ideator'
|
||||
TaskCreate({ subject: `IDEA-00${i+1}: ${angle}角度创意生成`, description: `话题: ${taskDescription}\n角度: ${angle}\n\nSession: ${sessionFolder}\n输出: ideas/idea-00${i+1}.md`, activeForm: `${angle}创意生成中` })
|
||||
TaskUpdate({ taskId: ideaIds[i], owner: "ideator" })
|
||||
TaskUpdate({ taskId: ideaIds[i], owner: ideatorName })
|
||||
})
|
||||
|
||||
// CHALLENGE-001: 批量挑战 (blockedBy all IDEA-001..003)
|
||||
|
||||
@@ -39,10 +39,14 @@
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Parse agent name for parallel instances (e.g., ideator-1, ideator-2)
|
||||
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
|
||||
const agentName = agentNameMatch ? agentNameMatch[1] : 'ideator'
|
||||
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('IDEA-') &&
|
||||
t.owner === 'ideator' &&
|
||||
t.owner === agentName && // Use agentName (e.g., 'ideator-1') instead of hardcoded 'ideator'
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -205,7 +209,7 @@ TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
// Check for next task
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('IDEA-') &&
|
||||
t.owner === 'ideator' &&
|
||||
t.owner === agentName && // Use agentName for parallel instance filtering
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
@@ -58,6 +58,7 @@ if (!roleMatch) {
|
||||
|
||||
const role = roleMatch[1]
|
||||
const teamName = "issue"
|
||||
const agentName = args.match(/--agent-name[=\s]+([\w-]+)/)?.[1] || role
|
||||
```
|
||||
|
||||
### Role Dispatch
|
||||
@@ -140,7 +141,6 @@ mcp__ccw-tools__team_msg({
|
||||
const TEAM_CONFIG = {
|
||||
name: "issue",
|
||||
sessionDir: ".workflow/.team-plan/issue/",
|
||||
msgDir: ".workflow/.team-msg/issue/",
|
||||
issueDataDir: ".workflow/issues/"
|
||||
}
|
||||
```
|
||||
@@ -188,7 +188,7 @@ Bash(`ccw team log --team "issue" --from "${role}" --to "coordinator" --type "<t
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) &&
|
||||
t.owner === role &&
|
||||
t.owner === agentName && // Use agentName (e.g., 'explorer-1') instead of role
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -241,12 +241,49 @@ function detectMode(issueIds, userMode) {
|
||||
```javascript
|
||||
TeamCreate({ team_name: "issue" })
|
||||
|
||||
// Explorer
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: "issue",
|
||||
name: "explorer",
|
||||
prompt: `你是 team "issue" 的 EXPLORER。
|
||||
// Explorer — conditional parallel spawn for Batch mode
|
||||
const isBatchMode = mode === 'batch'
|
||||
const maxParallelExplorers = Math.min(issueIds.length, 5)
|
||||
|
||||
if (isBatchMode && issueIds.length > 1) {
|
||||
// Batch mode: spawn N explorers for parallel exploration
|
||||
for (let i = 0; i < maxParallelExplorers; i++) {
|
||||
const agentName = `explorer-${i + 1}`
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: "issue",
|
||||
name: agentName,
|
||||
prompt: `你是 team "issue" 的 EXPLORER (${agentName})。
|
||||
你的 agent 名称是 "${agentName}",任务发现时用此名称匹配 owner。
|
||||
|
||||
当你收到 EXPLORE-* 任务时,调用 Skill(skill="team-issue", args="--role=explorer --agent-name=${agentName}") 执行。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 owner 为 "${agentName}" 的 EXPLORE-* 前缀任务
|
||||
- 所有输出(SendMessage、team_msg)必须带 [explorer] 标识前缀
|
||||
- 仅与 coordinator 通信,不得直接联系其他 worker
|
||||
- 不得使用 TaskCreate 为其他角色创建任务
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到 owner === "${agentName}" 的 EXPLORE-* 任务
|
||||
2. Skill(skill="team-issue", args="--role=explorer --agent-name=${agentName}") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator(带 [explorer] 标识)
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Quick/Full mode: single explorer
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: "issue",
|
||||
name: "explorer",
|
||||
prompt: `你是 team "issue" 的 EXPLORER。
|
||||
|
||||
当你收到 EXPLORE-* 任务时,调用 Skill(skill="team-issue", args="--role=explorer") 执行。
|
||||
|
||||
@@ -267,7 +304,8 @@ Task({
|
||||
2. Skill(skill="team-issue", args="--role=explorer") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator(带 [explorer] 标识)
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Planner
|
||||
Task({
|
||||
@@ -351,12 +389,46 @@ Task({
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
|
||||
// Implementer
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: "issue",
|
||||
name: "implementer",
|
||||
prompt: `你是 team "issue" 的 IMPLEMENTER。
|
||||
// Implementer — conditional parallel spawn for Batch mode (DAG parallel BUILD tasks)
|
||||
if (isBatchMode && issueIds.length > 2) {
|
||||
// Batch mode: spawn multiple implementers for parallel BUILD execution
|
||||
const maxParallelBuilders = Math.min(issueIds.length, 3)
|
||||
for (let i = 0; i < maxParallelBuilders; i++) {
|
||||
const agentName = `implementer-${i + 1}`
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: "issue",
|
||||
name: agentName,
|
||||
prompt: `你是 team "issue" 的 IMPLEMENTER (${agentName})。
|
||||
你的 agent 名称是 "${agentName}",任务发现时用此名称匹配 owner。
|
||||
|
||||
当你收到 BUILD-* 任务时,调用 Skill(skill="team-issue", args="--role=implementer --agent-name=${agentName}") 执行。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 owner 为 "${agentName}" 的 BUILD-* 前缀任务
|
||||
- 所有输出必须带 [implementer] 标识前缀
|
||||
- 仅与 coordinator 通信
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到 owner === "${agentName}" 的 BUILD-* 任务
|
||||
2. Skill(skill="team-issue", args="--role=implementer --agent-name=${agentName}") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Quick/Full mode: single implementer
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: "issue",
|
||||
name: "implementer",
|
||||
prompt: `你是 team "issue" 的 IMPLEMENTER。
|
||||
|
||||
当你收到 BUILD-* 任务时,调用 Skill(skill="team-issue", args="--role=implementer") 执行。
|
||||
|
||||
@@ -376,7 +448,8 @@ Task({
|
||||
2. Skill(skill="team-issue", args="--role=implementer") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
@@ -258,19 +258,22 @@ for (const issueId of issueIds) {
|
||||
// Group issues into batches
|
||||
const exploreBatches = chunkArray(issueIds, 5) // max 5 parallel
|
||||
const solveBatches = chunkArray(issueIds, 3) // max 3 parallel
|
||||
const maxParallelExplorers = Math.min(issueIds.length, 5)
|
||||
const maxParallelBuilders = Math.min(issueIds.length, 3)
|
||||
|
||||
// Create EXPLORE tasks — all parallel within each batch, batches run in rolling window
|
||||
// Each batch of ≤5 runs concurrently; next batch starts when current batch completes
|
||||
// Create EXPLORE tasks — distribute across parallel explorer agents (round-robin)
|
||||
const exploreTaskIds = []
|
||||
let prevBatchLastId = null
|
||||
for (const [batchIdx, batch] of exploreBatches.entries()) {
|
||||
const batchTaskIds = []
|
||||
for (const issueId of batch) {
|
||||
for (const [inBatchIdx, issueId] of batch.entries()) {
|
||||
const globalIdx = exploreTaskIds.length
|
||||
const explorerName = `explorer-${(globalIdx % maxParallelExplorers) + 1}`
|
||||
const id = TaskCreate({
|
||||
subject: `EXPLORE-${String(exploreTaskIds.length + 1).padStart(3, '0')}: Context for ${issueId}`,
|
||||
subject: `EXPLORE-${String(globalIdx + 1).padStart(3, '0')}: Context for ${issueId}`,
|
||||
description: `Batch ${batchIdx + 1}: Explore codebase context for issue ${issueId}.`,
|
||||
activeForm: `Exploring ${issueId}`,
|
||||
owner: "explorer",
|
||||
owner: explorerName, // Distribute across explorer-1, explorer-2, etc.
|
||||
// Only block on previous batch's LAST task (not within same batch)
|
||||
addBlockedBy: prevBatchLastId ? [prevBatchLastId] : []
|
||||
})
|
||||
@@ -312,6 +315,7 @@ const marshalId = TaskCreate({
|
||||
})
|
||||
|
||||
// BUILD tasks created dynamically after MARSHAL completes (based on DAG)
|
||||
// Each BUILD-* task is assigned to implementer-1, implementer-2, etc. (round-robin)
|
||||
// Each BUILD-* task description MUST include:
|
||||
// execution_method: ${executionMethod}
|
||||
// code_review: ${codeReviewTool}
|
||||
|
||||
@@ -55,10 +55,14 @@ Issue 上下文分析、代码探索、依赖识别、影响面评估。为 plan
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Parse agent name for parallel instances (e.g., explorer-1, explorer-2)
|
||||
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
|
||||
const agentName = agentNameMatch ? agentNameMatch[1] : 'explorer'
|
||||
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('EXPLORE-') &&
|
||||
t.owner === 'explorer' &&
|
||||
t.owner === agentName && // Use agentName (e.g., 'explorer-1') instead of hardcoded 'explorer'
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -216,7 +220,7 @@ TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
// Check for next task
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('EXPLORE-') &&
|
||||
t.owner === 'explorer' &&
|
||||
t.owner === agentName && // Use agentName for parallel instance filtering
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
@@ -135,10 +135,14 @@ Dependencies: ${explorerContext.dependencies?.join(', ') || 'N/A'}
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Parse agent name for parallel instances (e.g., implementer-1, implementer-2)
|
||||
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
|
||||
const agentName = agentNameMatch ? agentNameMatch[1] : 'implementer'
|
||||
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('BUILD-') &&
|
||||
t.owner === 'implementer' &&
|
||||
t.owner === agentName && // Use agentName (e.g., 'implementer-1') instead of hardcoded 'implementer'
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -370,7 +374,7 @@ TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
// Check for next BUILD-* task (parallel BUILD tasks or new batches)
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('BUILD-') &&
|
||||
t.owner === 'implementer' &&
|
||||
t.owner === agentName && // Use agentName for parallel instance filtering
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
@@ -145,7 +145,6 @@ mcp__ccw-tools__team_msg({ summary: `[${role}] ...` })
|
||||
const TEAM_CONFIG = {
|
||||
name: "quality-assurance",
|
||||
sessionDir: ".workflow/.team/QA-{slug}-{date}/",
|
||||
msgDir: ".workflow/.team-msg/quality-assurance/",
|
||||
sharedMemory: "shared-memory.json",
|
||||
testLayers: {
|
||||
L1: { name: "Unit Tests", coverage_target: 80 },
|
||||
@@ -338,12 +337,43 @@ Task({
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
|
||||
// Generator
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "generator",
|
||||
prompt: `你是 team "${teamName}" 的 GENERATOR。
|
||||
// Generator — parallel instances for full mode (generator-1, generator-2)
|
||||
const isFullMode = qaMode === 'full'
|
||||
|
||||
if (isFullMode) {
|
||||
for (let i = 1; i <= 2; i++) {
|
||||
const agentName = `generator-${i}`
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: agentName,
|
||||
prompt: `你是 team "${teamName}" 的 GENERATOR (${agentName})。
|
||||
你的 agent 名称是 "${agentName}",任务发现时用此名称匹配 owner。
|
||||
|
||||
当你收到 QAGEN-* 任务时,调用 Skill(skill="team-quality-assurance", args="--role=generator --agent-name=${agentName}") 执行。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 owner 为 "${agentName}" 的 QAGEN-* 前缀任务
|
||||
- 所有输出必须带 [generator] 标识前缀
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到 owner === "${agentName}" 的 QAGEN-* 任务
|
||||
2. Skill(skill="team-quality-assurance", args="--role=generator --agent-name=${agentName}") 执行
|
||||
3. team_msg log + SendMessage
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "generator",
|
||||
prompt: `你是 team "${teamName}" 的 GENERATOR。
|
||||
|
||||
当你收到 QAGEN-* 任务时,调用 Skill(skill="team-quality-assurance", args="--role=generator") 执行。
|
||||
|
||||
@@ -361,14 +391,44 @@ Task({
|
||||
2. Skill(skill="team-quality-assurance", args="--role=generator") 执行
|
||||
3. team_msg log + SendMessage
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Executor
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "executor",
|
||||
prompt: `你是 team "${teamName}" 的 EXECUTOR。
|
||||
// Executor — parallel instances for full mode (executor-1, executor-2)
|
||||
if (isFullMode) {
|
||||
for (let i = 1; i <= 2; i++) {
|
||||
const agentName = `executor-${i}`
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: agentName,
|
||||
prompt: `你是 team "${teamName}" 的 EXECUTOR (${agentName})。
|
||||
你的 agent 名称是 "${agentName}",任务发现时用此名称匹配 owner。
|
||||
|
||||
当你收到 QARUN-* 任务时,调用 Skill(skill="team-quality-assurance", args="--role=executor --agent-name=${agentName}") 执行。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 owner 为 "${agentName}" 的 QARUN-* 前缀任务
|
||||
- 所有输出必须带 [executor] 标识前缀
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到 owner === "${agentName}" 的 QARUN-* 任务
|
||||
2. Skill(skill="team-quality-assurance", args="--role=executor --agent-name=${agentName}") 执行
|
||||
3. team_msg log + SendMessage
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "executor",
|
||||
prompt: `你是 team "${teamName}" 的 EXECUTOR。
|
||||
|
||||
当你收到 QARUN-* 任务时,调用 Skill(skill="team-quality-assurance", args="--role=executor") 执行。
|
||||
|
||||
@@ -386,7 +446,8 @@ Task({
|
||||
2. Skill(skill="team-quality-assurance", args="--role=executor") 执行
|
||||
3. team_msg log + SendMessage
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Analyst
|
||||
Task({
|
||||
|
||||
@@ -43,10 +43,10 @@ function buildPipeline(qaMode, sessionFolder, taskDescription) {
|
||||
'full': [
|
||||
{ prefix: 'SCOUT', owner: 'scout', desc: '多视角问题扫描', blockedBy: [] },
|
||||
{ prefix: 'QASTRAT', owner: 'strategist', desc: '测试策略制定', blockedBy: ['SCOUT'] },
|
||||
{ prefix: 'QAGEN-L1', owner: 'generator', desc: '测试代码生成 (L1)', meta: 'layer: L1', blockedBy: ['QASTRAT'] },
|
||||
{ prefix: 'QAGEN-L2', owner: 'generator', desc: '测试代码生成 (L2)', meta: 'layer: L2', blockedBy: ['QASTRAT'] },
|
||||
{ prefix: 'QARUN-L1', owner: 'executor', desc: '测试执行 (L1)', meta: 'layer: L1', blockedBy: ['QAGEN-L1'] },
|
||||
{ prefix: 'QARUN-L2', owner: 'executor', desc: '测试执行 (L2)', meta: 'layer: L2', blockedBy: ['QAGEN-L2'] },
|
||||
{ prefix: 'QAGEN-L1', owner: 'generator-1', desc: '测试代码生成 (L1)', meta: 'layer: L1', blockedBy: ['QASTRAT'] },
|
||||
{ prefix: 'QAGEN-L2', owner: 'generator-2', desc: '测试代码生成 (L2)', meta: 'layer: L2', blockedBy: ['QASTRAT'] },
|
||||
{ prefix: 'QARUN-L1', owner: 'executor-1', desc: '测试执行 (L1)', meta: 'layer: L1', blockedBy: ['QAGEN-L1'] },
|
||||
{ prefix: 'QARUN-L2', owner: 'executor-2', desc: '测试执行 (L2)', meta: 'layer: L2', blockedBy: ['QAGEN-L2'] },
|
||||
{ prefix: 'QAANA', owner: 'analyst', desc: '质量分析报告', blockedBy: ['QARUN-L1', 'QARUN-L2'] },
|
||||
{ prefix: 'SCOUT-REG', owner: 'scout', desc: '回归扫描', blockedBy: ['QAANA'] }
|
||||
]
|
||||
|
||||
@@ -54,10 +54,14 @@
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Parse agent name for parallel instances (e.g., executor-1, executor-2)
|
||||
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
|
||||
const agentName = agentNameMatch ? agentNameMatch[1] : 'executor'
|
||||
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('QARUN-') &&
|
||||
t.owner === 'executor' &&
|
||||
t.owner === agentName &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -234,7 +238,7 @@ SendMessage({
|
||||
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('QARUN-') && t.owner === 'executor' &&
|
||||
t.subject.startsWith('QARUN-') && t.owner === agentName &&
|
||||
t.status === 'pending' && t.blockedBy.length === 0
|
||||
)
|
||||
if (nextTasks.length > 0) { /* back to Phase 1 */ }
|
||||
|
||||
@@ -59,10 +59,14 @@
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Parse agent name for parallel instances (e.g., generator-1, generator-2)
|
||||
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
|
||||
const agentName = agentNameMatch ? agentNameMatch[1] : 'generator'
|
||||
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('QAGEN-') &&
|
||||
t.owner === 'generator' &&
|
||||
t.owner === agentName &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -264,7 +268,7 @@ ${generatedTests.map(f => `- ${f}`).join('\n')}`,
|
||||
TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('QAGEN-') && t.owner === 'generator' &&
|
||||
t.subject.startsWith('QAGEN-') && t.owner === agentName &&
|
||||
t.status === 'pending' && t.blockedBy.length === 0
|
||||
)
|
||||
if (nextTasks.length > 0) { /* back to Phase 1 */ }
|
||||
|
||||
@@ -57,7 +57,7 @@ roles/
|
||||
|
||||
### Input Parsing
|
||||
|
||||
Parse `$ARGUMENTS` to extract `--role`:
|
||||
Parse `$ARGUMENTS` to extract `--role` and optional `--agent-name`:
|
||||
|
||||
```javascript
|
||||
const args = "$ARGUMENTS"
|
||||
@@ -69,6 +69,9 @@ if (!roleMatch) {
|
||||
|
||||
const role = roleMatch[1]
|
||||
const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "ultra-analyze"
|
||||
// --agent-name for parallel instances (e.g., explorer-1, analyst-2)
|
||||
// Passed through to role.md for task discovery filtering
|
||||
const agentName = args.match(/--agent-name[=\s]+([\w-]+)/)?.[1] || role
|
||||
```
|
||||
|
||||
### Role Dispatch
|
||||
@@ -139,7 +142,6 @@ mcp__ccw-tools__team_msg({ summary: `[${role}] ...` })
|
||||
const TEAM_CONFIG = {
|
||||
name: "ultra-analyze",
|
||||
sessionDir: ".workflow/.team/UAN-{slug}-{date}/",
|
||||
msgDir: ".workflow/.team-msg/ultra-analyze/",
|
||||
sharedMemory: "shared-memory.json",
|
||||
analysisDimensions: ["architecture", "implementation", "performance", "security", "concept", "comparison", "decision"],
|
||||
maxDiscussionRounds: 5
|
||||
@@ -272,12 +274,51 @@ coordinator(AskUser) → DISCUSS-N(deepen) → [optional ANALYZE-fix] → coordi
|
||||
```javascript
|
||||
TeamCreate({ team_name: teamName })
|
||||
|
||||
// Explorer
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "explorer",
|
||||
prompt: `你是 team "${teamName}" 的 EXPLORER。
|
||||
// ── Determine parallel agent count ──
|
||||
const perspectiveCount = selectedPerspectives.length
|
||||
const isParallel = perspectiveCount > 1
|
||||
|
||||
// ── Explorers ──
|
||||
// Quick mode (1 perspective): single "explorer"
|
||||
// Standard/Deep mode (N perspectives): "explorer-1", "explorer-2", ...
|
||||
if (isParallel) {
|
||||
for (let i = 0; i < perspectiveCount; i++) {
|
||||
const agentName = `explorer-${i + 1}`
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: agentName,
|
||||
prompt: `你是 team "${teamName}" 的 EXPLORER (${agentName})。
|
||||
你的 agent 名称是 "${agentName}",任务发现时用此名称匹配 owner。
|
||||
|
||||
当你收到 EXPLORE-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=explorer --agent-name=${agentName}") 执行。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 owner 为 "${agentName}" 的 EXPLORE-* 前缀任务
|
||||
- 所有输出(SendMessage、team_msg)必须带 [explorer] 标识前缀
|
||||
- 仅与 coordinator 通信,不得直接联系其他 worker
|
||||
- 不得使用 TaskCreate 为其他角色创建任务
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到 owner === "${agentName}" 的 EXPLORE-* 任务
|
||||
2. Skill(skill="team-ultra-analyze", args="--role=explorer --agent-name=${agentName}") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator(带 [explorer] 标识)
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Single explorer for quick mode
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "explorer",
|
||||
prompt: `你是 team "${teamName}" 的 EXPLORER。
|
||||
|
||||
当你收到 EXPLORE-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=explorer") 执行。
|
||||
|
||||
@@ -298,14 +339,47 @@ Task({
|
||||
2. Skill(skill="team-ultra-analyze", args="--role=explorer") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator(带 [explorer] 标识)
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Analyst
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "analyst",
|
||||
prompt: `你是 team "${teamName}" 的 ANALYST。
|
||||
// ── Analysts ──
|
||||
// Same pattern: parallel mode spawns N analysts, quick mode spawns 1
|
||||
if (isParallel) {
|
||||
for (let i = 0; i < perspectiveCount; i++) {
|
||||
const agentName = `analyst-${i + 1}`
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: agentName,
|
||||
prompt: `你是 team "${teamName}" 的 ANALYST (${agentName})。
|
||||
你的 agent 名称是 "${agentName}",任务发现时用此名称匹配 owner。
|
||||
|
||||
当你收到 ANALYZE-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=analyst --agent-name=${agentName}") 执行。
|
||||
|
||||
当前需求: ${taskDescription}
|
||||
约束: ${constraints}
|
||||
|
||||
## 角色准则(强制)
|
||||
- 你只能处理 owner 为 "${agentName}" 的 ANALYZE-* 前缀任务
|
||||
- 所有输出必须带 [analyst] 标识前缀
|
||||
- 仅与 coordinator 通信
|
||||
|
||||
## 消息总线(必须)
|
||||
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
|
||||
|
||||
工作流程:
|
||||
1. TaskList → 找到 owner === "${agentName}" 的 ANALYZE-* 任务
|
||||
2. Skill(skill="team-ultra-analyze", args="--role=analyst --agent-name=${agentName}") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
name: "analyst",
|
||||
prompt: `你是 team "${teamName}" 的 ANALYST。
|
||||
|
||||
当你收到 ANALYZE-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=analyst") 执行。
|
||||
|
||||
@@ -325,9 +399,10 @@ Task({
|
||||
2. Skill(skill="team-ultra-analyze", args="--role=analyst") 执行
|
||||
3. team_msg log + SendMessage 结果给 coordinator
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Discussant
|
||||
// ── Discussant (always single) ──
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
@@ -352,7 +427,7 @@ Task({
|
||||
4. TaskUpdate completed → 检查下一个任务`
|
||||
})
|
||||
|
||||
// Synthesizer
|
||||
// ── Synthesizer (always single) ──
|
||||
Task({
|
||||
subagent_type: "general-purpose",
|
||||
team_name: teamName,
|
||||
|
||||
@@ -61,10 +61,14 @@
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Parse agent name from --agent-name arg (for parallel instances) or default to 'analyst'
|
||||
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
|
||||
const agentName = agentNameMatch ? agentNameMatch[1] : 'analyst'
|
||||
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('ANALYZE-') &&
|
||||
t.owner === 'analyst' &&
|
||||
t.owner === agentName &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -269,7 +273,7 @@ TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
// Check for next task
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('ANALYZE-') &&
|
||||
t.owner === 'analyst' &&
|
||||
t.owner === agentName &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
@@ -39,23 +39,26 @@ function buildPipeline(pipelineMode, perspectives, sessionFolder, taskDescriptio
|
||||
function buildStandardPipeline(perspectives, dimensions) {
|
||||
const stages = []
|
||||
const perspectiveList = perspectives.length > 0 ? perspectives : ['technical']
|
||||
const isParallel = perspectiveList.length > 1
|
||||
|
||||
// Parallel explorations
|
||||
// Parallel explorations — each gets a distinct agent name for true parallelism
|
||||
perspectiveList.forEach((p, i) => {
|
||||
const num = String(i + 1).padStart(3, '0')
|
||||
const explorerName = isParallel ? `explorer-${i + 1}` : 'explorer'
|
||||
stages.push({
|
||||
prefix: 'EXPLORE', suffix: num, owner: 'explorer',
|
||||
prefix: 'EXPLORE', suffix: num, owner: explorerName,
|
||||
desc: `代码库探索 (${p})`,
|
||||
meta: `perspective: ${p}\ndimensions: ${dimensions.join(', ')}`,
|
||||
blockedBy: []
|
||||
})
|
||||
})
|
||||
|
||||
// Parallel analyses (blocked by corresponding exploration)
|
||||
// Parallel analyses — each gets a distinct agent name for true parallelism
|
||||
perspectiveList.forEach((p, i) => {
|
||||
const num = String(i + 1).padStart(3, '0')
|
||||
const analystName = isParallel ? `analyst-${i + 1}` : 'analyst'
|
||||
stages.push({
|
||||
prefix: 'ANALYZE', suffix: num, owner: 'analyst',
|
||||
prefix: 'ANALYZE', suffix: num, owner: analystName,
|
||||
desc: `深度分析 (${p})`,
|
||||
meta: `perspective: ${p}\ndimensions: ${dimensions.join(', ')}`,
|
||||
blockedBy: [`EXPLORE-${num}`]
|
||||
|
||||
@@ -211,7 +211,10 @@ Write(`${sessionFolder}/discussion.md`, `# Analysis Discussion
|
||||
TeamCreate({ team_name: teamName })
|
||||
|
||||
// Spawn teammates (see SKILL.md Coordinator Spawn Template)
|
||||
// Explorer, Analyst, Discussant, Synthesizer
|
||||
// Quick mode: 1 explorer + 1 analyst (single agents)
|
||||
// Standard/Deep mode: N explorers + N analysts (parallel agents with distinct names)
|
||||
// explorer-1, explorer-2... / analyst-1, analyst-2... for true parallel execution
|
||||
// Discussant and Synthesizer are always single instances
|
||||
```
|
||||
|
||||
### Phase 3: Create Task Chain
|
||||
|
||||
@@ -58,10 +58,14 @@
|
||||
### Phase 1: Task Discovery
|
||||
|
||||
```javascript
|
||||
// Parse agent name from --agent-name arg (for parallel instances) or default to 'explorer'
|
||||
const agentNameMatch = args.match(/--agent-name[=\s]+([\w-]+)/)
|
||||
const agentName = agentNameMatch ? agentNameMatch[1] : 'explorer'
|
||||
|
||||
const tasks = TaskList()
|
||||
const myTasks = tasks.filter(t =>
|
||||
t.subject.startsWith('EXPLORE-') &&
|
||||
t.owner === 'explorer' &&
|
||||
t.owner === agentName &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
@@ -222,7 +226,7 @@ TaskUpdate({ taskId: task.id, status: 'completed' })
|
||||
// Check for next task
|
||||
const nextTasks = TaskList().filter(t =>
|
||||
t.subject.startsWith('EXPLORE-') &&
|
||||
t.owner === 'explorer' &&
|
||||
t.owner === agentName &&
|
||||
t.status === 'pending' &&
|
||||
t.blockedBy.length === 0
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user