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:
catlog22
2026-02-23 22:42:53 +08:00
parent 02a203c6b2
commit 1efe2f469e
16 changed files with 373 additions and 87 deletions

View File

@@ -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({

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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

View File

@@ -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}

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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({

View File

@@ -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'] }
]

View File

@@ -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 */ }

View File

@@ -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 */ }

View File

@@ -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,

View File

@@ -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
)

View File

@@ -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}`]

View File

@@ -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

View File

@@ -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
)