mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +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:
@@ -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 |
|
||||
Reference in New Issue
Block a user