feat: Add explorer and synthesizer roles with commands for codebase exploration and synthesis

- Implemented `explorer` role for parallel codebase exploration using `cli-explore-agent`.
- Created `explore.md` command documentation detailing exploration strategy and execution steps.
- Established `synthesizer` role for integrating insights from explorations, analyses, and discussions.
- Developed `synthesize.md` command documentation outlining synthesis strategy and output format.
- Configured team settings in `team-config.json` to support new roles and pipeline modes.
- Added regression test for CodexLens bootstrap fallback to ensure robustness in error handling.
This commit is contained in:
catlog22
2026-02-18 18:40:12 +08:00
parent 32d2d534ab
commit 65762af254
14 changed files with 3465 additions and 0 deletions

View File

@@ -0,0 +1,391 @@
---
name: team-ultra-analyze
description: Unified team skill for deep collaborative analysis. All roles invoke this skill with --role arg for role-specific execution. Triggers on "team ultra-analyze", "team analyze".
allowed-tools: TeamCreate(*), TeamDelete(*), SendMessage(*), TaskCreate(*), TaskUpdate(*), TaskList(*), TaskGet(*), Task(*), AskUserQuestion(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*)
---
# Team Ultra Analyze
深度协作分析团队技能。将单体分析工作流拆分为 5 角色协作:探索→分析→讨论→综合。支持 Quick/Standard/Deep 三种管道模式,通过讨论循环实现用户引导的渐进式理解深化。所有成员通过 `--role=xxx` 路由到角色执行逻辑。
## Architecture Overview
```
┌─────────────────────────────────────────────────────────┐
│ Skill(skill="team-ultra-analyze", args="--role=xxx") │
└──────────────────────┬──────────────────────────────────┘
│ Role Router
┌──────────┬───────┼───────────┬───────────┐
↓ ↓ ↓ ↓ ↓
┌────────┐┌────────┐┌────────┐┌──────────┐┌───────────┐
│coordi- ││explorer││analyst ││discussant││synthesizer│
│nator ││EXPLORE-││ANALYZE-││DISCUSS-* ││SYNTH-* │
│ roles/ ││* roles/││* roles/││ roles/ ││ roles/ │
└────────┘└────────┘└────────┘└──────────┘└───────────┘
```
## Command Architecture
```
roles/
├── coordinator/
│ ├── role.md # 编排:话题澄清、管道选择、讨论循环、结果汇报
│ └── commands/
│ ├── dispatch.md # 任务链创建与依赖管理
│ └── monitor.md # 进度监控 + 讨论循环
├── explorer/
│ ├── role.md # 代码库探索
│ └── commands/
│ └── explore.md # cli-explore-agent 并行探索
├── analyst/
│ ├── role.md # 深度分析
│ └── commands/
│ └── analyze.md # CLI 多视角分析
├── discussant/
│ ├── role.md # 讨论处理 + 方向调整
│ └── commands/
│ └── deepen.md # 深入探索
└── synthesizer/
├── role.md # 综合结论
└── commands/
└── synthesize.md # 跨视角整合
```
**设计原则**: role.md 保留 Phase 1Task Discovery和 Phase 5Report内联。Phase 2-4 根据复杂度决定内联或委派到 `commands/*.md`
## Role Router
### Input Parsing
Parse `$ARGUMENTS` to extract `--role`:
```javascript
const args = "$ARGUMENTS"
const roleMatch = args.match(/--role[=\s]+(\w+)/)
if (!roleMatch) {
throw new Error("Missing --role argument. Available roles: coordinator, explorer, analyst, discussant, synthesizer")
}
const role = roleMatch[1]
const teamName = args.match(/--team[=\s]+([\w-]+)/)?.[1] || "ultra-analyze"
```
### Role Dispatch
```javascript
const VALID_ROLES = {
"coordinator": { file: "roles/coordinator/role.md", prefix: null },
"explorer": { file: "roles/explorer/role.md", prefix: "EXPLORE" },
"analyst": { file: "roles/analyst/role.md", prefix: "ANALYZE" },
"discussant": { file: "roles/discussant/role.md", prefix: "DISCUSS" },
"synthesizer": { file: "roles/synthesizer/role.md", prefix: "SYNTH" }
}
if (!VALID_ROLES[role]) {
throw new Error(`Unknown role: ${role}. Available: ${Object.keys(VALID_ROLES).join(', ')}`)
}
// Read and execute role-specific logic
Read(VALID_ROLES[role].file)
// → Execute the 5-phase process defined in that file
```
### Available Roles
| Role | Task Prefix | Responsibility | Role File |
|------|-------------|----------------|-----------|
| `coordinator` | N/A | 话题澄清、管道选择、会话管理、讨论循环 | [roles/coordinator/role.md](roles/coordinator/role.md) |
| `explorer` | EXPLORE-* | cli-explore-agent 多角度并行代码库探索 | [roles/explorer/role.md](roles/explorer/role.md) |
| `analyst` | ANALYZE-* | CLI 多视角深度分析 | [roles/analyst/role.md](roles/analyst/role.md) |
| `discussant` | DISCUSS-* | 用户反馈处理、方向调整、深入分析 | [roles/discussant/role.md](roles/discussant/role.md) |
| `synthesizer` | SYNTH-* | 跨视角整合、结论生成、决策追踪 | [roles/synthesizer/role.md](roles/synthesizer/role.md) |
## Shared Infrastructure
### Role Isolation Rules
**核心原则**: 每个角色仅能执行自己职责范围内的工作。
#### Output Tagging强制
所有角色的输出必须带 `[role_name]` 标识前缀:
```javascript
SendMessage({ content: `## [${role}] ...`, summary: `[${role}] ...` })
mcp__ccw-tools__team_msg({ summary: `[${role}] ...` })
```
#### Coordinator 隔离
| 允许 | 禁止 |
|------|------|
| 话题澄清 (AskUserQuestion) | ❌ 直接执行代码探索或分析 |
| 创建任务链 (TaskCreate) | ❌ 直接调用 cli-explore-agent |
| 管道选择 + 讨论循环驱动 | ❌ 直接调用 CLI 分析工具 |
| 监控进度 (消息总线) | ❌ 绕过 worker 自行完成 |
#### Worker 隔离
| 允许 | 禁止 |
|------|------|
| 处理自己前缀的任务 | ❌ 处理其他角色前缀的任务 |
| 读写 shared-memory.json (自己的字段) | ❌ 为其他角色创建任务 |
| SendMessage 给 coordinator | ❌ 直接与其他 worker 通信 |
### Team Configuration
```javascript
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
}
```
### Shared Memory核心产物
```javascript
// 各角色读取共享记忆
const memoryPath = `${sessionFolder}/shared-memory.json`
let sharedMemory = {}
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
// 各角色写入自己负责的字段:
// explorer → sharedMemory.explorations
// analyst → sharedMemory.analyses
// discussant → sharedMemory.discussions
// synthesizer → sharedMemory.synthesis
// coordinator → sharedMemory.decision_trail + current_understanding
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
```
### Message Bus (All Roles)
```javascript
mcp__ccw-tools__team_msg({
operation: "log",
team: teamName,
from: role,
to: "coordinator",
type: "<type>",
summary: `[${role}] <summary>`,
ref: "<file_path>"
})
```
| Role | Types |
|------|-------|
| coordinator | `pipeline_selected`, `discussion_round`, `direction_adjusted`, `task_unblocked`, `error`, `shutdown` |
| explorer | `exploration_ready`, `error` |
| analyst | `analysis_ready`, `error` |
| discussant | `discussion_processed`, `error` |
| synthesizer | `synthesis_ready`, `error` |
### CLI 回退
```javascript
Bash(`ccw team log --team "${teamName}" --from "${role}" --to "coordinator" --type "<type>" --summary "<摘要>" --json`)
```
### Task Lifecycle (All Worker Roles)
```javascript
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith(`${VALID_ROLES[role].prefix}-`) &&
t.owner === role &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
// Phase 2-4: Role-specific
// Phase 5: Report + Loop
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: role, to: "coordinator", type: "...", summary: `[${role}] ...` })
SendMessage({ type: "message", recipient: "coordinator", content: `## [${role}] ...`, summary: `[${role}] ...` })
TaskUpdate({ taskId: task.id, status: 'completed' })
```
## Three-Mode Pipeline Architecture
```
Quick: EXPLORE-001 → ANALYZE-001 → SYNTH-001
Standard: [EXPLORE-001..N](parallel) → [ANALYZE-001..N](parallel) → DISCUSS-001 → SYNTH-001
Deep: [EXPLORE-001..N] → [ANALYZE-001..N] → DISCUSS-001 → ANALYZE-fix → DISCUSS-002 → ... → SYNTH-001
```
### Mode Auto-Detection
```javascript
function detectPipelineMode(args, taskDescription) {
const modeMatch = args.match(/--mode[=\s]+(quick|standard|deep)/)
if (modeMatch) return modeMatch[1]
// 自动检测
if (/快速|quick|overview|概览/.test(taskDescription)) return 'quick'
if (/深入|deep|thorough|详细|全面/.test(taskDescription)) return 'deep'
return 'standard'
}
```
### Discussion Loop (Deep Mode)
```
coordinator(AskUser) → DISCUSS-N(deepen) → [optional ANALYZE-fix] → coordinator(AskUser) → ... → SYNTH
```
## Decision Recording Protocol
**⚠️ CRITICAL**: 继承自原 analyze-with-file 命令。分析过程中以下情况必须立即记录到 discussion.md
| Trigger | What to Record | Target Section |
|---------|---------------|----------------|
| **Direction choice** | 选择了什么、为什么、放弃了哪些替代方案 | `#### Decision Log` |
| **Key finding** | 发现内容、影响范围、置信度 | `#### Key Findings` |
| **Assumption change** | 旧假设→新理解、变更原因、影响 | `#### Corrected Assumptions` |
| **User feedback** | 用户原始输入、采纳/调整理由 | `#### User Input` |
## Unified Session Directory
```
.workflow/.team/UAN-{slug}-{YYYY-MM-DD}/
├── shared-memory.json # 探索/分析/讨论/综合 共享记忆
├── discussion.md # ⭐ 理解演进 & 讨论时间线
├── explorations/ # Explorer output
│ ├── exploration-001.json
│ └── exploration-002.json
├── analyses/ # Analyst output
│ ├── analysis-001.json
│ └── analysis-002.json
├── discussions/ # Discussant output
│ └── discussion-round-001.json
└── conclusions.json # Synthesizer output
```
## Coordinator Spawn Template
```javascript
TeamCreate({ team_name: teamName })
// Explorer
Task({
subagent_type: "general-purpose",
team_name: teamName,
name: "explorer",
prompt: `你是 team "${teamName}" 的 EXPLORER。
当你收到 EXPLORE-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=explorer") 执行。
当前需求: ${taskDescription}
约束: ${constraints}
## 角色准则(强制)
- 你只能处理 EXPLORE-* 前缀的任务,不得执行其他角色的工作
- 所有输出SendMessage、team_msg必须带 [explorer] 标识前缀
- 仅与 coordinator 通信,不得直接联系其他 worker
- 不得使用 TaskCreate 为其他角色创建任务
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
工作流程:
1. TaskList → 找到 EXPLORE-* 任务
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。
当你收到 ANALYZE-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=analyst") 执行。
当前需求: ${taskDescription}
约束: ${constraints}
## 角色准则(强制)
- 你只能处理 ANALYZE-* 前缀的任务
- 所有输出必须带 [analyst] 标识前缀
- 仅与 coordinator 通信
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
工作流程:
1. TaskList → 找到 ANALYZE-* 任务
2. Skill(skill="team-ultra-analyze", args="--role=analyst") 执行
3. team_msg log + SendMessage 结果给 coordinator
4. TaskUpdate completed → 检查下一个任务`
})
// Discussant
Task({
subagent_type: "general-purpose",
team_name: teamName,
name: "discussant",
prompt: `你是 team "${teamName}" 的 DISCUSSANT。
当你收到 DISCUSS-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=discussant") 执行。
当前需求: ${taskDescription}
## 角色准则(强制)
- 你只能处理 DISCUSS-* 前缀的任务
- 所有输出必须带 [discussant] 标识前缀
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
工作流程:
1. TaskList → 找到 DISCUSS-* 任务
2. Skill(skill="team-ultra-analyze", args="--role=discussant") 执行
3. team_msg log + SendMessage
4. TaskUpdate completed → 检查下一个任务`
})
// Synthesizer
Task({
subagent_type: "general-purpose",
team_name: teamName,
name: "synthesizer",
prompt: `你是 team "${teamName}" 的 SYNTHESIZER。
当你收到 SYNTH-* 任务时,调用 Skill(skill="team-ultra-analyze", args="--role=synthesizer") 执行。
当前需求: ${taskDescription}
## 角色准则(强制)
- 你只能处理 SYNTH-* 前缀的任务
- 所有输出必须带 [synthesizer] 标识前缀
## 消息总线(必须)
每次 SendMessage 前,先调用 mcp__ccw-tools__team_msg 记录。
工作流程:
1. TaskList → 找到 SYNTH-* 任务
2. Skill(skill="team-ultra-analyze", args="--role=synthesizer") 执行
3. team_msg log + SendMessage
4. TaskUpdate completed → 检查下一个任务`
})
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Unknown --role value | Error with available role list |
| Missing --role arg | Error with usage hint |
| Role file not found | Error with expected path (roles/{name}/role.md) |
| Task prefix conflict | Log warning, proceed |
| Discussion loop stuck >5 rounds | Force synthesis, offer continuation |
| CLI tool unavailable | Fallback chain: gemini → codex → manual analysis |
| Explorer agent fails | Continue with available context, note limitation |

View File

@@ -0,0 +1,210 @@
# Command: analyze
> CLI 多视角深度分析。基于探索结果,通过 CLI 工具执行深度分析并生成结构化洞察。
## When to Use
- Phase 3 of Analyst
- 探索结果已就绪,需要深度分析
- 每个 ANALYZE-* 任务触发一次
**Trigger conditions**:
- Analyst Phase 2 完成后(上下文已加载)
- 方向调整时创建的 ANALYZE-fix 任务
## Strategy
### Delegation Mode
**Mode**: CLI通过 ccw cli 执行分析Bash run_in_background: true
### Decision Logic
```javascript
// 根据 perspective 选择 CLI 工具和分析模板
function buildAnalysisConfig(perspective, isDirectionFix) {
const configs = {
'technical': {
tool: 'gemini',
rule: 'analysis-analyze-code-patterns',
focus: 'Implementation patterns, code quality, technical debt, feasibility',
tasks: [
'Analyze code structure and organization patterns',
'Identify technical debt and anti-patterns',
'Evaluate error handling and edge cases',
'Assess testing coverage and quality'
]
},
'architectural': {
tool: 'claude',
rule: 'analysis-review-architecture',
focus: 'System design, scalability, component coupling, boundaries',
tasks: [
'Evaluate module boundaries and coupling',
'Analyze data flow and component interactions',
'Assess scalability and extensibility',
'Review design pattern usage and consistency'
]
},
'business': {
tool: 'codex',
rule: 'analysis-analyze-code-patterns',
focus: 'Business logic, domain models, value delivery, stakeholder impact',
tasks: [
'Map business logic to code implementation',
'Identify domain model completeness',
'Evaluate business rule enforcement',
'Assess impact on stakeholders and users'
]
},
'domain_expert': {
tool: 'gemini',
rule: 'analysis-analyze-code-patterns',
focus: 'Domain-specific patterns, standards compliance, best practices',
tasks: [
'Compare against domain best practices',
'Check standards and convention compliance',
'Identify domain-specific anti-patterns',
'Evaluate domain model accuracy'
]
}
}
const config = configs[perspective] || configs['technical']
if (isDirectionFix) {
config.rule = 'analysis-diagnose-bug-root-cause'
config.tasks = [
'Re-analyze from adjusted perspective',
'Identify previously missed patterns',
'Generate new insights from fresh angle',
'Update discussion points based on direction change'
]
}
return config
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
const config = buildAnalysisConfig(perspective, isDirectionFix)
// 构建探索上下文摘要
const explorationSummary = `
PRIOR EXPLORATION CONTEXT:
- Key files: ${(explorationContext.relevant_files || []).slice(0, 8).map(f => f.path || f).join(', ')}
- Patterns found: ${(explorationContext.patterns || []).slice(0, 5).join('; ')}
- Key findings: ${(explorationContext.key_findings || []).slice(0, 5).join('; ')}
- Questions from exploration: ${(explorationContext.questions_for_analysis || []).slice(0, 3).join('; ')}`
```
### Step 2: Execute CLI Analysis
```javascript
const cliPrompt = `PURPOSE: ${isDirectionFix
? `Supplementary analysis with adjusted focus on "${adjustedFocus}" for topic "${topic}"`
: `Deep analysis of "${topic}" from ${perspective} perspective`}
Success: ${isDirectionFix
? 'New insights from adjusted direction with clear evidence'
: 'Actionable insights with confidence levels and evidence references'}
${explorationSummary}
TASK:
${config.tasks.map(t => `${t}`).join('\n')}
• Generate structured findings with confidence levels (high/medium/low)
• Identify discussion points requiring user input
• List open questions needing further exploration
MODE: analysis
CONTEXT: @**/* | Topic: ${topic}
EXPECTED: JSON-structured analysis with sections: key_insights (with confidence), key_findings (with evidence), discussion_points, open_questions, recommendations (with priority)
CONSTRAINTS: Focus on ${perspective} perspective | ${dimensions.join(', ')} dimensions${isDirectionFix ? ` | Adjusted focus: ${adjustedFocus}` : ''}`
Bash({
command: `ccw cli -p "${cliPrompt}" --tool ${config.tool} --mode analysis --rule ${config.rule}`,
run_in_background: true
})
// ⚠️ STOP POINT: Wait for CLI callback before continuing
```
### Step 3: Result Processing
```javascript
// CLI 结果返回后,解析并结构化
const outputPath = `${sessionFolder}/analyses/analysis-${analyzeNum}.json`
// 从 CLI 输出中提取结构化数据
// CLI 输出通常是 markdown需要解析为 JSON
const analysisResult = {
perspective,
dimensions,
is_direction_fix: isDirectionFix,
adjusted_focus: adjustedFocus || null,
key_insights: [
// 从 CLI 输出提取,每个包含 {insight, confidence, evidence}
],
key_findings: [
// 具体发现 {finding, file_ref, impact}
],
discussion_points: [
// 需要用户输入的讨论要点
],
open_questions: [
// 未解决的问题
],
recommendations: [
// {action, rationale, priority}
],
_metadata: {
cli_tool: config.tool,
cli_rule: config.rule,
perspective,
is_direction_fix: isDirectionFix,
timestamp: new Date().toISOString()
}
}
Write(outputPath, JSON.stringify(analysisResult, null, 2))
```
## Output Format
```json
{
"perspective": "technical",
"dimensions": ["architecture", "implementation"],
"is_direction_fix": false,
"key_insights": [
{"insight": "Authentication uses stateless JWT", "confidence": "high", "evidence": "src/auth/jwt.ts:L42"}
],
"key_findings": [
{"finding": "No rate limiting on login endpoint", "file_ref": "src/routes/auth.ts:L15", "impact": "Security risk"}
],
"discussion_points": [
"Should we implement token rotation for refresh tokens?"
],
"open_questions": [
"What is the expected concurrent user load?"
],
"recommendations": [
{"action": "Add rate limiting to auth endpoints", "rationale": "Prevent brute force attacks", "priority": "high"}
],
"_metadata": {"cli_tool": "gemini", "cli_rule": "analysis-analyze-code-patterns", "timestamp": "..."}
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| CLI tool unavailable | Try fallback: gemini → codex → claude |
| CLI timeout | Retry with shorter prompt, or use exploration results directly |
| CLI returns empty | Use exploration findings as-is, note analysis gap |
| Invalid CLI output | Extract what's parseable, fill gaps with defaults |
| Exploration context missing | Analyze with topic keywords only |

View File

@@ -0,0 +1,290 @@
# Role: analyst
深度分析师。基于 explorer 的代码库探索结果,通过 CLI 多视角深度分析,生成结构化洞察和讨论要点。
## Role Identity
- **Name**: `analyst`
- **Task Prefix**: `ANALYZE-*`
- **Responsibility**: Read-only analysis深度分析
- **Communication**: SendMessage to coordinator only
- **Output Tag**: `[analyst]`
## Role Boundaries
### MUST
- 仅处理 `ANALYZE-*` 前缀的任务
- 所有输出必须带 `[analyst]` 标识
- 仅通过 SendMessage 与 coordinator 通信
- 基于 explorer 的探索结果进行深度分析
- 将分析结果写入 shared-memory.json 的 `analyses` 字段
### MUST NOT
- ❌ 执行代码库探索(属于 explorer
- ❌ 处理用户反馈(属于 discussant
- ❌ 生成最终结论(属于 synthesizer
- ❌ 为其他角色创建任务
- ❌ 直接与其他 worker 通信
- ❌ 修改源代码
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `analysis_ready` | analyst → coordinator | 分析完成 | 包含洞察、讨论要点、开放问题 |
| `error` | analyst → coordinator | 分析失败 | 阻塞性错误 |
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `analyze` | [commands/analyze.md](commands/analyze.md) | Phase 3 | CLI 多视角深度分析 |
### Subagent Capabilities
> Analyst 不直接使用 subagent
### CLI Capabilities
| CLI Tool | Mode | Used By | Purpose |
|----------|------|---------|---------|
| `gemini` | analysis | analyze.md | 技术/领域分析 |
| `codex` | analysis | analyze.md | 业务视角分析 |
| `claude` | analysis | analyze.md | 架构视角分析 |
## Execution (5-Phase)
### Phase 1: Task Discovery
```javascript
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('ANALYZE-') &&
t.owner === 'analyst' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return // idle
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Context Loading
```javascript
// 从任务描述中提取上下文
const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1]?.trim()
const topic = task.description.match(/topic:\s*(.+)/)?.[1]?.trim()
const perspective = task.description.match(/perspective:\s*(.+)/)?.[1]?.trim() || 'technical'
const dimensions = (task.description.match(/dimensions:\s*(.+)/)?.[1]?.trim() || 'general').split(', ')
const isDirectionFix = task.description.includes('type: direction-fix')
const adjustedFocus = task.description.match(/adjusted_focus:\s*(.+)/)?.[1]?.trim()
// 读取 shared memory
let sharedMemory = {}
try { sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) } catch {}
// 读取对应的探索结果
const analyzeNum = task.subject.match(/ANALYZE-(\w+)/)?.[1] || '001'
let explorationContext = {}
if (isDirectionFix) {
// 方向调整:读取所有已有探索结果
const explorationFiles = Glob({ pattern: `${sessionFolder}/explorations/*.json` })
const allExplorations = explorationFiles.map(f => JSON.parse(Read(f)))
explorationContext = {
relevant_files: allExplorations.flatMap(e => e.relevant_files || []).slice(0, 10),
patterns: allExplorations.flatMap(e => e.patterns || []),
key_findings: allExplorations.flatMap(e => e.key_findings || [])
}
} else {
// 正常分析:读取对应编号的探索结果
try {
explorationContext = JSON.parse(Read(`${sessionFolder}/explorations/exploration-${analyzeNum}.json`))
} catch {
// 尝试读取任意可用的探索结果
const explorationFiles = Glob({ pattern: `${sessionFolder}/explorations/*.json` })
if (explorationFiles.length > 0) {
explorationContext = JSON.parse(Read(explorationFiles[0]))
}
}
}
// 确定 CLI 工具
const PERSPECTIVE_TOOLS = {
'technical': 'gemini',
'architectural': 'claude',
'business': 'codex',
'domain_expert': 'gemini'
}
const cliTool = PERSPECTIVE_TOOLS[perspective] || 'gemini'
```
### Phase 3: Deep Analysis via CLI
```javascript
// Read commands/analyze.md for full CLI analysis implementation
Read("commands/analyze.md")
```
**核心策略**: 基于探索结果,通过 CLI 执行深度分析
```javascript
const analysisPrompt = isDirectionFix
? `PURPOSE: 补充分析 - 方向调整至 "${adjustedFocus}"
Success: 针对新方向的深入洞察
PRIOR EXPLORATION CONTEXT:
- Key files: ${(explorationContext.relevant_files || []).slice(0, 5).map(f => f.path || f).join(', ')}
- Patterns: ${(explorationContext.patterns || []).slice(0, 3).join(', ')}
- Previous findings: ${(explorationContext.key_findings || []).slice(0, 3).join(', ')}
TASK:
• Focus analysis on: ${adjustedFocus}
• Build on previous exploration findings
• Identify new insights from adjusted perspective
• Generate discussion points for user
MODE: analysis
CONTEXT: @**/* | Topic: ${topic}
EXPECTED: Structured analysis with adjusted focus, new insights, updated discussion points
CONSTRAINTS: Focus on ${adjustedFocus}`
: `PURPOSE: Analyze topic '${topic}' from ${perspective} perspective across ${dimensions.join(', ')} dimensions
Success: Actionable insights with clear reasoning and evidence
PRIOR EXPLORATION CONTEXT:
- Key files: ${(explorationContext.relevant_files || []).slice(0, 5).map(f => f.path || f).join(', ')}
- Patterns found: ${(explorationContext.patterns || []).slice(0, 3).join(', ')}
- Key findings: ${(explorationContext.key_findings || []).slice(0, 3).join(', ')}
TASK:
• Build on exploration findings above
• Analyze from ${perspective} perspective: ${dimensions.join(', ')}
• Identify patterns, anti-patterns, and opportunities
• Generate discussion points for user clarification
• Assess confidence level for each insight
MODE: analysis
CONTEXT: @**/* | Topic: ${topic}
EXPECTED: Structured analysis with: key insights (with confidence), discussion points, open questions, recommendations with rationale
CONSTRAINTS: Focus on ${dimensions.join(', ')} | ${perspective} perspective`
Bash({
command: `ccw cli -p "${analysisPrompt}" --tool ${cliTool} --mode analysis`,
run_in_background: true
})
// ⚠️ STOP POINT: Wait for CLI callback
```
### Phase 4: Result Aggregation
```javascript
// CLI 结果返回后,构建分析输出
const outputPath = `${sessionFolder}/analyses/analysis-${analyzeNum}.json`
const analysisResult = {
perspective,
dimensions,
is_direction_fix: isDirectionFix,
adjusted_focus: adjustedFocus || null,
key_insights: [], // 从 CLI 结果提取
key_findings: [], // 具体发现
discussion_points: [], // 讨论要点
open_questions: [], // 开放问题
recommendations: [], // 建议
confidence_levels: {}, // 各洞察的置信度
evidence: [], // 证据引用
_metadata: {
cli_tool: cliTool,
perspective,
timestamp: new Date().toISOString()
}
}
Write(outputPath, JSON.stringify(analysisResult, null, 2))
```
### Phase 5: Report to Coordinator
```javascript
// 更新 shared memory
sharedMemory.analyses = sharedMemory.analyses || []
sharedMemory.analyses.push({
id: `analysis-${analyzeNum}`,
perspective,
is_direction_fix: isDirectionFix,
insight_count: analysisResult.key_insights?.length || 0,
finding_count: analysisResult.key_findings?.length || 0,
timestamp: new Date().toISOString()
})
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
const resultSummary = `${perspective} 视角: ${analysisResult.key_insights?.length || 0} 个洞察, ${analysisResult.discussion_points?.length || 0} 个讨论点`
mcp__ccw-tools__team_msg({
operation: "log",
team: teamName,
from: "analyst",
to: "coordinator",
type: "analysis_ready",
summary: `[analyst] ${resultSummary}`,
ref: outputPath
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## [analyst] Analysis Results
**Task**: ${task.subject}
**Perspective**: ${perspective}${isDirectionFix ? ` (Direction Fix: ${adjustedFocus})` : ''}
**CLI Tool**: ${cliTool}
### Summary
${resultSummary}
### Key Insights
${(analysisResult.key_insights || []).slice(0, 5).map(i => `- ${i}`).join('\n')}
### Discussion Points
${(analysisResult.discussion_points || []).slice(0, 3).map(p => `- ${p}`).join('\n')}
### Open Questions
${(analysisResult.open_questions || []).slice(0, 3).map(q => `- ${q}`).join('\n')}
### Output
${outputPath}`,
summary: `[analyst] ANALYZE complete: ${resultSummary}`
})
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for next task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('ANALYZE-') &&
t.owner === 'analyst' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task → back to Phase 1
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No ANALYZE-* tasks available | Idle, wait for coordinator assignment |
| CLI tool unavailable | Fallback chain: gemini → codex → claude |
| No exploration results found | Analyze with topic keywords only, note limitation |
| CLI timeout | Use partial results, report incomplete |
| Invalid exploration JSON | Skip context, analyze from scratch |

View File

@@ -0,0 +1,234 @@
# Command: dispatch
> 任务链创建与依赖管理。根据管道模式创建 pipeline 任务链并分配给 worker 角色。
## When to Use
- Phase 3 of Coordinator
- 管道模式已确定,需要创建任务链
- 团队已创建worker 已 spawn
**Trigger conditions**:
- Coordinator Phase 2 完成后
- 讨论循环中需要创建补充分析任务
- 方向调整需要创建新探索/分析任务
## Strategy
### Delegation Mode
**Mode**: Directcoordinator 直接操作 TaskCreate/TaskUpdate
### Decision Logic
```javascript
// 根据 pipelineMode 和 perspectives 选择 pipeline
function buildPipeline(pipelineMode, perspectives, sessionFolder, taskDescription, dimensions) {
const pipelines = {
'quick': [
{ prefix: 'EXPLORE', suffix: '001', owner: 'explorer', desc: '代码库探索', meta: `perspective: general\ndimensions: ${dimensions.join(', ')}`, blockedBy: [] },
{ prefix: 'ANALYZE', suffix: '001', owner: 'analyst', desc: '综合分析', meta: `perspective: technical\ndimensions: ${dimensions.join(', ')}`, blockedBy: ['EXPLORE-001'] },
{ prefix: 'SYNTH', suffix: '001', owner: 'synthesizer', desc: '结论综合', blockedBy: ['ANALYZE-001'] }
],
'standard': buildStandardPipeline(perspectives, dimensions),
'deep': buildDeepPipeline(perspectives, dimensions)
}
return pipelines[pipelineMode] || pipelines['standard']
}
function buildStandardPipeline(perspectives, dimensions) {
const stages = []
const perspectiveList = perspectives.length > 0 ? perspectives : ['technical']
// Parallel explorations
perspectiveList.forEach((p, i) => {
const num = String(i + 1).padStart(3, '0')
stages.push({
prefix: 'EXPLORE', suffix: num, owner: 'explorer',
desc: `代码库探索 (${p})`,
meta: `perspective: ${p}\ndimensions: ${dimensions.join(', ')}`,
blockedBy: []
})
})
// Parallel analyses (blocked by corresponding exploration)
perspectiveList.forEach((p, i) => {
const num = String(i + 1).padStart(3, '0')
stages.push({
prefix: 'ANALYZE', suffix: num, owner: 'analyst',
desc: `深度分析 (${p})`,
meta: `perspective: ${p}\ndimensions: ${dimensions.join(', ')}`,
blockedBy: [`EXPLORE-${num}`]
})
})
// Discussion (blocked by all analyses)
const analyzeIds = perspectiveList.map((_, i) => `ANALYZE-${String(i + 1).padStart(3, '0')}`)
stages.push({
prefix: 'DISCUSS', suffix: '001', owner: 'discussant',
desc: '讨论处理 (Round 1)',
meta: `round: 1\ntype: initial`,
blockedBy: analyzeIds
})
// Synthesis (blocked by discussion)
stages.push({
prefix: 'SYNTH', suffix: '001', owner: 'synthesizer',
desc: '结论综合',
blockedBy: ['DISCUSS-001']
})
return stages
}
function buildDeepPipeline(perspectives, dimensions) {
// Same as standard but SYNTH is not created initially
// It will be created after discussion loop completes
const stages = buildStandardPipeline(perspectives, dimensions)
// Remove SYNTH — will be created dynamically after discussion loop
return stages.filter(s => s.prefix !== 'SYNTH')
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
const pipeline = buildPipeline(pipelineMode, selectedPerspectives, sessionFolder, taskDescription, dimensions)
```
### Step 2: Execute Strategy
```javascript
const taskIds = {}
for (const stage of pipeline) {
const taskSubject = `${stage.prefix}-${stage.suffix}: ${stage.desc}`
// 构建任务描述(包含 session 和上下文信息)
const fullDesc = [
stage.desc,
`\nsession: ${sessionFolder}`,
`\ntopic: ${taskDescription}`,
stage.meta ? `\n${stage.meta}` : '',
`\n\n目标: ${taskDescription}`
].join('')
// 创建任务
TaskCreate({
subject: taskSubject,
description: fullDesc,
activeForm: `${stage.desc}进行中`
})
// 记录任务 ID
const allTasks = TaskList()
const newTask = allTasks.find(t => t.subject.startsWith(`${stage.prefix}-${stage.suffix}`))
taskIds[`${stage.prefix}-${stage.suffix}`] = newTask.id
// 设置 owner 和依赖
const blockedByIds = stage.blockedBy
.map(dep => taskIds[dep])
.filter(Boolean)
TaskUpdate({
taskId: newTask.id,
owner: stage.owner,
addBlockedBy: blockedByIds
})
}
```
### Step 3: Result Processing
```javascript
// 验证任务链
const allTasks = TaskList()
const chainTasks = pipeline.map(s => taskIds[`${s.prefix}-${s.suffix}`]).filter(Boolean)
const chainValid = chainTasks.length === pipeline.length
if (!chainValid) {
mcp__ccw-tools__team_msg({
operation: "log", team: teamName, from: "coordinator",
to: "user", type: "error",
summary: `[coordinator] 任务链创建不完整: ${chainTasks.length}/${pipeline.length}`
})
}
```
## Discussion Loop Task Creation
讨论循环中动态创建任务:
```javascript
// 创建新一轮讨论任务
function createDiscussionTask(round, type, userFeedback, sessionFolder) {
const suffix = String(round).padStart(3, '0')
TaskCreate({
subject: `DISCUSS-${suffix}: 讨论处理 (Round ${round})`,
description: `讨论处理\nsession: ${sessionFolder}\nround: ${round}\ntype: ${type}\nuser_feedback: ${userFeedback}`,
activeForm: `讨论 Round ${round} 进行中`
})
const allTasks = TaskList()
const newTask = allTasks.find(t => t.subject.startsWith(`DISCUSS-${suffix}`))
TaskUpdate({ taskId: newTask.id, owner: 'discussant' })
return newTask.id
}
// 创建补充分析任务(方向调整时)
function createAnalysisFix(round, adjustedFocus, sessionFolder) {
const suffix = `fix-${round}`
TaskCreate({
subject: `ANALYZE-${suffix}: 补充分析 (方向调整 Round ${round})`,
description: `补充分析\nsession: ${sessionFolder}\nadjusted_focus: ${adjustedFocus}\ntype: direction-fix`,
activeForm: `补充分析 Round ${round} 进行中`
})
const allTasks = TaskList()
const newTask = allTasks.find(t => t.subject.startsWith(`ANALYZE-${suffix}`))
TaskUpdate({ taskId: newTask.id, owner: 'analyst' })
return newTask.id
}
// 创建最终综合任务
function createSynthesisTask(sessionFolder, blockedByIds) {
TaskCreate({
subject: `SYNTH-001: 结论综合`,
description: `跨视角整合\nsession: ${sessionFolder}\ntype: final`,
activeForm: `结论综合进行中`
})
const allTasks = TaskList()
const newTask = allTasks.find(t => t.subject.startsWith('SYNTH-001'))
TaskUpdate({
taskId: newTask.id,
owner: 'synthesizer',
addBlockedBy: blockedByIds
})
return newTask.id
}
```
## Output Format
```
## Task Chain Created
### Mode: [quick|standard|deep]
### Pipeline Stages: [count]
- [prefix]-[suffix]: [description] (owner: [role], blocked by: [deps])
### Verification: PASS/FAIL
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Task creation fails | Retry once, then report to user |
| Dependency cycle detected | Flatten dependencies, warn coordinator |
| Invalid pipelineMode | Default to 'standard' mode |
| Too many perspectives (>4) | Truncate to first 4, warn user |
| Timeout (>5 min) | Report partial results, notify coordinator |

View File

@@ -0,0 +1,376 @@
# Command: monitor
> 阶段驱动的协调循环 + 讨论循环。按 pipeline 阶段顺序等待 worker 完成,驱动讨论循环,执行最终综合触发。
## When to Use
- Phase 4 of Coordinator
- 任务链已创建并分发
- 需要持续监控直到所有任务完成
**Trigger conditions**:
- dispatch 完成后立即启动
- 讨论循环创建新任务后重新进入
## Strategy
### Delegation Mode
**Mode**: Stage-driven按阶段顺序等待非轮询+ Discussion-loop讨论循环由 coordinator 驱动)
### 设计原则
> **模型执行没有时间概念**。禁止空转 while 循环检查状态。
> 使用固定 sleep 间隔 + 最大轮询次数,避免无意义的 API 调用浪费。
### Decision Logic
```javascript
// 消息路由表
const routingTable = {
// Explorer 完成
'exploration_ready': { action: 'Mark EXPLORE complete, unblock ANALYZE' },
// Analyst 完成
'analysis_ready': { action: 'Mark ANALYZE complete, unblock DISCUSS or SYNTH' },
// Discussant 完成
'discussion_processed': { action: 'Mark DISCUSS complete, trigger user feedback collection', special: 'discussion_feedback' },
// Synthesizer 完成
'synthesis_ready': { action: 'Mark SYNTH complete, prepare final report', special: 'finalize' },
// 错误
'error': { action: 'Assess severity, retry or escalate', special: 'error_handler' }
}
```
### 等待策略常量
```javascript
const POLL_INTERVAL_SEC = 300 // 每次检查间隔 5 分钟
const MAX_POLLS_PER_STAGE = 6 // 单阶段最多等待 6 次(~30 分钟)
const SLEEP_CMD = process.platform === 'win32'
? `timeout /t ${POLL_INTERVAL_SEC} /nobreak >nul 2>&1`
: `sleep ${POLL_INTERVAL_SEC}`
// ★ 统一 auto mode 检测
const autoYes = /\b(-y|--yes)\b/.test(args)
```
## Execution Steps
### Step 1: Context Preparation
```javascript
// 从 shared memory 获取当前状态
const sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
let discussionRound = 0
const MAX_DISCUSSION_ROUNDS = pipelineMode === 'deep' ? 5 : (pipelineMode === 'standard' ? 1 : 0)
// 获取 pipeline 阶段列表(来自 dispatch 创建的任务链)
const allTasks = TaskList()
const pipelineTasks = allTasks
.filter(t => t.owner && t.owner !== 'coordinator')
.sort((a, b) => Number(a.id) - Number(b.id))
```
### Step 2: Stage-Driven Execution (Exploration + Analysis)
> 按 pipeline 阶段顺序,逐阶段等待完成。
```javascript
// 处理 EXPLORE 和 ANALYZE 阶段
const preDiscussionTasks = pipelineTasks.filter(t =>
t.subject.startsWith('EXPLORE-') || t.subject.startsWith('ANALYZE-')
)
for (const stageTask of preDiscussionTasks) {
let stageComplete = false
let pollCount = 0
while (!stageComplete && pollCount < MAX_POLLS_PER_STAGE) {
Bash(SLEEP_CMD)
pollCount++
// 1. 检查消息总线
const messages = mcp__ccw-tools__team_msg({
operation: "list", team: teamName, last: 5
})
// 2. 路由消息
for (const msg of messages) {
const handler = routingTable[msg.type]
if (!handler) continue
processMessage(msg, handler)
}
// 3. 确认任务状态(兜底)
const currentTask = TaskGet({ taskId: stageTask.id })
stageComplete = currentTask.status === 'completed' || currentTask.status === 'deleted'
}
// 阶段超时处理
if (!stageComplete) {
handleStageTimeout(stageTask, pollCount, autoYes)
}
}
```
### Step 2.1: Update discussion.md with Round 1
```javascript
// 读取所有探索和分析结果
const explorationFiles = Glob({ pattern: `${sessionFolder}/explorations/*.json` })
const analysisFiles = Glob({ pattern: `${sessionFolder}/analyses/*.json` })
const explorations = explorationFiles.map(f => JSON.parse(Read(f)))
const analyses = analysisFiles.map(f => JSON.parse(Read(f)))
// 更新 discussion.md — Round 1
const round1Content = `
### Round 1 - Initial Exploration & Analysis (${new Date().toISOString()})
#### Exploration Results
${explorations.map(e => `- **${e.perspective || 'general'}**: ${e.key_findings?.slice(0, 3).join('; ') || 'No findings'}`).join('\n')}
#### Analysis Results
${analyses.map(a => `- **${a.perspective || 'general'}**: ${a.key_insights?.slice(0, 3).join('; ') || 'No insights'}`).join('\n')}
#### Key Findings
${analyses.flatMap(a => a.key_findings || []).slice(0, 5).map(f => `- ${f}`).join('\n')}
#### Discussion Points
${analyses.flatMap(a => a.discussion_points || []).slice(0, 5).map(p => `- ${p}`).join('\n')}
#### Decision Log
> **Decision**: Selected ${pipelineMode} pipeline with ${explorations.length} exploration(s) and ${analyses.length} analysis perspective(s)
> - **Context**: Topic analysis and user preference
> - **Chosen**: ${pipelineMode} mode — **Reason**: ${pipelineMode === 'quick' ? 'Fast overview requested' : pipelineMode === 'deep' ? 'Thorough analysis needed' : 'Balanced depth and breadth'}
`
Edit({
file_path: `${sessionFolder}/discussion.md`,
old_string: '## Discussion Timeline\n',
new_string: `## Discussion Timeline\n${round1Content}\n`
})
```
### Step 3: Discussion Loop (Standard/Deep mode)
```javascript
if (MAX_DISCUSSION_ROUNDS === 0) {
// Quick mode: skip discussion, go to synthesis
createSynthesisTask(sessionFolder, [lastAnalyzeTaskId])
} else {
// Wait for initial DISCUSS-001 to complete
// Then enter discussion loop
while (discussionRound < MAX_DISCUSSION_ROUNDS) {
// 等待当前 DISCUSS 任务完成
const currentDiscussId = `DISCUSS-${String(discussionRound + 1).padStart(3, '0')}`
// ... wait for completion (same pattern as Step 2)
// 收集用户反馈
const feedbackResult = AskUserQuestion({
questions: [{
question: `Round ${discussionRound + 1} 分析结果已就绪。请选择下一步:`,
header: "Discussion Feedback",
multiSelect: false,
options: [
{ label: "同意,继续深入", description: "分析方向正确,继续深入探索" },
{ label: "需要调整方向", description: "有不同理解或关注点" },
{ label: "分析完成", description: "已获得足够信息" },
{ label: "有具体问题", description: "有特定问题需要解答" }
]
}]
})
const feedback = feedbackResult["Discussion Feedback"]
// 📌 记录用户反馈到 decision_trail
const latestMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
latestMemory.decision_trail.push({
round: discussionRound + 1,
decision: feedback,
context: `User feedback at discussion round ${discussionRound + 1}`,
timestamp: new Date().toISOString()
})
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(latestMemory, null, 2))
if (feedback === "分析完成") {
// 📌 Record completion decision
appendToDiscussion(sessionFolder, discussionRound + 1, {
user_input: "分析完成",
decision: "Exit discussion loop, proceed to synthesis",
reason: "User satisfied with current analysis depth"
})
break
}
if (feedback === "需要调整方向") {
// 收集调整方向
const directionResult = AskUserQuestion({
questions: [{
question: "请选择新的关注方向:",
header: "Direction Adjustment",
multiSelect: false,
options: [
{ label: "代码细节", description: "深入具体代码实现" },
{ label: "架构层面", description: "关注系统架构设计" },
{ label: "最佳实践", description: "对比行业最佳实践" },
{ label: "自定义", description: "输入自定义方向" }
]
}]
})
const newDirection = directionResult["Direction Adjustment"]
// 📌 Record direction change
appendToDiscussion(sessionFolder, discussionRound + 1, {
user_input: `调整方向: ${newDirection}`,
decision: `Direction adjusted to: ${newDirection}`,
reason: "User requested focus change"
})
// 创建补充分析 + 新讨论任务
const fixId = createAnalysisFix(discussionRound + 1, newDirection, sessionFolder)
discussionRound++
createDiscussionTask(discussionRound + 1, 'direction-adjusted', newDirection, sessionFolder)
continue
}
if (feedback === "有具体问题") {
// 📌 Record question
appendToDiscussion(sessionFolder, discussionRound + 1, {
user_input: "有具体问题(由 discussant 处理)",
decision: "Create discussion task for specific questions"
})
discussionRound++
createDiscussionTask(discussionRound + 1, 'specific-questions', 'User has specific questions', sessionFolder)
continue
}
// 同意,继续深入
appendToDiscussion(sessionFolder, discussionRound + 1, {
user_input: "同意,继续深入",
decision: "Continue deepening in current direction"
})
discussionRound++
if (discussionRound < MAX_DISCUSSION_ROUNDS) {
createDiscussionTask(discussionRound + 1, 'deepen', 'Continue current direction', sessionFolder)
}
}
// 创建最终综合任务
const lastDiscussTaskId = getLastCompletedTaskId('DISCUSS')
createSynthesisTask(sessionFolder, [lastDiscussTaskId])
}
```
### Step 3.1: Discussion Helper Functions
```javascript
function appendToDiscussion(sessionFolder, round, data) {
const roundContent = `
### Round ${round + 1} - Discussion (${new Date().toISOString()})
#### User Input
${data.user_input}
#### Decision Log
> **Decision**: ${data.decision}
> - **Context**: Discussion round ${round + 1}
> - **Reason**: ${data.reason || 'User-directed'}
#### Updated Understanding
${data.updated_understanding || '(Updated by discussant)'}
`
// Append to discussion.md
const currentContent = Read(`${sessionFolder}/discussion.md`)
Write(`${sessionFolder}/discussion.md`, currentContent + roundContent)
}
function handleStageTimeout(stageTask, pollCount, autoYes) {
const elapsedMin = Math.round(pollCount * POLL_INTERVAL_SEC / 60)
if (autoYes) {
mcp__ccw-tools__team_msg({
operation: "log", team: teamName, from: "coordinator",
to: "user", type: "error",
summary: `[coordinator] [auto] 阶段 ${stageTask.subject} 超时 (${elapsedMin}min),自动跳过`
})
TaskUpdate({ taskId: stageTask.id, status: 'deleted' })
return
}
const decision = AskUserQuestion({
questions: [{
question: `阶段 "${stageTask.subject}" 已等待 ${elapsedMin} 分钟仍未完成。如何处理?`,
header: "Stage Wait",
multiSelect: false,
options: [
{ label: "继续等待", description: `再等 ${MAX_POLLS_PER_STAGE}` },
{ label: "跳过此阶段", description: "标记为跳过,继续后续流水线" },
{ label: "终止流水线", description: "停止整个分析流程" }
]
}]
})
const answer = decision["Stage Wait"]
if (answer === "跳过此阶段") {
TaskUpdate({ taskId: stageTask.id, status: 'deleted' })
} else if (answer === "终止流水线") {
mcp__ccw-tools__team_msg({
operation: "log", team: teamName, from: "coordinator",
to: "user", type: "shutdown",
summary: `[coordinator] 用户终止流水线,当前阶段: ${stageTask.subject}`
})
}
}
```
### Step 4: Wait for Synthesis + Result Processing
```javascript
// 等待 SYNTH-001 完成
// ... same wait pattern
// 汇总所有结果
const finalMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
const allFinalTasks = TaskList()
const workerTasks = allFinalTasks.filter(t => t.owner && t.owner !== 'coordinator')
const summary = {
total_tasks: workerTasks.length,
completed_tasks: workerTasks.filter(t => t.status === 'completed').length,
discussion_rounds: discussionRound,
has_synthesis: !!finalMemory.synthesis,
decisions_made: finalMemory.decision_trail?.length || 0
}
```
## Output Format
```
## Coordination Summary
### Pipeline Status: COMPLETE
### Mode: [quick|standard|deep]
### Tasks: [completed]/[total]
### Discussion Rounds: [count]
### Decisions Made: [count]
### Message Log (last 10)
- [timestamp] [from] → [to]: [type] - [summary]
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Message bus unavailable | Fall back to TaskList polling only |
| Stage timeout (交互模式) | AskUserQuestion继续等待 / 跳过 / 终止 |
| Stage timeout (自动模式) | 自动跳过,记录日志 |
| Teammate unresponsive (2x) | Respawn teammate with same task |
| Discussion loop stuck >5 rounds | Force synthesis, offer continuation |
| Synthesis fails | Report partial results from analyses |

View File

@@ -0,0 +1,328 @@
# Role: coordinator
分析团队协调者。编排 pipeline话题澄清 → 管道选择 → 团队创建 → 任务分发 → 讨论循环 → 结果汇报。
## Role Identity
- **Name**: `coordinator`
- **Task Prefix**: N/A (coordinator creates tasks, doesn't receive them)
- **Responsibility**: Orchestration
- **Communication**: SendMessage to all teammates
- **Output Tag**: `[coordinator]`
## Role Boundaries
### MUST
- 所有输出SendMessage、team_msg、日志必须带 `[coordinator]` 标识
- 仅负责话题澄清、管道选择、任务创建/分发、讨论循环驱动、结果汇报
- 通过 TaskCreate 创建任务并分配给 worker 角色
- 通过消息总线监控 worker 进度并路由消息
- 讨论循环中通过 AskUserQuestion 收集用户反馈
### MUST NOT
-**直接执行任何业务任务**代码探索、CLI 分析、综合整合等)
- ❌ 直接调用 cli-explore-agent、code-developer 等实现类 subagent
- ❌ 直接调用 CLI 分析工具ccw cli
- ❌ 绕过 worker 角色自行完成应委派的工作
- ❌ 在输出中省略 `[coordinator]` 标识
> **核心原则**: coordinator 是指挥者,不是执行者。所有实际工作必须通过 TaskCreate 委派给 worker 角色。
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `pipeline_selected` | coordinator → all | 管道模式确定 | Quick/Standard/Deep |
| `discussion_round` | coordinator → discussant | 用户反馈收集后 | 触发讨论处理 |
| `direction_adjusted` | coordinator → analyst | 方向调整 | 触发补充分析 |
| `task_unblocked` | coordinator → worker | 依赖解除 | 任务可执行 |
| `error` | coordinator → user | 协调错误 | 阻塞性问题 |
| `shutdown` | coordinator → all | 团队关闭 | 清理资源 |
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `dispatch` | [commands/dispatch.md](commands/dispatch.md) | Phase 3 | 任务链创建与依赖管理 |
| `monitor` | [commands/monitor.md](commands/monitor.md) | Phase 4 | 讨论循环 + 进度监控 |
### Subagent Capabilities
> Coordinator 不直接使用 subagent通过 worker 角色间接使用)
### CLI Capabilities
> Coordinator 不直接使用 CLI 分析工具
## Execution
### Phase 1: Topic Understanding & Requirement Clarification
```javascript
const args = "$ARGUMENTS"
// 提取话题描述
const taskDescription = args.replace(/--role[=\s]+\w+/, '').replace(/--team[=\s]+[\w-]+/, '').replace(/--mode[=\s]+\w+/, '').trim()
// ★ 统一 auto mode 检测
const autoYes = /\b(-y|--yes)\b/.test(args)
// 管道模式选择
function detectPipelineMode(args, desc) {
const modeMatch = args.match(/--mode[=\s]+(quick|standard|deep)/)
if (modeMatch) return modeMatch[1]
if (/快速|quick|overview|概览/.test(desc)) return 'quick'
if (/深入|deep|thorough|详细|全面/.test(desc)) return 'deep'
return 'standard'
}
let pipelineMode = detectPipelineMode(args, taskDescription)
// 维度检测
const DIMENSION_KEYWORDS = {
architecture: /架构|architecture|design|structure|设计/,
implementation: /实现|implement|code|coding|代码/,
performance: /性能|performance|optimize|bottleneck|优化/,
security: /安全|security|auth|permission|权限/,
concept: /概念|concept|theory|principle|原理/,
comparison: /比较|compare|vs|difference|区别/,
decision: /决策|decision|choice|tradeoff|选择/
}
const detectedDimensions = Object.entries(DIMENSION_KEYWORDS)
.filter(([_, regex]) => regex.test(taskDescription))
.map(([dim]) => dim)
const dimensions = detectedDimensions.length > 0 ? detectedDimensions : ['general']
// 交互式澄清(非 auto 模式)
if (!autoYes) {
// 1. Focus 方向选择
const DIMENSION_DIRECTIONS = {
architecture: ['System Design', 'Component Interactions', 'Technology Choices', 'Design Patterns', 'Scalability Strategy'],
implementation: ['Code Structure', 'Implementation Details', 'Code Patterns', 'Error Handling', 'Algorithm Analysis'],
performance: ['Performance Bottlenecks', 'Optimization Opportunities', 'Resource Utilization', 'Caching Strategy'],
security: ['Security Vulnerabilities', 'Authentication/Authorization', 'Access Control', 'Data Protection'],
concept: ['Conceptual Foundation', 'Core Mechanisms', 'Fundamental Patterns', 'Trade-offs & Reasoning'],
comparison: ['Solution Comparison', 'Pros & Cons Analysis', 'Technology Evaluation'],
decision: ['Decision Criteria', 'Trade-off Analysis', 'Risk Assessment', 'Impact Analysis'],
general: ['Overview', 'Key Patterns', 'Potential Issues', 'Improvement Opportunities']
}
const directionOptions = dimensions.flatMap(d => (DIMENSION_DIRECTIONS[d] || []).slice(0, 3))
.map(d => ({ label: d, description: `Focus on ${d}` }))
const focusResult = AskUserQuestion({
questions: [{
question: "选择分析方向(可多选)",
header: "Analysis Focus",
multiSelect: true,
options: directionOptions
}]
})
// 2. 视角选择Standard/Deep 模式)
let selectedPerspectives = ['technical']
if (pipelineMode !== 'quick') {
const perspectiveResult = AskUserQuestion({
questions: [{
question: "选择分析视角可多选最多4个",
header: "Analysis Perspectives",
multiSelect: true,
options: [
{ label: "Technical", description: "实现、代码模式、技术可行性" },
{ label: "Architectural", description: "系统设计、可扩展性、组件交互" },
{ label: "Business", description: "价值、ROI、利益相关者影响" },
{ label: "Domain Expert", description: "领域特定模式、最佳实践、标准" }
]
}]
})
// Parse selected perspectives
}
// 3. 深度选择
const depthResult = AskUserQuestion({
questions: [{
question: "选择分析深度",
header: "Analysis Depth",
multiSelect: false,
options: [
{ label: "Quick Overview", description: "快速概览 (10-15min)" },
{ label: "Standard Analysis", description: "标准分析 (30-60min)" },
{ label: "Deep Dive", description: "深度分析 (1-2hr)" }
]
}]
})
const depthMap = { 'Quick Overview': 'quick', 'Standard Analysis': 'standard', 'Deep Dive': 'deep' }
pipelineMode = depthMap[depthResult["Analysis Depth"]] || pipelineMode
}
```
### Phase 2: Create Team + Spawn Teammates
```javascript
const teamName = "ultra-analyze"
const sessionSlug = taskDescription.slice(0, 30).replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, '-')
const sessionDate = new Date().toISOString().slice(0, 10)
const sessionFolder = `.workflow/.team/UAN-${sessionSlug}-${sessionDate}`
Bash(`mkdir -p "${sessionFolder}/explorations" "${sessionFolder}/analyses" "${sessionFolder}/discussions"`)
// 初始化 shared memory
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify({
explorations: [],
analyses: [],
discussions: [],
synthesis: null,
decision_trail: [],
current_understanding: {
established: [],
clarified: [],
key_insights: []
}
}, null, 2))
// 初始化 discussion.md
Write(`${sessionFolder}/discussion.md`, `# Analysis Discussion
## Session Metadata
- **ID**: UAN-${sessionSlug}-${sessionDate}
- **Topic**: ${taskDescription}
- **Started**: ${new Date().toISOString()}
- **Dimensions**: ${dimensions.join(', ')}
- **Pipeline**: ${pipelineMode}
## User Context
- **Focus Areas**: ${dimensions.join(', ')}
- **Analysis Depth**: ${pipelineMode}
## Initial Understanding
- **Dimensions**: ${dimensions.join(', ')}
- **Scope**: ${taskDescription}
## Discussion Timeline
`)
TeamCreate({ team_name: teamName })
// Spawn teammates (see SKILL.md Coordinator Spawn Template)
// Explorer, Analyst, Discussant, Synthesizer
```
### Phase 3: Create Task Chain
根据 pipelineMode 创建不同的任务链:
```javascript
// Read commands/dispatch.md for full implementation
Read("commands/dispatch.md")
```
**Quick Mode**:
```
EXPLORE-001 → ANALYZE-001 → SYNTH-001
```
**Standard Mode**:
```
[EXPLORE-001..N](parallel) → [ANALYZE-001..N](parallel) → DISCUSS-001 → SYNTH-001
```
**Deep Mode**:
```
[EXPLORE-001..N](parallel) → [ANALYZE-001..N](parallel) → DISCUSS-001 → [ANALYZE-fix] → DISCUSS-002 → ... → SYNTH-001
```
### Phase 4: Discussion Loop + Coordination
```javascript
// Read commands/monitor.md for full implementation
Read("commands/monitor.md")
```
| Received Message | Action |
|-----------------|--------|
| `exploration_ready` | 标记 EXPLORE complete → 解锁 ANALYZE |
| `analysis_ready` | 标记 ANALYZE complete → 解锁 DISCUSS 或 SYNTH |
| `discussion_processed` | 标记 DISCUSS complete → AskUser → 决定下一步 |
| `synthesis_ready` | 标记 SYNTH complete → 进入 Phase 5 |
| Worker: `error` | 评估严重性 → 重试或上报用户 |
**讨论循环逻辑** (Standard/Deep mode):
```javascript
let discussionRound = 0
const MAX_ROUNDS = pipelineMode === 'deep' ? 5 : 1
while (discussionRound < MAX_ROUNDS) {
// 等待 DISCUSS-N 完成
// AskUserQuestion: 同意继续 / 调整方向 / 分析完成 / 有具体问题
// 根据用户选择:
// 同意继续 → 创建 DISCUSS-(N+1)
// 调整方向 → 创建 ANALYZE-fix + DISCUSS-(N+1)
// 分析完成 → 退出循环,创建 SYNTH-001
// 有具体问题 → 创建 DISCUSS-(N+1) with questions
discussionRound++
}
```
### Phase 5: Report + Persist
```javascript
// 读取 shared memory 汇总结果
const memory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`))
const report = {
mode: pipelineMode,
topic: taskDescription,
explorations_count: memory.explorations?.length || 0,
analyses_count: memory.analyses?.length || 0,
discussion_rounds: memory.discussions?.length || 0,
decisions_made: memory.decision_trail?.length || 0,
has_synthesis: !!memory.synthesis
}
mcp__ccw-tools__team_msg({
operation: "log", team: teamName, from: "coordinator",
to: "user", type: "pipeline_selected",
summary: `[coordinator] 分析完成: ${report.explorations_count}次探索, ${report.analyses_count}次分析, ${report.discussion_rounds}轮讨论`
})
SendMessage({
content: `## [coordinator] Analysis Complete\n\n${JSON.stringify(report, null, 2)}\n\n📄 Discussion: ${sessionFolder}/discussion.md\n📊 Conclusions: ${sessionFolder}/conclusions.json`,
summary: `[coordinator] Analysis complete: ${pipelineMode} mode`
})
// 询问下一步auto 模式跳过,默认关闭团队)
if (!autoYes) {
AskUserQuestion({
questions: [{
question: "分析流程已完成。下一步:",
header: "Next",
multiSelect: false,
options: [
{ label: "创建Issue", description: "基于结论创建 Issue" },
{ label: "生成任务", description: "启动 workflow-lite-plan 规划实施" },
{ label: "导出报告", description: "生成独立分析报告" },
{ label: "关闭团队", description: "关闭所有 teammate 并清理" }
]
}]
})
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| Teammate unresponsive | Send follow-up, 2x → respawn |
| Explorer finds nothing | Continue with limited context, note limitation |
| Discussion loop stuck >5 rounds | Force synthesis, offer continuation |
| CLI unavailable | Fallback chain: gemini → codex → manual |
| User timeout in discussion | Save state, show resume command |
| Max rounds reached | Force synthesis, offer continuation option |
| Session folder conflict | Append timestamp suffix |

View File

@@ -0,0 +1,222 @@
# Command: deepen
> 深入探索与补充分析。根据讨论类型执行针对性的代码探索或 CLI 分析。
## When to Use
- Phase 3 of Discussant
- 用户反馈已收集,需要深入处理
- 每个 DISCUSS-* 任务触发一次
**Trigger conditions**:
- initial: 首轮讨论,汇总分析结果
- deepen: 继续深入当前方向
- direction-adjusted: 方向调整后重新分析
- specific-questions: 回答用户具体问题
## Strategy
### Delegation Mode
**Mode**: Mixed简单汇总内联深入探索用 subagent/CLI
### Decision Logic
```javascript
function selectDeepenStrategy(discussType, complexity) {
const strategies = {
'initial': {
mode: 'inline',
description: 'Summarize all analysis results into discussion format'
},
'deepen': {
mode: complexity === 'High' ? 'cli' : 'subagent',
description: 'Further exploration in current direction'
},
'direction-adjusted': {
mode: 'cli',
description: 'Re-analyze from new perspective'
},
'specific-questions': {
mode: 'subagent',
description: 'Targeted exploration to answer questions'
}
}
return strategies[discussType] || strategies['initial']
}
```
## Execution Steps
### Step 1: Strategy Selection
```javascript
const strategy = selectDeepenStrategy(discussType, assessComplexity(userFeedback))
```
### Step 2: Execute by Type
#### Initial Discussion
```javascript
function processInitialDiscussion() {
// 汇总所有分析结果
const summary = {
perspectives_analyzed: allAnalyses.map(a => a.perspective),
total_insights: currentInsights.length,
total_findings: currentFindings.length,
convergent_themes: identifyConvergentThemes(allAnalyses),
conflicting_views: identifyConflicts(allAnalyses),
top_discussion_points: discussionPoints.slice(0, 5),
open_questions: openQuestions.slice(0, 5)
}
roundContent.updated_understanding.new_insights = summary.convergent_themes
roundContent.new_findings = currentFindings.slice(0, 10)
roundContent.new_questions = openQuestions.slice(0, 5)
}
function identifyConvergentThemes(analyses) {
// 跨视角找共同主题
const allInsights = analyses.flatMap(a =>
(a.key_insights || []).map(i => typeof i === 'string' ? i : i.insight)
)
// 简单去重 + 聚合
return [...new Set(allInsights)].slice(0, 5)
}
function identifyConflicts(analyses) {
// 识别视角间的矛盾
return [] // 由实际分析结果决定
}
```
#### Deepen Discussion
```javascript
function processDeepenDiscussion() {
// 在当前方向上进一步探索
Task({
subagent_type: "cli-explore-agent",
run_in_background: false,
description: `Deepen exploration: ${topic} (round ${round})`,
prompt: `
## Context
Topic: ${topic}
Round: ${round}
Previous findings: ${currentFindings.slice(0, 5).join('; ')}
Open questions: ${openQuestions.slice(0, 3).join('; ')}
## MANDATORY FIRST STEPS
1. Focus on open questions from previous analysis
2. Search for specific patterns mentioned in findings
3. Look for edge cases and exceptions
## Exploration Focus
- Deepen understanding of confirmed patterns
- Investigate open questions
- Find additional evidence for uncertain insights
## Output
Write to: ${sessionFolder}/discussions/deepen-${discussNum}.json
Schema: {new_findings, answered_questions, remaining_questions, evidence}
`
})
// 读取深入探索结果
let deepenResult = {}
try {
deepenResult = JSON.parse(Read(`${sessionFolder}/discussions/deepen-${discussNum}.json`))
} catch {}
roundContent.updated_understanding.new_insights = deepenResult.new_findings || []
roundContent.new_findings = deepenResult.new_findings || []
roundContent.new_questions = deepenResult.remaining_questions || []
}
```
#### Direction Adjusted
```javascript
function processDirectionAdjusted() {
// 方向调整后,通过 CLI 重新分析
Bash({
command: `ccw cli -p "PURPOSE: Re-analyze '${topic}' with adjusted focus on '${userFeedback}'
Success: New insights from adjusted direction
PREVIOUS ANALYSIS CONTEXT:
- Previous insights: ${currentInsights.slice(0, 5).map(i => typeof i === 'string' ? i : i.insight).join('; ')}
- Direction change reason: User requested focus on '${userFeedback}'
TASK:
• Re-evaluate findings from new perspective
• Identify what changes with adjusted focus
• Find new patterns relevant to adjusted direction
• Note what previous findings remain valid
MODE: analysis
CONTEXT: @**/* | Topic: ${topic}
EXPECTED: Updated analysis with: validated findings, new insights, invalidated assumptions
CONSTRAINTS: Focus on ${userFeedback}
" --tool gemini --mode analysis`,
run_in_background: true
})
// ⚠️ STOP: Wait for CLI callback
roundContent.updated_understanding.corrected = ['Direction adjusted per user request']
roundContent.updated_understanding.new_insights = [] // From CLI result
}
```
#### Specific Questions
```javascript
function processSpecificQuestions() {
// 针对用户问题进行探索
Task({
subagent_type: "cli-explore-agent",
run_in_background: false,
description: `Answer questions: ${topic}`,
prompt: `
## Context
Topic: ${topic}
User questions: ${userFeedback}
Known findings: ${currentFindings.slice(0, 5).join('; ')}
## MANDATORY FIRST STEPS
1. Search for code related to user's questions
2. Trace execution paths relevant to questions
3. Check configuration and environment factors
## Output
Write to: ${sessionFolder}/discussions/questions-${discussNum}.json
Schema: {answers: [{question, answer, evidence, confidence}], follow_up_questions}
`
})
let questionResult = {}
try {
questionResult = JSON.parse(Read(`${sessionFolder}/discussions/questions-${discussNum}.json`))
} catch {}
roundContent.updated_understanding.new_insights =
(questionResult.answers || []).map(a => `Q: ${a.question} → A: ${a.answer}`)
roundContent.new_questions = questionResult.follow_up_questions || []
}
```
### Step 3: Result Processing
```javascript
// 结果已写入 roundContent由 role.md Phase 4 处理
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| cli-explore-agent fails | Use existing analysis results, note limitation |
| CLI timeout | Report partial results |
| No previous analyses | Process as initial with empty context |
| User feedback unparseable | Treat as 'deepen' type |

View File

@@ -0,0 +1,273 @@
# Role: discussant
讨论处理者。根据 coordinator 传递的用户反馈,执行方向调整、深入探索或补充分析,更新讨论时间线。
## Role Identity
- **Name**: `discussant`
- **Task Prefix**: `DISCUSS-*`
- **Responsibility**: Analysis + Exploration讨论处理
- **Communication**: SendMessage to coordinator only
- **Output Tag**: `[discussant]`
## Role Boundaries
### MUST
- 仅处理 `DISCUSS-*` 前缀的任务
- 所有输出必须带 `[discussant]` 标识
- 仅通过 SendMessage 与 coordinator 通信
- 基于用户反馈和已有分析结果执行深入探索
- 将讨论结果写入 shared-memory.json 的 `discussions` 字段
- 更新 discussion.md 的讨论时间线
### MUST NOT
- ❌ 直接与用户交互AskUserQuestion 由 coordinator 驱动)
- ❌ 生成最终结论(属于 synthesizer
- ❌ 为其他角色创建任务
- ❌ 直接与其他 worker 通信
- ❌ 修改源代码
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `discussion_processed` | discussant → coordinator | 讨论处理完成 | 包含更新的理解和新发现 |
| `error` | discussant → coordinator | 处理失败 | 阻塞性错误 |
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `deepen` | [commands/deepen.md](commands/deepen.md) | Phase 3 | 深入探索与补充分析 |
### Subagent Capabilities
| Agent Type | Used By | Purpose |
|------------|---------|---------|
| `cli-explore-agent` | deepen.md | 针对性代码库探索 |
### CLI Capabilities
| CLI Tool | Mode | Used By | Purpose |
|----------|------|---------|---------|
| `gemini` | analysis | deepen.md | 深入分析 |
## Execution (5-Phase)
### Phase 1: Task Discovery
```javascript
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('DISCUSS-') &&
t.owner === 'discussant' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return // idle
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Context Loading
```javascript
// 从任务描述中提取上下文
const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1]?.trim()
const topic = task.description.match(/topic:\s*(.+)/)?.[1]?.trim()
const round = parseInt(task.description.match(/round:\s*(\d+)/)?.[1] || '1')
const discussType = task.description.match(/type:\s*(.+)/)?.[1]?.trim() || 'initial'
const userFeedback = task.description.match(/user_feedback:\s*(.+)/)?.[1]?.trim() || ''
// 读取 shared memory
let sharedMemory = {}
try { sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) } catch {}
// 读取已有分析结果
const analysisFiles = Glob({ pattern: `${sessionFolder}/analyses/*.json` })
const allAnalyses = analysisFiles.map(f => {
try { return JSON.parse(Read(f)) } catch { return null }
}).filter(Boolean)
// 读取已有探索结果
const explorationFiles = Glob({ pattern: `${sessionFolder}/explorations/*.json` })
const allExplorations = explorationFiles.map(f => {
try { return JSON.parse(Read(f)) } catch { return null }
}).filter(Boolean)
// 聚合当前理解
const currentFindings = allAnalyses.flatMap(a => a.key_findings || [])
const currentInsights = allAnalyses.flatMap(a => a.key_insights || [])
const openQuestions = allAnalyses.flatMap(a => a.open_questions || [])
const discussionPoints = allAnalyses.flatMap(a => a.discussion_points || [])
```
### Phase 3: Discussion Processing
```javascript
// Read commands/deepen.md for full implementation
Read("commands/deepen.md")
```
**根据 discussType 选择处理策略**:
```javascript
const discussNum = task.subject.match(/DISCUSS-(\d+)/)?.[1] || '001'
const outputPath = `${sessionFolder}/discussions/discussion-round-${discussNum}.json`
switch (discussType) {
case 'initial':
// 首轮讨论:汇总所有分析结果,生成讨论摘要
processInitialDiscussion()
break
case 'deepen':
// 继续深入:在当前方向上进一步探索
processDeepenDiscussion()
break
case 'direction-adjusted':
// 方向调整:基于新方向重新组织发现
processDirectionAdjusted()
break
case 'specific-questions':
// 具体问题:针对用户问题进行分析
processSpecificQuestions()
break
}
```
### Phase 4: Update Discussion Timeline
```javascript
// 构建讨论轮次内容
const roundContent = {
round,
type: discussType,
user_feedback: userFeedback,
updated_understanding: {
confirmed: [], // 确认的假设
corrected: [], // 纠正的假设
new_insights: [] // 新发现
},
new_findings: [],
new_questions: [],
timestamp: new Date().toISOString()
}
Write(outputPath, JSON.stringify(roundContent, null, 2))
// 更新 discussion.md
const discussionMdContent = `
### Round ${round + 1} - Discussion (${new Date().toISOString()})
#### Type
${discussType}
#### User Input
${userFeedback || '(Initial discussion round)'}
#### Updated Understanding
${roundContent.updated_understanding.confirmed.length > 0
? `**Confirmed**: ${roundContent.updated_understanding.confirmed.map(c => `\n- ✅ ${c}`).join('')}` : ''}
${roundContent.updated_understanding.corrected.length > 0
? `**Corrected**: ${roundContent.updated_understanding.corrected.map(c => `\n- 🔄 ${c}`).join('')}` : ''}
${roundContent.updated_understanding.new_insights.length > 0
? `**New Insights**: ${roundContent.updated_understanding.new_insights.map(i => `\n- 💡 ${i}`).join('')}` : ''}
#### New Findings
${(roundContent.new_findings || []).map(f => `- ${f}`).join('\n') || '(None)'}
#### Open Questions
${(roundContent.new_questions || []).map(q => `- ${q}`).join('\n') || '(None)'}
`
const currentDiscussion = Read(`${sessionFolder}/discussion.md`)
Write(`${sessionFolder}/discussion.md`, currentDiscussion + discussionMdContent)
```
### Phase 5: Report to Coordinator
```javascript
// 更新 shared memory
sharedMemory.discussions = sharedMemory.discussions || []
sharedMemory.discussions.push({
id: `discussion-round-${discussNum}`,
round,
type: discussType,
new_insight_count: roundContent.updated_understanding.new_insights?.length || 0,
corrected_count: roundContent.updated_understanding.corrected?.length || 0,
timestamp: new Date().toISOString()
})
// 更新 current_understanding
sharedMemory.current_understanding = sharedMemory.current_understanding || { established: [], clarified: [], key_insights: [] }
sharedMemory.current_understanding.established.push(...(roundContent.updated_understanding.confirmed || []))
sharedMemory.current_understanding.clarified.push(...(roundContent.updated_understanding.corrected || []))
sharedMemory.current_understanding.key_insights.push(...(roundContent.updated_understanding.new_insights || []))
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
const resultSummary = `Round ${round}: ${roundContent.updated_understanding.new_insights?.length || 0} 新洞察, ${roundContent.updated_understanding.corrected?.length || 0} 纠正`
mcp__ccw-tools__team_msg({
operation: "log",
team: teamName,
from: "discussant",
to: "coordinator",
type: "discussion_processed",
summary: `[discussant] ${resultSummary}`,
ref: outputPath
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## [discussant] Discussion Round ${round} Results
**Task**: ${task.subject}
**Type**: ${discussType}
### Summary
${resultSummary}
### Key Updates
${roundContent.updated_understanding.new_insights?.slice(0, 3).map(i => `- 💡 ${i}`).join('\n') || '(No new insights)'}
${roundContent.updated_understanding.corrected?.slice(0, 3).map(c => `- 🔄 ${c}`).join('\n') || ''}
### Output
${outputPath}`,
summary: `[discussant] DISCUSS complete: ${resultSummary}`
})
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for next task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('DISCUSS-') &&
t.owner === 'discussant' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task → back to Phase 1
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No DISCUSS-* tasks available | Idle, wait for coordinator assignment |
| No analysis results found | Report empty discussion, notify coordinator |
| CLI tool unavailable | Use existing analysis results for discussion |
| User feedback unclear | Process as 'deepen' type, note ambiguity |
| Session folder missing | Error to coordinator |

View File

@@ -0,0 +1,194 @@
# Command: explore
> cli-explore-agent 并行代码库探索。根据话题和视角,通过 subagent 收集代码库上下文。
## When to Use
- Phase 3 of Explorer
- 需要收集代码库上下文供后续分析
- 每个 EXPLORE-* 任务触发一次
**Trigger conditions**:
- Explorer Phase 2 完成后
- 任务包含明确的 perspective 和 dimensions
## Strategy
### Delegation Mode
**Mode**: Subagentcli-explore-agent 执行实际探索)
### Decision Logic
```javascript
// 根据 perspective 确定探索策略
function buildExplorationStrategy(perspective, dimensions, topic) {
const strategies = {
'general': {
focus: 'Overall codebase structure and patterns',
searches: [topic, ...dimensions],
depth: 'broad'
},
'technical': {
focus: 'Implementation details, code patterns, technical feasibility',
searches: [`${topic} implementation`, `${topic} pattern`, `${topic} handler`],
depth: 'medium'
},
'architectural': {
focus: 'System design, module boundaries, component interactions',
searches: [`${topic} module`, `${topic} service`, `${topic} interface`],
depth: 'broad'
},
'business': {
focus: 'Business logic, domain models, value flows',
searches: [`${topic} model`, `${topic} domain`, `${topic} workflow`],
depth: 'medium'
},
'domain_expert': {
focus: 'Domain-specific patterns, standards compliance, best practices',
searches: [`${topic} standard`, `${topic} convention`, `${topic} best practice`],
depth: 'deep'
}
}
return strategies[perspective] || strategies['general']
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
const strategy = buildExplorationStrategy(perspective, dimensions, topic)
const exploreNum = task.subject.match(/EXPLORE-(\d+)/)?.[1] || '001'
const outputPath = `${sessionFolder}/explorations/exploration-${exploreNum}.json`
```
### Step 2: Execute Exploration
```javascript
Task({
subagent_type: "cli-explore-agent",
run_in_background: false,
description: `Explore codebase: ${topic} (${perspective})`,
prompt: `
## Analysis Context
Topic: ${topic}
Perspective: ${perspective}${strategy.focus}
Dimensions: ${dimensions.join(', ')}
Session: ${sessionFolder}
## MANDATORY FIRST STEPS
1. Run: ccw tool exec get_modules_by_depth '{}'
2. Execute searches: ${strategy.searches.map(s => `"${s}"`).join(', ')}
3. Read: .workflow/project-tech.json (if exists)
## Exploration Focus (${perspective} angle)
- **Depth**: ${strategy.depth}
- **Focus**: ${strategy.focus}
${dimensions.map(d => `- ${d}: Identify relevant code patterns, structures, and relationships`).join('\n')}
## Search Strategy
${strategy.searches.map((s, i) => `${i + 1}. Search for: "${s}" — find related files, functions, types`).join('\n')}
## Additional Exploration
- Identify entry points related to the topic
- Map dependencies between relevant modules
- Note any configuration or environment dependencies
- Look for test files that reveal expected behavior
## Output
Write findings to: ${outputPath}
Schema:
{
"perspective": "${perspective}",
"relevant_files": [
{"path": "string", "relevance": "high|medium|low", "summary": "what this file does"}
],
"patterns": ["pattern descriptions found in codebase"],
"key_findings": ["important discoveries"],
"module_map": {"module_name": ["related_files"]},
"questions_for_analysis": ["questions that need deeper analysis"],
"_metadata": {
"agent": "cli-explore-agent",
"perspective": "${perspective}",
"search_queries": ${JSON.stringify(strategy.searches)},
"timestamp": "ISO string"
}
}
`
})
```
### Step 3: Result Processing
```javascript
// 验证输出文件
let result = {}
try {
result = JSON.parse(Read(outputPath))
} catch {
// Fallback: ACE search
const aceResults = mcp__ace-tool__search_context({
project_root_path: ".",
query: `${topic} ${perspective}`
})
result = {
perspective,
relevant_files: [],
patterns: [],
key_findings: [`ACE fallback: ${aceResults?.summary || 'No results'}`],
questions_for_analysis: [`What is the ${perspective} perspective on ${topic}?`],
_metadata: {
agent: 'ace-fallback',
perspective,
timestamp: new Date().toISOString()
}
}
Write(outputPath, JSON.stringify(result, null, 2))
}
// 质量验证
const quality = {
has_files: (result.relevant_files?.length || 0) > 0,
has_findings: (result.key_findings?.length || 0) > 0,
has_patterns: (result.patterns?.length || 0) > 0
}
if (!quality.has_files && !quality.has_findings) {
// 补充搜索
const supplementary = mcp__ace-tool__search_context({
project_root_path: ".",
query: topic
})
// Merge supplementary results
}
```
## Output Format
```json
{
"perspective": "technical",
"relevant_files": [
{"path": "src/auth/handler.ts", "relevance": "high", "summary": "Authentication request handler"}
],
"patterns": ["Repository pattern used for data access", "Middleware chain for auth"],
"key_findings": ["JWT tokens stored in HTTP-only cookies", "Rate limiting at gateway level"],
"module_map": {"auth": ["src/auth/handler.ts", "src/auth/middleware.ts"]},
"questions_for_analysis": ["Is the token refresh mechanism secure?"],
"_metadata": {"agent": "cli-explore-agent", "perspective": "technical", "timestamp": "..."}
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| cli-explore-agent unavailable | Fall back to ACE search + Grep |
| Agent produces no output file | Create minimal result with ACE fallback |
| Agent timeout | Use partial results if available |
| Invalid JSON output | Attempt repair, fall back to raw text extraction |
| Session folder missing | Create directory, continue |

View File

@@ -0,0 +1,243 @@
# Role: explorer
代码库探索者。通过 cli-explore-agent 多角度并行探索代码库,收集结构化上下文供后续分析使用。
## Role Identity
- **Name**: `explorer`
- **Task Prefix**: `EXPLORE-*`
- **Responsibility**: Orchestration代码库探索编排
- **Communication**: SendMessage to coordinator only
- **Output Tag**: `[explorer]`
## Role Boundaries
### MUST
- 仅处理 `EXPLORE-*` 前缀的任务
- 所有输出必须带 `[explorer]` 标识
- 仅通过 SendMessage 与 coordinator 通信
- 严格在代码库探索职责范围内工作
- 将探索结果写入 shared-memory.json 的 `explorations` 字段
### MUST NOT
- ❌ 执行深度分析(属于 analyst
- ❌ 处理用户反馈(属于 discussant
- ❌ 生成结论或建议(属于 synthesizer
- ❌ 为其他角色创建任务
- ❌ 直接与其他 worker 通信
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `exploration_ready` | explorer → coordinator | 探索完成 | 包含发现的文件、模式、关键发现 |
| `error` | explorer → coordinator | 探索失败 | 阻塞性错误 |
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `explore` | [commands/explore.md](commands/explore.md) | Phase 3 | cli-explore-agent 并行探索 |
### Subagent Capabilities
| Agent Type | Used By | Purpose |
|------------|---------|---------|
| `cli-explore-agent` | explore.md | 多角度代码库探索 |
### CLI Capabilities
> Explorer 不直接使用 CLI 分析工具(通过 cli-explore-agent 间接使用)
## Execution (5-Phase)
### Phase 1: Task Discovery
```javascript
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('EXPLORE-') &&
t.owner === 'explorer' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return // idle
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Context & Scope Assessment
```javascript
// 从任务描述中提取上下文
const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1]?.trim()
const topic = task.description.match(/topic:\s*(.+)/)?.[1]?.trim()
const perspective = task.description.match(/perspective:\s*(.+)/)?.[1]?.trim() || 'general'
const dimensions = (task.description.match(/dimensions:\s*(.+)/)?.[1]?.trim() || 'general').split(', ')
// 读取 shared memory
let sharedMemory = {}
try { sharedMemory = JSON.parse(Read(`${sessionFolder}/shared-memory.json`)) } catch {}
// 评估探索范围
const exploreNum = task.subject.match(/EXPLORE-(\d+)/)?.[1] || '001'
```
### Phase 3: Codebase Exploration
```javascript
// Read commands/explore.md for full cli-explore-agent implementation
Read("commands/explore.md")
```
**核心策略**: 通过 cli-explore-agent 执行代码库探索
```javascript
Task({
subagent_type: "cli-explore-agent",
run_in_background: false,
description: `Explore codebase: ${topic} (${perspective})`,
prompt: `
## Analysis Context
Topic: ${topic}
Perspective: ${perspective}
Dimensions: ${dimensions.join(', ')}
Session: ${sessionFolder}
## MANDATORY FIRST STEPS
1. Run: ccw tool exec get_modules_by_depth '{}'
2. Execute relevant searches based on topic keywords
3. Read: .workflow/project-tech.json (if exists)
## Exploration Focus (${perspective} angle)
${dimensions.map(d => `- ${d}: Identify relevant code patterns and structures`).join('\n')}
## Output
Write findings to: ${sessionFolder}/explorations/exploration-${exploreNum}.json
Schema: {
perspective: "${perspective}",
relevant_files: [{path, relevance, summary}],
patterns: [string],
key_findings: [string],
questions_for_analysis: [string],
_metadata: {agent: "cli-explore-agent", timestamp}
}
`
})
```
### Phase 4: Result Validation
```javascript
// 验证探索结果
const outputPath = `${sessionFolder}/explorations/exploration-${exploreNum}.json`
let explorationResult = {}
try {
explorationResult = JSON.parse(Read(outputPath))
} catch {
// Agent 未写入文件,使用空结果
explorationResult = {
perspective,
relevant_files: [],
patterns: [],
key_findings: ['Exploration produced no structured output'],
questions_for_analysis: [],
_metadata: { agent: 'cli-explore-agent', timestamp: new Date().toISOString(), status: 'partial' }
}
Write(outputPath, JSON.stringify(explorationResult, null, 2))
}
// 基本质量检查
const hasFiles = explorationResult.relevant_files?.length > 0
const hasFindings = explorationResult.key_findings?.length > 0
if (!hasFiles && !hasFindings) {
// 探索结果为空,尝试 ACE 搜索兜底
const aceResults = mcp__ace-tool__search_context({
project_root_path: ".",
query: topic
})
// 补充到结果中
}
```
### Phase 5: Report to Coordinator
```javascript
// 更新 shared memory
sharedMemory.explorations = sharedMemory.explorations || []
sharedMemory.explorations.push({
id: `exploration-${exploreNum}`,
perspective,
file_count: explorationResult.relevant_files?.length || 0,
finding_count: explorationResult.key_findings?.length || 0,
timestamp: new Date().toISOString()
})
Write(`${sessionFolder}/shared-memory.json`, JSON.stringify(sharedMemory, null, 2))
const resultSummary = `${perspective} 视角: ${explorationResult.relevant_files?.length || 0} 个相关文件, ${explorationResult.key_findings?.length || 0} 个发现`
mcp__ccw-tools__team_msg({
operation: "log",
team: teamName,
from: "explorer",
to: "coordinator",
type: "exploration_ready",
summary: `[explorer] ${resultSummary}`,
ref: outputPath
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## [explorer] Exploration Results
**Task**: ${task.subject}
**Perspective**: ${perspective}
**Status**: ${hasFiles || hasFindings ? 'Findings Available' : 'Limited Results'}
### Summary
${resultSummary}
### Top Findings
${(explorationResult.key_findings || []).slice(0, 5).map(f => `- ${f}`).join('\n')}
### Questions for Analysis
${(explorationResult.questions_for_analysis || []).slice(0, 3).map(q => `- ${q}`).join('\n')}
### Output
${outputPath}`,
summary: `[explorer] EXPLORE complete: ${resultSummary}`
})
TaskUpdate({ taskId: task.id, status: 'completed' })
// Check for next task
const nextTasks = TaskList().filter(t =>
t.subject.startsWith('EXPLORE-') &&
t.owner === 'explorer' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (nextTasks.length > 0) {
// Continue with next task → back to Phase 1
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No EXPLORE-* tasks available | Idle, wait for coordinator assignment |
| cli-explore-agent fails | Fall back to ACE search + Grep inline |
| Exploration scope too broad | Narrow to topic keywords, report partial |
| Agent timeout | Use partial results, note incomplete |
| Session folder missing | Create it, warn coordinator |

View File

@@ -0,0 +1,255 @@
# Command: synthesize
> 跨视角整合。从所有探索、分析、讨论结果中提取主题、解决冲突、生成最终结论和建议。
## When to Use
- Phase 3 of Synthesizer
- 所有探索、分析、讨论已完成
- 每个 SYNTH-* 任务触发一次
**Trigger conditions**:
- 讨论循环结束后(用户选择"分析完成"或达到最大轮次)
- Quick 模式下分析完成后直接触发
## Strategy
### Delegation Mode
**Mode**: Inline纯整合不调用外部工具
### Decision Logic
```javascript
function buildSynthesisStrategy(explorationCount, analysisCount, discussionCount) {
if (analysisCount <= 1 && discussionCount === 0) {
return 'simple' // Quick mode: 单视角直接总结
}
if (discussionCount > 2) {
return 'deep' // Deep mode: 多轮讨论需要追踪演进
}
return 'standard' // Standard: 多视角交叉整合
}
```
## Execution Steps
### Step 1: Context Preparation
```javascript
const strategy = buildSynthesisStrategy(
allExplorations.length, allAnalyses.length, allDiscussions.length
)
// 提取所有洞察
const allInsights = allAnalyses.flatMap(a =>
(a.key_insights || []).map(i => ({
...(typeof i === 'string' ? { insight: i } : i),
perspective: a.perspective
}))
)
// 提取所有发现
const allFindings = allAnalyses.flatMap(a =>
(a.key_findings || []).map(f => ({
...(typeof f === 'string' ? { finding: f } : f),
perspective: a.perspective
}))
)
// 提取所有建议
const allRecommendations = allAnalyses.flatMap(a =>
(a.recommendations || []).map(r => ({
...(typeof r === 'string' ? { action: r } : r),
perspective: a.perspective
}))
)
// 提取讨论演进
const discussionEvolution = allDiscussions.map(d => ({
round: d.round,
type: d.type,
confirmed: d.updated_understanding?.confirmed || [],
corrected: d.updated_understanding?.corrected || [],
new_insights: d.updated_understanding?.new_insights || []
}))
```
### Step 2: Cross-Perspective Synthesis
```javascript
// 1. Theme Extraction — 跨视角共同主题
const themes = extractThemes(allInsights)
// 2. Conflict Resolution — 视角间矛盾
const conflicts = identifyConflicts(allAnalyses)
// 3. Evidence Consolidation — 证据汇总
const consolidatedEvidence = consolidateEvidence(allFindings)
// 4. Recommendation Prioritization — 建议优先级排序
const prioritizedRecommendations = prioritizeRecommendations(allRecommendations)
// 5. Decision Trail Integration — 决策追踪整合
const decisionSummary = summarizeDecisions(decisionTrail)
function extractThemes(insights) {
// 按关键词聚类,识别跨视角共同主题
const themeMap = {}
for (const insight of insights) {
const text = insight.insight || insight
// 简单聚类:相似洞察归为同一主题
const key = text.slice(0, 30)
if (!themeMap[key]) {
themeMap[key] = { theme: text, perspectives: [], count: 0 }
}
themeMap[key].perspectives.push(insight.perspective)
themeMap[key].count++
}
return Object.values(themeMap)
.sort((a, b) => b.count - a.count)
.slice(0, 10)
}
function identifyConflicts(analyses) {
// 识别不同视角间的矛盾发现
const conflicts = []
for (let i = 0; i < analyses.length; i++) {
for (let j = i + 1; j < analyses.length; j++) {
// 比较两个视角的发现是否矛盾
// 实际实现中需要语义比较
}
}
return conflicts
}
function consolidateEvidence(findings) {
// 去重并按文件引用聚合
const byFile = {}
for (const f of findings) {
const ref = f.file_ref || f.finding
if (!byFile[ref]) byFile[ref] = []
byFile[ref].push(f)
}
return byFile
}
function prioritizeRecommendations(recommendations) {
const priorityOrder = { high: 0, medium: 1, low: 2 }
return recommendations
.sort((a, b) => (priorityOrder[a.priority] || 2) - (priorityOrder[b.priority] || 2))
.slice(0, 10)
}
function summarizeDecisions(trail) {
return trail.map(d => ({
round: d.round,
decision: d.decision,
context: d.context,
impact: d.impact || 'Shaped analysis direction'
}))
}
```
### Step 3: Build Conclusions
```javascript
const conclusions = {
session_id: sessionFolder.split('/').pop(),
topic,
completed: new Date().toISOString(),
total_rounds: allDiscussions.length,
strategy_used: strategy,
summary: generateSummary(themes, allFindings, allDiscussions),
key_conclusions: themes.slice(0, 7).map(t => ({
point: t.theme,
evidence: t.perspectives.join(', ') + ' perspectives',
confidence: t.count >= 3 ? 'high' : t.count >= 2 ? 'medium' : 'low'
})),
recommendations: prioritizedRecommendations.map(r => ({
action: r.action,
rationale: r.rationale || 'Based on analysis findings',
priority: r.priority || 'medium',
source_perspective: r.perspective
})),
open_questions: allAnalyses
.flatMap(a => a.open_questions || [])
.filter((q, i, arr) => arr.indexOf(q) === i)
.slice(0, 5),
follow_up_suggestions: generateFollowUps(conclusions),
decision_trail: decisionSummary,
cross_perspective_synthesis: {
convergent_themes: themes.filter(t => t.perspectives.length > 1),
conflicts_resolved: conflicts,
unique_contributions: allAnalyses.map(a => ({
perspective: a.perspective,
unique_insights: (a.key_insights || []).slice(0, 2)
}))
},
_metadata: {
explorations: allExplorations.length,
analyses: allAnalyses.length,
discussions: allDiscussions.length,
decisions: decisionTrail.length,
synthesis_strategy: strategy
}
}
function generateSummary(themes, findings, discussions) {
const topThemes = themes.slice(0, 3).map(t => t.theme).join('; ')
const roundCount = discussions.length
return `Analysis of "${topic}" identified ${themes.length} key themes across ${allAnalyses.length} perspective(s) and ${roundCount} discussion round(s). Top themes: ${topThemes}`
}
function generateFollowUps(conclusions) {
const suggestions = []
if ((conclusions.open_questions || []).length > 2) {
suggestions.push({ type: 'deeper-analysis', summary: 'Further analysis needed for open questions' })
}
if ((conclusions.recommendations || []).some(r => r.priority === 'high')) {
suggestions.push({ type: 'issue-creation', summary: 'Create issues for high-priority recommendations' })
}
suggestions.push({ type: 'implementation-plan', summary: 'Generate implementation plan from recommendations' })
return suggestions
}
```
## Output Format
```json
{
"session_id": "UAN-auth-analysis-2026-02-18",
"topic": "认证架构优化",
"completed": "2026-02-18T...",
"total_rounds": 2,
"summary": "Analysis identified 5 key themes...",
"key_conclusions": [
{"point": "JWT stateless approach is sound", "evidence": "technical, architectural", "confidence": "high"}
],
"recommendations": [
{"action": "Add rate limiting", "rationale": "Prevent brute force", "priority": "high"}
],
"open_questions": ["Token rotation strategy?"],
"decision_trail": [
{"round": 1, "decision": "Focus on security", "context": "User preference"}
]
}
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No analyses available | Synthesize from explorations only |
| Single perspective only | Generate focused synthesis without cross-perspective |
| Irreconcilable conflicts | Present both sides with trade-off analysis |
| Empty discussion rounds | Skip discussion evolution, focus on analysis results |
| Shared memory corrupted | Rebuild from individual JSON files |

View File

@@ -0,0 +1,225 @@
# Role: synthesizer
综合整合者。跨视角整合所有探索、分析、讨论结果,生成最终结论、建议和决策追踪。
## Role Identity
- **Name**: `synthesizer`
- **Task Prefix**: `SYNTH-*`
- **Responsibility**: Read-only analysis综合结论
- **Communication**: SendMessage to coordinator only
- **Output Tag**: `[synthesizer]`
## Role Boundaries
### MUST
- 仅处理 `SYNTH-*` 前缀的任务
- 所有输出必须带 `[synthesizer]` 标识
- 仅通过 SendMessage 与 coordinator 通信
- 整合所有角色的产出生成最终结论
- 将综合结果写入 shared-memory.json 的 `synthesis` 字段
- 更新 discussion.md 的结论部分
### MUST NOT
- ❌ 执行新的代码探索或 CLI 分析
- ❌ 与用户直接交互
- ❌ 为其他角色创建任务
- ❌ 直接与其他 worker 通信
- ❌ 修改源代码
## Message Types
| Type | Direction | Trigger | Description |
|------|-----------|---------|-------------|
| `synthesis_ready` | synthesizer → coordinator | 综合完成 | 包含最终结论和建议 |
| `error` | synthesizer → coordinator | 综合失败 | 阻塞性错误 |
## Toolbox
### Available Commands
| Command | File | Phase | Description |
|---------|------|-------|-------------|
| `synthesize` | [commands/synthesize.md](commands/synthesize.md) | Phase 3 | 跨视角整合 |
### Subagent Capabilities
> Synthesizer 不使用 subagent
### CLI Capabilities
> Synthesizer 不使用 CLI 工具(纯整合角色)
## Execution (5-Phase)
### Phase 1: Task Discovery
```javascript
const tasks = TaskList()
const myTasks = tasks.filter(t =>
t.subject.startsWith('SYNTH-') &&
t.owner === 'synthesizer' &&
t.status === 'pending' &&
t.blockedBy.length === 0
)
if (myTasks.length === 0) return
const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })
```
### Phase 2: Context Loading + Shared Memory Read
```javascript
const sessionFolder = task.description.match(/session:\s*(.+)/)?.[1]?.trim()
const topic = task.description.match(/topic:\s*(.+)/)?.[1]?.trim()
const memoryPath = `${sessionFolder}/shared-memory.json`
let sharedMemory = {}
try { sharedMemory = JSON.parse(Read(memoryPath)) } catch {}
// Read all explorations
const explorationFiles = Glob({ pattern: `${sessionFolder}/explorations/*.json` })
const allExplorations = explorationFiles.map(f => {
try { return JSON.parse(Read(f)) } catch { return null }
}).filter(Boolean)
// Read all analyses
const analysisFiles = Glob({ pattern: `${sessionFolder}/analyses/*.json` })
const allAnalyses = analysisFiles.map(f => {
try { return JSON.parse(Read(f)) } catch { return null }
}).filter(Boolean)
// Read all discussion rounds
const discussionFiles = Glob({ pattern: `${sessionFolder}/discussions/discussion-round-*.json` })
const allDiscussions = discussionFiles.map(f => {
try { return JSON.parse(Read(f)) } catch { return null }
}).filter(Boolean)
// Read decision trail
const decisionTrail = sharedMemory.decision_trail || []
const currentUnderstanding = sharedMemory.current_understanding || {}
```
### Phase 3: Synthesis Execution
```javascript
// Read commands/synthesize.md for full implementation
Read("commands/synthesize.md")
```
### Phase 4: Write Conclusions + Update discussion.md
```javascript
const synthNum = task.subject.match(/SYNTH-(\d+)/)?.[1] || '001'
const conclusionsPath = `${sessionFolder}/conclusions.json`
// 写入 conclusions.json
Write(conclusionsPath, JSON.stringify(conclusions, null, 2))
// 更新 discussion.md — 结论部分
const conclusionsMd = `
## Conclusions
### Summary
${conclusions.summary}
### Key Conclusions
${conclusions.key_conclusions.map((c, i) => `${i + 1}. **${c.point}** (Confidence: ${c.confidence})
- Evidence: ${c.evidence}`).join('\n')}
### Recommendations
${conclusions.recommendations.map((r, i) => `${i + 1}. **[${r.priority}]** ${r.action}
- Rationale: ${r.rationale}`).join('\n')}
### Remaining Questions
${(conclusions.open_questions || []).map(q => `- ${q}`).join('\n') || '(None)'}
## Decision Trail
### Critical Decisions
${decisionTrail.map(d => `- **Round ${d.round}**: ${d.decision}${d.context}`).join('\n') || '(None)'}
## Current Understanding (Final)
### What We Established
${(currentUnderstanding.established || []).map(e => `- ${e}`).join('\n') || '(None)'}
### What Was Clarified/Corrected
${(currentUnderstanding.clarified || []).map(c => `- ${c}`).join('\n') || '(None)'}
### Key Insights
${(currentUnderstanding.key_insights || []).map(i => `- ${i}`).join('\n') || '(None)'}
## Session Statistics
- **Explorations**: ${allExplorations.length}
- **Analyses**: ${allAnalyses.length}
- **Discussion Rounds**: ${allDiscussions.length}
- **Decisions Made**: ${decisionTrail.length}
- **Completed**: ${new Date().toISOString()}
`
const currentDiscussion = Read(`${sessionFolder}/discussion.md`)
Write(`${sessionFolder}/discussion.md`, currentDiscussion + conclusionsMd)
```
### Phase 5: Report to Coordinator + Shared Memory Write
```javascript
sharedMemory.synthesis = {
conclusion_count: conclusions.key_conclusions?.length || 0,
recommendation_count: conclusions.recommendations?.length || 0,
open_question_count: conclusions.open_questions?.length || 0,
timestamp: new Date().toISOString()
}
Write(memoryPath, JSON.stringify(sharedMemory, null, 2))
const resultSummary = `${conclusions.key_conclusions?.length || 0} 结论, ${conclusions.recommendations?.length || 0} 建议`
mcp__ccw-tools__team_msg({
operation: "log",
team: teamName,
from: "synthesizer",
to: "coordinator",
type: "synthesis_ready",
summary: `[synthesizer] Synthesis complete: ${resultSummary}`,
ref: conclusionsPath
})
SendMessage({
type: "message",
recipient: "coordinator",
content: `## [synthesizer] Synthesis Results
**Task**: ${task.subject}
**Topic**: ${topic}
### Summary
${conclusions.summary}
### Top Conclusions
${(conclusions.key_conclusions || []).slice(0, 3).map((c, i) => `${i + 1}. **${c.point}** (${c.confidence})`).join('\n')}
### Top Recommendations
${(conclusions.recommendations || []).slice(0, 3).map((r, i) => `${i + 1}. [${r.priority}] ${r.action}`).join('\n')}
### Artifacts
- 📄 Discussion: ${sessionFolder}/discussion.md
- 📊 Conclusions: ${conclusionsPath}`,
summary: `[synthesizer] SYNTH complete: ${resultSummary}`
})
TaskUpdate({ taskId: task.id, status: 'completed' })
```
## Error Handling
| Scenario | Resolution |
|----------|------------|
| No SYNTH-* tasks | Idle, wait for assignment |
| No analyses/discussions found | Synthesize from explorations only |
| Conflicting analyses | Present both sides, recommend user decision |
| Empty shared memory | Generate minimal conclusions from discussion.md |
| Only one perspective | Create focused single-perspective synthesis |

View File

@@ -0,0 +1,131 @@
{
"team_name": "ultra-analyze",
"version": "1.0.0",
"description": "深度分析团队 - 将单体分析工作流拆分为5角色协作探索→分析→讨论→综合支持多管道模式和讨论循环",
"skill_entry": "team-ultra-analyze",
"invocation": "Skill(skill=\"team-ultra-analyze\", args=\"--role=coordinator ...\")",
"roles": {
"coordinator": {
"name": "coordinator",
"responsibility": "Orchestration",
"task_prefix": null,
"description": "分析团队协调者。话题澄清、管道选择、会话管理、讨论循环驱动、结果汇报",
"message_types_sent": ["pipeline_selected", "discussion_round", "direction_adjusted", "task_unblocked", "error", "shutdown"],
"message_types_received": ["exploration_ready", "analysis_ready", "discussion_processed", "synthesis_ready", "error"],
"commands": ["dispatch", "monitor"]
},
"explorer": {
"name": "explorer",
"responsibility": "Orchestration (代码库探索编排)",
"task_prefix": "EXPLORE-*",
"description": "代码库探索者。通过 cli-explore-agent 多角度并行探索代码库,收集上下文",
"message_types_sent": ["exploration_ready", "error"],
"message_types_received": [],
"commands": ["explore"],
"subagents": ["cli-explore-agent"]
},
"analyst": {
"name": "analyst",
"responsibility": "Read-only analysis (深度分析)",
"task_prefix": "ANALYZE-*",
"description": "深度分析师。基于探索结果,通过 CLI 多视角深度分析,生成结构化洞察",
"message_types_sent": ["analysis_ready", "error"],
"message_types_received": [],
"commands": ["analyze"],
"cli_tools": ["gemini", "codex", "claude"]
},
"discussant": {
"name": "discussant",
"responsibility": "Analysis + Exploration (讨论处理)",
"task_prefix": "DISCUSS-*",
"description": "讨论处理者。根据用户反馈调整分析方向,执行深入探索或补充分析",
"message_types_sent": ["discussion_processed", "error"],
"message_types_received": [],
"commands": ["deepen"],
"cli_tools": ["gemini"],
"subagents": ["cli-explore-agent"]
},
"synthesizer": {
"name": "synthesizer",
"responsibility": "Read-only analysis (综合结论)",
"task_prefix": "SYNTH-*",
"description": "综合整合者。跨视角整合所有探索、分析、讨论结果,生成最终结论和建议",
"message_types_sent": ["synthesis_ready", "error"],
"message_types_received": [],
"commands": ["synthesize"]
}
},
"pipeline_modes": {
"quick": {
"description": "快速分析:单探索→单分析→直接综合",
"stages": ["EXPLORE", "ANALYZE", "SYNTH"],
"entry_role": "explorer",
"estimated_time": "10-15min"
},
"standard": {
"description": "标准分析:多角度并行探索→多视角分析→讨论→综合",
"stages": ["EXPLORE-multi", "ANALYZE-multi", "DISCUSS", "SYNTH"],
"entry_role": "explorer",
"parallel_stages": [["EXPLORE-001", "EXPLORE-002"], ["ANALYZE-001", "ANALYZE-002"]],
"estimated_time": "30-60min"
},
"deep": {
"description": "深度分析:多探索→多分析→讨论循环→综合",
"stages": ["EXPLORE-multi", "ANALYZE-multi", "DISCUSS-loop", "SYNTH"],
"entry_role": "explorer",
"parallel_stages": [["EXPLORE-001", "EXPLORE-002", "EXPLORE-003"], ["ANALYZE-001", "ANALYZE-002", "ANALYZE-003"]],
"discussion_loop": { "max_rounds": 5, "participants": ["discussant", "analyst"] },
"estimated_time": "1-2hr"
}
},
"discussion_loop": {
"max_rounds": 5,
"trigger": "user feedback via coordinator",
"participants": ["discussant", "analyst"],
"flow": "coordinator(AskUser) → DISCUSS-N(deepen) → [optional ANALYZE-fix] → coordinator(AskUser) → ... → SYNTH"
},
"shared_memory": {
"file": "shared-memory.json",
"fields": {
"explorations": { "owner": "explorer", "type": "array" },
"analyses": { "owner": "analyst", "type": "array" },
"discussions": { "owner": "discussant", "type": "array" },
"synthesis": { "owner": "synthesizer", "type": "object" },
"decision_trail": { "owner": "coordinator", "type": "array" },
"current_understanding": { "owner": "coordinator", "type": "object" }
}
},
"collaboration_patterns": [
"CP-1: Linear Pipeline (Quick mode)",
"CP-3: Fan-out (Explorer/Analyst parallel exploration)",
"CP-2: Review-Fix Cycle (Discussion loop: Discussant ↔ Analyst)",
"CP-8: User-in-the-loop (Coordinator ↔ User discussion rounds)"
],
"session_directory": {
"pattern": ".workflow/.team/UAN-{slug}-{date}",
"subdirectories": ["explorations", "analyses", "discussions"]
},
"analysis_dimensions": {
"architecture": ["架构", "architecture", "design", "structure", "设计"],
"implementation": ["实现", "implement", "code", "coding", "代码"],
"performance": ["性能", "performance", "optimize", "bottleneck", "优化"],
"security": ["安全", "security", "auth", "permission", "权限"],
"concept": ["概念", "concept", "theory", "principle", "原理"],
"comparison": ["比较", "compare", "vs", "difference", "区别"],
"decision": ["决策", "decision", "choice", "tradeoff", "选择"]
},
"analysis_perspectives": {
"technical": { "tool": "gemini", "focus": "Implementation, code patterns, technical feasibility" },
"architectural": { "tool": "claude", "focus": "System design, scalability, component interactions" },
"business": { "tool": "codex", "focus": "Value, ROI, stakeholder impact, strategy" },
"domain_expert": { "tool": "gemini", "focus": "Domain-specific patterns, best practices, standards" }
}
}

View File

@@ -0,0 +1,93 @@
/**
* Regression test: CodexLens bootstrap falls back to pip when UV bootstrap fails.
*
* We simulate a "broken UV" by pointing CCW_UV_PATH to the current Node executable.
* `node --version` exits 0 so isUvAvailable() returns true, but `node venv ...` fails,
* forcing the bootstrap code to try the pip path.
*
* This test runs bootstrapVenv in a child process to avoid mutating process-wide
* environment variables that could affect other tests.
*/
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { mkdtempSync, rmSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { tmpdir } from 'node:os';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// repo root: <repo>/ccw/tests -> <repo>
const REPO_ROOT = join(__dirname, '..', '..');
function runNodeEvalModule(script, env) {
return new Promise((resolve, reject) => {
const child = spawn(process.execPath, ['--input-type=module', '-e', script], {
cwd: REPO_ROOT,
env,
stdio: ['ignore', 'pipe', 'pipe'],
windowsHide: true,
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (d) => { stdout += d.toString(); });
child.stderr.on('data', (d) => { stderr += d.toString(); });
child.on('error', (err) => reject(err));
child.on('close', (code) => resolve({ code, stdout, stderr }));
});
}
describe('CodexLens bootstrap fallback', () => {
it('falls back to pip when UV bootstrap fails', { timeout: 10 * 60 * 1000 }, async () => {
const dataDir = mkdtempSync(join(tmpdir(), 'codexlens-bootstrap-fallback-'));
try {
const script = `
import { bootstrapVenv } from './ccw/dist/tools/codex-lens.js';
(async () => {
const result = await bootstrapVenv();
console.log('@@RESULT@@' + JSON.stringify(result));
})().catch((e) => {
console.error(e?.stack || String(e));
process.exit(1);
});
`;
const env = {
...process.env,
// Isolate test venv + dependencies from user/global CodexLens state.
CODEXLENS_DATA_DIR: dataDir,
// Make isUvAvailable() return true, but createVenv() fail.
CCW_UV_PATH: process.execPath,
};
const { code, stdout, stderr } = await runNodeEvalModule(script, env);
assert.equal(code, 0, `bootstrapVenv child process failed:\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}`);
const marker = '@@RESULT@@';
const idx = stdout.lastIndexOf(marker);
assert.ok(idx !== -1, `Missing result marker in stdout:\n${stdout}`);
const jsonText = stdout.slice(idx + marker.length).trim();
const parsed = JSON.parse(jsonText);
assert.equal(parsed?.success, true, `Expected success=true, got:\n${jsonText}`);
assert.ok(Array.isArray(parsed.warnings), 'Expected warnings array on pip fallback result');
assert.ok(parsed.warnings.some((w) => String(w).includes('UV bootstrap failed')), `Expected UV failure warning, got: ${JSON.stringify(parsed.warnings)}`);
} finally {
try {
rmSync(dataDir, { recursive: true, force: true });
} catch {
// Best effort cleanup; leave artifacts only if Windows locks prevent removal.
}
}
});
});