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

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

View File

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

View File

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