mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-27 09:13:07 +08:00
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:
391
.claude/skills/team-ultra-analyze/SKILL.md
Normal file
391
.claude/skills/team-ultra-analyze/SKILL.md
Normal 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 1(Task Discovery)和 Phase 5(Report)内联。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 |
|
||||
@@ -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 |
|
||||
290
.claude/skills/team-ultra-analyze/roles/analyst/role.md
Normal file
290
.claude/skills/team-ultra-analyze/roles/analyst/role.md
Normal 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 |
|
||||
@@ -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**: Direct(coordinator 直接操作 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 |
|
||||
@@ -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 |
|
||||
328
.claude/skills/team-ultra-analyze/roles/coordinator/role.md
Normal file
328
.claude/skills/team-ultra-analyze/roles/coordinator/role.md
Normal 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 |
|
||||
@@ -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 |
|
||||
273
.claude/skills/team-ultra-analyze/roles/discussant/role.md
Normal file
273
.claude/skills/team-ultra-analyze/roles/discussant/role.md
Normal 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 |
|
||||
@@ -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**: Subagent(cli-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 |
|
||||
243
.claude/skills/team-ultra-analyze/roles/explorer/role.md
Normal file
243
.claude/skills/team-ultra-analyze/roles/explorer/role.md
Normal 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 |
|
||||
@@ -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 |
|
||||
225
.claude/skills/team-ultra-analyze/roles/synthesizer/role.md
Normal file
225
.claude/skills/team-ultra-analyze/roles/synthesizer/role.md
Normal 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 |
|
||||
131
.claude/skills/team-ultra-analyze/specs/team-config.json
Normal file
131
.claude/skills/team-ultra-analyze/specs/team-config.json
Normal 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" }
|
||||
}
|
||||
}
|
||||
93
ccw/tests/codex-lens-bootstrap-fallback.test.js
Normal file
93
ccw/tests/codex-lens-bootstrap-fallback.test.js
Normal 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.
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user