mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-27 09:13:07 +08:00
feat: Implement team artifacts feature with tree navigation and file preview
This commit is contained in:
@@ -11,6 +11,23 @@ Interactive orchestration tool: analyze task → discover commands → recommend
|
||||
|
||||
**Execution Model**: Pseudocode guidance. Claude intelligently executes each phase based on context.
|
||||
|
||||
## Skill 映射
|
||||
|
||||
命令端口定义中的 workflow 操作通过 `Skill()` 调用。
|
||||
|
||||
| Skill | 包含操作 |
|
||||
|-------|---------|
|
||||
| `workflow-lite-plan` | lite-plan, lite-execute |
|
||||
| `workflow-plan` | plan, plan-verify, replan |
|
||||
| `workflow-execute` | execute |
|
||||
| `workflow-multi-cli-plan` | multi-cli-plan |
|
||||
| `workflow-test-fix` | test-fix-gen, test-cycle-execute |
|
||||
| `workflow-tdd` | tdd-plan, tdd-verify |
|
||||
| `review-cycle` | review-session-cycle, review-module-cycle, review-cycle-fix |
|
||||
| `brainstorm` | auto-parallel, artifacts, role-analysis, synthesis |
|
||||
|
||||
独立命令:workflow:brainstorm-with-file, workflow:debug-with-file, workflow:analyze-with-file, issue:*
|
||||
|
||||
## Core Concept: Minimum Execution Units (最小执行单元)
|
||||
|
||||
### What is a Minimum Execution Unit?
|
||||
@@ -30,7 +47,7 @@ Interactive orchestration tool: analyze task → discover commands → recommend
|
||||
|-----------|----------|---------|--------|
|
||||
| **Quick Implementation** | lite-plan → lite-execute | Lightweight plan and immediate execution | Working code |
|
||||
| **Multi-CLI Planning** | multi-cli-plan → lite-execute | Multi-perspective analysis and execution | Working code |
|
||||
| **Bug Fix** | lite-fix → lite-execute | Quick bug diagnosis and fix execution | Fixed code |
|
||||
| **Bug Fix** | lite-plan (--bugfix) → lite-execute | Quick bug diagnosis and fix execution | Fixed code |
|
||||
| **Full Planning + Execution** | plan → execute | Detailed planning and execution | Working code |
|
||||
| **Verified Planning + Execution** | plan → plan-verify → execute | Planning with verification and execution | Working code |
|
||||
| **Replanning + Execution** | replan → execute | Update plan and execute changes | Working code |
|
||||
@@ -70,9 +87,8 @@ Interactive orchestration tool: analyze task → discover commands → recommend
|
||||
|
||||
| Command | Can Precede | Atomic Units |
|
||||
|---------|-----------|--------------|
|
||||
| lite-plan | lite-execute, convert-to-plan | Quick Implementation, Rapid-to-Issue |
|
||||
| lite-plan | lite-execute, convert-to-plan | Quick Implementation, Rapid-to-Issue, Bug Fix |
|
||||
| multi-cli-plan | lite-execute | Multi-CLI Planning |
|
||||
| lite-fix | lite-execute | Bug Fix |
|
||||
| plan | plan-verify, execute | Full Planning + Execution, Verified Planning + Execution |
|
||||
| plan-verify | execute | Verified Planning + Execution |
|
||||
| replan | execute | Replanning + Execution |
|
||||
@@ -173,6 +189,16 @@ Each command has input/output ports (tags) for pipeline composition:
|
||||
|
||||
```javascript
|
||||
// Port labels represent data types flowing through the pipeline
|
||||
// Type classification:
|
||||
// skill: workflow-lite-plan (lite-plan, lite-execute),
|
||||
// workflow-plan (plan, plan-verify, replan),
|
||||
// workflow-execute (execute),
|
||||
// workflow-multi-cli-plan (multi-cli-plan),
|
||||
// workflow-test-fix (test-fix-gen, test-cycle-execute),
|
||||
// workflow-tdd (tdd-plan, tdd-verify),
|
||||
// review-cycle (review-session-cycle, review-module-cycle, review-cycle-fix)
|
||||
// command: debug, test-gen, review, workflow:brainstorm-with-file,
|
||||
// workflow:debug-with-file, workflow:analyze-with-file, issue:*
|
||||
const commandPorts = {
|
||||
'lite-plan': {
|
||||
name: 'lite-plan',
|
||||
@@ -183,13 +209,13 @@ const commandPorts = {
|
||||
},
|
||||
'lite-execute': {
|
||||
name: 'lite-execute',
|
||||
input: ['plan', 'multi-cli-plan', 'lite-fix'], // 输入端口:可接受多种规划输出
|
||||
input: ['plan', 'multi-cli-plan'], // 输入端口:可接受多种规划输出
|
||||
output: ['code'], // 输出端口:代码
|
||||
tags: ['execution'],
|
||||
atomic_groups: [ // 可参与多个最小单元
|
||||
'quick-implementation', // lite-plan → lite-execute
|
||||
'multi-cli-planning', // multi-cli-plan → lite-execute
|
||||
'bug-fix' // lite-fix → lite-execute
|
||||
'bug-fix' // lite-plan (--bugfix) → lite-execute
|
||||
]
|
||||
},
|
||||
'plan': {
|
||||
@@ -250,12 +276,15 @@ const commandPorts = {
|
||||
output: ['tdd-verified'],
|
||||
tags: ['testing']
|
||||
},
|
||||
'lite-fix': {
|
||||
name: 'lite-fix',
|
||||
// Bug Fix (使用 lite-plan 的 bugfix 变体,lite-fix 已移除)
|
||||
'lite-plan-bugfix': {
|
||||
name: 'lite-plan',
|
||||
input: ['bug-report'], // 输入端口:bug 报告
|
||||
output: ['lite-fix'], // 输出端口:修复计划(供 lite-execute 执行)
|
||||
tags: ['bugfix'],
|
||||
atomic_group: 'bug-fix' // 最小单元:与 lite-execute 绑定
|
||||
output: ['plan'], // 输出端口:修复计划(供 lite-execute 执行)
|
||||
tags: ['bugfix', 'planning'],
|
||||
atomic_group: 'bug-fix', // 最小单元:与 lite-execute 绑定
|
||||
type: 'skill', // Skill 触发器: workflow-lite-plan
|
||||
note: '通过 --bugfix 参数传递 bugfix 语义'
|
||||
},
|
||||
'debug': {
|
||||
name: 'debug',
|
||||
@@ -291,11 +320,12 @@ const commandPorts = {
|
||||
tags: ['review'],
|
||||
atomic_group: 'code-review' // 最小单元:与 review-session-cycle/review-module-cycle 绑定
|
||||
},
|
||||
'brainstorm:auto-parallel': {
|
||||
name: 'brainstorm:auto-parallel',
|
||||
'brainstorm': {
|
||||
name: 'brainstorm',
|
||||
input: ['exploration-topic'], // 输入端口:探索主题
|
||||
output: ['brainstorm-analysis'],
|
||||
tags: ['brainstorm']
|
||||
tags: ['brainstorm'],
|
||||
type: 'skill' // 统一 Skill:brainstorm (auto-parallel, artifacts, role-analysis, synthesis)
|
||||
},
|
||||
'multi-cli-plan': {
|
||||
name: 'multi-cli-plan',
|
||||
@@ -618,14 +648,18 @@ function formatCommand(cmd, previousResults, analysis) {
|
||||
const plan = previousResults.find(r => r.command.includes('plan'));
|
||||
if (plan?.session_id) prompt += ` --resume-session="${plan.session_id}"`;
|
||||
|
||||
// Bug fix commands - take bug description
|
||||
} else if (['lite-fix', 'debug'].includes(name)) {
|
||||
// Bug fix commands - use lite-plan with bugfix flag (lite-fix removed)
|
||||
} else if (name === 'lite-plan' && analysis.task_type === 'bugfix') {
|
||||
prompt += ` --bugfix "${analysis.goal}"`;
|
||||
|
||||
// Debug commands - take bug description
|
||||
} else if (name === 'debug') {
|
||||
prompt += ` "${analysis.goal}"`;
|
||||
|
||||
// Brainstorm - take topic description
|
||||
} else if (name === 'brainstorm:auto-parallel' || name === 'auto-parallel') {
|
||||
// Brainstorm - take topic description (unified brainstorm skill)
|
||||
} else if (name === 'brainstorm') {
|
||||
prompt += ` "${analysis.goal}"`;
|
||||
|
||||
prompt = `/brainstorm -y ${prompt.trim()}`;
|
||||
// Test generation from session - needs source session
|
||||
} else if (name === 'test-gen') {
|
||||
const impl = previousResults.find(r =>
|
||||
@@ -859,26 +893,19 @@ failed ←───────────────────────
|
||||
- `completed`: Successfully finished
|
||||
- `failed`: Failed to execute
|
||||
|
||||
## CommandRegistry Integration
|
||||
## Skill & Command Discovery
|
||||
|
||||
Sole CCW tool for command discovery:
|
||||
workflow 操作通过 `Skill()` 调用对应的 Skill。
|
||||
|
||||
```javascript
|
||||
import { CommandRegistry } from 'ccw/tools/command-registry';
|
||||
// Skill 调用方式
|
||||
Skill({ skill: 'workflow-lite-plan', args: '"task description"' });
|
||||
Skill({ skill: 'workflow-execute', args: '--resume-session="WFS-xxx"' });
|
||||
Skill({ skill: 'brainstorm', args: '"exploration topic"' });
|
||||
|
||||
const registry = new CommandRegistry();
|
||||
|
||||
// Get all commands
|
||||
const allCommands = registry.getAllCommandsSummary();
|
||||
// Map<"/workflow:lite-plan" => {name, description}>
|
||||
|
||||
// Get categorized
|
||||
const byCategory = registry.getAllCommandsByCategory();
|
||||
// {planning, execution, testing, review, other}
|
||||
|
||||
// Get single command metadata
|
||||
const cmd = registry.getCommand('lite-plan');
|
||||
// {name, command, description, argumentHint, allowedTools, filePath}
|
||||
// 独立命令调用方式
|
||||
Skill({ skill: 'workflow:brainstorm-with-file', args: '"topic"' });
|
||||
Skill({ skill: 'issue:discover', args: '' });
|
||||
```
|
||||
|
||||
## Universal Prompt Template
|
||||
@@ -917,7 +944,7 @@ Task: <task_description>
|
||||
| **Execution (with plan)** | `--resume-session="WFS-xxx"` | `/workflow:execute -y --resume-session="WFS-plan-001"` |
|
||||
| **Execution (standalone)** | `--in-memory` or `"task"` | `/workflow:lite-execute -y --in-memory` |
|
||||
| **Session-based** | `--session="WFS-xxx"` | `/workflow:test-fix-gen -y --session="WFS-impl-001"` |
|
||||
| **Fix/Debug** | `"problem description"` | `/workflow:lite-fix -y "Fix timeout bug"` |
|
||||
| **Fix/Debug** | `--bugfix "problem description"` | `/workflow:lite-plan -y --bugfix "Fix timeout bug"` |
|
||||
|
||||
### Complete Examples
|
||||
|
||||
@@ -940,7 +967,7 @@ Previous results:
|
||||
|
||||
**Standalone Lite Execution**:
|
||||
```bash
|
||||
ccw cli -p '/workflow:lite-fix -y "Fix login timeout in auth module"
|
||||
ccw cli -p '/workflow:lite-plan -y --bugfix "Fix login timeout in auth module"
|
||||
|
||||
Task: Fix login timeout' --tool claude --mode write
|
||||
```
|
||||
@@ -1048,34 +1075,40 @@ break; // ⚠️ STOP HERE - DO NOT use TaskOutput polling
|
||||
```
|
||||
|
||||
|
||||
## Available Commands
|
||||
## Available Skills & Commands
|
||||
|
||||
All from `~/.claude/commands/workflow/` and `~/.claude/commands/issue/`:
|
||||
### Skills
|
||||
|
||||
**Planning**: lite-plan, plan, multi-cli-plan, plan-verify, tdd-plan
|
||||
**Execution**: lite-execute, execute, develop-with-file
|
||||
**Testing**: test-cycle-execute, test-gen, test-fix-gen, tdd-verify
|
||||
**Review**: review, review-session-cycle, review-module-cycle, review-cycle-fix
|
||||
**Bug Fixes**: lite-fix, debug, debug-with-file
|
||||
**Brainstorming**: brainstorm:auto-parallel, brainstorm:artifacts, brainstorm:synthesis
|
||||
**Design**: ui-design:*, animation-extract, layout-extract, style-extract, codify-style
|
||||
**Session Management**: session:start, session:resume, session:complete, session:solidify, session:list
|
||||
**Tools**: context-gather, test-context-gather, task-generate, conflict-resolution, action-plan-verify
|
||||
**Utility**: clean, init, replan
|
||||
**Issue Workflow**: issue:discover, issue:plan, issue:queue, issue:execute, issue:convert-to-plan, issue:from-brainstorm
|
||||
**With-File Workflows**: brainstorm-with-file, debug-with-file, analyze-with-file
|
||||
| Skill | 包含操作 |
|
||||
|-------|---------|
|
||||
| `workflow-lite-plan` | lite-plan, lite-execute |
|
||||
| `workflow-plan` | plan, plan-verify, replan |
|
||||
| `workflow-execute` | execute |
|
||||
| `workflow-multi-cli-plan` | multi-cli-plan |
|
||||
| `workflow-test-fix` | test-fix-gen, test-cycle-execute |
|
||||
| `workflow-tdd` | tdd-plan, tdd-verify |
|
||||
| `review-cycle` | review-session-cycle, review-module-cycle, review-cycle-fix |
|
||||
|
||||
### Commands(命名空间 Skill)
|
||||
|
||||
**With-File Workflows**: workflow:brainstorm-with-file, workflow:debug-with-file, workflow:analyze-with-file
|
||||
**Design**: workflow:ui-design:*
|
||||
**Session Management**: workflow:session:start, workflow:session:resume, workflow:session:complete, workflow:session:solidify, workflow:session:list
|
||||
**Tools**: workflow:tools:context-gather, workflow:tools:test-context-gather, workflow:tools:task-generate-agent, workflow:tools:conflict-resolution
|
||||
**Utility**: workflow:clean, workflow:init
|
||||
**Issue Workflow**: issue:discover, issue:plan, issue:queue, issue:execute, issue:convert-to-plan, issue:from-brainstorm, issue:new
|
||||
|
||||
### Testing Commands Distinction
|
||||
|
||||
| Command | Purpose | Output | Follow-up |
|
||||
|---------|---------|--------|-----------|
|
||||
| **test-gen** | 广泛测试示例生成并进行测试 | test-tasks (IMPL-001, IMPL-002) | `/workflow:execute` |
|
||||
| **test-fix-gen** | 针对特定问题生成测试并在测试中修正 | test-tasks | `/workflow:test-cycle-execute` |
|
||||
| **test-gen** | 广泛测试示例生成并进行测试 | test-tasks (IMPL-001, IMPL-002) | Skill(workflow-execute) |
|
||||
| **test-fix-gen** | 针对特定问题生成测试并在测试中修正 | test-tasks | Skill(workflow-test-fix) → test-cycle-execute |
|
||||
| **test-cycle-execute** | 执行测试周期(迭代测试和修复) | test-passed | N/A (终点) |
|
||||
|
||||
**流程说明**:
|
||||
- **test-gen → execute**: 生成全面的测试套件,execute 执行生成和测试
|
||||
- **test-fix-gen → test-cycle-execute**: 针对特定问题生成修复任务,test-cycle-execute 迭代测试和修复直到通过
|
||||
- **test-gen → Skill(workflow-execute)**: 生成全面的测试套件,execute 执行生成和测试
|
||||
- **test-fix-gen → test-cycle-execute**: 同属 Skill(workflow-test-fix),针对特定问题生成修复任务并迭代测试和修复直到通过
|
||||
|
||||
### Task Type Routing (Pipeline Summary)
|
||||
|
||||
@@ -1085,7 +1118,7 @@ All from `~/.claude/commands/workflow/` and `~/.claude/commands/issue/`:
|
||||
|-----------|----------|---|
|
||||
| **feature** (simple) | 需求 →【lite-plan → lite-execute】→ 代码 →【test-fix-gen → test-cycle-execute】→ 测试通过 | Quick Implementation + Test Validation |
|
||||
| **feature** (complex) | 需求 →【plan → plan-verify】→ validate → execute → 代码 → review → fix | Full Planning + Code Review + Testing |
|
||||
| **bugfix** | Bug报告 → lite-fix → 修复代码 →【test-fix-gen → test-cycle-execute】→ 测试通过 | Bug Fix + Test Validation |
|
||||
| **bugfix** | Bug报告 → lite-plan (--bugfix) → 修复代码 →【test-fix-gen → test-cycle-execute】→ 测试通过 | Bug Fix + Test Validation |
|
||||
| **tdd** | 需求 → tdd-plan → TDD任务 → execute → 代码 → tdd-verify | TDD Planning + Execution |
|
||||
| **test-fix** | 失败测试 →【test-fix-gen → test-cycle-execute】→ 测试通过 | Test Validation |
|
||||
| **test-gen** | 代码/会话 →【test-gen → execute】→ 测试通过 | Test Generation + Execution |
|
||||
@@ -1099,4 +1132,4 @@ All from `~/.claude/commands/workflow/` and `~/.claude/commands/issue/`:
|
||||
| **debug-file** | Bug报告 → debug-with-file → understanding.md (自包含) | Debug With File |
|
||||
| **analyze-file** | 分析主题 → analyze-with-file → discussion.md (自包含) | Analyze With File |
|
||||
|
||||
Use `CommandRegistry.getAllCommandsSummary()` to discover all commands dynamically.
|
||||
Refer to the Skill 映射 section above for available Skills and Commands.
|
||||
|
||||
@@ -9,6 +9,14 @@ allowed-tools: Skill(*), TodoWrite(*), AskUserQuestion(*), Read(*), Bash(*)
|
||||
|
||||
Debug orchestrator: issue analysis → strategy selection → debug execution.
|
||||
|
||||
## Skill 映射
|
||||
|
||||
| Skill | 包含操作 |
|
||||
|-------|---------|
|
||||
| `workflow-test-fix` | test-fix-gen, test-cycle-execute |
|
||||
|
||||
独立命令:workflow:debug-with-file
|
||||
|
||||
## Core Concept: Debug Units (调试单元)
|
||||
|
||||
**Definition**: Debug commands grouped into logical units for different root cause strategies.
|
||||
|
||||
@@ -9,6 +9,17 @@ allowed-tools: Skill(*), TodoWrite(*), AskUserQuestion(*), Read(*), Grep(*), Glo
|
||||
|
||||
Planning orchestrator: requirement analysis → strategy selection → planning execution.
|
||||
|
||||
## Skill 映射
|
||||
|
||||
| Skill | 包含操作 |
|
||||
|-------|---------|
|
||||
| `workflow-lite-plan` | lite-plan, lite-execute |
|
||||
| `workflow-plan` | plan, plan-verify, replan |
|
||||
| `workflow-multi-cli-plan` | multi-cli-plan |
|
||||
| `brainstorm` | brainstorm |
|
||||
|
||||
独立命令:workflow:brainstorm-with-file, workflow:analyze-with-file, issue:*
|
||||
|
||||
## Core Concept: Planning Units (规划单元)
|
||||
|
||||
**Definition**: Planning commands are grouped into logical units based on verification requirements and collaboration strategies.
|
||||
@@ -255,7 +266,7 @@ CCWP:lite: [1/1] /workflow:lite-plan [in_progress]
|
||||
CCWP:multi-cli: [1/1] /workflow:multi-cli-plan [in_progress]
|
||||
|
||||
// Full mode (brainstorm + planning with optional verification)
|
||||
CCWP:full: [1/2] /workflow:brainstorm [in_progress]
|
||||
CCWP:full: [1/2] /brainstorm [in_progress]
|
||||
CCWP:full: [2/2] /workflow:plan [pending]
|
||||
```
|
||||
|
||||
|
||||
@@ -9,6 +9,14 @@ allowed-tools: Skill(*), TodoWrite(*), AskUserQuestion(*), Read(*), Bash(*)
|
||||
|
||||
Test orchestrator: testing needs analysis → strategy selection → test execution.
|
||||
|
||||
## Skill 映射
|
||||
|
||||
| Skill | 包含操作 |
|
||||
|-------|---------|
|
||||
| `workflow-test-fix` | test-fix-gen, test-cycle-execute |
|
||||
| `workflow-tdd` | tdd-plan, tdd-verify |
|
||||
| `workflow-execute` | execute |
|
||||
|
||||
## Core Concept: Test Units (测试单元)
|
||||
|
||||
**Definition**: Test commands grouped into logical units based on testing objectives.
|
||||
|
||||
@@ -9,6 +9,23 @@ allowed-tools: Skill(*), TodoWrite(*), AskUserQuestion(*), Read(*), Grep(*), Glo
|
||||
|
||||
Main process orchestrator: intent analysis → workflow selection → command chain execution.
|
||||
|
||||
## Skill 映射
|
||||
|
||||
命令链中的 workflow 操作通过 `Skill()` 调用。
|
||||
|
||||
| Skill | 包含操作 |
|
||||
|-------|---------|
|
||||
| `workflow-lite-plan` | lite-plan, lite-execute |
|
||||
| `workflow-plan` | plan, plan-verify, replan |
|
||||
| `workflow-execute` | execute |
|
||||
| `workflow-multi-cli-plan` | multi-cli-plan |
|
||||
| `workflow-test-fix` | test-fix-gen, test-cycle-execute |
|
||||
| `workflow-tdd` | tdd-plan, tdd-verify |
|
||||
| `review-cycle` | review-session-cycle, review-module-cycle, review-cycle-fix |
|
||||
| `brainstorm` | auto-parallel, artifacts, role-analysis, synthesis |
|
||||
|
||||
独立命令:workflow:brainstorm-with-file, workflow:debug-with-file, workflow:analyze-with-file, issue:*
|
||||
|
||||
## Core Concept: Minimum Execution Units (最小执行单元)
|
||||
|
||||
**Definition**: A set of commands that must execute together as an atomic group to achieve a meaningful workflow milestone.
|
||||
@@ -127,7 +144,7 @@ function selectWorkflow(analysis) {
|
||||
'issue-batch': { level: 'Issue', flow: 'issue' },
|
||||
'issue-transition': { level: 2.5, flow: 'rapid-to-issue' }, // Bridge workflow
|
||||
'exploration': { level: 4, flow: 'full' },
|
||||
'quick-task': { level: 1, flow: 'lite-lite-lite' },
|
||||
'quick-task': { level: 2, flow: 'rapid' },
|
||||
'ui-design': { level: analysis.complexity === 'high' ? 4 : 3, flow: 'ui' },
|
||||
'tdd': { level: 3, flow: 'tdd' },
|
||||
'test-fix': { level: 3, flow: 'test-fix-gen' },
|
||||
@@ -143,11 +160,6 @@ function selectWorkflow(analysis) {
|
||||
// Build command chain (port-based matching with Minimum Execution Units)
|
||||
function buildCommandChain(workflow, analysis) {
|
||||
const chains = {
|
||||
// Level 1 - Rapid
|
||||
'lite-lite-lite': [
|
||||
{ cmd: '/workflow:lite-lite-lite', args: `"${analysis.goal}"` }
|
||||
],
|
||||
|
||||
// Level 2 - Lightweight
|
||||
'rapid': [
|
||||
// Unit: Quick Implementation【lite-plan → lite-execute】
|
||||
@@ -171,8 +183,8 @@ function buildCommandChain(workflow, analysis) {
|
||||
],
|
||||
|
||||
'bugfix.standard': [
|
||||
// Unit: Bug Fix【lite-fix → lite-execute】
|
||||
{ cmd: '/workflow:lite-fix', args: `"${analysis.goal}"`, unit: 'bug-fix' },
|
||||
// Unit: Bug Fix【lite-plan → lite-execute】
|
||||
{ cmd: '/workflow:lite-plan', args: `--bugfix "${analysis.goal}"`, unit: 'bug-fix' },
|
||||
{ cmd: '/workflow:lite-execute', args: '--in-memory', unit: 'bug-fix' },
|
||||
// Unit: Test Validation【test-fix-gen → test-cycle-execute】
|
||||
...(analysis.constraints?.includes('skip-tests') ? [] : [
|
||||
@@ -182,7 +194,7 @@ function buildCommandChain(workflow, analysis) {
|
||||
],
|
||||
|
||||
'bugfix.hotfix': [
|
||||
{ cmd: '/workflow:lite-fix', args: `--hotfix "${analysis.goal}"` }
|
||||
{ cmd: '/workflow:lite-plan', args: `--hotfix "${analysis.goal}"` }
|
||||
],
|
||||
|
||||
'multi-cli-plan': [
|
||||
@@ -275,7 +287,7 @@ function buildCommandChain(workflow, analysis) {
|
||||
|
||||
// Level 4 - Brainstorm
|
||||
'full': [
|
||||
{ cmd: '/workflow:brainstorm:auto-parallel', args: `"${analysis.goal}"` },
|
||||
{ cmd: '/brainstorm', args: `"${analysis.goal}"` },
|
||||
// Unit: Verified Planning【plan → plan-verify】
|
||||
{ cmd: '/workflow:plan', args: '', unit: 'verified-planning' },
|
||||
{ cmd: '/workflow:plan-verify', args: '', unit: 'verified-planning' },
|
||||
@@ -516,7 +528,7 @@ Phase 5: Execute Command Chain
|
||||
| Input | Type | Level | Pipeline (with Units) |
|
||||
|-------|------|-------|-----------------------|
|
||||
| "Add API endpoint" | feature (low) | 2 |【lite-plan → lite-execute】→【test-fix-gen → test-cycle-execute】|
|
||||
| "Fix login timeout" | bugfix | 2 |【lite-fix → lite-execute】→【test-fix-gen → test-cycle-execute】|
|
||||
| "Fix login timeout" | bugfix | 2 |【lite-plan → lite-execute】→【test-fix-gen → test-cycle-execute】|
|
||||
| "Use issue workflow" | issue-transition | 2.5 |【lite-plan → convert-to-plan】→ queue → execute |
|
||||
| "头脑风暴: 通知系统重构" | brainstorm | 4 | brainstorm-with-file → (built-in post-completion) |
|
||||
| "从头脑风暴创建 issue" | brainstorm-to-issue | 4 | from-brainstorm → queue → execute |
|
||||
@@ -524,7 +536,7 @@ Phase 5: Execute Command Chain
|
||||
| "协作分析: 认证架构优化" | analyze-file | 3 | analyze-with-file → (multi-round discussion) |
|
||||
| "OAuth2 system" | feature (high) | 3 |【plan → plan-verify】→ execute →【review-session-cycle → review-cycle-fix】→【test-fix-gen → test-cycle-execute】|
|
||||
| "Implement with TDD" | tdd | 3 |【tdd-plan → execute】→ tdd-verify |
|
||||
| "Uncertain: real-time arch" | exploration | 4 | brainstorm:auto-parallel →【plan → plan-verify】→ execute →【test-fix-gen → test-cycle-execute】|
|
||||
| "Uncertain: real-time arch" | exploration | 4 | brainstorm →【plan → plan-verify】→ execute →【test-fix-gen → test-cycle-execute】|
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ async function designTemplate(input) {
|
||||
question: "What complexity level?",
|
||||
header: "Level",
|
||||
options: [
|
||||
{ label: "Level 1 (Rapid)", description: "1-2 steps, ultra-lightweight (lite-lite-lite)" },
|
||||
{ label: "Level 1 (Rapid)", description: "1-2 steps, ultra-lightweight hotfix" },
|
||||
{ label: "Level 2 (Lightweight)", description: "2-4 steps, quick implementation" },
|
||||
{ label: "Level 3 (Standard)", description: "4-6 steps, with verification and testing" },
|
||||
{ label: "Level 4 (Full)", description: "6+ steps, brainstorm + full workflow" }
|
||||
@@ -89,8 +89,8 @@ async function selectCommandCategory() {
|
||||
{ label: "Execution", description: "lite-execute, execute, unified-execute-with-file" },
|
||||
{ label: "Testing", description: "test-fix-gen, test-cycle-execute, test-gen, tdd-verify" },
|
||||
{ label: "Review", description: "review-session-cycle, review-module-cycle, review-cycle-fix" },
|
||||
{ label: "Bug Fix", description: "lite-fix, debug-with-file" },
|
||||
{ label: "Brainstorm", description: "brainstorm-with-file, brainstorm:auto-parallel" },
|
||||
{ label: "Bug Fix", description: "lite-plan --bugfix, debug-with-file" },
|
||||
{ label: "Brainstorm", description: "brainstorm-with-file, brainstorm (unified skill)" },
|
||||
{ label: "Analysis", description: "analyze-with-file" },
|
||||
{ label: "Issue", description: "discover, plan, queue, execute, from-brainstorm, convert-to-plan" },
|
||||
{ label: "Utility", description: "clean, init, replan, status" }
|
||||
@@ -118,8 +118,7 @@ async function selectCommand(category) {
|
||||
'Execution': [
|
||||
{ label: "/workflow:lite-execute", description: "Execute from in-memory plan" },
|
||||
{ label: "/workflow:execute", description: "Execute from planning session" },
|
||||
{ label: "/workflow:unified-execute-with-file", description: "Universal execution engine" },
|
||||
{ label: "/workflow:lite-lite-lite", description: "Ultra-lightweight multi-tool execution" }
|
||||
{ label: "/workflow:unified-execute-with-file", description: "Universal execution engine" }
|
||||
],
|
||||
'Testing': [
|
||||
{ label: "/workflow:test-fix-gen", description: "Generate test tasks for specific issues" },
|
||||
@@ -134,12 +133,12 @@ async function selectCommand(category) {
|
||||
{ label: "/workflow:review", description: "Post-implementation review" }
|
||||
],
|
||||
'Bug Fix': [
|
||||
{ label: "/workflow:lite-fix", description: "Lightweight bug diagnosis and fix" },
|
||||
{ label: "/workflow:lite-plan", description: "Lightweight bug diagnosis and fix (with --bugfix flag)" },
|
||||
{ label: "/workflow:debug-with-file", description: "Hypothesis-driven debugging with documentation" }
|
||||
],
|
||||
'Brainstorm': [
|
||||
{ label: "/workflow:brainstorm-with-file", description: "Multi-perspective ideation with documentation" },
|
||||
{ label: "/workflow:brainstorm:auto-parallel", description: "Parallel multi-role brainstorming" }
|
||||
{ label: "/brainstorm", description: "Unified brainstorming skill (auto-parallel + role analysis)" }
|
||||
],
|
||||
'Analysis': [
|
||||
{ label: "/workflow:analyze-with-file", description: "Collaborative analysis with documentation" }
|
||||
@@ -194,7 +193,7 @@ async function selectExecutionUnit() {
|
||||
// Review Units
|
||||
{ label: "code-review", description: "【review-*-cycle → review-cycle-fix】" },
|
||||
// Bug Fix Units
|
||||
{ label: "bug-fix", description: "【lite-fix → lite-execute】" },
|
||||
{ label: "bug-fix", description: "【lite-plan --bugfix → lite-execute】" },
|
||||
// Issue Units
|
||||
{ label: "issue-workflow", description: "【discover → plan → queue → execute】" },
|
||||
{ label: "rapid-to-issue", description: "【lite-plan → convert-to-plan → queue → execute】" },
|
||||
@@ -337,7 +336,7 @@ async function defineSteps(templateDesign) {
|
||||
"description": "Bug diagnosis and fix with testing",
|
||||
"level": 2,
|
||||
"steps": [
|
||||
{ "cmd": "/workflow:lite-fix", "args": "\"{{goal}}\"", "unit": "bug-fix", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Diagnose and plan bug fix" },
|
||||
{ "cmd": "/workflow:lite-plan", "args": "--bugfix \"{{goal}}\"", "unit": "bug-fix", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Diagnose and plan bug fix" },
|
||||
{ "cmd": "/workflow:lite-execute", "args": "--in-memory", "unit": "bug-fix", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Execute bug fix" },
|
||||
{ "cmd": "/workflow:test-fix-gen", "unit": "test-validation", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Generate regression tests" },
|
||||
{ "cmd": "/workflow:test-cycle-execute", "unit": "test-validation", "execution": { "type": "slash-command", "mode": "async" }, "contextHint": "Verify fix with tests" }
|
||||
@@ -352,7 +351,7 @@ async function defineSteps(templateDesign) {
|
||||
"description": "Urgent production bug fix (no tests)",
|
||||
"level": 2,
|
||||
"steps": [
|
||||
{ "cmd": "/workflow:lite-fix", "args": "--hotfix \"{{goal}}\"", "unit": "standalone", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Emergency hotfix mode" }
|
||||
{ "cmd": "/workflow:lite-plan", "args": "--hotfix \"{{goal}}\"", "unit": "standalone", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Emergency hotfix mode" }
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -486,7 +485,7 @@ async function defineSteps(templateDesign) {
|
||||
"description": "Complete workflow: brainstorm → plan → execute → test",
|
||||
"level": 4,
|
||||
"steps": [
|
||||
{ "cmd": "/workflow:brainstorm:auto-parallel", "args": "\"{{goal}}\"", "unit": "standalone", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Parallel multi-perspective brainstorming" },
|
||||
{ "cmd": "/brainstorm", "args": "\"{{goal}}\"", "unit": "standalone", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Unified brainstorming with multi-perspective exploration" },
|
||||
{ "cmd": "/workflow:plan", "unit": "verified-planning-execution", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Create detailed plan from brainstorm" },
|
||||
{ "cmd": "/workflow:plan-verify", "unit": "verified-planning-execution", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Verify plan quality" },
|
||||
{ "cmd": "/workflow:execute", "unit": "verified-planning-execution", "execution": { "type": "slash-command", "mode": "async" }, "contextHint": "Execute implementation" },
|
||||
@@ -512,16 +511,8 @@ async function defineSteps(templateDesign) {
|
||||
```
|
||||
|
||||
### Ultra-Lightweight (Level 1)
|
||||
```json
|
||||
{
|
||||
"name": "lite-lite-lite",
|
||||
"description": "Ultra-lightweight multi-tool execution",
|
||||
"level": 1,
|
||||
"steps": [
|
||||
{ "cmd": "/workflow:lite-lite-lite", "args": "\"{{goal}}\"", "unit": "standalone", "execution": { "type": "slash-command", "mode": "mainprocess" }, "contextHint": "Direct execution with minimal overhead" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
> **Note**: `lite-lite-lite` has been removed. Use `bugfix-hotfix` for Level 1 urgent tasks, or `rapid` (Level 2) for simple features.
|
||||
|
||||
---
|
||||
|
||||
@@ -539,7 +530,7 @@ Each command has input/output ports for pipeline composition:
|
||||
| tdd-plan | requirement | tdd-tasks | tdd-planning-execution |
|
||||
| replan | session, feedback | replan | replanning-execution |
|
||||
| **Execution** |
|
||||
| lite-execute | plan, multi-cli-plan, lite-fix | code | (multiple) |
|
||||
| lite-execute | plan, multi-cli-plan | code | (multiple) |
|
||||
| execute | detailed-plan, verified-plan, replan, tdd-tasks | code | (multiple) |
|
||||
| **Testing** |
|
||||
| test-fix-gen | failing-tests, session | test-tasks | test-validation |
|
||||
@@ -551,7 +542,7 @@ Each command has input/output ports for pipeline composition:
|
||||
| review-module-cycle | module-pattern | review-verified | code-review |
|
||||
| review-cycle-fix | review-findings | fixed-code | code-review |
|
||||
| **Bug Fix** |
|
||||
| lite-fix | bug-report | lite-fix | bug-fix |
|
||||
| lite-plan --bugfix | bug-report | plan | bug-fix |
|
||||
| debug-with-file | bug-report | understanding-document | debug-with-file |
|
||||
| **With-File** |
|
||||
| brainstorm-with-file | exploration-topic | brainstorm-document | brainstorm-with-file |
|
||||
@@ -574,7 +565,7 @@ Each command has input/output ports for pipeline composition:
|
||||
|-----------|----------|---------|
|
||||
| **quick-implementation** | lite-plan → lite-execute | Lightweight plan and execution |
|
||||
| **multi-cli-planning** | multi-cli-plan → lite-execute | Multi-perspective planning and execution |
|
||||
| **bug-fix** | lite-fix → lite-execute | Bug diagnosis and fix |
|
||||
| **bug-fix** | lite-plan --bugfix → lite-execute | Bug diagnosis and fix |
|
||||
| **full-planning-execution** | plan → execute | Detailed planning and execution |
|
||||
| **verified-planning-execution** | plan → plan-verify → execute | Planning with verification |
|
||||
| **replanning-execution** | replan → execute | Update plan and execute |
|
||||
@@ -664,7 +655,7 @@ async function generateTemplate(design, steps, outputPath) {
|
||||
→ Purpose: Feature Development
|
||||
→ Level: 3 (Standard)
|
||||
→ Steps: Customize
|
||||
→ Step 1: /workflow:brainstorm:auto-parallel (standalone, mainprocess)
|
||||
→ Step 1: /brainstorm (standalone, mainprocess)
|
||||
→ Step 2: /workflow:plan (verified-planning-execution, mainprocess)
|
||||
→ Step 3: /workflow:plan-verify (verified-planning-execution, mainprocess)
|
||||
→ Step 4: /workflow:execute (verified-planning-execution, async)
|
||||
|
||||
@@ -1,522 +0,0 @@
|
||||
---
|
||||
name: artifacts
|
||||
description: Interactive clarification generating confirmed guidance specification through role-based analysis and synthesis
|
||||
argument-hint: "[-y|--yes] topic or challenge description [--count N]"
|
||||
allowed-tools: TodoWrite(*), Read(*), Write(*), Glob(*), AskUserQuestion(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-select recommended roles, skip all clarification questions, use default answers.
|
||||
|
||||
## Overview
|
||||
|
||||
Seven-phase workflow: **Context collection** → **Topic analysis** → **Role selection** → **Role questions** → **Conflict resolution** → **Final check** → **Generate specification**
|
||||
|
||||
All user interactions use AskUserQuestion tool (max 4 questions per call, multi-round).
|
||||
|
||||
**Input**: `"GOAL: [objective] SCOPE: [boundaries] CONTEXT: [background]" [--count N]`
|
||||
**Output**: `.workflow/active/WFS-{topic}/.brainstorming/guidance-specification.md`
|
||||
**Core Principle**: Questions dynamically generated from project context + topic keywords, NOT generic templates
|
||||
|
||||
**Parameters**:
|
||||
- `topic` (required): Topic or challenge description (structured format recommended)
|
||||
- `--count N` (optional): Number of roles to select (system recommends N+2 options, default: 3)
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Phase Summary
|
||||
|
||||
| Phase | Goal | AskUserQuestion | Storage |
|
||||
|-------|------|-----------------|---------|
|
||||
| 0 | Context collection | - | context-package.json |
|
||||
| 1 | Topic analysis | 2-4 questions | intent_context |
|
||||
| 2 | Role selection | 1 multi-select | selected_roles |
|
||||
| 3 | Role questions | 3-4 per role | role_decisions[role] |
|
||||
| 4 | Conflict resolution | max 4 per round | cross_role_decisions |
|
||||
| 4.5 | Final check + Feature decomposition | progressive rounds | additional_decisions, feature_list |
|
||||
| 5 | Generate spec | - | guidance-specification.md |
|
||||
|
||||
### AskUserQuestion Pattern
|
||||
|
||||
```javascript
|
||||
// Single-select (Phase 1, 3, 4)
|
||||
AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: "{问题文本}",
|
||||
header: "{短标签}", // max 12 chars
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "{选项}", description: "{说明和影响}" },
|
||||
{ label: "{选项}", description: "{说明和影响}" },
|
||||
{ label: "{选项}", description: "{说明和影响}" }
|
||||
]
|
||||
}
|
||||
// ... max 4 questions per call
|
||||
]
|
||||
})
|
||||
|
||||
// Multi-select (Phase 2)
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "请选择 {count} 个角色",
|
||||
header: "角色选择",
|
||||
multiSelect: true,
|
||||
options: [/* max 4 options per call */]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
### Multi-Round Execution
|
||||
|
||||
```javascript
|
||||
const BATCH_SIZE = 4;
|
||||
for (let i = 0; i < allQuestions.length; i += BATCH_SIZE) {
|
||||
const batch = allQuestions.slice(i, i + BATCH_SIZE);
|
||||
AskUserQuestion({ questions: batch });
|
||||
// Store responses before next round
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task Tracking
|
||||
|
||||
**TodoWrite Rule**: EXTEND auto-parallel's task list (NOT replace/overwrite)
|
||||
|
||||
**When called from auto-parallel**:
|
||||
- Find artifacts parent task → Mark "in_progress"
|
||||
- APPEND sub-tasks (Phase 0-5) → Mark each as completes
|
||||
- When Phase 5 completes → Mark parent "completed"
|
||||
- PRESERVE all other auto-parallel tasks
|
||||
|
||||
**Standalone Mode**:
|
||||
```json
|
||||
[
|
||||
{"content": "Initialize session", "status": "pending", "activeForm": "Initializing"},
|
||||
{"content": "Phase 0: Context collection", "status": "pending", "activeForm": "Phase 0"},
|
||||
{"content": "Phase 1: Topic analysis (2-4 questions)", "status": "pending", "activeForm": "Phase 1"},
|
||||
{"content": "Phase 2: Role selection", "status": "pending", "activeForm": "Phase 2"},
|
||||
{"content": "Phase 3: Role questions (per role)", "status": "pending", "activeForm": "Phase 3"},
|
||||
{"content": "Phase 4: Conflict resolution", "status": "pending", "activeForm": "Phase 4"},
|
||||
{"content": "Phase 4.5: Final clarification + Feature decomposition", "status": "pending", "activeForm": "Phase 4.5"},
|
||||
{"content": "Phase 5: Generate specification", "status": "pending", "activeForm": "Phase 5"}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Phases
|
||||
|
||||
### Session Management
|
||||
|
||||
- Check `.workflow/active/` for existing sessions
|
||||
- Multiple → Prompt selection | Single → Use it | None → Create `WFS-[topic-slug]`
|
||||
- Parse `--count N` parameter (default: 3)
|
||||
- Store decisions in `workflow-session.json`
|
||||
|
||||
### Phase 0: Context Collection
|
||||
|
||||
**Goal**: Gather project context BEFORE user interaction
|
||||
|
||||
**Steps**:
|
||||
1. Check if `context-package.json` exists → Skip if valid
|
||||
2. Invoke `context-search-agent` (BRAINSTORM MODE - lightweight)
|
||||
3. Output: `.workflow/active/WFS-{session-id}/.process/context-package.json`
|
||||
|
||||
**Graceful Degradation**: If agent fails, continue to Phase 1 without context
|
||||
|
||||
```javascript
|
||||
Task(
|
||||
subagent_type="context-search-agent",
|
||||
run_in_background=false,
|
||||
description="Gather project context for brainstorm",
|
||||
prompt=`
|
||||
Execute context-search-agent in BRAINSTORM MODE (Phase 1-2 only).
|
||||
|
||||
Session: ${session_id}
|
||||
Task: ${task_description}
|
||||
Output: .workflow/${session_id}/.process/context-package.json
|
||||
|
||||
Required fields: metadata, project_context, assets, dependencies, conflict_detection
|
||||
`
|
||||
)
|
||||
```
|
||||
|
||||
### Phase 1: Topic Analysis
|
||||
|
||||
**Goal**: Extract keywords/challenges enriched by Phase 0 context
|
||||
|
||||
**Steps**:
|
||||
1. Load Phase 0 context (tech_stack, modules, conflict_risk)
|
||||
2. Deep topic analysis (entities, challenges, constraints, metrics)
|
||||
3. Generate 2-4 context-aware probing questions
|
||||
4. AskUserQuestion → Store to `session.intent_context`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: "实时协作平台的主要技术挑战?",
|
||||
header: "核心挑战",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "实时数据同步", description: "100+用户同时在线,状态同步复杂度高" },
|
||||
{ label: "可扩展性架构", description: "用户规模增长时的系统扩展能力" },
|
||||
{ label: "冲突解决机制", description: "多用户同时编辑的冲突处理策略" }
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "MVP阶段最关注的指标?",
|
||||
header: "优先级",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "功能完整性", description: "实现所有核心功能" },
|
||||
{ label: "用户体验", description: "流畅的交互体验和响应速度" },
|
||||
{ label: "系统稳定性", description: "高可用性和数据一致性" }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
**⚠️ CRITICAL**: Questions MUST reference topic keywords. Generic "Project type?" violates dynamic generation.
|
||||
|
||||
### Phase 2: Role Selection
|
||||
|
||||
**Goal**: User selects roles from intelligent recommendations
|
||||
|
||||
**Available Roles**: data-architect, product-manager, product-owner, scrum-master, subject-matter-expert, system-architect, test-strategist, ui-designer, ux-expert
|
||||
|
||||
**Steps**:
|
||||
1. Analyze Phase 1 keywords → Recommend count+2 roles with rationale
|
||||
2. AskUserQuestion (multiSelect=true) → Store to `session.selected_roles`
|
||||
3. If count+2 > 4, split into multiple rounds
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "请选择 3 个角色参与头脑风暴分析",
|
||||
header: "角色选择",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{ label: "system-architect", description: "实时同步架构设计和技术选型" },
|
||||
{ label: "ui-designer", description: "协作界面用户体验和状态展示" },
|
||||
{ label: "product-manager", description: "功能优先级和MVP范围决策" },
|
||||
{ label: "data-architect", description: "数据同步模型和存储方案设计" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
**⚠️ CRITICAL**: User MUST interact. NEVER auto-select without confirmation.
|
||||
|
||||
### Phase 3: Role-Specific Questions
|
||||
|
||||
**Goal**: Generate deep questions mapping role expertise to Phase 1 challenges
|
||||
|
||||
**Algorithm**:
|
||||
1. FOR each selected role:
|
||||
- Map Phase 1 challenges to role domain
|
||||
- Generate 3-4 questions (implementation depth, trade-offs, edge cases)
|
||||
- AskUserQuestion per role → Store to `session.role_decisions[role]`
|
||||
2. Process roles sequentially (one at a time for clarity)
|
||||
3. If role needs > 4 questions, split into multiple rounds
|
||||
|
||||
**Example** (system-architect):
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: "100+ 用户实时状态同步方案?",
|
||||
header: "状态同步",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Event Sourcing", description: "完整事件历史,支持回溯,存储成本高" },
|
||||
{ label: "集中式状态管理", description: "实现简单,单点瓶颈风险" },
|
||||
{ label: "CRDT", description: "去中心化,自动合并,学习曲线陡" }
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "两个用户同时编辑冲突如何解决?",
|
||||
header: "冲突解决",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "自动合并", description: "用户无感知,可能产生意外结果" },
|
||||
{ label: "手动解决", description: "用户控制,增加交互复杂度" },
|
||||
{ label: "版本控制", description: "保留历史,需要分支管理" }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Phase 4: Conflict Resolution
|
||||
|
||||
**Goal**: Resolve ACTUAL conflicts from Phase 3 answers
|
||||
|
||||
**Algorithm**:
|
||||
1. Analyze Phase 3 answers for conflicts:
|
||||
- Contradictory choices (e.g., "fast iteration" vs "complex Event Sourcing")
|
||||
- Missing integration (e.g., "Optimistic updates" but no conflict handling)
|
||||
- Implicit dependencies (e.g., "Live cursors" but no auth defined)
|
||||
2. Generate clarification questions referencing SPECIFIC Phase 3 choices
|
||||
3. AskUserQuestion (max 4 per call, multi-round) → Store to `session.cross_role_decisions`
|
||||
4. If NO conflicts: Skip Phase 4 (inform user: "未检测到跨角色冲突,跳过Phase 4")
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "CRDT 与 UI 回滚期望冲突,如何解决?\n背景:system-architect选择CRDT,ui-designer期望回滚UI",
|
||||
header: "架构冲突",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "采用 CRDT", description: "保持去中心化,调整UI期望" },
|
||||
{ label: "显示合并界面", description: "增加用户交互,展示冲突详情" },
|
||||
{ label: "切换到 OT", description: "支持回滚,增加服务器复杂度" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
### Phase 4.5: Final Clarification
|
||||
|
||||
**Purpose**: Ensure no important points missed before generating specification
|
||||
|
||||
**Steps**:
|
||||
1. Ask initial check:
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "在生成最终规范之前,是否有前面未澄清的重点需要补充?",
|
||||
header: "补充确认",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "无需补充", description: "前面的讨论已经足够完整" },
|
||||
{ label: "需要补充", description: "还有重要内容需要澄清" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
2. If "需要补充":
|
||||
- Analyze user's additional points
|
||||
- Generate progressive questions (not role-bound, interconnected)
|
||||
- AskUserQuestion (max 4 per round) → Store to `session.additional_decisions`
|
||||
- Repeat until user confirms completion
|
||||
3. If "无需补充": Proceed to Phase 5
|
||||
|
||||
**Progressive Pattern**: Questions interconnected, each round informs next, continue until resolved.
|
||||
|
||||
#### Feature Decomposition
|
||||
|
||||
After final clarification, extract implementable feature units from all Phase 1-4 decisions.
|
||||
|
||||
**Steps**:
|
||||
1. Analyze all accumulated decisions (`intent_context` + `role_decisions` + `cross_role_decisions` + `additional_decisions`)
|
||||
2. Extract candidate features: each must be an independently implementable unit with clear boundaries
|
||||
3. Generate candidate list (max 8 features) with structured format:
|
||||
- Feature ID: `F-{3-digit}` (e.g., F-001)
|
||||
- Name: kebab-case slug (e.g., `real-time-sync`, `user-auth`)
|
||||
- Description: one-sentence summary of the feature's scope
|
||||
- Related roles: which roles' decisions drive this feature
|
||||
- Priority: High / Medium / Low
|
||||
4. Present candidate list to user for confirmation:
|
||||
```javascript
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "以下是从讨论中提取的功能点清单:\n\nF-001: [name] - [description]\nF-002: [name] - [description]\n...\n\n是否需要调整?",
|
||||
header: "功能点确认",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "确认无误", description: "功能点清单完整且合理,继续生成规范" },
|
||||
{ label: "需要调整", description: "需要增加、删除或修改功能点" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
```
|
||||
5. If "需要调整": Collect adjustments and re-present until user confirms
|
||||
6. Store confirmed list to `session.feature_list`
|
||||
|
||||
**Constraints**:
|
||||
- Maximum 8 features (if more candidates, merge related items)
|
||||
- Each feature MUST be independently implementable (no implicit cross-feature dependencies)
|
||||
- Feature ID format: `F-{3-digit}` (F-001, F-002, ...)
|
||||
- Feature slug: kebab-case, descriptive of the feature scope
|
||||
|
||||
**Granularity Guidelines** (用于验证功能点粒度是否合适):
|
||||
|
||||
| Signal | Too Coarse | Just Right | Too Fine |
|
||||
|--------|-----------|------------|----------|
|
||||
| 实现范围 | 需要 5+ 个独立模块协同 | 1-3 个模块,边界清晰 | 单个函数或单个 API 端点 |
|
||||
| 角色关注 | 所有角色都深度涉及 | 2-4 个角色有实质贡献 | 仅 1 个角色关注 |
|
||||
| 可测试性 | 无法写出清晰的验收标准 | 可定义 3-5 条可测量验收标准 | 验收标准等同于单元测试 |
|
||||
| 依赖关系 | 与其他功能点循环依赖 | 依赖关系单向且可识别 | 无任何外部依赖(可能遗漏) |
|
||||
|
||||
**Quality Validation** (Step 3 提取候选功能点后,逐条验证):
|
||||
1. **独立性检查**: 该功能点是否可以在其他功能点未完成时独立交付?若否 → 考虑合并或重新划分
|
||||
2. **完整性检查**: 该功能点是否覆盖了一个用户可感知的完整价值?若否 → 可能太细,考虑合并
|
||||
3. **粒度均衡检查**: 各功能点之间的复杂度是否大致均衡(最大不超过最小的 3 倍)?若否 → 拆分过大的或合并过小的
|
||||
4. **边界清晰检查**: 是否能用一句话描述该功能点的输入和输出?若否 → 边界模糊,需重新定义
|
||||
|
||||
**Handling Vague Requirements** (当用户需求模糊时的额外步骤):
|
||||
- 如果 Phase 1-4 的决策不足以支撑功能点分解(如缺少具体业务场景、技术选型未定),在 Step 4 确认时**主动告知用户**哪些功能点的粒度可能不够精确
|
||||
- 对不确定的功能点标注 `Priority: TBD`,在后续 synthesis 阶段通过跨角色分析进一步明确
|
||||
- 如果候选功能点 ≤ 2 个,可能是需求过于抽象 → 提示用户补充更多具体场景后再分解
|
||||
|
||||
### Phase 5: Generate Specification
|
||||
|
||||
**Steps**:
|
||||
1. Load all decisions: `intent_context` + `selected_roles` + `role_decisions` + `cross_role_decisions` + `additional_decisions` + `feature_list`
|
||||
2. Transform Q&A to declarative: Questions → Headers, Answers → CONFIRMED/SELECTED statements
|
||||
3. Generate `guidance-specification.md`
|
||||
4. Update `workflow-session.json` (metadata only)
|
||||
5. Validate: No interrogative sentences, all decisions traceable
|
||||
|
||||
---
|
||||
|
||||
## Question Guidelines
|
||||
|
||||
### Core Principle
|
||||
|
||||
**Target**: 开发者(理解技术但需要从用户需求出发)
|
||||
|
||||
**Question Structure**: `[业务场景/需求前提] + [技术关注点]`
|
||||
**Option Structure**: `标签:[技术方案] + 说明:[业务影响] + [技术权衡]`
|
||||
|
||||
### Quality Rules
|
||||
|
||||
**MUST Include**:
|
||||
- ✅ All questions in Chinese (用中文提问)
|
||||
- ✅ 业务场景作为问题前提
|
||||
- ✅ 技术选项的业务影响说明
|
||||
- ✅ 量化指标和约束条件
|
||||
|
||||
**MUST Avoid**:
|
||||
- ❌ 纯技术选型无业务上下文
|
||||
- ❌ 过度抽象的用户体验问题
|
||||
- ❌ 脱离话题的通用架构问题
|
||||
|
||||
### Phase-Specific Requirements
|
||||
|
||||
| Phase | Focus | Key Requirements |
|
||||
|-------|-------|------------------|
|
||||
| 1 | 意图理解 | Reference topic keywords, 用户场景、业务约束、优先级 |
|
||||
| 2 | 角色推荐 | Intelligent analysis (NOT keyword mapping), explain relevance |
|
||||
| 3 | 角色问题 | Reference Phase 1 keywords, concrete options with trade-offs |
|
||||
| 4 | 冲突解决 | Reference SPECIFIC Phase 3 choices, explain impact on both roles |
|
||||
|
||||
---
|
||||
|
||||
## Output & Governance
|
||||
|
||||
### Output Template
|
||||
|
||||
**File**: `.workflow/active/WFS-{topic}/.brainstorming/guidance-specification.md`
|
||||
|
||||
```markdown
|
||||
# [Project] - Confirmed Guidance Specification
|
||||
|
||||
**Metadata**: [timestamp, type, focus, roles]
|
||||
|
||||
## 1. Project Positioning & Goals
|
||||
**CONFIRMED Objectives**: [from topic + Phase 1]
|
||||
**CONFIRMED Success Criteria**: [from Phase 1 answers]
|
||||
|
||||
## 2-N. [Role] Decisions
|
||||
### SELECTED Choices
|
||||
**[Question topic]**: [User's answer]
|
||||
- **Rationale**: [From option description]
|
||||
- **Impact**: [Implications]
|
||||
|
||||
### Cross-Role Considerations
|
||||
**[Conflict resolved]**: [Resolution from Phase 4]
|
||||
- **Affected Roles**: [Roles involved]
|
||||
|
||||
## Cross-Role Integration
|
||||
**CONFIRMED Integration Points**: [API/Data/Auth from multiple roles]
|
||||
|
||||
## Risks & Constraints
|
||||
**Identified Risks**: [From answers] → Mitigation: [Approach]
|
||||
|
||||
## Next Steps
|
||||
**⚠️ Automatic Continuation** (when called from auto-parallel):
|
||||
- auto-parallel assigns agents for role-specific analysis
|
||||
- Each selected role gets conceptual-planning-agent
|
||||
- Agents read this guidance-specification.md for context
|
||||
|
||||
## Feature Decomposition
|
||||
|
||||
**Constraints**: Max 8 features | Each independently implementable | ID format: F-{3-digit}
|
||||
|
||||
| Feature ID | Name | Description | Related Roles | Priority |
|
||||
|------------|------|-------------|---------------|----------|
|
||||
| F-001 | [kebab-case-slug] | [One-sentence scope description] | [role1, role2] | High/Medium/Low |
|
||||
| F-002 | [kebab-case-slug] | [One-sentence scope description] | [role1] | High/Medium/Low |
|
||||
|
||||
## Appendix: Decision Tracking
|
||||
| Decision ID | Category | Question | Selected | Phase | Rationale |
|
||||
|-------------|----------|----------|----------|-------|-----------|
|
||||
| D-001 | Intent | [Q] | [A] | 1 | [Why] |
|
||||
| D-002 | Roles | [Selected] | [Roles] | 2 | [Why] |
|
||||
| D-003+ | [Role] | [Q] | [A] | 3 | [Why] |
|
||||
```
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
.workflow/active/WFS-[topic]/
|
||||
├── workflow-session.json # Metadata ONLY
|
||||
├── .process/
|
||||
│ └── context-package.json # Phase 0 output
|
||||
└── .brainstorming/
|
||||
└── guidance-specification.md # Full guidance content
|
||||
```
|
||||
|
||||
### Session Metadata
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "WFS-{topic-slug}",
|
||||
"type": "brainstorming",
|
||||
"topic": "{original user input}",
|
||||
"selected_roles": ["system-architect", "ui-designer", "product-manager"],
|
||||
"phase_completed": "artifacts",
|
||||
"timestamp": "2025-10-24T10:30:00Z",
|
||||
"count_parameter": 3
|
||||
}
|
||||
```
|
||||
|
||||
**⚠️ Rule**: Session JSON stores ONLY metadata. All guidance content goes to guidance-specification.md.
|
||||
|
||||
### Validation Checklist
|
||||
|
||||
- ✅ No interrogative sentences (use CONFIRMED/SELECTED)
|
||||
- ✅ Every decision traceable to user answer
|
||||
- ✅ Cross-role conflicts resolved or documented
|
||||
- ✅ Next steps concrete and specific
|
||||
- ✅ No content duplication between .json and .md
|
||||
|
||||
### Update Mechanism
|
||||
|
||||
```
|
||||
IF guidance-specification.md EXISTS:
|
||||
Prompt: "Regenerate completely / Update sections / Cancel"
|
||||
ELSE:
|
||||
Run full Phase 0-5 flow
|
||||
```
|
||||
|
||||
### Governance Rules
|
||||
|
||||
- All decisions MUST use CONFIRMED/SELECTED (NO "?" in decision sections)
|
||||
- Every decision MUST trace to user answer
|
||||
- Conflicts MUST be resolved (not marked "TBD")
|
||||
- Next steps MUST be actionable
|
||||
- Topic preserved as authoritative reference
|
||||
|
||||
**CRITICAL**: Guidance is single source of truth for downstream phases. Ambiguity violates governance.
|
||||
@@ -1,433 +0,0 @@
|
||||
---
|
||||
name: auto-parallel
|
||||
description: Parallel brainstorming automation with dynamic role selection and concurrent execution across multiple perspectives
|
||||
argument-hint: "[-y|--yes] topic or challenge description [--count N]"
|
||||
allowed-tools: Skill(*), Task(*), TodoWrite(*), Read(*), Write(*), Bash(*), Glob(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-select recommended roles, skip all clarification questions, use default answers.
|
||||
|
||||
# Workflow Brainstorm Parallel Auto Command
|
||||
|
||||
## Coordinator Role
|
||||
|
||||
**This command is a pure orchestrator**: Executes 3 phases in sequence (interactive framework → parallel role analysis → synthesis), coordinating specialized commands/agents through task attachment model.
|
||||
|
||||
**Task Attachment Model**:
|
||||
- Skill execute **expands workflow** by attaching sub-tasks to current TodoWrite
|
||||
- Task agent execute **attaches analysis tasks** to orchestrator's TodoWrite
|
||||
- Phase 1: artifacts command attaches its internal tasks (Phase 1-5)
|
||||
- Phase 2: N conceptual-planning-agent tasks attached in parallel
|
||||
- Phase 3: synthesis command attaches its internal tasks
|
||||
- Orchestrator **executes these attached tasks** sequentially (Phase 1, 3) or in parallel (Phase 2)
|
||||
- After completion, attached tasks are **collapsed** back to high-level phase summary
|
||||
- This is **task expansion**, not external delegation
|
||||
|
||||
**Execution Model - Auto-Continue Workflow**:
|
||||
|
||||
This workflow runs **fully autonomously** once triggered. Phase 1 (artifacts) handles user interaction, Phase 2 (role agents) runs in parallel.
|
||||
|
||||
1. **User triggers**: `/workflow:brainstorm:auto-parallel "topic" [--count N]`
|
||||
2. **Execute Phase 1** → artifacts command (tasks ATTACHED) → Auto-continues
|
||||
3. **Execute Phase 2** → Parallel role agents (N tasks ATTACHED concurrently) → Auto-continues
|
||||
4. **Execute Phase 3** → Synthesis command (tasks ATTACHED) → Reports final summary
|
||||
|
||||
**Auto-Continue Mechanism**:
|
||||
- TodoList tracks current phase status and dynamically manages task attachment/collapse
|
||||
- When Phase 1 (artifacts) finishes executing, automatically load roles and launch Phase 2 agents
|
||||
- When Phase 2 (all agents) finishes executing, automatically execute Phase 3 synthesis
|
||||
- **⚠️ CONTINUOUS EXECUTION** - Do not stop until all phases complete
|
||||
|
||||
## Core Rules
|
||||
|
||||
1. **Start Immediately**: First action is TodoWrite initialization, second action is execute Phase 1 command
|
||||
2. **No Preliminary Analysis**: Do not analyze topic before Phase 1 - artifacts handles all analysis
|
||||
3. **Parse Every Output**: Extract selected_roles from workflow-session.json after Phase 1
|
||||
4. **Auto-Continue via TodoList**: Check TodoList status to execute next pending phase automatically
|
||||
5. **Track Progress**: Update TodoWrite dynamically with task attachment/collapse pattern
|
||||
6. **Task Attachment Model**: Skill and Task executes **attach** sub-tasks to current workflow. Orchestrator **executes** these attached tasks itself, then **collapses** them after completion
|
||||
7. **⚠️ CRITICAL: DO NOT STOP**: Continuous multi-phase workflow. After executing all attached tasks, immediately collapse them and execute next phase
|
||||
8. **Parallel Execution**: Phase 2 attaches multiple agent tasks simultaneously for concurrent execution
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
/workflow:brainstorm:auto-parallel "<topic>" [--count N] [--style-skill package-name]
|
||||
```
|
||||
|
||||
**Recommended Structured Format**:
|
||||
```bash
|
||||
/workflow:brainstorm:auto-parallel "GOAL: [objective] SCOPE: [boundaries] CONTEXT: [background]" [--count N] [--style-skill package-name]
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `topic` (required): Topic or challenge description (structured format recommended)
|
||||
- `--count N` (optional): Number of roles to select (default: 3, max: 9)
|
||||
- `--style-skill package-name` (optional): Style SKILL package to load for UI design (located at `.claude/skills/style-{package-name}/`)
|
||||
|
||||
## 3-Phase Execution
|
||||
|
||||
### Phase 1: Interactive Framework Generation
|
||||
|
||||
**Step 1: Execute** - Interactive framework generation via artifacts command
|
||||
|
||||
```javascript
|
||||
Skill(skill="workflow:brainstorm:artifacts", args="\"{topic}\" --count {N}")
|
||||
```
|
||||
|
||||
**What It Does**:
|
||||
- Topic analysis: Extract challenges, generate task-specific questions
|
||||
- Role selection: Recommend count+2 roles, user selects via AskUserQuestion
|
||||
- Role questions: Generate 3-4 questions per role, collect user decisions
|
||||
- Conflict resolution: Detect and resolve cross-role conflicts
|
||||
- Guidance generation: Transform Q&A to declarative guidance-specification.md
|
||||
|
||||
**Parse Output**:
|
||||
- **⚠️ Memory Check**: If `selected_roles[]` already in conversation memory from previous load, skip file read
|
||||
- Extract: `selected_roles[]` from workflow-session.json (if not in memory)
|
||||
- Extract: `session_id` from workflow-session.json (if not in memory)
|
||||
- Verify: guidance-specification.md exists
|
||||
|
||||
**Validation**:
|
||||
- guidance-specification.md created with confirmed decisions
|
||||
- workflow-session.json contains selected_roles[] (metadata only, no content duplication)
|
||||
- Session directory `.workflow/active/WFS-{topic}/.brainstorming/` exists
|
||||
|
||||
**TodoWrite Update (Phase 1 Skill executed - tasks attached)**:
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 0: Parameter Parsing", "status": "completed", "activeForm": "Parsing count parameter"},
|
||||
{"content": "Phase 1: Interactive Framework Generation", "status": "in_progress", "activeForm": "Executing artifacts interactive framework"},
|
||||
{"content": " → Topic analysis and question generation", "status": "in_progress", "activeForm": "Analyzing topic"},
|
||||
{"content": " → Role selection and user confirmation", "status": "pending", "activeForm": "Selecting roles"},
|
||||
{"content": " → Role questions and user decisions", "status": "pending", "activeForm": "Collecting role questions"},
|
||||
{"content": " → Conflict detection and resolution", "status": "pending", "activeForm": "Resolving conflicts"},
|
||||
{"content": " → Guidance specification generation", "status": "pending", "activeForm": "Generating guidance"},
|
||||
{"content": "Phase 2: Parallel Role Analysis", "status": "pending", "activeForm": "Executing parallel role analysis"},
|
||||
{"content": "Phase 3: Synthesis Integration", "status": "pending", "activeForm": "Executing synthesis integration"}
|
||||
]
|
||||
```
|
||||
|
||||
**Note**: Skill execute **attaches** artifacts' 5 internal tasks. Orchestrator **executes** these tasks sequentially.
|
||||
|
||||
**Next Action**: Tasks attached → **Execute Phase 1.1-1.5** sequentially
|
||||
|
||||
**TodoWrite Update (Phase 1 completed - tasks collapsed)**:
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 0: Parameter Parsing", "status": "completed", "activeForm": "Parsing count parameter"},
|
||||
{"content": "Phase 1: Interactive Framework Generation", "status": "completed", "activeForm": "Executing artifacts interactive framework"},
|
||||
{"content": "Phase 2: Parallel Role Analysis", "status": "pending", "activeForm": "Executing parallel role analysis"},
|
||||
{"content": "Phase 3: Synthesis Integration", "status": "pending", "activeForm": "Executing synthesis integration"}
|
||||
]
|
||||
```
|
||||
|
||||
**Note**: Phase 1 tasks completed and collapsed to summary.
|
||||
|
||||
**After Phase 1**: Auto-continue to Phase 2 (parallel role agent execution)
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Parallel Role Analysis Execution
|
||||
|
||||
**For Each Selected Role** (unified role-analysis command):
|
||||
```bash
|
||||
Skill(skill="workflow:brainstorm:role-analysis", args="{role-name} --session {session-id} --skip-questions")
|
||||
```
|
||||
|
||||
**What It Does**:
|
||||
- Unified command execution for each role
|
||||
- Loads topic framework from guidance-specification.md
|
||||
- Applies role-specific template and context
|
||||
- Generates analysis.md addressing framework discussion points
|
||||
- Supports optional interactive context gathering (via --include-questions flag)
|
||||
|
||||
**Parallel Execution**:
|
||||
- Launch N Skill calls simultaneously (one message with multiple Skill invokes)
|
||||
- Each role command **attached** to orchestrator's TodoWrite
|
||||
- All roles execute concurrently, each reading same guidance-specification.md
|
||||
- Each role operates independently
|
||||
- For ui-designer only: append `--style-skill {style_skill_package}` if provided
|
||||
|
||||
**Input**:
|
||||
- `selected_roles[]` from Phase 1
|
||||
- `session_id` from Phase 1
|
||||
- `guidance-specification.md` (framework reference)
|
||||
- `style_skill_package` (for ui-designer only)
|
||||
|
||||
**Validation**:
|
||||
- Each role creates `.workflow/active/WFS-{topic}/.brainstorming/{role}/analysis.md`
|
||||
- Optionally with `analysis-{slug}.md` sub-documents (max 5)
|
||||
- **File pattern**: `analysis*.md` for globbing
|
||||
- **FORBIDDEN**: `recommendations.md` or any non-`analysis` prefixed files
|
||||
- All N role analyses completed
|
||||
|
||||
|
||||
**TodoWrite Update (Phase 2 agents executed - tasks attached in parallel)**:
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 0: Parameter Parsing", "status": "completed", "activeForm": "Parsing count parameter"},
|
||||
{"content": "Phase 1: Interactive Framework Generation", "status": "completed", "activeForm": "Executing artifacts interactive framework"},
|
||||
{"content": "Phase 2: Parallel Role Analysis", "status": "in_progress", "activeForm": "Executing parallel role analysis"},
|
||||
{"content": " → Execute system-architect analysis", "status": "in_progress", "activeForm": "Executing system-architect analysis"},
|
||||
{"content": " → Execute ui-designer analysis", "status": "in_progress", "activeForm": "Executing ui-designer analysis"},
|
||||
{"content": " → Execute product-manager analysis", "status": "in_progress", "activeForm": "Executing product-manager analysis"},
|
||||
{"content": "Phase 3: Synthesis Integration", "status": "pending", "activeForm": "Executing synthesis integration"}
|
||||
]
|
||||
```
|
||||
|
||||
**Note**: Multiple Task executes **attach** N role analysis tasks simultaneously. Orchestrator **executes** these tasks in parallel.
|
||||
|
||||
**Next Action**: Tasks attached → **Execute Phase 2.1-2.N** concurrently
|
||||
|
||||
**TodoWrite Update (Phase 2 completed - tasks collapsed)**:
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 0: Parameter Parsing", "status": "completed", "activeForm": "Parsing count parameter"},
|
||||
{"content": "Phase 1: Interactive Framework Generation", "status": "completed", "activeForm": "Executing artifacts interactive framework"},
|
||||
{"content": "Phase 2: Parallel Role Analysis", "status": "completed", "activeForm": "Executing parallel role analysis"},
|
||||
{"content": "Phase 3: Synthesis Integration", "status": "pending", "activeForm": "Executing synthesis integration"}
|
||||
]
|
||||
```
|
||||
|
||||
**Note**: Phase 2 parallel tasks completed and collapsed to summary.
|
||||
|
||||
**After Phase 2**: Auto-continue to Phase 3 (synthesis)
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Synthesis Generation
|
||||
|
||||
**Step 3: Execute** - Synthesis integration via synthesis command
|
||||
|
||||
```javascript
|
||||
Skill(skill="workflow:brainstorm:synthesis", args="--session {sessionId}")
|
||||
```
|
||||
|
||||
**What It Does**:
|
||||
- Load original user intent from workflow-session.json
|
||||
- Read all role analysis.md files
|
||||
- Integrate role insights into synthesis-specification.md
|
||||
- Validate alignment with user's original objectives
|
||||
|
||||
**Input**: `sessionId` from Phase 1
|
||||
|
||||
**Validation**:
|
||||
- `.workflow/active/WFS-{topic}/.brainstorming/synthesis-specification.md` exists
|
||||
- Synthesis references all role analyses
|
||||
|
||||
**TodoWrite Update (Phase 3 Skill executed - tasks attached)**:
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 0: Parameter Parsing", "status": "completed", "activeForm": "Parsing count parameter"},
|
||||
{"content": "Phase 1: Interactive Framework Generation", "status": "completed", "activeForm": "Executing artifacts interactive framework"},
|
||||
{"content": "Phase 2: Parallel Role Analysis", "status": "completed", "activeForm": "Executing parallel role analysis"},
|
||||
{"content": "Phase 3: Synthesis Integration", "status": "in_progress", "activeForm": "Executing synthesis integration"},
|
||||
{"content": " → Load role analysis files", "status": "in_progress", "activeForm": "Loading role analyses"},
|
||||
{"content": " → Integrate insights across roles", "status": "pending", "activeForm": "Integrating insights"},
|
||||
{"content": " → Generate synthesis specification", "status": "pending", "activeForm": "Generating synthesis"}
|
||||
]
|
||||
```
|
||||
|
||||
**Note**: Skill execute **attaches** synthesis' internal tasks. Orchestrator **executes** these tasks sequentially.
|
||||
|
||||
**Next Action**: Tasks attached → **Execute Phase 3.1-3.3** sequentially
|
||||
|
||||
**TodoWrite Update (Phase 3 completed - tasks collapsed)**:
|
||||
```json
|
||||
[
|
||||
{"content": "Phase 0: Parameter Parsing", "status": "completed", "activeForm": "Parsing count parameter"},
|
||||
{"content": "Phase 1: Interactive Framework Generation", "status": "completed", "activeForm": "Executing artifacts interactive framework"},
|
||||
{"content": "Phase 2: Parallel Role Analysis", "status": "completed", "activeForm": "Executing parallel role analysis"},
|
||||
{"content": "Phase 3: Synthesis Integration", "status": "completed", "activeForm": "Executing synthesis integration"}
|
||||
]
|
||||
```
|
||||
|
||||
**Note**: Phase 3 tasks completed and collapsed to summary.
|
||||
|
||||
**Return to User**:
|
||||
```
|
||||
Brainstorming complete for session: {sessionId}
|
||||
Roles analyzed: {count}
|
||||
Synthesis: .workflow/active/WFS-{topic}/.brainstorming/synthesis-specification.md
|
||||
|
||||
✅ Next Steps:
|
||||
1. /workflow:concept-clarify --session {sessionId} # Optional refinement
|
||||
2. /workflow:plan --session {sessionId} # Generate implementation plan
|
||||
```
|
||||
|
||||
## TodoWrite Pattern
|
||||
|
||||
**Core Concept**: Dynamic task attachment and collapse for parallel brainstorming workflow with interactive framework generation and concurrent role analysis.
|
||||
|
||||
### Key Principles
|
||||
|
||||
1. **Task Attachment** (when Skill/Task executed):
|
||||
- Sub-command's or agent's internal tasks are **attached** to orchestrator's TodoWrite
|
||||
- Phase 1: `/workflow:brainstorm:artifacts` attaches 5 internal tasks (Phase 1.1-1.5)
|
||||
- Phase 2: Multiple `Task(conceptual-planning-agent)` calls attach N role analysis tasks simultaneously
|
||||
- Phase 3: `/workflow:brainstorm:synthesis` attaches 3 internal tasks (Phase 3.1-3.3)
|
||||
- First attached task marked as `in_progress`, others as `pending`
|
||||
- Orchestrator **executes** these attached tasks (sequentially for Phase 1, 3; in parallel for Phase 2)
|
||||
|
||||
2. **Task Collapse** (after sub-tasks complete):
|
||||
- Remove detailed sub-tasks from TodoWrite
|
||||
- **Collapse** to high-level phase summary
|
||||
- Example: Phase 1.1-1.5 collapse to "Execute artifacts interactive framework generation: completed"
|
||||
- Phase 2: Multiple role tasks collapse to "Execute parallel role analysis: completed"
|
||||
- Phase 3: Synthesis tasks collapse to "Execute synthesis integration: completed"
|
||||
- Maintains clean orchestrator-level view
|
||||
|
||||
3. **Continuous Execution**:
|
||||
- After collapse, automatically proceed to next pending phase
|
||||
- No user intervention required between phases
|
||||
- TodoWrite dynamically reflects current execution state
|
||||
|
||||
**Lifecycle Summary**: Initial pending tasks → Phase 1 executed (artifacts tasks ATTACHED) → Artifacts sub-tasks executed → Phase 1 completed (tasks COLLAPSED) → Phase 2 executed (N role tasks ATTACHED in parallel) → Role analyses executed concurrently → Phase 2 completed (tasks COLLAPSED) → Phase 3 executed (synthesis tasks ATTACHED) → Synthesis sub-tasks executed → Phase 3 completed (tasks COLLAPSED) → Workflow complete.
|
||||
|
||||
### Brainstorming Workflow Specific Features
|
||||
|
||||
- **Phase 1**: Interactive framework generation with user Q&A (Skill attachment)
|
||||
- **Phase 2**: Parallel role analysis execution with N concurrent agents (Task agent attachments)
|
||||
- **Phase 3**: Cross-role synthesis integration (Skill attachment)
|
||||
- **Dynamic Role Count**: `--count N` parameter determines number of Phase 2 parallel tasks (default: 3, max: 9)
|
||||
- **Mixed Execution**: Sequential (Phase 1, 3) and Parallel (Phase 2) task execution
|
||||
|
||||
|
||||
## Input Processing
|
||||
|
||||
**Count Parameter Parsing**:
|
||||
```javascript
|
||||
// Extract --count from user input
|
||||
IF user_input CONTAINS "--count":
|
||||
EXTRACT count_value FROM "--count N" pattern
|
||||
IF count_value > 9:
|
||||
count_value = 9 // Cap at maximum 9 roles
|
||||
ELSE:
|
||||
count_value = 3 // Default to 3 roles
|
||||
|
||||
// Pass to artifacts command
|
||||
EXECUTE: /workflow:brainstorm:artifacts "{topic}" --count {count_value}
|
||||
```
|
||||
|
||||
**Style-Skill Parameter Parsing**:
|
||||
```javascript
|
||||
// Extract --style-skill from user input
|
||||
IF user_input CONTAINS "--style-skill":
|
||||
EXTRACT style_skill_name FROM "--style-skill package-name" pattern
|
||||
|
||||
// Validate SKILL package exists
|
||||
skill_path = ".claude/skills/style-{style_skill_name}/SKILL.md"
|
||||
IF file_exists(skill_path):
|
||||
style_skill_package = style_skill_name
|
||||
style_reference_path = ".workflow/reference_style/{style_skill_name}"
|
||||
echo("✓ Style SKILL package found: style-{style_skill_name}")
|
||||
echo(" Design reference: {style_reference_path}")
|
||||
ELSE:
|
||||
echo("⚠ WARNING: Style SKILL package not found: {style_skill_name}")
|
||||
echo(" Expected location: {skill_path}")
|
||||
echo(" Continuing without style reference...")
|
||||
style_skill_package = null
|
||||
ELSE:
|
||||
style_skill_package = null
|
||||
echo("No style-skill specified, ui-designer will use default workflow")
|
||||
|
||||
// Store for Phase 2 ui-designer context
|
||||
CONTEXT_VARS:
|
||||
- style_skill_package: {style_skill_package}
|
||||
- style_reference_path: {style_reference_path}
|
||||
```
|
||||
|
||||
**Topic Structuring**:
|
||||
1. **Already Structured** → Pass directly to artifacts
|
||||
```
|
||||
User: "GOAL: Build platform SCOPE: 100 users CONTEXT: Real-time"
|
||||
→ Pass as-is to artifacts
|
||||
```
|
||||
|
||||
2. **Simple Text** → Pass directly (artifacts handles structuring)
|
||||
```
|
||||
User: "Build collaboration platform"
|
||||
→ artifacts will analyze and structure
|
||||
```
|
||||
|
||||
## Session Management
|
||||
|
||||
**⚡ FIRST ACTION**: Check `.workflow/active/` for existing sessions before Phase 1
|
||||
|
||||
**Multiple Sessions Support**:
|
||||
- Different Claude instances can have different brainstorming sessions
|
||||
- If multiple sessions found, prompt user to select
|
||||
- If single session found, use it
|
||||
- If no session exists, create `WFS-[topic-slug]`
|
||||
|
||||
**Session Continuity**:
|
||||
- MUST use selected session for all phases
|
||||
- Each role's context stored in session directory
|
||||
- Session isolation: Each session maintains independent state
|
||||
|
||||
## Output Structure
|
||||
|
||||
**Phase 1 Output**:
|
||||
- `.workflow/active/WFS-{topic}/.brainstorming/guidance-specification.md` (framework content)
|
||||
- `.workflow/active/WFS-{topic}/workflow-session.json` (metadata: selected_roles[], topic, timestamps, style_skill_package)
|
||||
|
||||
**Phase 2 Output**:
|
||||
- `.workflow/active/WFS-{topic}/.brainstorming/{role}/analysis.md` (one per role)
|
||||
- `.superdesign/design_iterations/` (ui-designer artifacts, if --style-skill provided)
|
||||
|
||||
**Phase 3 Output**:
|
||||
- `.workflow/active/WFS-{topic}/.brainstorming/synthesis-specification.md` (integrated analysis)
|
||||
|
||||
**⚠️ Storage Separation**: Guidance content in .md files, metadata in .json (no duplication)
|
||||
**⚠️ Style References**: When --style-skill provided, workflow-session.json stores style_skill_package name, ui-designer loads from `.claude/skills/style-{package-name}/`
|
||||
|
||||
## Available Roles
|
||||
|
||||
- data-architect (数据架构师)
|
||||
- product-manager (产品经理)
|
||||
- product-owner (产品负责人)
|
||||
- scrum-master (敏捷教练)
|
||||
- subject-matter-expert (领域专家)
|
||||
- system-architect (系统架构师)
|
||||
- test-strategist (测试策略师)
|
||||
- ui-designer (UI 设计师)
|
||||
- ux-expert (UX 专家)
|
||||
|
||||
**Role Selection**: Handled by artifacts command (intelligent recommendation + user selection)
|
||||
|
||||
## Error Handling
|
||||
|
||||
- **Role selection failure**: artifacts defaults to product-manager with explanation
|
||||
- **Agent execution failure**: Agent-specific retry with minimal dependencies
|
||||
- **Template loading issues**: Agent handles graceful degradation
|
||||
- **Synthesis conflicts**: Synthesis highlights disagreements without resolution
|
||||
- **Context overflow protection**: See below for automatic context management
|
||||
|
||||
## Context Overflow Protection
|
||||
|
||||
**Per-role limits**: See `conceptual-planning-agent.md` (< 3000 words main, < 2000 words sub-docs, max 5 sub-docs)
|
||||
|
||||
**Synthesis protection**: If total analysis > 100KB, synthesis reads only `analysis.md` files (not sub-documents)
|
||||
|
||||
**Recovery**: Check logs → reduce scope (--count 2) → use --summary-only → manual synthesis
|
||||
|
||||
**Prevention**: Start with --count 3, use structured topic format, review output sizes before synthesis
|
||||
|
||||
## Reference Information
|
||||
|
||||
**File Structure**:
|
||||
```
|
||||
.workflow/active/WFS-[topic]/
|
||||
├── workflow-session.json # Session metadata ONLY
|
||||
└── .brainstorming/
|
||||
├── guidance-specification.md # Framework (Phase 1)
|
||||
├── {role}/
|
||||
│ ├── analysis.md # Main document (with optional @references)
|
||||
│ └── analysis-{slug}.md # Section documents (max 5)
|
||||
└── synthesis-specification.md # Integration (Phase 3)
|
||||
```
|
||||
|
||||
**Template Source**: `~/.ccw/workflows/cli-templates/planning-roles/`
|
||||
@@ -1,829 +0,0 @@
|
||||
---
|
||||
name: role-analysis
|
||||
description: Unified role-specific analysis generation with interactive context gathering and incremental updates
|
||||
argument-hint: "[role-name] [--session session-id] [--update] [--include-questions] [--skip-questions]"
|
||||
allowed-tools: Task(conceptual-planning-agent), AskUserQuestion(*), TodoWrite(*), Read(*), Write(*), Edit(*), Glob(*)
|
||||
---
|
||||
|
||||
## 🎯 **Unified Role Analysis Generator**
|
||||
|
||||
### Purpose
|
||||
**Unified command for generating and updating role-specific analysis** with interactive context gathering, framework alignment, and incremental update support. Replaces 9 individual role commands with single parameterized workflow.
|
||||
|
||||
### Core Function
|
||||
- **Multi-Role Support**: Single command supports all 9 brainstorming roles
|
||||
- **Interactive Context**: Dynamic question generation based on role and framework
|
||||
- **Incremental Updates**: Merge new insights into existing analyses
|
||||
- **Framework Alignment**: Address guidance-specification.md discussion points
|
||||
- **Agent Delegation**: Use conceptual-planning-agent with role-specific templates
|
||||
|
||||
### Supported Roles
|
||||
|
||||
| Role ID | Title | Focus Area | Context Questions |
|
||||
|---------|-------|------------|-------------------|
|
||||
| `ux-expert` | UX专家 | User research, information architecture, user journey | 4 |
|
||||
| `ui-designer` | UI设计师 | Visual design, high-fidelity mockups, design systems | 4 |
|
||||
| `system-architect` | 系统架构师 | Technical architecture, scalability, integration patterns | 5 |
|
||||
| `product-manager` | 产品经理 | Product strategy, roadmap, prioritization | 4 |
|
||||
| `product-owner` | 产品负责人 | Backlog management, user stories, acceptance criteria | 4 |
|
||||
| `scrum-master` | 敏捷教练 | Process facilitation, impediment removal, team dynamics | 3 |
|
||||
| `subject-matter-expert` | 领域专家 | Domain knowledge, business rules, compliance | 4 |
|
||||
| `data-architect` | 数据架构师 | Data models, storage strategies, data flow | 5 |
|
||||
| `api-designer` | API设计师 | API contracts, versioning, integration patterns | 4 |
|
||||
|
||||
---
|
||||
|
||||
## 📋 **Usage**
|
||||
|
||||
```bash
|
||||
# Generate new analysis with interactive context
|
||||
/workflow:brainstorm:role-analysis ux-expert
|
||||
|
||||
# Generate with existing framework + context questions
|
||||
/workflow:brainstorm:role-analysis system-architect --session WFS-xxx --include-questions
|
||||
|
||||
# Update existing analysis (incremental merge)
|
||||
/workflow:brainstorm:role-analysis ui-designer --session WFS-xxx --update
|
||||
|
||||
# Quick generation (skip interactive context)
|
||||
/workflow:brainstorm:role-analysis product-manager --session WFS-xxx --skip-questions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ **Execution Protocol**
|
||||
|
||||
### Phase 1: Detection & Validation
|
||||
|
||||
**Step 1.1: Role Validation**
|
||||
```bash
|
||||
VALIDATE role_name IN [
|
||||
ux-expert, ui-designer, system-architect, product-manager,
|
||||
product-owner, scrum-master, subject-matter-expert,
|
||||
data-architect, api-designer
|
||||
]
|
||||
IF invalid:
|
||||
ERROR: "Unknown role: {role_name}. Use one of: ux-expert, ui-designer, ..."
|
||||
EXIT
|
||||
```
|
||||
|
||||
**Step 1.2: Session Detection**
|
||||
```bash
|
||||
IF --session PROVIDED:
|
||||
session_id = --session
|
||||
brainstorm_dir = .workflow/active/{session_id}/.brainstorming/
|
||||
ELSE:
|
||||
FIND .workflow/active/WFS-*/
|
||||
IF multiple:
|
||||
PROMPT user to select
|
||||
ELSE IF single:
|
||||
USE existing
|
||||
ELSE:
|
||||
ERROR: "No active session. Run /workflow:brainstorm:artifacts first"
|
||||
EXIT
|
||||
|
||||
VALIDATE brainstorm_dir EXISTS
|
||||
```
|
||||
|
||||
**Step 1.3: Framework Detection & Feature List Extraction**
|
||||
```bash
|
||||
framework_file = {brainstorm_dir}/guidance-specification.md
|
||||
IF framework_file EXISTS:
|
||||
framework_mode = true
|
||||
LOAD framework_content
|
||||
# Extract Feature Decomposition table from guidance-specification.md
|
||||
feature_list = EXTRACT_TABLE(framework_content, "Feature Decomposition")
|
||||
# feature_list format: [{id: "F-001", slug: "real-time-sync", description: "...", roles: [...], priority: "High"}, ...]
|
||||
IF feature_list NOT EMPTY:
|
||||
feature_mode = true # Use feature-point organization for sub-documents
|
||||
ELSE:
|
||||
feature_mode = false # Fall back to arbitrary-topic organization
|
||||
ELSE:
|
||||
WARN: "No framework found - will create standalone analysis"
|
||||
framework_mode = false
|
||||
feature_mode = false
|
||||
```
|
||||
|
||||
**Step 1.4: Update Mode Detection**
|
||||
```bash
|
||||
existing_analysis = {brainstorm_dir}/{role_name}/analysis*.md
|
||||
IF --update FLAG OR existing_analysis EXISTS:
|
||||
update_mode = true
|
||||
IF --update NOT PROVIDED:
|
||||
ASK: "Analysis exists. Update or regenerate?"
|
||||
OPTIONS: ["Incremental update", "Full regenerate", "Cancel"]
|
||||
ELSE:
|
||||
update_mode = false
|
||||
```
|
||||
|
||||
### Phase 2: Interactive Context Gathering
|
||||
|
||||
**Trigger Conditions**:
|
||||
- Default: Always ask unless `--skip-questions` provided
|
||||
- `--include-questions`: Force context gathering even if analysis exists
|
||||
- `--skip-questions`: Skip all interactive questions
|
||||
|
||||
**Step 2.1: Load Role Configuration**
|
||||
```javascript
|
||||
const roleConfig = {
|
||||
'ux-expert': {
|
||||
title: 'UX专家',
|
||||
focus_area: 'User research, information architecture, user journey',
|
||||
question_categories: ['User Intent', 'Requirements', 'UX'],
|
||||
question_count: 4,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/ux-expert.md'
|
||||
},
|
||||
'ui-designer': {
|
||||
title: 'UI设计师',
|
||||
focus_area: 'Visual design, high-fidelity mockups, design systems',
|
||||
question_categories: ['Requirements', 'UX', 'Feasibility'],
|
||||
question_count: 4,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/ui-designer.md'
|
||||
},
|
||||
'system-architect': {
|
||||
title: '系统架构师',
|
||||
focus_area: 'Technical architecture, scalability, integration patterns',
|
||||
question_categories: ['Scale & Performance', 'Technical Constraints', 'Architecture Complexity', 'Non-Functional Requirements'],
|
||||
question_count: 5,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/system-architect.md'
|
||||
},
|
||||
'product-manager': {
|
||||
title: '产品经理',
|
||||
focus_area: 'Product strategy, roadmap, prioritization',
|
||||
question_categories: ['User Intent', 'Requirements', 'Process'],
|
||||
question_count: 4,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/product-manager.md'
|
||||
},
|
||||
'product-owner': {
|
||||
title: '产品负责人',
|
||||
focus_area: 'Backlog management, user stories, acceptance criteria',
|
||||
question_categories: ['Requirements', 'Decisions', 'Process'],
|
||||
question_count: 4,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/product-owner.md'
|
||||
},
|
||||
'scrum-master': {
|
||||
title: '敏捷教练',
|
||||
focus_area: 'Process facilitation, impediment removal, team dynamics',
|
||||
question_categories: ['Process', 'Risk', 'Decisions'],
|
||||
question_count: 3,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/scrum-master.md'
|
||||
},
|
||||
'subject-matter-expert': {
|
||||
title: '领域专家',
|
||||
focus_area: 'Domain knowledge, business rules, compliance',
|
||||
question_categories: ['Requirements', 'Feasibility', 'Terminology'],
|
||||
question_count: 4,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/subject-matter-expert.md'
|
||||
},
|
||||
'data-architect': {
|
||||
title: '数据架构师',
|
||||
focus_area: 'Data models, storage strategies, data flow',
|
||||
question_categories: ['Architecture', 'Scale & Performance', 'Technical Constraints', 'Feasibility'],
|
||||
question_count: 5,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/data-architect.md'
|
||||
},
|
||||
'api-designer': {
|
||||
title: 'API设计师',
|
||||
focus_area: 'API contracts, versioning, integration patterns',
|
||||
question_categories: ['Architecture', 'Requirements', 'Feasibility', 'Decisions'],
|
||||
question_count: 4,
|
||||
template: '~/.ccw/workflows/cli-templates/planning-roles/api-designer.md'
|
||||
}
|
||||
};
|
||||
|
||||
config = roleConfig[role_name];
|
||||
```
|
||||
|
||||
**Step 2.2: Generate Role-Specific Questions**
|
||||
|
||||
**9-Category Taxonomy** (from synthesis.md):
|
||||
|
||||
| Category | Focus | Example Question Pattern |
|
||||
|----------|-------|--------------------------|
|
||||
| User Intent | 用户目标 | "该分析的核心目标是什么?" |
|
||||
| Requirements | 需求细化 | "需求的优先级如何排序?" |
|
||||
| Architecture | 架构决策 | "技术栈的选择考量?" |
|
||||
| UX | 用户体验 | "交互复杂度的取舍?" |
|
||||
| Feasibility | 可行性 | "资源约束下的实现范围?" |
|
||||
| Risk | 风险管理 | "风险容忍度是多少?" |
|
||||
| Process | 流程规范 | "开发迭代的节奏?" |
|
||||
| Decisions | 决策确认 | "冲突的解决方案?" |
|
||||
| Terminology | 术语统一 | "统一使用哪个术语?" |
|
||||
| Scale & Performance | 性能扩展 | "预期的负载和性能要求?" |
|
||||
| Technical Constraints | 技术约束 | "现有技术栈的限制?" |
|
||||
| Architecture Complexity | 架构复杂度 | "架构的复杂度权衡?" |
|
||||
| Non-Functional Requirements | 非功能需求 | "可用性和可维护性要求?" |
|
||||
|
||||
**Question Generation Algorithm**:
|
||||
```javascript
|
||||
async function generateQuestions(role_name, framework_content) {
|
||||
const config = roleConfig[role_name];
|
||||
const questions = [];
|
||||
|
||||
// Parse framework for keywords
|
||||
const keywords = extractKeywords(framework_content);
|
||||
|
||||
// Generate category-specific questions
|
||||
for (const category of config.question_categories) {
|
||||
const question = generateCategoryQuestion(category, keywords, role_name);
|
||||
questions.push(question);
|
||||
}
|
||||
|
||||
return questions.slice(0, config.question_count);
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2.3: Multi-Round Question Execution**
|
||||
|
||||
```javascript
|
||||
const BATCH_SIZE = 4;
|
||||
const user_context = {};
|
||||
|
||||
for (let i = 0; i < questions.length; i += BATCH_SIZE) {
|
||||
const batch = questions.slice(i, i + BATCH_SIZE);
|
||||
const currentRound = Math.floor(i / BATCH_SIZE) + 1;
|
||||
const totalRounds = Math.ceil(questions.length / BATCH_SIZE);
|
||||
|
||||
console.log(`\n[Round ${currentRound}/${totalRounds}] ${config.title} 上下文询问\n`);
|
||||
|
||||
AskUserQuestion({
|
||||
questions: batch.map(q => ({
|
||||
question: q.question,
|
||||
header: q.category.substring(0, 12),
|
||||
multiSelect: false,
|
||||
options: q.options.map(opt => ({
|
||||
label: opt.label,
|
||||
description: opt.description
|
||||
}))
|
||||
}))
|
||||
});
|
||||
|
||||
// Store responses before next round
|
||||
for (const answer of responses) {
|
||||
user_context[answer.question] = {
|
||||
answer: answer.selected,
|
||||
category: answer.category,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Save context to file
|
||||
Write(
|
||||
`${brainstorm_dir}/${role_name}/${role_name}-context.md`,
|
||||
formatUserContext(user_context)
|
||||
);
|
||||
```
|
||||
|
||||
**Question Quality Rules** (from artifacts.md):
|
||||
|
||||
**MUST Include**:
|
||||
- ✅ All questions in Chinese (用中文提问)
|
||||
- ✅ 业务场景作为问题前提
|
||||
- ✅ 技术选项的业务影响说明
|
||||
- ✅ 量化指标和约束条件
|
||||
|
||||
**MUST Avoid**:
|
||||
- ❌ 纯技术选型无业务上下文
|
||||
- ❌ 过度抽象的通用问题
|
||||
- ❌ 脱离框架的重复询问
|
||||
|
||||
### Phase 3: Agent Execution
|
||||
|
||||
**Step 3.1: Load Session Metadata**
|
||||
```bash
|
||||
session_metadata = Read(.workflow/active/{session_id}/workflow-session.json)
|
||||
original_topic = session_metadata.topic
|
||||
selected_roles = session_metadata.selected_roles
|
||||
```
|
||||
|
||||
**Step 3.2: Prepare Agent Context**
|
||||
```javascript
|
||||
const agentContext = {
|
||||
role_name: role_name,
|
||||
role_config: roleConfig[role_name],
|
||||
output_location: `${brainstorm_dir}/${role_name}/`,
|
||||
framework_mode: framework_mode,
|
||||
feature_mode: feature_mode,
|
||||
feature_list: feature_mode ? feature_list : null, // From guidance-spec Feature Decomposition
|
||||
framework_path: framework_mode ? `${brainstorm_dir}/guidance-specification.md` : null,
|
||||
update_mode: update_mode,
|
||||
user_context: user_context,
|
||||
original_topic: original_topic,
|
||||
session_id: session_id
|
||||
};
|
||||
```
|
||||
|
||||
**Step 3.3: Execute Conceptual Planning Agent**
|
||||
|
||||
**Framework-Based Analysis** (when guidance-specification.md exists):
|
||||
```javascript
|
||||
// Build feature list injection block (only when feature_mode is true)
|
||||
const featureListBlock = feature_mode ? `
|
||||
## Feature Point List (from guidance-specification.md Feature Decomposition)
|
||||
${feature_list.map(f => `- **${f.id}** (${f.slug}): ${f.description} [Priority: ${f.priority}]`).join('\n')}
|
||||
|
||||
**IMPORTANT - Feature-Point Output Organization**:
|
||||
- Generate ONE sub-document per feature: analysis-F-{id}-{slug}.md (e.g., analysis-${feature_list[0].id}-${feature_list[0].slug}.md)
|
||||
- Generate ONE cross-cutting document: analysis-cross-cutting.md
|
||||
- analysis.md is a role overview INDEX only (< 1500 words), NOT a full analysis
|
||||
- Each feature sub-document < 2000 words, cross-cutting < 2000 words
|
||||
- Total across all files < 15000 words
|
||||
` : `
|
||||
## Output Organization (fallback: no feature list available)
|
||||
- Generate analysis.md as main document (< 3000 words)
|
||||
- Optionally split into analysis-{slug}.md sub-documents (max 5, < 2000 words each)
|
||||
- Total < 15000 words
|
||||
`;
|
||||
|
||||
Task(
|
||||
subagent_type="conceptual-planning-agent",
|
||||
run_in_background=false,
|
||||
description=`Generate ${role_name} analysis`,
|
||||
prompt=`
|
||||
[FLOW_CONTROL]
|
||||
|
||||
Execute ${role_name} analysis for existing topic framework
|
||||
|
||||
## Context Loading
|
||||
ASSIGNED_ROLE: ${role_name}
|
||||
OUTPUT_LOCATION: ${agentContext.output_location}
|
||||
ANALYSIS_MODE: ${framework_mode ? "framework_based" : "standalone"}
|
||||
FEATURE_MODE: ${feature_mode}
|
||||
UPDATE_MODE: ${update_mode}
|
||||
|
||||
## Flow Control Steps
|
||||
1. **load_topic_framework**
|
||||
- Action: Load structured topic discussion framework
|
||||
- Command: Read(${agentContext.framework_path})
|
||||
- Output: topic_framework_content
|
||||
|
||||
2. **load_role_template**
|
||||
- Action: Load ${role_name} planning template
|
||||
- Command: Read(${roleConfig[role_name].template})
|
||||
- Output: role_template_guidelines
|
||||
|
||||
3. **load_session_metadata**
|
||||
- Action: Load session metadata and user intent
|
||||
- Command: Read(.workflow/active/${session_id}/workflow-session.json)
|
||||
- Output: session_context
|
||||
|
||||
4. **load_user_context** (if exists)
|
||||
- Action: Load interactive context responses
|
||||
- Command: Read(${brainstorm_dir}/${role_name}/${role_name}-context.md)
|
||||
- Output: user_context_answers
|
||||
|
||||
5. **${update_mode ? 'load_existing_analysis' : 'skip'}**
|
||||
${update_mode ? `
|
||||
- Action: Load existing analysis for incremental update
|
||||
- Command: Read(${brainstorm_dir}/${role_name}/analysis.md)
|
||||
- Output: existing_analysis_content
|
||||
` : ''}
|
||||
|
||||
${featureListBlock}
|
||||
|
||||
## Analysis Requirements
|
||||
**Primary Reference**: Original user prompt from workflow-session.json is authoritative
|
||||
**Framework Source**: Address all discussion points in guidance-specification.md from ${role_name} perspective
|
||||
**User Context Integration**: Incorporate interactive Q&A responses into analysis
|
||||
**Role Focus**: ${roleConfig[role_name].focus_area}
|
||||
**Template Integration**: Apply role template guidelines within framework structure
|
||||
${feature_mode ? `**Feature Organization**: Organize analysis by feature points - each feature gets its own sub-document. Cross-cutting concerns (architecture decisions, tech choices, shared patterns spanning multiple features) go into analysis-cross-cutting.md.` : ''}
|
||||
|
||||
## Expected Deliverables
|
||||
${feature_mode ? `
|
||||
1. **analysis.md** - Role overview index (< 1500 words): role perspective summary, feature point index with @-references to sub-documents, cross-cutting summary
|
||||
2. **analysis-cross-cutting.md** - Cross-feature decisions (< 2000 words): architecture decisions, technology choices, shared patterns that span multiple features
|
||||
3. **analysis-F-{id}-{slug}.md** - One per feature (< 2000 words each): role-specific analysis, recommendations, considerations for that feature
|
||||
4. **Framework Reference**: @../guidance-specification.md (if framework_mode)
|
||||
5. **User Context Reference**: @./${role_name}-context.md (if user context exists)
|
||||
6. **User Intent Alignment**: Validate against session_context
|
||||
` : `
|
||||
1. **analysis.md** (main document, optionally with analysis-{slug}.md sub-documents)
|
||||
2. **Framework Reference**: @../guidance-specification.md (if framework_mode)
|
||||
3. **User Context Reference**: @./${role_name}-context.md (if user context exists)
|
||||
4. **User Intent Alignment**: Validate against session_context
|
||||
`}
|
||||
|
||||
## Update Requirements (if UPDATE_MODE)
|
||||
- **Preserve Structure**: Maintain existing analysis structure
|
||||
- **Add "Clarifications" Section**: Document new user context with timestamp
|
||||
- **Merge Insights**: Integrate new perspectives without removing existing content
|
||||
- **Resolve Conflicts**: If new context contradicts existing analysis, document both and recommend resolution
|
||||
|
||||
## Completion Criteria
|
||||
- Address each discussion point from guidance-specification.md with ${role_name} expertise
|
||||
- Provide actionable recommendations from ${role_name} perspective within analysis files
|
||||
- All output files MUST start with "analysis" prefix (no recommendations.md or other naming)
|
||||
${feature_mode ? `- Each feature from the feature list has a corresponding analysis-F-{id}-{slug}.md file
|
||||
- analysis-cross-cutting.md exists with cross-feature decisions
|
||||
- analysis.md serves as index (< 1500 words), NOT a full analysis document` : ''}
|
||||
- Reference framework document using @ notation for integration
|
||||
- Update workflow-session.json with completion status
|
||||
`
|
||||
);
|
||||
```
|
||||
|
||||
### Phase 4: Validation & Finalization
|
||||
|
||||
**Step 4.1: Validate Output**
|
||||
```bash
|
||||
VERIFY EXISTS: ${brainstorm_dir}/${role_name}/analysis.md
|
||||
VERIFY CONTAINS: "@../guidance-specification.md" (if framework_mode)
|
||||
IF user_context EXISTS:
|
||||
VERIFY CONTAINS: "@./${role_name}-context.md" OR "## Clarifications" section
|
||||
```
|
||||
|
||||
**Step 4.2: Update Session Metadata**
|
||||
```json
|
||||
{
|
||||
"phases": {
|
||||
"BRAINSTORM": {
|
||||
"${role_name}": {
|
||||
"status": "${update_mode ? 'updated' : 'completed'}",
|
||||
"completed_at": "timestamp",
|
||||
"framework_addressed": true,
|
||||
"context_gathered": user_context ? true : false,
|
||||
"output_location": "${brainstorm_dir}/${role_name}/analysis.md",
|
||||
"update_history": [
|
||||
{
|
||||
"timestamp": "ISO8601",
|
||||
"mode": "${update_mode ? 'incremental' : 'initial'}",
|
||||
"context_questions": question_count
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4.3: Completion Report**
|
||||
```markdown
|
||||
✅ ${roleConfig[role_name].title} Analysis Complete
|
||||
|
||||
**Output**: ${brainstorm_dir}/${role_name}/analysis.md
|
||||
**Mode**: ${update_mode ? 'Incremental Update' : 'New Generation'}
|
||||
**Framework**: ${framework_mode ? '✓ Aligned' : '✗ Standalone'}
|
||||
**Context Questions**: ${question_count} answered
|
||||
|
||||
${update_mode ? '
|
||||
**Changes**:
|
||||
- Added "Clarifications" section with new user context
|
||||
- Merged new insights into existing sections
|
||||
- Resolved conflicts with framework alignment
|
||||
' : ''}
|
||||
|
||||
**Next Steps**:
|
||||
${selected_roles.length > 1 ? `
|
||||
- Continue with other roles: ${selected_roles.filter(r => r !== role_name).join(', ')}
|
||||
- Run synthesis: /workflow:brainstorm:synthesis --session ${session_id}
|
||||
` : `
|
||||
- Clarify insights: /workflow:brainstorm:synthesis --session ${session_id}
|
||||
- Generate plan: /workflow:plan --session ${session_id}
|
||||
`}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 **TodoWrite Integration**
|
||||
|
||||
### Workflow Progress Tracking
|
||||
|
||||
```javascript
|
||||
TodoWrite({
|
||||
todos: [
|
||||
{
|
||||
content: "Phase 1: Detect session and validate role configuration",
|
||||
status: "in_progress",
|
||||
activeForm: "Detecting session and role"
|
||||
},
|
||||
{
|
||||
content: "Phase 2: Interactive context gathering with AskUserQuestion",
|
||||
status: "pending",
|
||||
activeForm: "Gathering user context"
|
||||
},
|
||||
{
|
||||
content: "Phase 3: Execute conceptual-planning-agent for role analysis",
|
||||
status: "pending",
|
||||
activeForm: "Executing agent analysis"
|
||||
},
|
||||
{
|
||||
content: "Phase 4: Validate output and update session metadata",
|
||||
status: "pending",
|
||||
activeForm: "Finalizing and validating"
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 **Output Structure**
|
||||
|
||||
### Directory Layout
|
||||
|
||||
**Feature-point mode** (when `feature_list` available):
|
||||
```
|
||||
.workflow/active/WFS-{session}/.brainstorming/
|
||||
├── guidance-specification.md # Framework with Feature Decomposition
|
||||
└── {role-name}/
|
||||
├── {role-name}-context.md # Interactive Q&A responses
|
||||
├── analysis.md # Role overview INDEX (< 1500 words)
|
||||
├── analysis-cross-cutting.md # Cross-feature decisions (< 2000 words)
|
||||
├── analysis-F-001-{slug}.md # Per-feature analysis (< 2000 words)
|
||||
├── analysis-F-002-{slug}.md # Per-feature analysis (< 2000 words)
|
||||
└── analysis-F-00N-{slug}.md # One per feature (max 8)
|
||||
```
|
||||
|
||||
**Fallback mode** (when `feature_list` NOT available):
|
||||
```
|
||||
.workflow/active/WFS-{session}/.brainstorming/
|
||||
├── guidance-specification.md # Framework (if exists)
|
||||
└── {role-name}/
|
||||
├── {role-name}-context.md # Interactive Q&A responses
|
||||
├── analysis.md # Main analysis (REQUIRED)
|
||||
└── analysis-{slug}.md # Section documents (optional, max 5)
|
||||
```
|
||||
|
||||
### Analysis Document Structure - Feature-Point Mode (New Generation)
|
||||
|
||||
**analysis.md** (Role Overview Index, < 1500 words):
|
||||
```markdown
|
||||
# ${roleConfig[role_name].title} Analysis: [Topic from Framework]
|
||||
|
||||
## Framework Reference
|
||||
**Topic Framework**: @../guidance-specification.md
|
||||
**Role Focus**: ${roleConfig[role_name].focus_area}
|
||||
**User Context**: @./${role_name}-context.md
|
||||
|
||||
## Role Perspective Overview
|
||||
[Brief summary of this role's perspective on the overall project]
|
||||
|
||||
## Feature Point Index
|
||||
| Feature | Sub-document | Key Insight |
|
||||
|---------|-------------|-------------|
|
||||
| F-001: [name] | @./analysis-F-001-{slug}.md | [One-line summary] |
|
||||
| F-002: [name] | @./analysis-F-002-{slug}.md | [One-line summary] |
|
||||
|
||||
## Cross-Cutting Summary
|
||||
**Full analysis**: @./analysis-cross-cutting.md
|
||||
[Brief overview of cross-feature decisions and shared patterns]
|
||||
|
||||
---
|
||||
*Generated by ${role_name} analysis addressing structured framework*
|
||||
```
|
||||
|
||||
**analysis-cross-cutting.md** (< 2000 words):
|
||||
```markdown
|
||||
# Cross-Cutting Analysis: ${roleConfig[role_name].title}
|
||||
|
||||
## Architecture Decisions
|
||||
[Decisions that span multiple features]
|
||||
|
||||
## Technology Choices
|
||||
[Shared technology selections and rationale]
|
||||
|
||||
## Shared Patterns
|
||||
[Common patterns, constraints, and conventions across features]
|
||||
|
||||
## ${roleConfig[role_name].title} Recommendations
|
||||
[Role-wide strategic recommendations]
|
||||
```
|
||||
|
||||
**analysis-F-{id}-{slug}.md** (< 2000 words each):
|
||||
```markdown
|
||||
# Feature ${id}: [Feature Name] - ${roleConfig[role_name].title} Analysis
|
||||
|
||||
## Feature Overview
|
||||
[Role-specific perspective on this feature's scope and goals]
|
||||
|
||||
## Analysis
|
||||
[Detailed role-specific analysis for this feature]
|
||||
|
||||
## Recommendations
|
||||
[Actionable recommendations for this feature from role perspective]
|
||||
|
||||
## Dependencies & Risks
|
||||
[Cross-feature dependencies and risks from role viewpoint]
|
||||
```
|
||||
|
||||
### Analysis Document Structure - Fallback Mode (New Generation)
|
||||
|
||||
```markdown
|
||||
# ${roleConfig[role_name].title} Analysis: [Topic from Framework]
|
||||
|
||||
## Framework Reference
|
||||
**Topic Framework**: @../guidance-specification.md
|
||||
**Role Focus**: ${roleConfig[role_name].focus_area}
|
||||
**User Context**: @./${role_name}-context.md
|
||||
|
||||
## User Context Summary
|
||||
**Context Gathered**: ${question_count} questions answered
|
||||
**Categories**: ${question_categories.join(', ')}
|
||||
|
||||
${user_context ? formatContextSummary(user_context) : ''}
|
||||
|
||||
## Discussion Points Analysis
|
||||
[Address each point from guidance-specification.md with ${role_name} expertise]
|
||||
|
||||
### Core Requirements (from framework)
|
||||
[Role-specific perspective on requirements]
|
||||
|
||||
### Technical Considerations (from framework)
|
||||
[Role-specific technical analysis]
|
||||
|
||||
### User Experience Factors (from framework)
|
||||
[Role-specific UX considerations]
|
||||
|
||||
### Implementation Challenges (from framework)
|
||||
[Role-specific challenges and solutions]
|
||||
|
||||
### Success Metrics (from framework)
|
||||
[Role-specific metrics and KPIs]
|
||||
|
||||
## ${roleConfig[role_name].title} Specific Recommendations
|
||||
[Role-specific actionable strategies]
|
||||
|
||||
---
|
||||
*Generated by ${role_name} analysis addressing structured framework*
|
||||
*Context gathered: ${new Date().toISOString()}*
|
||||
```
|
||||
|
||||
### Analysis Document Structure (Incremental Update)
|
||||
|
||||
```markdown
|
||||
# ${roleConfig[role_name].title} Analysis: [Topic]
|
||||
|
||||
## Framework Reference
|
||||
[Existing content preserved]
|
||||
|
||||
## Clarifications
|
||||
### Session ${new Date().toISOString().split('T')[0]}
|
||||
${Object.entries(user_context).map(([q, a]) => `
|
||||
- **Q**: ${q} (Category: ${a.category})
|
||||
**A**: ${a.answer}
|
||||
`).join('\n')}
|
||||
|
||||
## User Context Summary
|
||||
[Updated with new context]
|
||||
|
||||
## Discussion Points Analysis
|
||||
[Existing content enhanced with new insights]
|
||||
|
||||
[Rest of sections updated based on clarifications]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 **Integration with Other Commands**
|
||||
|
||||
### Called By
|
||||
- `/workflow:brainstorm:auto-parallel` (Phase 2 - parallel role execution)
|
||||
- Manual invocation for single-role analysis
|
||||
|
||||
### Calls To
|
||||
- `conceptual-planning-agent` (agent execution)
|
||||
- `AskUserQuestion` (interactive context gathering)
|
||||
|
||||
### Coordinates With
|
||||
- `/workflow:brainstorm:artifacts` (creates framework for role analysis)
|
||||
- `/workflow:brainstorm:synthesis` (reads role analyses for integration)
|
||||
|
||||
---
|
||||
|
||||
## ✅ **Quality Assurance**
|
||||
|
||||
### Required Analysis Elements
|
||||
- [ ] Framework discussion points addressed (if framework_mode)
|
||||
- [ ] User context integrated (if context gathered)
|
||||
- [ ] Role template guidelines applied
|
||||
- [ ] Output files follow naming convention (analysis*.md only)
|
||||
- [ ] Framework reference using @ notation
|
||||
- [ ] Session metadata updated
|
||||
- [ ] Feature-point organization used when feature_list available (if feature_mode)
|
||||
- [ ] analysis.md is index only (< 1500 words) when in feature_mode
|
||||
- [ ] analysis-cross-cutting.md exists when in feature_mode
|
||||
- [ ] One analysis-F-{id}-{slug}.md per feature when in feature_mode
|
||||
|
||||
### Context Quality
|
||||
- [ ] Questions in Chinese with business context
|
||||
- [ ] Options include technical trade-offs
|
||||
- [ ] Categories aligned with role focus
|
||||
- [ ] No generic questions unrelated to framework
|
||||
|
||||
### Update Quality (if update_mode)
|
||||
- [ ] "Clarifications" section added with timestamp
|
||||
- [ ] New insights merged without content loss
|
||||
- [ ] Conflicts documented and resolved
|
||||
- [ ] Framework alignment maintained
|
||||
|
||||
---
|
||||
|
||||
## 🎛️ **Command Parameters**
|
||||
|
||||
### Required Parameters
|
||||
- `[role-name]`: Role identifier (ux-expert, ui-designer, system-architect, etc.)
|
||||
|
||||
### Optional Parameters
|
||||
- `--session [session-id]`: Specify brainstorming session (auto-detect if omitted)
|
||||
- `--update`: Force incremental update mode (auto-detect if analysis exists)
|
||||
- `--include-questions`: Force context gathering even if analysis exists
|
||||
- `--skip-questions`: Skip all interactive context gathering
|
||||
- `--style-skill [package]`: For ui-designer only, load style SKILL package
|
||||
|
||||
### Parameter Combinations
|
||||
|
||||
| Scenario | Command | Behavior |
|
||||
|----------|---------|----------|
|
||||
| New analysis | `role-analysis ux-expert` | Generate + ask context questions |
|
||||
| Quick generation | `role-analysis ux-expert --skip-questions` | Generate without context |
|
||||
| Update existing | `role-analysis ux-expert --update` | Ask clarifications + merge |
|
||||
| Force questions | `role-analysis ux-expert --include-questions` | Ask even if exists |
|
||||
| Specific session | `role-analysis ux-expert --session WFS-xxx` | Target specific session |
|
||||
|
||||
---
|
||||
|
||||
## 🚫 **Error Handling**
|
||||
|
||||
### Invalid Role Name
|
||||
```
|
||||
ERROR: Unknown role: "ui-expert"
|
||||
Valid roles: ux-expert, ui-designer, system-architect, product-manager,
|
||||
product-owner, scrum-master, subject-matter-expert,
|
||||
data-architect, api-designer
|
||||
```
|
||||
|
||||
### No Active Session
|
||||
```
|
||||
ERROR: No active brainstorming session found
|
||||
Run: /workflow:brainstorm:artifacts "[topic]" to create session
|
||||
```
|
||||
|
||||
### Missing Framework (with warning)
|
||||
```
|
||||
WARN: No guidance-specification.md found
|
||||
Generating standalone analysis without framework alignment
|
||||
Recommend: Run /workflow:brainstorm:artifacts first for better results
|
||||
```
|
||||
|
||||
### Agent Execution Failure
|
||||
```
|
||||
ERROR: Conceptual planning agent failed
|
||||
Check: ${brainstorm_dir}/${role_name}/error.log
|
||||
Action: Retry with --skip-questions or check framework validity
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 **Advanced Usage**
|
||||
|
||||
### Batch Role Generation (via auto-parallel)
|
||||
```bash
|
||||
# This command handles multiple roles in parallel
|
||||
/workflow:brainstorm:auto-parallel "topic" --count 3
|
||||
# → Internally calls role-analysis for each selected role
|
||||
```
|
||||
|
||||
### Manual Multi-Role Workflow
|
||||
```bash
|
||||
# 1. Create framework
|
||||
/workflow:brainstorm:artifacts "Build real-time collaboration platform" --count 3
|
||||
|
||||
# 2. Generate each role with context
|
||||
/workflow:brainstorm:role-analysis system-architect --include-questions
|
||||
/workflow:brainstorm:role-analysis ui-designer --include-questions
|
||||
/workflow:brainstorm:role-analysis product-manager --include-questions
|
||||
|
||||
# 3. Synthesize insights
|
||||
/workflow:brainstorm:synthesis --session WFS-xxx
|
||||
```
|
||||
|
||||
### Iterative Refinement
|
||||
```bash
|
||||
# Initial generation
|
||||
/workflow:brainstorm:role-analysis ux-expert
|
||||
|
||||
# User reviews and wants more depth
|
||||
/workflow:brainstorm:role-analysis ux-expert --update --include-questions
|
||||
# → Asks clarification questions, merges new insights
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 **Reference Information**
|
||||
|
||||
### Role Template Locations
|
||||
- Templates: `~/.ccw/workflows/cli-templates/planning-roles/`
|
||||
- Format: `{role-name}.md` (e.g., `ux-expert.md`, `system-architect.md`)
|
||||
|
||||
### Related Commands
|
||||
- `/workflow:brainstorm:artifacts` - Create framework and select roles
|
||||
- `/workflow:brainstorm:auto-parallel` - Parallel multi-role execution
|
||||
- `/workflow:brainstorm:synthesis` - Integrate role analyses
|
||||
- `/workflow:plan` - Generate implementation plan from synthesis
|
||||
|
||||
### Context Package
|
||||
- Location: `.workflow/active/WFS-{session}/.process/context-package.json`
|
||||
- Used by: `context-search-agent` (Phase 0 of artifacts)
|
||||
- Contains: Project context, tech stack, conflict risks
|
||||
@@ -1,847 +0,0 @@
|
||||
---
|
||||
name: synthesis
|
||||
description: Clarify and refine role analyses through intelligent Q&A and targeted updates with synthesis agent
|
||||
argument-hint: "[-y|--yes] [optional: --session session-id]"
|
||||
allowed-tools: Task(conceptual-planning-agent), TodoWrite(*), Read(*), Write(*), Edit(*), Glob(*), AskUserQuestion(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-select all enhancements, skip clarification questions, use default answers.
|
||||
|
||||
## Overview
|
||||
|
||||
Eight-phase workflow to eliminate ambiguities, enhance conceptual depth, and generate per-feature specifications:
|
||||
|
||||
**Phase 1-2**: Session detection → File discovery → Path preparation
|
||||
**Phase 3A**: Cross-role analysis agent → Generate recommendations + feature_conflict_map
|
||||
**Phase 4**: User selects enhancements → User answers clarifications (via AskUserQuestion)
|
||||
**Phase 5**: Parallel update agents (one per role)
|
||||
**Phase 6**: Parallel feature spec generation (one agent per feature) [feature_mode only]
|
||||
**Phase 6.5**: Feature index generation (feature-index.json) [feature_mode only]
|
||||
**Phase 7**: Context package update → Metadata update → Completion report
|
||||
|
||||
All user interactions use AskUserQuestion tool (max 4 questions per call, multi-round).
|
||||
|
||||
**Document Flow**:
|
||||
- Input: `[role]/analysis.md` (index files), `guidance-specification.md`, session metadata
|
||||
- Output: Updated `[role]/analysis*.md` with Enhancements + Clarifications sections
|
||||
- Output (feature_mode): `feature-specs/F-{id}-{slug}.md` per feature + `feature-index.json`
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Phase Summary
|
||||
|
||||
| Phase | Goal | Executor | Output |
|
||||
|-------|------|----------|--------|
|
||||
| 1 | Session detection | Main flow | session_id, brainstorm_dir |
|
||||
| 2 | File discovery | Main flow | role_analysis_paths |
|
||||
| 3A | Cross-role analysis | Agent | enhancement_recommendations, feature_conflict_map |
|
||||
| 4 | User interaction | Main flow + AskUserQuestion | update_plan |
|
||||
| 5 | Document updates | Parallel agents | Updated analysis*.md |
|
||||
| 6 | Feature spec generation | Parallel agents | feature-specs/F-{id}-{slug}.md [feature_mode] |
|
||||
| 6.5 | Feature index generation | Main flow | feature-index.json [feature_mode] |
|
||||
| 7 | Finalization | Main flow | context-package.json, report |
|
||||
|
||||
### AskUserQuestion Pattern
|
||||
|
||||
```javascript
|
||||
// Enhancement selection (multi-select)
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: "请选择要应用的改进建议",
|
||||
header: "改进选择",
|
||||
multiSelect: true,
|
||||
options: [
|
||||
{ label: "EP-001: API Contract", description: "添加详细的请求/响应 schema 定义" },
|
||||
{ label: "EP-002: User Intent", description: "明确用户需求优先级和验收标准" }
|
||||
]
|
||||
}]
|
||||
})
|
||||
|
||||
// Clarification questions (single-select, multi-round)
|
||||
AskUserQuestion({
|
||||
questions: [
|
||||
{
|
||||
question: "MVP 阶段的核心目标是什么?",
|
||||
header: "用户意图",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "快速验证", description: "最小功能集,快速上线获取反馈" },
|
||||
{ label: "技术壁垒", description: "完善架构,为长期发展打基础" },
|
||||
{ label: "功能完整", description: "覆盖所有规划功能,延迟上线" }
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task Tracking
|
||||
|
||||
```json
|
||||
[
|
||||
{"content": "Detect session and validate analyses", "status": "pending", "activeForm": "Detecting session"},
|
||||
{"content": "Discover role analysis file paths", "status": "pending", "activeForm": "Discovering paths"},
|
||||
{"content": "Execute analysis agent (cross-role analysis + feature conflict map)", "status": "pending", "activeForm": "Executing analysis"},
|
||||
{"content": "Present enhancements via AskUserQuestion", "status": "pending", "activeForm": "Selecting enhancements"},
|
||||
{"content": "Clarification questions via AskUserQuestion", "status": "pending", "activeForm": "Clarifying"},
|
||||
{"content": "Execute parallel update agents", "status": "pending", "activeForm": "Updating documents"},
|
||||
{"content": "Generate parallel feature specs (feature_mode only)", "status": "pending", "activeForm": "Generating feature specs"},
|
||||
{"content": "Generate feature-index.json (feature_mode only)", "status": "pending", "activeForm": "Building feature index"},
|
||||
{"content": "Update context package and metadata", "status": "pending", "activeForm": "Finalizing"}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Execution Phases
|
||||
|
||||
### Phase 1: Discovery & Validation
|
||||
|
||||
1. **Detect Session**: Use `--session` parameter or find `.workflow/active/WFS-*`
|
||||
2. **Validate Files**:
|
||||
- `guidance-specification.md` (optional, warn if missing)
|
||||
- `*/analysis*.md` (required, error if empty)
|
||||
3. **Load User Intent**: Extract from `workflow-session.json`
|
||||
4. **Detect Feature Mode**: Check if role analyses use feature-point organization
|
||||
```javascript
|
||||
// Feature mode is active when:
|
||||
// 1. guidance-specification.md contains Feature Decomposition table
|
||||
// 2. Role directories contain analysis-F-{id}-*.md files
|
||||
const has_feature_decomposition = guidanceSpecContent &&
|
||||
guidanceSpecContent.includes('Feature Decomposition');
|
||||
const has_feature_subdocs = Glob(`${brainstorm_dir}/*/analysis-F-*-*.md`).length > 0;
|
||||
const feature_mode = has_feature_decomposition && has_feature_subdocs;
|
||||
|
||||
// Extract feature_list from guidance-spec if feature_mode
|
||||
if (feature_mode) {
|
||||
feature_list = extractFeatureDecompositionTable(guidanceSpecContent);
|
||||
// feature_list: [{id, slug, description, roles, priority}, ...]
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Role Discovery & Path Preparation
|
||||
|
||||
**Main flow prepares file paths for Agent**:
|
||||
|
||||
1. **Discover Analysis Files**:
|
||||
- Glob: `.workflow/active/WFS-{session}/.brainstorming/*/analysis*.md`
|
||||
- Supports: analysis.md + analysis-{slug}.md (max 5)
|
||||
|
||||
2. **Extract Role Information**:
|
||||
- `role_analysis_paths`: Relative paths
|
||||
- `participating_roles`: Role names from directories
|
||||
|
||||
3. **Pass to Agent**: session_id, brainstorm_dir, role_analysis_paths, participating_roles
|
||||
|
||||
### Phase 3A: Analysis & Enhancement Agent
|
||||
|
||||
**Agent executes cross-role analysis**:
|
||||
|
||||
**Input Optimization (feature_mode)**: When feature_mode is active, only read `{role}/analysis.md` index files (NOT sub-documents like `analysis-F-{id}-*.md` or `analysis-cross-cutting.md`). This reduces input tokens from ~39K to ~4.5K while preserving the role perspective overview, feature point index, and cross-cutting summary needed for conflict detection.
|
||||
|
||||
**Input (fallback mode)**: When feature_mode is NOT active, read all `{role}/analysis*.md` files as before.
|
||||
|
||||
```javascript
|
||||
// Prepare input paths based on mode
|
||||
const analysis_input_paths = feature_mode
|
||||
? participating_roles.map(r => `${brainstorm_dir}/${r}/analysis.md`) // Index files only (~4.5K total)
|
||||
: role_analysis_paths; // All analysis files (fallback)
|
||||
|
||||
Task(conceptual-planning-agent, `
|
||||
## Agent Mission
|
||||
Analyze role documents, identify conflicts/gaps, generate enhancement recommendations.
|
||||
${feature_mode ? 'Additionally, generate feature_conflict_map for per-feature consensus/conflicts across roles.' : ''}
|
||||
|
||||
## Input
|
||||
- brainstorm_dir: ${brainstorm_dir}
|
||||
- analysis_input_paths: ${analysis_input_paths}
|
||||
- participating_roles: ${participating_roles}
|
||||
- feature_mode: ${feature_mode}
|
||||
${feature_mode ? `- guidance_spec_path: ${brainstorm_dir}/guidance-specification.md (read Feature Decomposition section only)` : ''}
|
||||
|
||||
## Flow Control Steps
|
||||
1. load_session_metadata → Read workflow-session.json
|
||||
2. load_role_analyses → Read analysis files from analysis_input_paths
|
||||
${feature_mode ? '(INDEX files only - each ~500-800 words with role overview, feature index table, cross-cutting summary)' : '(All analysis files)'}
|
||||
${feature_mode ? `3. load_feature_decomposition → Read Feature Decomposition table from guidance-specification.md
|
||||
4. cross_role_analysis → Identify consensus, conflicts, gaps, ambiguities
|
||||
5. generate_feature_conflict_map → For each feature in Feature Decomposition, extract per-feature consensus/conflicts/cross-references from role index summaries
|
||||
6. generate_recommendations → Format as EP-001, EP-002, ...` : `3. cross_role_analysis → Identify consensus, conflicts, gaps, ambiguities
|
||||
4. generate_recommendations → Format as EP-001, EP-002, ...`}
|
||||
|
||||
## Output Format
|
||||
|
||||
### enhancement_recommendations (always)
|
||||
[
|
||||
{
|
||||
"id": "EP-001",
|
||||
"title": "API Contract Specification",
|
||||
"affected_roles": ["system-architect", "api-designer"],
|
||||
"category": "Architecture",
|
||||
"current_state": "High-level API descriptions",
|
||||
"enhancement": "Add detailed contract definitions",
|
||||
"rationale": "Enables precise implementation",
|
||||
"priority": "High"
|
||||
}
|
||||
]
|
||||
|
||||
${feature_mode ? `### feature_conflict_map (feature_mode only)
|
||||
Bridge artifact from Phase 3A to Phase 6. One entry per feature from Feature Decomposition.
|
||||
|
||||
{
|
||||
"F-001": {
|
||||
"consensus": [
|
||||
"All roles agree on real-time sync via WebSocket",
|
||||
"Event-driven architecture preferred"
|
||||
],
|
||||
"conflicts": [
|
||||
{
|
||||
"topic": "State management approach",
|
||||
"views": {
|
||||
"system-architect": "Server-authoritative with CRDT",
|
||||
"ux-expert": "Optimistic local-first updates",
|
||||
"data-architect": "Event-sourced append-only log"
|
||||
},
|
||||
"resolution": "Hybrid: optimistic local with server reconciliation via CRDT"
|
||||
}
|
||||
],
|
||||
"cross_refs": [
|
||||
"F-003 (offline-mode) depends on sync conflict resolution strategy",
|
||||
"analysis-cross-cutting.md#shared-patterns references this feature"
|
||||
]
|
||||
},
|
||||
"F-002": { ... }
|
||||
}
|
||||
|
||||
**feature_conflict_map Rules**:
|
||||
- One entry per feature ID from guidance-specification.md Feature Decomposition
|
||||
- consensus[]: Statements where 2+ roles explicitly agree (extracted from index summaries)
|
||||
- conflicts[]: Disagreements with topic, per-role positions, and suggested resolution
|
||||
- cross_refs[]: References to other features or cross-cutting docs that relate to this feature
|
||||
- If a feature has no conflicts, set conflicts to empty array (consensus-only is valid)
|
||||
- Keep each entry concise: aim for 100-200 words per feature
|
||||
|
||||
**Resolution Quality Requirements** (每条 conflict 的 resolution 必须满足):
|
||||
1. **Actionable**: resolution 必须是可直接执行的技术方案,而非模糊描述。Bad: "需要权衡" → Good: "采用 JWT 无状态认证,RefreshToken 存 HttpOnly Cookie"
|
||||
2. **Justified**: 说明为什么选择该方案而非其他。格式: "[方案] because [原因],tradeoff: [代价]"
|
||||
3. **Scoped**: 明确 resolution 的适用范围。如果仅适用于特定场景,标注 "Applies when: [条件]"
|
||||
4. **Resolution Confidence**: 每条 conflict 标注置信度
|
||||
- `[RESOLVED]`: Phase 3A 从角色分析中找到明确共识或用户已决策 → Phase 6 直接采用
|
||||
- `[SUGGESTED]`: Phase 3A 基于角色分析推荐方案,但未被用户显式确认 → Phase 6 采用但标注来源
|
||||
- `[UNRESOLVED]`: 角色之间存在根本分歧且无法从现有信息推导 → Phase 6 标注 [DECISION NEEDED],列出所有选项供 plan 阶段决策
|
||||
|
||||
**conflict entry 增强 schema**:
|
||||
```json
|
||||
{
|
||||
"topic": "State management approach",
|
||||
"views": {
|
||||
"system-architect": "Server-authoritative with CRDT",
|
||||
"ux-expert": "Optimistic local-first updates"
|
||||
},
|
||||
"resolution": "Hybrid: optimistic local with server reconciliation via CRDT because balances UX responsiveness with data consistency, tradeoff: increased client complexity",
|
||||
"confidence": "[RESOLVED]",
|
||||
"applies_when": "Online mode with collaborative editing"
|
||||
}
|
||||
```
|
||||
` : ''}
|
||||
`)
|
||||
```
|
||||
|
||||
**Phase 3A Output Storage**:
|
||||
```javascript
|
||||
// Store enhancement_recommendations for Phase 4
|
||||
const enhancement_recommendations = agent_output.enhancement_recommendations;
|
||||
|
||||
// Store feature_conflict_map for Phase 6 (feature_mode only)
|
||||
const feature_conflict_map = feature_mode ? agent_output.feature_conflict_map : null;
|
||||
```
|
||||
```
|
||||
|
||||
### Phase 4: User Interaction
|
||||
|
||||
**All interactions via AskUserQuestion (Chinese questions)**
|
||||
|
||||
#### Step 1: Enhancement Selection
|
||||
|
||||
```javascript
|
||||
// If enhancements > 4, split into multiple rounds
|
||||
const enhancements = [...]; // from Phase 3A
|
||||
const BATCH_SIZE = 4;
|
||||
|
||||
for (let i = 0; i < enhancements.length; i += BATCH_SIZE) {
|
||||
const batch = enhancements.slice(i, i + BATCH_SIZE);
|
||||
|
||||
AskUserQuestion({
|
||||
questions: [{
|
||||
question: `请选择要应用的改进建议 (第${Math.floor(i/BATCH_SIZE)+1}轮)`,
|
||||
header: "改进选择",
|
||||
multiSelect: true,
|
||||
options: batch.map(ep => ({
|
||||
label: `${ep.id}: ${ep.title}`,
|
||||
description: `影响: ${ep.affected_roles.join(', ')} | ${ep.enhancement}`
|
||||
}))
|
||||
}]
|
||||
})
|
||||
|
||||
// Store selections before next round
|
||||
}
|
||||
|
||||
// User can also skip: provide "跳过" option
|
||||
```
|
||||
|
||||
#### Step 2: Clarification Questions
|
||||
|
||||
```javascript
|
||||
// Generate questions based on 9-category taxonomy scan
|
||||
// Categories: User Intent, Requirements, Architecture, UX, Feasibility, Risk, Process, Decisions, Terminology
|
||||
|
||||
const clarifications = [...]; // from analysis
|
||||
const BATCH_SIZE = 4;
|
||||
|
||||
for (let i = 0; i < clarifications.length; i += BATCH_SIZE) {
|
||||
const batch = clarifications.slice(i, i + BATCH_SIZE);
|
||||
const currentRound = Math.floor(i / BATCH_SIZE) + 1;
|
||||
const totalRounds = Math.ceil(clarifications.length / BATCH_SIZE);
|
||||
|
||||
AskUserQuestion({
|
||||
questions: batch.map(q => ({
|
||||
question: q.question,
|
||||
header: q.category.substring(0, 12),
|
||||
multiSelect: false,
|
||||
options: q.options.map(opt => ({
|
||||
label: opt.label,
|
||||
description: opt.description
|
||||
}))
|
||||
}))
|
||||
})
|
||||
|
||||
// Store answers before next round
|
||||
}
|
||||
```
|
||||
|
||||
### Question Guidelines
|
||||
|
||||
**Target**: 开发者(理解技术但需要从用户需求出发)
|
||||
|
||||
**Question Structure**: `[跨角色分析发现] + [需要澄清的决策点]`
|
||||
**Option Structure**: `标签:[具体方案] + 说明:[业务影响] + [技术权衡]`
|
||||
|
||||
**9-Category Taxonomy**:
|
||||
|
||||
| Category | Focus | Example Question Pattern |
|
||||
|----------|-------|--------------------------|
|
||||
| User Intent | 用户目标 | "MVP阶段核心目标?" + 验证/壁垒/完整性 |
|
||||
| Requirements | 需求细化 | "功能优先级如何排序?" + 核心/增强/可选 |
|
||||
| Architecture | 架构决策 | "技术栈选择考量?" + 熟悉度/先进性/成熟度 |
|
||||
| UX | 用户体验 | "交互复杂度取舍?" + 简洁/丰富/渐进 |
|
||||
| Feasibility | 可行性 | "资源约束下的范围?" + 最小/标准/完整 |
|
||||
| Risk | 风险管理 | "风险容忍度?" + 保守/平衡/激进 |
|
||||
| Process | 流程规范 | "迭代节奏?" + 快速/稳定/灵活 |
|
||||
| Decisions | 决策确认 | "冲突解决方案?" + 方案A/方案B/折中 |
|
||||
| Terminology | 术语统一 | "统一使用哪个术语?" + 术语A/术语B |
|
||||
|
||||
**Quality Rules**:
|
||||
|
||||
**MUST Include**:
|
||||
- ✅ All questions in Chinese (用中文提问)
|
||||
- ✅ 基于跨角色分析的具体发现
|
||||
- ✅ 选项包含业务影响说明
|
||||
- ✅ 解决实际的模糊点或冲突
|
||||
|
||||
**MUST Avoid**:
|
||||
- ❌ 与角色分析无关的通用问题
|
||||
- ❌ 重复已在 artifacts 阶段确认的内容
|
||||
- ❌ 过于细节的实现级问题
|
||||
|
||||
#### Step 3: Build Update Plan
|
||||
|
||||
```javascript
|
||||
update_plan = {
|
||||
"role1": {
|
||||
"enhancements": ["EP-001", "EP-003"],
|
||||
"clarifications": [
|
||||
{"question": "...", "answer": "...", "category": "..."}
|
||||
]
|
||||
},
|
||||
"role2": {
|
||||
"enhancements": ["EP-002"],
|
||||
"clarifications": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Parallel Document Update Agents
|
||||
|
||||
**Execute in parallel** (one agent per role):
|
||||
|
||||
```javascript
|
||||
// Single message with multiple Task calls for parallelism
|
||||
Task(conceptual-planning-agent, `
|
||||
## Agent Mission
|
||||
Apply enhancements and clarifications to ${role} analysis
|
||||
|
||||
## Input
|
||||
- role: ${role}
|
||||
- analysis_path: ${brainstorm_dir}/${role}/analysis.md
|
||||
- enhancements: ${role_enhancements}
|
||||
- clarifications: ${role_clarifications}
|
||||
- original_user_intent: ${intent}
|
||||
|
||||
## Flow Control Steps
|
||||
1. load_current_analysis → Read analysis file
|
||||
2. add_clarifications_section → Insert Q&A section
|
||||
3. apply_enhancements → Integrate into relevant sections
|
||||
4. resolve_contradictions → Remove conflicts
|
||||
5. enforce_terminology → Align terminology
|
||||
6. validate_intent → Verify alignment with user intent
|
||||
7. write_updated_file → Save changes
|
||||
|
||||
## Output
|
||||
Updated ${role}/analysis.md
|
||||
`)
|
||||
```
|
||||
|
||||
**Agent Characteristics**:
|
||||
- **Isolation**: Each agent updates exactly ONE role (parallel safe)
|
||||
- **Dependencies**: Zero cross-agent dependencies
|
||||
- **Validation**: All updates must align with original_user_intent
|
||||
|
||||
### Phase 6: Parallel Feature Spec Generation [feature_mode only]
|
||||
|
||||
**Skip condition**: If `feature_mode` is false (no feature list in guidance-spec, or role analyses were not organized by feature points), skip Phase 6 and Phase 6.5 entirely. Proceed directly to Phase 7.
|
||||
|
||||
**Purpose**: Generate one consolidated feature specification per feature by aggregating all role perspectives. Each agent reads the detailed per-feature sub-documents from all roles, applies the conflict map from Phase 3A, and produces a unified spec.
|
||||
|
||||
#### Step 1: Prepare Feature Spec Directory
|
||||
|
||||
```javascript
|
||||
// Create feature-specs directory
|
||||
const feature_specs_dir = `${brainstorm_dir}/feature-specs`;
|
||||
// Ensure directory exists (create if not)
|
||||
```
|
||||
|
||||
#### Step 2: Build Per-Feature Input Bundles
|
||||
|
||||
```javascript
|
||||
// For each feature in feature_list (from guidance-spec Feature Decomposition)
|
||||
const feature_bundles = feature_list.map(feature => {
|
||||
const fid = feature.id; // e.g., "F-001"
|
||||
const slug = feature.slug; // e.g., "real-time-sync"
|
||||
|
||||
// Collect per-feature analysis files from all roles
|
||||
const role_analysis_files = participating_roles
|
||||
.map(role => `${brainstorm_dir}/${role}/analysis-${fid}-${slug}.md`)
|
||||
.filter(path => fileExists(path)); // Only existing files
|
||||
|
||||
return {
|
||||
feature_id: fid,
|
||||
feature_slug: slug,
|
||||
feature_name: feature.description,
|
||||
feature_priority: feature.priority,
|
||||
conflict_map_entry: feature_conflict_map[fid], // From Phase 3A
|
||||
role_analysis_files: role_analysis_files,
|
||||
contributing_roles: role_analysis_files.map(f => extractRoleName(f)),
|
||||
output_path: `${feature_specs_dir}/${fid}-${slug}.md`
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
#### Step 3: Execute Parallel Feature Spec Agents
|
||||
|
||||
**Execute in parallel** (one agent per feature, mirrors Phase 5 pattern):
|
||||
|
||||
```javascript
|
||||
// Single message with multiple Task calls for parallelism
|
||||
// Each agent generates ONE feature spec document
|
||||
|
||||
// For each feature bundle:
|
||||
Task(conceptual-planning-agent, `
|
||||
## Agent Mission
|
||||
Generate consolidated feature specification for ${feature.feature_id}: ${feature.feature_name}
|
||||
by aggregating all role-specific analyses with conflict resolution.
|
||||
|
||||
## Input
|
||||
- feature_id: ${feature.feature_id}
|
||||
- feature_slug: ${feature.feature_slug}
|
||||
- feature_name: ${feature.feature_name}
|
||||
- feature_priority: ${feature.feature_priority}
|
||||
- role_analysis_files: ${feature.role_analysis_files}
|
||||
- conflict_map_entry: ${JSON.stringify(feature.conflict_map_entry)}
|
||||
- output_path: ${feature.output_path}
|
||||
- guidance_spec_feature_section: (Feature Decomposition row for this feature from guidance-specification.md)
|
||||
|
||||
## Flow Control Steps
|
||||
1. load_role_analyses → Read all role-specific analysis files for this feature
|
||||
(Each file ~1500-2000 words, total ~6.5K words for 3-4 roles)
|
||||
2. apply_conflict_map → Use conflict_map_entry to identify resolved/unresolved conflicts
|
||||
3. four_layer_aggregation → Apply aggregation rules (see below)
|
||||
4. generate_feature_spec → Write consolidated spec using template (see below)
|
||||
5. write_output → Save to output_path
|
||||
|
||||
## Four-Layer Aggregation Rules
|
||||
|
||||
### Layer 1: Direct Reference
|
||||
- Quote role analyses directly when consensus exists (from conflict_map.consensus)
|
||||
- Format: "[Role] recommends: [direct quote]"
|
||||
- Use for undisputed technical recommendations
|
||||
|
||||
### Layer 2: Structured Extraction
|
||||
- Extract and organize key information from each role into unified structure
|
||||
- Merge complementary perspectives (e.g., UX user flows + architect data flows)
|
||||
- De-duplicate overlapping content across roles
|
||||
|
||||
### Layer 3: Conflict Distillation
|
||||
- For each conflict in conflict_map_entry.conflicts, handle by confidence level:
|
||||
- **[RESOLVED]**: State the resolution directly as a design decision. Format: "**Decision**: [resolution]. **Rationale**: [from conflict.resolution]. **Trade-off**: [tradeoff]."
|
||||
- **[SUGGESTED]**: Adopt the suggested resolution but mark source. Format: "**Recommended**: [resolution] (suggested by Phase 3A cross-role analysis). **Rationale**: [reason]. **Alternative**: [strongest competing view]."
|
||||
- **[UNRESOLVED]**: Do NOT pick a side. Present all options neutrally for downstream decision. Format: "**[DECISION NEEDED]**: [topic]. **Options**: [role1: approach1] vs [role2: approach2]. **Evaluation**: [pros/cons of each]. **Impact if deferred**: [consequence of not deciding]."
|
||||
- For each conflict regardless of confidence:
|
||||
- Present all role positions concisely (who said what)
|
||||
- Document trade-offs of the chosen or suggested approach
|
||||
- If `applies_when` is set, note the scope limitation
|
||||
- **Unresolved conflict escalation**: If a feature has 2+ [UNRESOLVED] conflicts, add a prominent warning at the top of Section 2: "⚠ This feature has N unresolved design decisions. Plan stage must resolve before task generation."
|
||||
|
||||
### Layer 4: Cross-Feature Annotation
|
||||
- For each cross_ref in conflict_map_entry.cross_refs:
|
||||
- Add explicit dependency notes with feature IDs
|
||||
- Document integration points with other features
|
||||
- Note shared constraints or patterns
|
||||
|
||||
## Feature Spec Template (7 Sections, target 1500-2500 words)
|
||||
|
||||
The output MUST follow this template:
|
||||
|
||||
---
|
||||
# Feature Spec: ${feature.feature_id} - ${feature.feature_name}
|
||||
|
||||
**Priority**: ${feature.feature_priority}
|
||||
**Contributing Roles**: [list of roles that analyzed this feature]
|
||||
**Status**: Draft (from synthesis)
|
||||
|
||||
## 1. Requirements Summary
|
||||
[Consolidated requirements from all role perspectives]
|
||||
- Functional requirements (from product-manager, product-owner)
|
||||
- User experience requirements (from ux-expert, ui-designer)
|
||||
- Technical requirements (from system-architect, data-architect, api-designer)
|
||||
- Domain requirements (from subject-matter-expert)
|
||||
|
||||
## 2. Design Decisions [CORE SECTION]
|
||||
[Key architectural and design decisions with rationale]
|
||||
For each decision:
|
||||
- **Decision**: [What was decided]
|
||||
- **Context**: [Why this decision was needed]
|
||||
- **Options Considered**: [Alternatives from different roles]
|
||||
- **Chosen Approach**: [Selected option with rationale]
|
||||
- **Trade-offs**: [What we gain vs. what we sacrifice]
|
||||
- **Source**: [Which role(s) drove this decision]
|
||||
|
||||
## 3. Interface Contract
|
||||
[API endpoints, data models, component interfaces]
|
||||
- External interfaces (API contracts from api-designer)
|
||||
- Internal interfaces (component boundaries from system-architect)
|
||||
- Data interfaces (schemas from data-architect)
|
||||
- User interfaces (interaction patterns from ux-expert/ui-designer)
|
||||
|
||||
## 4. Constraints & Risks
|
||||
[Technical constraints, business risks, mitigation strategies]
|
||||
- Performance constraints (from system-architect)
|
||||
- Data constraints (from data-architect)
|
||||
- UX constraints (from ux-expert)
|
||||
- Business/domain constraints (from subject-matter-expert)
|
||||
- Risk mitigation strategies (from scrum-master)
|
||||
|
||||
## 5. Acceptance Criteria
|
||||
[Testable criteria for feature completion]
|
||||
- Functional acceptance (from product-owner user stories)
|
||||
- Performance acceptance (from system-architect NFRs)
|
||||
- UX acceptance (from ux-expert usability criteria)
|
||||
- Data integrity acceptance (from data-architect)
|
||||
|
||||
## 6. Detailed Analysis References
|
||||
[Pointers back to role-specific analysis documents]
|
||||
- @../{role}/analysis-${feature.feature_id}-${feature.feature_slug}.md for each contributing role
|
||||
- @../guidance-specification.md#feature-decomposition
|
||||
|
||||
## 7. Cross-Feature Dependencies
|
||||
[Dependencies on and from other features]
|
||||
- **Depends on**: [Feature IDs this feature requires]
|
||||
- **Required by**: [Feature IDs that depend on this feature]
|
||||
- **Shared patterns**: References to analysis-cross-cutting.md patterns
|
||||
- **Integration points**: [Specific interfaces between features]
|
||||
---
|
||||
|
||||
## Completion Criteria
|
||||
- All 7 sections populated with aggregated content
|
||||
- Section 2 (Design Decisions) is the most detailed section (40%+ of word count)
|
||||
- All conflicts from conflict_map_entry addressed with resolutions
|
||||
- Cross-feature dependencies explicitly documented
|
||||
- Word count between 1500-2500
|
||||
- No placeholder text (TODO/TBD) except [DECISION NEEDED] for genuinely unresolved items
|
||||
`)
|
||||
```
|
||||
|
||||
**Agent Characteristics (Phase 6)**:
|
||||
- **Isolation**: Each agent processes exactly ONE feature (parallel safe)
|
||||
- **Dependencies**: Requires Phase 3A feature_conflict_map and Phase 5 updated role analyses
|
||||
- **Input budget**: ~6.5K words per agent (3-4 role sub-docs + conflict map entry)
|
||||
- **Output budget**: 1500-2500 words per feature spec
|
||||
|
||||
### Phase 6.5: Feature Index Generation [feature_mode only]
|
||||
|
||||
**Skip condition**: Same as Phase 6 - skip if `feature_mode` is false.
|
||||
|
||||
**Purpose**: Collect all Phase 6 outputs and generate a structured `feature-index.json` for downstream consumption by action-planning-agent and code-developer.
|
||||
|
||||
#### Step 1: Collect Feature Spec Outputs
|
||||
|
||||
```javascript
|
||||
// Read all generated feature spec files
|
||||
const feature_spec_files = Glob(`${brainstorm_dir}/feature-specs/F-*-*.md`);
|
||||
|
||||
// Also collect cross-cutting spec paths
|
||||
const cross_cutting_specs = participating_roles
|
||||
.map(role => `${brainstorm_dir}/${role}/analysis-cross-cutting.md`)
|
||||
.filter(path => fileExists(path));
|
||||
```
|
||||
|
||||
#### Step 2: Generate feature-index.json
|
||||
|
||||
```javascript
|
||||
const feature_index = {
|
||||
"version": "1.0",
|
||||
"generated_at": new Date().toISOString(),
|
||||
"session_id": session_id,
|
||||
"feature_mode": true,
|
||||
"features": feature_list.map(feature => {
|
||||
const fid = feature.id;
|
||||
const slug = feature.slug;
|
||||
const spec_path = `feature-specs/${fid}-${slug}.md`;
|
||||
const spec_exists = fileExists(`${brainstorm_dir}/${spec_path}`);
|
||||
|
||||
// Extract contributing roles from the spec file header
|
||||
const contributing_roles = participating_roles.filter(role =>
|
||||
fileExists(`${brainstorm_dir}/${role}/analysis-${fid}-${slug}.md`)
|
||||
);
|
||||
|
||||
// Extract cross-cutting references from conflict_map
|
||||
const cross_cutting_refs = feature_conflict_map[fid]
|
||||
? feature_conflict_map[fid].cross_refs
|
||||
: [];
|
||||
|
||||
return {
|
||||
"id": fid,
|
||||
"slug": slug,
|
||||
"name": feature.description,
|
||||
"priority": feature.priority,
|
||||
"spec_path": spec_exists ? spec_path : null,
|
||||
"contributing_roles": contributing_roles,
|
||||
"cross_cutting_refs": cross_cutting_refs
|
||||
};
|
||||
}),
|
||||
"cross_cutting_specs": cross_cutting_specs.map(path =>
|
||||
path.replace(brainstorm_dir + '/', '') // Relative path
|
||||
)
|
||||
};
|
||||
|
||||
Write(
|
||||
`${brainstorm_dir}/feature-index.json`,
|
||||
JSON.stringify(feature_index, null, 2)
|
||||
);
|
||||
```
|
||||
|
||||
#### feature-index.json Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"generated_at": "2026-02-11T10:00:00.000Z",
|
||||
"session_id": "WFS-xxx",
|
||||
"feature_mode": true,
|
||||
"features": [
|
||||
{
|
||||
"id": "F-001",
|
||||
"slug": "real-time-sync",
|
||||
"name": "Real-time collaborative synchronization",
|
||||
"priority": "High",
|
||||
"spec_path": "feature-specs/F-001-real-time-sync.md",
|
||||
"contributing_roles": ["system-architect", "ux-expert", "data-architect"],
|
||||
"cross_cutting_refs": ["F-003 offline-mode depends on sync strategy"]
|
||||
},
|
||||
{
|
||||
"id": "F-002",
|
||||
"slug": "user-permissions",
|
||||
"name": "Role-based user permissions and access control",
|
||||
"priority": "High",
|
||||
"spec_path": "feature-specs/F-002-user-permissions.md",
|
||||
"contributing_roles": ["system-architect", "product-manager", "subject-matter-expert"],
|
||||
"cross_cutting_refs": []
|
||||
}
|
||||
],
|
||||
"cross_cutting_specs": [
|
||||
"system-architect/analysis-cross-cutting.md",
|
||||
"ux-expert/analysis-cross-cutting.md",
|
||||
"data-architect/analysis-cross-cutting.md"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Consumers**: `action-planning-agent` reads feature-index.json to generate task JSONs with feature_spec references. `code-developer` loads individual feature specs (3-5K words) as implementation context.
|
||||
|
||||
### Phase 7: Finalization
|
||||
|
||||
#### Step 1: Update Context Package
|
||||
|
||||
```javascript
|
||||
// Sync updated analyses to context-package.json
|
||||
const context_pkg = Read(".workflow/active/WFS-{session}/.process/context-package.json")
|
||||
|
||||
// Update guidance-specification if exists
|
||||
// Update synthesis-specification if exists
|
||||
// Re-read all role analysis files
|
||||
// Update metadata timestamps
|
||||
|
||||
// If feature_mode: add feature-index.json and feature-specs paths
|
||||
if (feature_mode) {
|
||||
context_pkg.feature_index_path = `${brainstorm_dir}/feature-index.json`;
|
||||
context_pkg.feature_specs_dir = `${brainstorm_dir}/feature-specs/`;
|
||||
context_pkg.feature_mode = true;
|
||||
}
|
||||
|
||||
Write(context_pkg_path, JSON.stringify(context_pkg))
|
||||
```
|
||||
|
||||
#### Step 2: Update Session Metadata
|
||||
|
||||
```json
|
||||
{
|
||||
"phases": {
|
||||
"BRAINSTORM": {
|
||||
"status": "clarification_completed",
|
||||
"clarification_completed": true,
|
||||
"completed_at": "timestamp",
|
||||
"participating_roles": [...],
|
||||
"clarification_results": {
|
||||
"enhancements_applied": ["EP-001", "EP-002"],
|
||||
"questions_asked": 3,
|
||||
"categories_clarified": ["Architecture", "UX"],
|
||||
"roles_updated": ["role1", "role2"]
|
||||
},
|
||||
"feature_spec_results": {
|
||||
"feature_mode": true,
|
||||
"features_generated": ["F-001", "F-002", "F-003"],
|
||||
"feature_index_path": ".brainstorming/feature-index.json",
|
||||
"feature_specs_dir": ".brainstorming/feature-specs/",
|
||||
"conflict_map_generated": true
|
||||
},
|
||||
"quality_metrics": {
|
||||
"user_intent_alignment": "validated",
|
||||
"ambiguity_resolution": "complete",
|
||||
"terminology_consistency": "enforced"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: `feature_spec_results` is only present when `feature_mode` is true. When `feature_mode` is false, this key is omitted entirely.
|
||||
|
||||
#### Step 3: Completion Report
|
||||
|
||||
```markdown
|
||||
## Clarification Complete
|
||||
|
||||
**Enhancements Applied**: EP-001, EP-002, EP-003
|
||||
**Questions Answered**: 3/5
|
||||
**Roles Updated**: role1, role2, role3
|
||||
|
||||
### Feature Specs (feature_mode only)
|
||||
**Feature Specs Generated**: F-001, F-002, F-003
|
||||
**Feature Index**: .brainstorming/feature-index.json
|
||||
**Spec Directory**: .brainstorming/feature-specs/
|
||||
|
||||
### Next Steps
|
||||
PROCEED: `/workflow:plan --session WFS-{session-id}`
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
**Location (role analyses)**: `.workflow/active/WFS-{session}/.brainstorming/[role]/analysis*.md`
|
||||
**Location (feature specs)**: `.workflow/active/WFS-{session}/.brainstorming/feature-specs/F-{id}-{slug}.md` [feature_mode]
|
||||
**Location (feature index)**: `.workflow/active/WFS-{session}/.brainstorming/feature-index.json` [feature_mode]
|
||||
|
||||
**Updated Directory Structure** (feature_mode):
|
||||
```
|
||||
.workflow/active/WFS-{session}/.brainstorming/
|
||||
├── guidance-specification.md
|
||||
├── feature-index.json # Phase 6.5 output
|
||||
├── feature-specs/ # Phase 6 output directory
|
||||
│ ├── F-001-{slug}.md # Consolidated feature spec (1500-2500 words)
|
||||
│ ├── F-002-{slug}.md
|
||||
│ └── F-00N-{slug}.md
|
||||
├── {role-1}/
|
||||
│ ├── analysis.md # Role overview index (read by Phase 3A)
|
||||
│ ├── analysis-cross-cutting.md
|
||||
│ ├── analysis-F-001-{slug}.md # Per-feature detail (read by Phase 6)
|
||||
│ └── analysis-F-002-{slug}.md
|
||||
└── {role-N}/
|
||||
└── ...
|
||||
```
|
||||
|
||||
**Updated Role Analysis Structure**:
|
||||
```markdown
|
||||
## Clarifications
|
||||
### Session {date}
|
||||
- **Q**: {question} (Category: {category})
|
||||
**A**: {answer}
|
||||
|
||||
## {Existing Sections}
|
||||
{Refined content based on clarifications}
|
||||
```
|
||||
|
||||
**Changes**:
|
||||
- User intent validated/corrected
|
||||
- Requirements more specific/measurable
|
||||
- Architecture with rationale
|
||||
- Ambiguities resolved, placeholders removed
|
||||
- Consistent terminology
|
||||
- Feature specs generated with cross-role conflict resolution [feature_mode]
|
||||
- Feature index provides structured access for downstream consumers [feature_mode]
|
||||
|
||||
---
|
||||
|
||||
## Quality Checklist
|
||||
|
||||
**Content**:
|
||||
- All role analyses loaded/analyzed
|
||||
- Cross-role analysis (consensus, conflicts, gaps)
|
||||
- 9-category ambiguity scan
|
||||
- Questions prioritized
|
||||
|
||||
**Analysis**:
|
||||
- User intent validated
|
||||
- Cross-role synthesis complete
|
||||
- Ambiguities resolved
|
||||
- Terminology consistent
|
||||
|
||||
**Documents**:
|
||||
- Clarifications section formatted
|
||||
- Sections reflect answers
|
||||
- No placeholders (TODO/TBD)
|
||||
- Valid Markdown
|
||||
|
||||
**Feature Specs (feature_mode only)**:
|
||||
- Phase 3A reads only analysis.md index files (not sub-documents), input token <= 5K words
|
||||
- feature_conflict_map generated with consensus/conflicts/cross_refs per feature
|
||||
- Phase 6 parallel agents defined: one per feature, input token <= 7K words each
|
||||
- Feature spec template has 7 sections, Section 2 (Design Decisions) is core
|
||||
- Four-layer aggregation rules applied (direct reference/structured extraction/conflict distillation/cross-feature annotation)
|
||||
- Each feature spec is 1500-2500 words
|
||||
- feature-index.json generated with features[] + cross_cutting_specs[]
|
||||
- feature-specs/ directory created with F-{id}-{slug}.md files
|
||||
@@ -494,7 +494,7 @@ Select workflow template:
|
||||
|
||||
○ rapid lite-plan → lite-execute → test-cycle-execute
|
||||
○ coupled plan → plan-verify → execute → review → test
|
||||
○ bugfix lite-fix → lite-execute → test-cycle-execute
|
||||
○ bugfix lite-plan --bugfix → lite-execute → test-cycle-execute
|
||||
○ tdd tdd-plan → execute → tdd-verify
|
||||
○ Other (more templates or custom)
|
||||
```
|
||||
@@ -546,13 +546,13 @@ Templates discovered from `templates/*.json`:
|
||||
|----------|----------|-------|
|
||||
| rapid | Simple feature | /workflow:lite-plan → /workflow:lite-execute → /workflow:test-cycle-execute |
|
||||
| coupled | Complex feature | /workflow:plan → /workflow:plan-verify → /workflow:execute → /workflow:review-session-cycle → /workflow:test-fix-gen |
|
||||
| bugfix | Bug fix | /workflow:lite-fix → /workflow:lite-execute → /workflow:test-cycle-execute |
|
||||
| bugfix | Bug fix | /workflow:lite-plan --bugfix → /workflow:lite-execute → /workflow:test-cycle-execute |
|
||||
| tdd | Test-driven | /workflow:tdd-plan → /workflow:execute → /workflow:tdd-verify |
|
||||
| test-fix | Fix failing tests | /workflow:test-fix-gen → /workflow:test-cycle-execute |
|
||||
| brainstorm | Exploration | /workflow:brainstorm-with-file |
|
||||
| debug | Debug with docs | /workflow:debug-with-file |
|
||||
| analyze | Collaborative analysis | /workflow:analyze-with-file |
|
||||
| issue | Issue workflow | /workflow:issue:plan → /workflow:issue:queue → /workflow:issue:execute |
|
||||
| issue | Issue workflow | /issue:discover → /issue:plan → /issue:queue → /issue:execute |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"level": 4,
|
||||
"steps": [
|
||||
{
|
||||
"cmd": "/workflow:issue:from-brainstorm",
|
||||
"cmd": "/issue:from-brainstorm",
|
||||
"args": "--auto",
|
||||
"unit": "brainstorm-to-issue",
|
||||
"execution": {
|
||||
@@ -14,7 +14,7 @@
|
||||
"contextHint": "Convert brainstorm session findings into issue plans and solutions"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:queue",
|
||||
"cmd": "/issue:queue",
|
||||
"unit": "brainstorm-to-issue",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
@@ -23,7 +23,7 @@
|
||||
"contextHint": "Build execution queue from converted brainstorm issues"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:execute",
|
||||
"cmd": "/issue:execute",
|
||||
"args": "--queue auto",
|
||||
"unit": "brainstorm-to-issue",
|
||||
"execution": {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"level": 1,
|
||||
"steps": [
|
||||
{
|
||||
"cmd": "/workflow:lite-fix",
|
||||
"cmd": "/workflow:lite-plan",
|
||||
"args": "--hotfix \"{{goal}}\"",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
"level": 2,
|
||||
"steps": [
|
||||
{
|
||||
"cmd": "/workflow:lite-fix",
|
||||
"args": "\"{{goal}}\"",
|
||||
"cmd": "/workflow:lite-plan",
|
||||
"args": "--bugfix \"{{goal}}\"",
|
||||
"unit": "bug-fix",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
@@ -21,7 +21,7 @@
|
||||
"type": "slash-command",
|
||||
"mode": "async"
|
||||
},
|
||||
"contextHint": "Implement fix based on diagnosis. Execute against in-memory state from lite-fix analysis."
|
||||
"contextHint": "Implement fix based on diagnosis. Execute against in-memory state from lite-plan analysis."
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:test-fix-gen",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"level": 4,
|
||||
"steps": [
|
||||
{
|
||||
"cmd": "/workflow:brainstorm:auto-parallel",
|
||||
"cmd": "/brainstorm",
|
||||
"args": "\"{{goal}}\"",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"level": "issue",
|
||||
"steps": [
|
||||
{
|
||||
"cmd": "/workflow:issue:discover",
|
||||
"cmd": "/issue:discover",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
"mode": "mainprocess"
|
||||
@@ -12,7 +12,7 @@
|
||||
"contextHint": "Discover pending issues from codebase for potential fixes"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:plan",
|
||||
"cmd": "/issue:plan",
|
||||
"args": "--all-pending",
|
||||
"unit": "issue-workflow",
|
||||
"execution": {
|
||||
@@ -22,7 +22,7 @@
|
||||
"contextHint": "Create execution plans for all discovered pending issues"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:queue",
|
||||
"cmd": "/issue:queue",
|
||||
"unit": "issue-workflow",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
@@ -31,7 +31,7 @@
|
||||
"contextHint": "Build execution queue with issue prioritization and dependencies"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:execute",
|
||||
"cmd": "/issue:execute",
|
||||
"unit": "issue-workflow",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "lite-lite-lite",
|
||||
"description": "Ultra-lightweight for immediate simple tasks - minimal overhead, direct execution",
|
||||
"level": 1,
|
||||
"steps": [
|
||||
{
|
||||
"cmd": "/workflow:lite-lite-lite",
|
||||
"args": "\"{{goal}}\"",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
"mode": "async"
|
||||
},
|
||||
"contextHint": "Direct task execution with minimal analysis and zero documentation overhead"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
"contextHint": "Create lightweight plan for the task"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:convert-to-plan",
|
||||
"cmd": "/issue:convert-to-plan",
|
||||
"args": "--latest-lite-plan -y",
|
||||
"unit": "rapid-to-issue",
|
||||
"execution": {
|
||||
@@ -24,7 +24,7 @@
|
||||
"contextHint": "Convert lite plan to structured issue plan"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:queue",
|
||||
"cmd": "/issue:queue",
|
||||
"unit": "rapid-to-issue",
|
||||
"execution": {
|
||||
"type": "slash-command",
|
||||
@@ -33,7 +33,7 @@
|
||||
"contextHint": "Build execution queue from converted plan"
|
||||
},
|
||||
{
|
||||
"cmd": "/workflow:issue:execute",
|
||||
"cmd": "/issue:execute",
|
||||
"args": "--queue auto",
|
||||
"unit": "rapid-to-issue",
|
||||
"execution": {
|
||||
|
||||
@@ -1,111 +1,57 @@
|
||||
// ========================================
|
||||
// TeamArtifacts Component
|
||||
// ========================================
|
||||
// Displays team artifacts grouped by pipeline phase (plan/impl/test/review)
|
||||
// Displays team artifacts with hybrid layout: tree navigation + file preview
|
||||
|
||||
import * as React from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Folder,
|
||||
FileText,
|
||||
ClipboardList,
|
||||
Code2,
|
||||
TestTube2,
|
||||
SearchCheck,
|
||||
Database,
|
||||
FileJson,
|
||||
Package,
|
||||
Loader2,
|
||||
} from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/Card';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import MarkdownModal from '@/components/shared/MarkdownModal';
|
||||
import { fetchFileContent } from '@/lib/api';
|
||||
import { fetchTeamArtifacts, fetchArtifactContent } from '@/lib/api';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { TeamMessage } from '@/types/team';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import type { ArtifactNode, ContentType } from '@/types/team';
|
||||
|
||||
// ========================================
|
||||
// Types
|
||||
// ========================================
|
||||
|
||||
type ArtifactPhase = 'plan' | 'impl' | 'test' | 'review';
|
||||
|
||||
interface Artifact {
|
||||
id: string;
|
||||
message: TeamMessage;
|
||||
phase: ArtifactPhase;
|
||||
ref?: string;
|
||||
}
|
||||
|
||||
interface TeamArtifactsProps {
|
||||
messages: TeamMessage[];
|
||||
teamName: string;
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Constants
|
||||
// ========================================
|
||||
|
||||
const PHASE_MESSAGE_MAP: Record<string, ArtifactPhase> = {
|
||||
plan_ready: 'plan',
|
||||
plan_approved: 'plan',
|
||||
plan_revision: 'plan',
|
||||
impl_complete: 'impl',
|
||||
impl_progress: 'impl',
|
||||
test_result: 'test',
|
||||
review_result: 'review',
|
||||
};
|
||||
|
||||
const PHASE_CONFIG: Record<ArtifactPhase, { icon: typeof FileText; color: string }> = {
|
||||
plan: { icon: ClipboardList, color: 'text-blue-500' },
|
||||
impl: { icon: Code2, color: 'text-green-500' },
|
||||
test: { icon: TestTube2, color: 'text-amber-500' },
|
||||
review: { icon: SearchCheck, color: 'text-purple-500' },
|
||||
};
|
||||
|
||||
const PHASE_ORDER: ArtifactPhase[] = ['plan', 'impl', 'test', 'review'];
|
||||
|
||||
// ========================================
|
||||
// Helpers
|
||||
// ========================================
|
||||
|
||||
function extractArtifacts(messages: TeamMessage[]): Artifact[] {
|
||||
const artifacts: Artifact[] = [];
|
||||
for (const msg of messages) {
|
||||
const phase = PHASE_MESSAGE_MAP[msg.type];
|
||||
if (!phase) continue;
|
||||
// Include messages that have ref OR data (inline artifacts)
|
||||
if (!msg.ref && !msg.data) continue;
|
||||
artifacts.push({
|
||||
id: msg.id,
|
||||
message: msg,
|
||||
phase,
|
||||
ref: msg.ref,
|
||||
});
|
||||
}
|
||||
return artifacts;
|
||||
function getContentTypeFromPath(path: string): ContentType {
|
||||
if (path.endsWith('.json')) return 'json';
|
||||
if (path.endsWith('.md')) return 'markdown';
|
||||
if (path.endsWith('.txt') || path.endsWith('.log') || path.endsWith('.tsv') || path.endsWith('.csv')) return 'text';
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function groupByPhase(artifacts: Artifact[]): Record<ArtifactPhase, Artifact[]> {
|
||||
const groups: Record<ArtifactPhase, Artifact[]> = {
|
||||
plan: [],
|
||||
impl: [],
|
||||
test: [],
|
||||
review: [],
|
||||
};
|
||||
for (const a of artifacts) {
|
||||
groups[a.phase].push(a);
|
||||
}
|
||||
return groups;
|
||||
function formatSize(bytes?: number): string {
|
||||
if (!bytes) return '';
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
}
|
||||
|
||||
function getContentType(ref: string): 'markdown' | 'json' | 'text' {
|
||||
if (ref.endsWith('.json')) return 'json';
|
||||
if (ref.endsWith('.md')) return 'markdown';
|
||||
return 'text';
|
||||
}
|
||||
|
||||
function formatTimestamp(ts: string): string {
|
||||
function formatDate(ts?: string): string {
|
||||
if (!ts) return '';
|
||||
try {
|
||||
return new Date(ts).toLocaleString();
|
||||
const date = new Date(ts);
|
||||
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
||||
} catch {
|
||||
return ts;
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,83 +59,92 @@ function formatTimestamp(ts: string): string {
|
||||
// Sub-components
|
||||
// ========================================
|
||||
|
||||
function ArtifactCard({
|
||||
artifact,
|
||||
onView,
|
||||
}: {
|
||||
artifact: Artifact;
|
||||
onView: (artifact: Artifact) => void;
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
const config = PHASE_CONFIG[artifact.phase];
|
||||
const Icon = config.icon;
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="cursor-pointer hover:bg-accent/50 transition-colors"
|
||||
onClick={() => onView(artifact)}
|
||||
>
|
||||
<CardContent className="p-3 flex items-start gap-3">
|
||||
<div className={cn('mt-0.5', config.color)}>
|
||||
{artifact.ref ? (
|
||||
<Icon className="w-4 h-4" />
|
||||
) : (
|
||||
<Database className="w-4 h-4" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium truncate">{artifact.message.summary}</p>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
{artifact.ref ? (
|
||||
<span className="text-xs text-muted-foreground font-mono truncate">
|
||||
{artifact.ref.split('/').pop()}
|
||||
</span>
|
||||
) : (
|
||||
<Badge variant="outline" className="text-[10px]">
|
||||
{formatMessage({ id: 'team.artifacts.noRef' })}
|
||||
</Badge>
|
||||
)}
|
||||
<span className="text-[10px] text-muted-foreground ml-auto shrink-0">
|
||||
{formatTimestamp(artifact.message.ts)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
function FileIcon({ contentType }: { contentType?: ContentType }) {
|
||||
if (contentType === 'json') {
|
||||
return <FileJson className="w-4 h-4 text-blue-500" />;
|
||||
}
|
||||
return <FileText className="w-4 h-4 text-gray-500" />;
|
||||
}
|
||||
|
||||
function PhaseGroup({
|
||||
phase,
|
||||
artifacts,
|
||||
onView,
|
||||
}: {
|
||||
phase: ArtifactPhase;
|
||||
artifacts: Artifact[];
|
||||
onView: (artifact: Artifact) => void;
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
if (artifacts.length === 0) return null;
|
||||
interface TreeNodeProps {
|
||||
node: ArtifactNode;
|
||||
depth: number;
|
||||
expanded: Set<string>;
|
||||
selectedPath?: string;
|
||||
onToggle: (path: string) => void;
|
||||
onSelect: (node: ArtifactNode) => void;
|
||||
}
|
||||
|
||||
const config = PHASE_CONFIG[phase];
|
||||
const Icon = config.icon;
|
||||
function TreeNode({
|
||||
node,
|
||||
depth,
|
||||
expanded,
|
||||
selectedPath,
|
||||
onToggle,
|
||||
onSelect,
|
||||
}: TreeNodeProps) {
|
||||
if (node.type === 'directory') {
|
||||
const isExpanded = expanded.has(node.path);
|
||||
return (
|
||||
<div key={node.path}>
|
||||
<div
|
||||
className="flex items-center gap-1 py-1.5 px-2 cursor-pointer hover:bg-accent/50 rounded transition-colors"
|
||||
style={{ paddingLeft: depth * 16 + 8 }}
|
||||
onClick={() => onToggle(node.path)}
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="w-4 h-4 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronRight className="w-4 h-4 text-muted-foreground" />
|
||||
)}
|
||||
<Folder
|
||||
className={cn(
|
||||
'w-4 h-4',
|
||||
isExpanded ? 'text-amber-500' : 'text-amber-400'
|
||||
)}
|
||||
/>
|
||||
<span className="text-sm truncate">{node.name}</span>
|
||||
</div>
|
||||
{isExpanded && node.children && (
|
||||
<div>
|
||||
{node.children.map((child) => (
|
||||
<TreeNode
|
||||
key={child.path}
|
||||
node={child}
|
||||
depth={depth + 1}
|
||||
expanded={expanded}
|
||||
selectedPath={selectedPath}
|
||||
onToggle={onToggle}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// File node
|
||||
const isSelected = selectedPath === node.path;
|
||||
const contentType = node.contentType || getContentTypeFromPath(node.path);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon className={cn('w-4 h-4', config.color)} />
|
||||
<h4 className="text-sm font-medium">
|
||||
{formatMessage({ id: `team.artifacts.${phase}` })}
|
||||
</h4>
|
||||
<Badge variant="secondary" className="text-[10px]">
|
||||
{artifacts.length}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="space-y-2 pl-6">
|
||||
{artifacts.map((artifact) => (
|
||||
<ArtifactCard key={artifact.id} artifact={artifact} onView={onView} />
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
key={node.path}
|
||||
className={cn(
|
||||
'flex items-center gap-1 py-1.5 px-2 cursor-pointer hover:bg-accent/50 rounded transition-colors',
|
||||
isSelected && 'bg-accent'
|
||||
)}
|
||||
style={{ paddingLeft: depth * 16 + 28 }}
|
||||
onClick={() => onSelect(node)}
|
||||
>
|
||||
<FileIcon contentType={contentType} />
|
||||
<span className="text-sm truncate flex-1">{node.name}</span>
|
||||
{node.size !== undefined && (
|
||||
<span className="text-xs text-muted-foreground ml-2">
|
||||
{formatSize(node.size)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -198,88 +153,410 @@ function PhaseGroup({
|
||||
// Main Component
|
||||
// ========================================
|
||||
|
||||
export function TeamArtifacts({ messages }: TeamArtifactsProps) {
|
||||
export function TeamArtifacts({ teamName }: TeamArtifactsProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
const [selectedArtifact, setSelectedArtifact] = React.useState<Artifact | null>(null);
|
||||
const [modalContent, setModalContent] = React.useState('');
|
||||
const [isLoading, setIsLoading] = React.useState(false);
|
||||
const [tree, setTree] = React.useState<ArtifactNode[]>([]);
|
||||
const [expanded, setExpanded] = React.useState<Set<string>>(new Set());
|
||||
const [selectedFile, setSelectedFile] = React.useState<ArtifactNode | null>(null);
|
||||
const [content, setContent] = React.useState<string>('');
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [treeLoading, setTreeLoading] = React.useState(true);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
const artifacts = React.useMemo(() => extractArtifacts(messages), [messages]);
|
||||
const grouped = React.useMemo(() => groupByPhase(artifacts), [artifacts]);
|
||||
// Load artifacts tree
|
||||
React.useEffect(() => {
|
||||
if (!teamName) return;
|
||||
|
||||
const handleView = React.useCallback(async (artifact: Artifact) => {
|
||||
setSelectedArtifact(artifact);
|
||||
setTreeLoading(true);
|
||||
setError(null);
|
||||
|
||||
if (artifact.ref) {
|
||||
setIsLoading(true);
|
||||
setModalContent('');
|
||||
try {
|
||||
const result = await fetchFileContent(artifact.ref);
|
||||
setModalContent(result.content);
|
||||
} catch {
|
||||
setModalContent(`Failed to load: ${artifact.ref}`);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
fetchTeamArtifacts(teamName)
|
||||
.then((data) => {
|
||||
setTree(data.tree || []);
|
||||
// Auto-expand first level directories
|
||||
const firstLevelDirs = (data.tree || [])
|
||||
.filter((n) => n.type === 'directory')
|
||||
.map((n) => n.path);
|
||||
setExpanded(new Set(firstLevelDirs));
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Failed to load artifacts:', err);
|
||||
setError(formatMessage({ id: 'team.artifacts.loadError', defaultMessage: 'Failed to load artifacts' }));
|
||||
})
|
||||
.finally(() => {
|
||||
setTreeLoading(false);
|
||||
});
|
||||
}, [teamName, formatMessage]);
|
||||
|
||||
const handleToggle = React.useCallback((path: string) => {
|
||||
setExpanded((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(path)) {
|
||||
next.delete(path);
|
||||
} else {
|
||||
next.add(path);
|
||||
}
|
||||
} else if (artifact.message.data) {
|
||||
setModalContent(JSON.stringify(artifact.message.data, null, 2));
|
||||
} else {
|
||||
setModalContent(artifact.message.summary);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleClose = React.useCallback(() => {
|
||||
setSelectedArtifact(null);
|
||||
setModalContent('');
|
||||
}, []);
|
||||
const handleSelect = React.useCallback(
|
||||
async (node: ArtifactNode) => {
|
||||
if (node.type === 'directory') return;
|
||||
|
||||
// Empty state
|
||||
if (artifacts.length === 0) {
|
||||
setSelectedFile(node);
|
||||
setLoading(true);
|
||||
setContent('');
|
||||
|
||||
try {
|
||||
const result = await fetchArtifactContent(teamName, node.path);
|
||||
setContent(result.content);
|
||||
} catch (err) {
|
||||
console.error('Failed to load file content:', err);
|
||||
setContent(formatMessage({ id: 'team.artifacts.contentError', defaultMessage: 'Failed to load file content' }));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[teamName, formatMessage]
|
||||
);
|
||||
|
||||
// Get content type for preview
|
||||
const previewContentType = selectedFile
|
||||
? selectedFile.contentType || getContentTypeFromPath(selectedFile.path)
|
||||
: 'text';
|
||||
|
||||
// Map to MarkdownModal content type
|
||||
const modalContentType: 'markdown' | 'json' | 'text' =
|
||||
previewContentType === 'json' ? 'json' : previewContentType === 'markdown' ? 'markdown' : 'text';
|
||||
|
||||
// Loading state
|
||||
if (treeLoading) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<Package className="h-12 w-12 text-muted-foreground mb-4" />
|
||||
<h3 className="text-lg font-medium text-foreground mb-2">
|
||||
{formatMessage({ id: 'team.artifacts.noArtifacts' })}
|
||||
</h3>
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<span className="ml-3 text-muted-foreground">
|
||||
{formatMessage({ id: 'team.artifacts.loading', defaultMessage: 'Loading artifacts...' })}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Determine content type for modal
|
||||
const modalContentType = selectedArtifact?.ref
|
||||
? getContentType(selectedArtifact.ref)
|
||||
: selectedArtifact?.message.data
|
||||
? 'json'
|
||||
: 'text';
|
||||
// Error state
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<Package className="h-12 w-12 text-destructive mb-4" />
|
||||
<p className="text-muted-foreground">{error}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const modalTitle = selectedArtifact?.ref
|
||||
? selectedArtifact.ref.split('/').pop() || 'File'
|
||||
: selectedArtifact?.message.summary || 'Data';
|
||||
// Empty state
|
||||
if (tree.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<Package className="h-12 w-12 text-muted-foreground mb-4" />
|
||||
<h3 className="text-lg font-medium text-foreground mb-2">
|
||||
{formatMessage({ id: 'team.artifacts.noArtifacts', defaultMessage: 'No artifacts yet' })}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{formatMessage({ id: 'team.artifacts.emptyHint', defaultMessage: 'Artifacts will appear here when the team generates them' })}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="space-y-6">
|
||||
{PHASE_ORDER.map((phase) => (
|
||||
<PhaseGroup
|
||||
key={phase}
|
||||
phase={phase}
|
||||
artifacts={grouped[phase]}
|
||||
onView={handleView}
|
||||
/>
|
||||
))}
|
||||
<div className="flex h-[600px] border rounded-lg overflow-hidden">
|
||||
{/* Left: Tree Navigation */}
|
||||
<div className="w-72 shrink-0 border-r bg-muted/30 flex flex-col">
|
||||
<div className="p-3 border-b bg-background shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<Package className="w-4 h-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{formatMessage({ id: 'team.artifacts.title', defaultMessage: 'Artifacts' })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto py-2">
|
||||
{tree.map((node) => (
|
||||
<TreeNode
|
||||
key={node.path}
|
||||
node={node}
|
||||
depth={0}
|
||||
expanded={expanded}
|
||||
selectedPath={selectedFile?.path}
|
||||
onToggle={handleToggle}
|
||||
onSelect={handleSelect}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedArtifact && (
|
||||
<MarkdownModal
|
||||
isOpen={!!selectedArtifact}
|
||||
onClose={handleClose}
|
||||
title={modalTitle}
|
||||
content={modalContent}
|
||||
contentType={modalContentType}
|
||||
maxWidth="3xl"
|
||||
isLoading={isLoading}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
{/* Right: File Preview */}
|
||||
<div className="flex-1 flex flex-col min-w-0">
|
||||
{selectedFile ? (
|
||||
<>
|
||||
<div className="p-3 border-b bg-muted/30 shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<FileIcon contentType={previewContentType} />
|
||||
<span className="text-sm font-medium truncate">
|
||||
{selectedFile.name}
|
||||
</span>
|
||||
{selectedFile.size !== undefined && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formatSize(selectedFile.size)}
|
||||
</span>
|
||||
)}
|
||||
{selectedFile.modifiedAt && (
|
||||
<span className="text-xs text-muted-foreground ml-auto">
|
||||
{formatDate(selectedFile.modifiedAt)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
) : (
|
||||
<PreviewContent
|
||||
content={content}
|
||||
contentType={modalContentType}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center text-center p-8">
|
||||
<div>
|
||||
<FileText className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
|
||||
<p className="text-muted-foreground">
|
||||
{formatMessage({ id: 'team.artifacts.selectFile', defaultMessage: 'Select a file to preview' })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// JSON Card Viewer
|
||||
// ========================================
|
||||
|
||||
function JsonCardViewer({ data, depth = 0 }: { data: unknown; depth?: number }) {
|
||||
// Primitive values
|
||||
if (data === null) {
|
||||
return <span className="text-red-500 font-mono text-sm">null</span>;
|
||||
}
|
||||
if (data === undefined) {
|
||||
return <span className="text-muted-foreground font-mono text-sm">undefined</span>;
|
||||
}
|
||||
if (typeof data === 'boolean') {
|
||||
return (
|
||||
<span className={cn('font-mono text-sm', data ? 'text-orange-500' : 'text-red-500')}>
|
||||
{data ? 'true' : 'false'}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (typeof data === 'number') {
|
||||
return <span className="text-blue-500 font-mono text-sm">{data}</span>;
|
||||
}
|
||||
if (typeof data === 'string') {
|
||||
// Check if it's a long string that should be truncated
|
||||
if (data.length > 200) {
|
||||
return (
|
||||
<span className="text-green-600 dark:text-green-400 font-mono text-sm break-all">
|
||||
"{data.slice(0, 200)}..."
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return <span className="text-green-600 dark:text-green-400 font-mono text-sm">"{data}"</span>;
|
||||
}
|
||||
|
||||
// Array
|
||||
if (Array.isArray(data)) {
|
||||
if (data.length === 0) {
|
||||
return <span className="text-muted-foreground text-sm">[]</span>;
|
||||
}
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{data.map((item, index) => (
|
||||
<div key={index} className="flex items-start gap-2">
|
||||
<Badge variant="outline" className="text-xs shrink-0 mt-0.5">
|
||||
{index}
|
||||
</Badge>
|
||||
<div className="flex-1 min-w-0">
|
||||
{typeof item === 'object' && item !== null ? (
|
||||
<div className="bg-muted/30 rounded-lg p-2 border">
|
||||
<JsonCardViewer data={item} depth={depth + 1} />
|
||||
</div>
|
||||
) : (
|
||||
<JsonCardViewer data={item} depth={depth + 1} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Object
|
||||
if (typeof data === 'object') {
|
||||
const entries = Object.entries(data);
|
||||
if (entries.length === 0) {
|
||||
return <span className="text-muted-foreground text-sm">{'{}'}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('space-y-2', depth > 0 && 'pl-2 border-l-2 border-border')}>
|
||||
{entries.map(([key, value]) => {
|
||||
const isExpandable = typeof value === 'object' && value !== null;
|
||||
const isArray = Array.isArray(value);
|
||||
const itemCount = isArray ? value.length : isExpandable ? Object.keys(value).length : 0;
|
||||
|
||||
return (
|
||||
<div key={key} className="group">
|
||||
<div className="flex items-center gap-2 py-1">
|
||||
<span className="text-purple-600 dark:text-purple-400 font-medium text-sm shrink-0">
|
||||
{key}
|
||||
</span>
|
||||
{isExpandable && (
|
||||
<Badge variant="secondary" className="text-[10px] h-4 px-1">
|
||||
{isArray ? `${itemCount} items` : `${itemCount} fields`}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
{isExpandable ? (
|
||||
<div className="bg-muted/20 rounded-md p-2 border">
|
||||
<JsonCardViewer data={value} depth={depth + 1} />
|
||||
</div>
|
||||
) : (
|
||||
<JsonCardViewer data={value} depth={depth + 1} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <span className="text-sm">{String(data)}</span>;
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// JSON Summary Card
|
||||
// ========================================
|
||||
|
||||
function JsonSummaryCard({ data }: { data: unknown }) {
|
||||
const stats = React.useMemo(() => {
|
||||
const result = {
|
||||
type: '',
|
||||
fields: 0,
|
||||
items: 0,
|
||||
depth: 0,
|
||||
};
|
||||
|
||||
const analyze = (obj: unknown, currentDepth: number): void => {
|
||||
result.depth = Math.max(result.depth, currentDepth);
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
result.type = 'Array';
|
||||
result.items = obj.length;
|
||||
obj.forEach(item => analyze(item, currentDepth + 1));
|
||||
} else if (typeof obj === 'object' && obj !== null) {
|
||||
result.type = 'Object';
|
||||
result.fields = Object.keys(obj).length;
|
||||
Object.values(obj).forEach(val => analyze(val, currentDepth + 1));
|
||||
}
|
||||
};
|
||||
|
||||
analyze(data, 0);
|
||||
return result;
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-4 p-3 bg-muted/50 rounded-lg mb-4 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline">{stats.type}</Badge>
|
||||
</div>
|
||||
{stats.type === 'Object' && (
|
||||
<div className="text-muted-foreground">
|
||||
<span className="font-medium text-foreground">{stats.fields}</span> fields
|
||||
</div>
|
||||
)}
|
||||
{stats.type === 'Array' && (
|
||||
<div className="text-muted-foreground">
|
||||
<span className="font-medium text-foreground">{stats.items}</span> items
|
||||
</div>
|
||||
)}
|
||||
<div className="text-muted-foreground">
|
||||
<span className="font-medium text-foreground">{stats.depth}</span> levels deep
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Preview Content Component
|
||||
// ========================================
|
||||
|
||||
function PreviewContent({
|
||||
content,
|
||||
contentType,
|
||||
}: {
|
||||
content: string;
|
||||
contentType: 'markdown' | 'json' | 'text';
|
||||
}) {
|
||||
if (!content) {
|
||||
return (
|
||||
<div className="text-muted-foreground text-sm">
|
||||
No content
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (contentType === 'json') {
|
||||
try {
|
||||
const parsed = JSON.parse(content);
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<JsonSummaryCard data={parsed} />
|
||||
<div className="bg-card rounded-lg">
|
||||
<JsonCardViewer data={parsed} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} catch {
|
||||
return (
|
||||
<pre className="text-xs bg-muted/50 p-4 rounded-lg overflow-auto font-mono whitespace-pre-wrap break-words border">
|
||||
{content}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType === 'markdown') {
|
||||
return (
|
||||
<div className="prose prose-sm dark:prose-invert max-w-none">
|
||||
<pre className="whitespace-pre-wrap break-words font-sans text-sm leading-relaxed bg-transparent p-0">
|
||||
{content}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<pre className="text-xs bg-muted/50 p-4 rounded-lg overflow-auto font-mono whitespace-pre-wrap break-words border">
|
||||
{content}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
||||
export default TeamArtifacts;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// Typed fetch functions for API communication with CSRF token handling
|
||||
|
||||
import type { SessionMetadata, TaskData, IndexStatus, IndexRebuildRequest, Rule, RuleCreateInput, RulesResponse, Prompt, PromptInsight, Pattern, Suggestion, McpTemplate, McpTemplateInstallRequest, AllProjectsResponse, OtherProjectsServersResponse, CrossCliCopyRequest, CrossCliCopyResponse } from '../types/store';
|
||||
import type { TeamArtifactsResponse } from '../types/team';
|
||||
|
||||
// Re-export types for backward compatibility
|
||||
export type { IndexStatus, IndexRebuildRequest, Rule, RuleCreateInput, RulesResponse, Prompt, PromptInsight, Pattern, Suggestion, McpTemplate, McpTemplateInstallRequest, AllProjectsResponse, OtherProjectsServersResponse, CrossCliCopyRequest, CrossCliCopyResponse };
|
||||
@@ -6139,6 +6140,19 @@ export async function fetchTeamStatus(
|
||||
return fetchApi(`/api/teams/${encodeURIComponent(teamName)}/status`);
|
||||
}
|
||||
|
||||
export async function fetchTeamArtifacts(
|
||||
teamName: string
|
||||
): Promise<TeamArtifactsResponse> {
|
||||
return fetchApi(`/api/teams/${encodeURIComponent(teamName)}/artifacts`);
|
||||
}
|
||||
|
||||
export async function fetchArtifactContent(
|
||||
teamName: string,
|
||||
artifactPath: string
|
||||
): Promise<{ content: string; contentType: string; path: string }> {
|
||||
return fetchApi(`/api/teams/${encodeURIComponent(teamName)}/artifacts/${encodeURIComponent(artifactPath)}`);
|
||||
}
|
||||
|
||||
// ========== CLI Sessions (PTY) API ==========
|
||||
|
||||
export interface CliSession {
|
||||
|
||||
@@ -73,6 +73,7 @@ interface FixProgressData {
|
||||
* Fix Progress Carousel Component
|
||||
* Displays fix progress with polling and carousel navigation
|
||||
*/
|
||||
// @ts-expect-error Component is defined for future use when backend implements /api/fix-progress
|
||||
function FixProgressCarousel({ sessionId }: { sessionId: string }) {
|
||||
const { formatMessage } = useIntl();
|
||||
const [fixProgressData, setFixProgressData] = React.useState<FixProgressData | null>(null);
|
||||
|
||||
@@ -101,7 +101,7 @@ export function TeamPage() {
|
||||
|
||||
{/* Artifacts Tab */}
|
||||
{detailTab === 'artifacts' && (
|
||||
<TeamArtifacts messages={messages} />
|
||||
<TeamArtifacts teamName={selectedTeam} />
|
||||
)}
|
||||
|
||||
{/* Messages Tab */}
|
||||
|
||||
@@ -50,7 +50,7 @@ export interface TeamSummaryExtended extends TeamSummary {
|
||||
archived_at?: string;
|
||||
pipeline_mode?: string;
|
||||
memberCount: number;
|
||||
members?: string[];
|
||||
members: string[]; // Always provided by backend
|
||||
}
|
||||
|
||||
export interface TeamMessagesResponse {
|
||||
@@ -76,3 +76,33 @@ export interface TeamMessageFilter {
|
||||
|
||||
export type PipelineStage = 'plan' | 'impl' | 'test' | 'review';
|
||||
export type PipelineStageStatus = 'completed' | 'in_progress' | 'pending' | 'blocked';
|
||||
|
||||
// ========================================
|
||||
// Team Artifacts Types
|
||||
// ========================================
|
||||
// Types for team artifacts tree visualization
|
||||
|
||||
export type ArtifactNodeType = 'file' | 'directory';
|
||||
|
||||
export type ContentType = 'markdown' | 'json' | 'text' | 'unknown';
|
||||
|
||||
export interface ArtifactNode {
|
||||
type: ArtifactNodeType;
|
||||
name: string;
|
||||
path: string;
|
||||
contentType: ContentType; // Always provided by backend
|
||||
size?: number;
|
||||
modifiedAt?: string;
|
||||
children?: ArtifactNode[];
|
||||
}
|
||||
|
||||
export interface TeamArtifactsResponse {
|
||||
teamName: string;
|
||||
sessionId: string;
|
||||
sessionPath: string;
|
||||
pipelineMode?: string;
|
||||
tree: ArtifactNode[];
|
||||
totalFiles: number;
|
||||
totalDirectories: number;
|
||||
totalSize: number;
|
||||
}
|
||||
|
||||
@@ -1,82 +1,346 @@
|
||||
/**
|
||||
* Team Routes - REST API for team message visualization & management
|
||||
*
|
||||
* Directory Structure (NEW - session-bound):
|
||||
* .workflow/.team/
|
||||
* ├── TLS-demo-2026-02-15/ # session-id (root)
|
||||
* │ ├── .msg/ # messages (session-level)
|
||||
* │ │ ├── meta.json
|
||||
* │ │ └── messages.jsonl
|
||||
* │ ├── spec/ # artifacts (siblings of .msg)
|
||||
* │ └── plan/
|
||||
*
|
||||
* Legacy Support: Also scans .workflow/.team-msg/{team-name}/
|
||||
*
|
||||
* Endpoints:
|
||||
* - GET /api/teams - List all teams (with ?location filter)
|
||||
* - GET /api/teams/:name/messages - Get messages (with filters)
|
||||
* - GET /api/teams/:name/status - Get member status summary
|
||||
* - POST /api/teams/:name/archive - Archive a team
|
||||
* - POST /api/teams/:name/unarchive - Unarchive a team
|
||||
* - DELETE /api/teams/:name - Delete a team
|
||||
* - GET /api/teams - List all teams (with ?location filter)
|
||||
* - GET /api/teams/:name/messages - Get messages (with filters)
|
||||
* - GET /api/teams/:name/status - Get member status summary
|
||||
* - GET /api/teams/:name/artifacts - Get artifacts tree structure
|
||||
* - GET /api/teams/:name/artifacts/*path - Get artifact file content
|
||||
* - POST /api/teams/:name/archive - Archive a team
|
||||
* - POST /api/teams/:name/unarchive - Unarchive a team
|
||||
* - DELETE /api/teams/:name - Delete a team
|
||||
*/
|
||||
|
||||
import { existsSync, readdirSync, rmSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { existsSync, readdirSync, rmSync, statSync, readFileSync } from 'fs';
|
||||
import { join, extname } from 'path';
|
||||
import type { RouteContext } from './types.js';
|
||||
import { readAllMessages, getLogDir, getEffectiveTeamMeta, readTeamMeta, writeTeamMeta } from '../../tools/team-msg.js';
|
||||
import type { TeamMeta } from '../../tools/team-msg.js';
|
||||
import { getProjectRoot } from '../../utils/path-validator.js';
|
||||
|
||||
/**
|
||||
* Artifact node structure for tree representation
|
||||
*/
|
||||
interface ArtifactNode {
|
||||
type: 'file' | 'directory';
|
||||
name: string;
|
||||
path: string; // Relative to session directory
|
||||
contentType: 'markdown' | 'json' | 'text' | 'unknown';
|
||||
size?: number; // File size (bytes)
|
||||
modifiedAt?: string; // Last modified time
|
||||
children?: ArtifactNode[]; // Directory children
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect content type from file extension
|
||||
*/
|
||||
function detectContentType(fileName: string): ArtifactNode['contentType'] {
|
||||
const ext = extname(fileName).toLowerCase();
|
||||
if (['.md', '.markdown'].includes(ext)) return 'markdown';
|
||||
if (['.json'].includes(ext)) return 'json';
|
||||
if (['.txt', '.log', '.tsv', '.csv'].includes(ext)) return 'text';
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scan artifacts directory
|
||||
* Skips .msg directory (message storage)
|
||||
*/
|
||||
function scanArtifactsDirectory(dirPath: string, basePath: string): ArtifactNode[] {
|
||||
const nodes: ArtifactNode[] = [];
|
||||
|
||||
if (!existsSync(dirPath)) {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = readdirSync(dirPath, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
// Skip .msg directory - that's for messages, not artifacts
|
||||
if (entry.name === '.msg') continue;
|
||||
|
||||
const fullPath = join(dirPath, entry.name);
|
||||
const relativePath = join(basePath, entry.name).replace(/\\/g, '/');
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
const children = scanArtifactsDirectory(fullPath, relativePath);
|
||||
const stat = statSync(fullPath);
|
||||
|
||||
nodes.push({
|
||||
type: 'directory',
|
||||
name: entry.name,
|
||||
path: relativePath,
|
||||
contentType: 'unknown',
|
||||
modifiedAt: stat.mtime.toISOString(),
|
||||
children,
|
||||
});
|
||||
} else if (entry.isFile()) {
|
||||
const stat = statSync(fullPath);
|
||||
|
||||
nodes.push({
|
||||
type: 'file',
|
||||
name: entry.name,
|
||||
path: relativePath,
|
||||
contentType: detectContentType(entry.name),
|
||||
size: stat.size,
|
||||
modifiedAt: stat.mtime.toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort: directories first, then files, both alphabetically
|
||||
nodes.sort((a, b) => {
|
||||
if (a.type !== b.type) {
|
||||
return a.type === 'directory' ? -1 : 1;
|
||||
}
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
} catch (e) {
|
||||
// Ignore errors (permission, etc.)
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session directory (artifacts are siblings of .msg/)
|
||||
* NEW: .workflow/.team/{session-id}/
|
||||
*/
|
||||
function getSessionDir(sessionId: string, root: string): string {
|
||||
return join(root, '.workflow', '.team', sessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get legacy team directory
|
||||
* OLD: .workflow/.team-msg/{team-name}/
|
||||
*/
|
||||
function getLegacyTeamDir(teamName: string, root: string): string {
|
||||
return join(root, '.workflow', '.team-msg', teamName);
|
||||
}
|
||||
|
||||
/**
|
||||
* List all sessions from new .team/ directory
|
||||
* Each subdirectory with .msg/ folder is a valid session
|
||||
*/
|
||||
function listSessions(root: string): Array<{ sessionId: string; path: string }> {
|
||||
const teamDir = join(root, '.workflow', '.team');
|
||||
const sessions: Array<{ sessionId: string; path: string }> = [];
|
||||
|
||||
console.log('[team-routes] listSessions - root:', root);
|
||||
console.log('[team-routes] listSessions - teamDir:', teamDir);
|
||||
console.log('[team-routes] listSessions - existsSync(teamDir):', existsSync(teamDir));
|
||||
|
||||
if (!existsSync(teamDir)) {
|
||||
return sessions;
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = readdirSync(teamDir, { withFileTypes: true });
|
||||
console.log('[team-routes] listSessions - entries:', entries.map(e => e.name));
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const msgDir = join(teamDir, entry.name, '.msg');
|
||||
console.log('[team-routes] listSessions - checking msgDir:', msgDir, 'exists:', existsSync(msgDir));
|
||||
if (existsSync(msgDir)) {
|
||||
sessions.push({ sessionId: entry.name, path: join(teamDir, entry.name) });
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[team-routes] listSessions error:', err);
|
||||
}
|
||||
|
||||
console.log('[team-routes] listSessions - found sessions:', sessions.length);
|
||||
return sessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* List teams from old .team-msg/ directory (backward compatibility)
|
||||
*/
|
||||
function listLegacyTeams(root: string): Array<{ teamName: string; path: string }> {
|
||||
const teamMsgDir = join(root, '.workflow', '.team-msg');
|
||||
const teams: Array<{ teamName: string; path: string }> = [];
|
||||
|
||||
if (!existsSync(teamMsgDir)) {
|
||||
return teams;
|
||||
}
|
||||
|
||||
try {
|
||||
const entries = readdirSync(teamMsgDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
teams.push({ teamName: entry.name, path: join(teamMsgDir, entry.name) });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
|
||||
return teams;
|
||||
}
|
||||
|
||||
function jsonResponse(res: import('http').ServerResponse, status: number, data: unknown): void {
|
||||
res.writeHead(status, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve project root from context
|
||||
* Priority: initialPath (from server startup) > getProjectRoot()
|
||||
*/
|
||||
function resolveProjectRoot(ctx: RouteContext): string {
|
||||
return ctx.initialPath || getProjectRoot();
|
||||
}
|
||||
|
||||
export async function handleTeamRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
const { pathname, req, res, url, handlePostRequest } = ctx;
|
||||
|
||||
if (!pathname.startsWith('/api/teams')) return false;
|
||||
|
||||
// ====== GET /api/teams/debug - Debug endpoint ======
|
||||
if (pathname === '/api/teams/debug' && req.method === 'GET') {
|
||||
const root = resolveProjectRoot(ctx);
|
||||
const teamDir = join(root, '.workflow', '.team');
|
||||
const legacyDir = join(root, '.workflow', '.team-msg');
|
||||
|
||||
const debug = {
|
||||
projectRoot: root,
|
||||
teamDir,
|
||||
legacyDir,
|
||||
teamDirExists: existsSync(teamDir),
|
||||
legacyDirExists: existsSync(legacyDir),
|
||||
teamDirContents: [] as string[],
|
||||
sessionMsgDirs: [] as { session: string; msgExists: boolean }[],
|
||||
};
|
||||
|
||||
if (existsSync(teamDir)) {
|
||||
try {
|
||||
const entries = readdirSync(teamDir, { withFileTypes: true });
|
||||
debug.teamDirContents = entries.map(e => `${e.name} (${e.isDirectory() ? 'dir' : 'file'})`);
|
||||
for (const entry of entries) {
|
||||
if (entry.isDirectory()) {
|
||||
const msgDir = join(teamDir, entry.name, '.msg');
|
||||
debug.sessionMsgDirs.push({
|
||||
session: entry.name,
|
||||
msgExists: existsSync(msgDir),
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
(debug as any).error = String(err);
|
||||
}
|
||||
}
|
||||
|
||||
jsonResponse(res, 200, debug);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ====== GET /api/teams - List all teams ======
|
||||
if (pathname === '/api/teams' && req.method === 'GET') {
|
||||
try {
|
||||
const root = getProjectRoot();
|
||||
const teamMsgDir = join(root, '.workflow', '.team-msg');
|
||||
const root = resolveProjectRoot(ctx);
|
||||
const locationFilter = url.searchParams.get('location') || 'active';
|
||||
|
||||
if (!existsSync(teamMsgDir)) {
|
||||
jsonResponse(res, 200, { teams: [] });
|
||||
return true;
|
||||
// Collect from new session-bound structure
|
||||
const sessions = listSessions(root);
|
||||
// Collect from legacy structure
|
||||
const legacyTeams = listLegacyTeams(root);
|
||||
|
||||
// Build unified team list
|
||||
const teams: Array<{
|
||||
name: string;
|
||||
messageCount: number;
|
||||
lastActivity: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
archived_at?: string;
|
||||
pipeline_mode?: string;
|
||||
memberCount: number;
|
||||
members: string[];
|
||||
isLegacy: boolean;
|
||||
}> = [];
|
||||
|
||||
// Process new sessions
|
||||
for (const session of sessions) {
|
||||
const messages = readAllMessages(session.sessionId);
|
||||
const lastMsg = messages[messages.length - 1];
|
||||
const meta = getEffectiveTeamMeta(session.sessionId);
|
||||
|
||||
const memberSet = new Set<string>();
|
||||
for (const msg of messages) {
|
||||
memberSet.add(msg.from);
|
||||
memberSet.add(msg.to);
|
||||
}
|
||||
|
||||
teams.push({
|
||||
name: session.sessionId,
|
||||
messageCount: messages.length,
|
||||
lastActivity: lastMsg?.ts || '',
|
||||
status: meta.status,
|
||||
created_at: meta.created_at,
|
||||
updated_at: meta.updated_at,
|
||||
archived_at: meta.archived_at,
|
||||
pipeline_mode: meta.pipeline_mode,
|
||||
memberCount: memberSet.size,
|
||||
members: Array.from(memberSet),
|
||||
isLegacy: false,
|
||||
});
|
||||
}
|
||||
|
||||
const locationFilter = url.searchParams.get('location') || 'active';
|
||||
const entries = readdirSync(teamMsgDir, { withFileTypes: true });
|
||||
// Process legacy teams
|
||||
for (const team of legacyTeams) {
|
||||
// Skip if already found in new structure (same name)
|
||||
if (teams.some(t => t.name === team.teamName)) continue;
|
||||
|
||||
const teams = entries
|
||||
.filter(e => e.isDirectory())
|
||||
.map(e => {
|
||||
const messages = readAllMessages(e.name);
|
||||
const lastMsg = messages[messages.length - 1];
|
||||
const meta = getEffectiveTeamMeta(e.name);
|
||||
const messages = readAllMessages(team.teamName);
|
||||
const lastMsg = messages[messages.length - 1];
|
||||
const meta = getEffectiveTeamMeta(team.teamName);
|
||||
|
||||
// Count unique members from messages
|
||||
const memberSet = new Set<string>();
|
||||
for (const msg of messages) {
|
||||
memberSet.add(msg.from);
|
||||
memberSet.add(msg.to);
|
||||
}
|
||||
const memberSet = new Set<string>();
|
||||
for (const msg of messages) {
|
||||
memberSet.add(msg.from);
|
||||
memberSet.add(msg.to);
|
||||
}
|
||||
|
||||
return {
|
||||
name: e.name,
|
||||
messageCount: messages.length,
|
||||
lastActivity: lastMsg?.ts || '',
|
||||
status: meta.status,
|
||||
created_at: meta.created_at,
|
||||
updated_at: meta.updated_at,
|
||||
archived_at: meta.archived_at,
|
||||
pipeline_mode: meta.pipeline_mode,
|
||||
memberCount: memberSet.size,
|
||||
members: Array.from(memberSet),
|
||||
};
|
||||
})
|
||||
teams.push({
|
||||
name: team.teamName,
|
||||
messageCount: messages.length,
|
||||
lastActivity: lastMsg?.ts || '',
|
||||
status: meta.status,
|
||||
created_at: meta.created_at,
|
||||
updated_at: meta.updated_at,
|
||||
archived_at: meta.archived_at,
|
||||
pipeline_mode: meta.pipeline_mode,
|
||||
memberCount: memberSet.size,
|
||||
members: Array.from(memberSet),
|
||||
isLegacy: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
const filteredTeams = teams
|
||||
.filter(t => {
|
||||
if (locationFilter === 'all') return true;
|
||||
if (locationFilter === 'archived') return t.status === 'archived';
|
||||
// 'active' = everything that's not archived
|
||||
return t.status !== 'archived';
|
||||
})
|
||||
.sort((a, b) => b.lastActivity.localeCompare(a.lastActivity));
|
||||
|
||||
jsonResponse(res, 200, { teams });
|
||||
jsonResponse(res, 200, { teams: filteredTeams });
|
||||
return true;
|
||||
} catch (error) {
|
||||
jsonResponse(res, 500, { error: (error as Error).message });
|
||||
@@ -126,14 +390,70 @@ export async function handleTeamRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
const deleteMatch = pathname.match(/^\/api\/teams\/([^/]+)$/);
|
||||
if (deleteMatch && req.method === 'DELETE') {
|
||||
const teamName = decodeURIComponent(deleteMatch[1]);
|
||||
const root = resolveProjectRoot(ctx);
|
||||
try {
|
||||
const dir = getLogDir(teamName);
|
||||
if (!existsSync(dir)) {
|
||||
jsonResponse(res, 404, { error: `Team "${teamName}" not found` });
|
||||
// Try new session-bound location first
|
||||
const sessionDir = getSessionDir(teamName, root);
|
||||
if (existsSync(sessionDir)) {
|
||||
rmSync(sessionDir, { recursive: true, force: true });
|
||||
jsonResponse(res, 200, { success: true, team: teamName, deleted: true });
|
||||
return true;
|
||||
}
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
jsonResponse(res, 200, { success: true, team: teamName, deleted: true });
|
||||
|
||||
// Fallback to legacy location
|
||||
const legacyDir = getLegacyTeamDir(teamName, root);
|
||||
if (existsSync(legacyDir)) {
|
||||
rmSync(legacyDir, { recursive: true, force: true });
|
||||
jsonResponse(res, 200, { success: true, team: teamName, deleted: true });
|
||||
return true;
|
||||
}
|
||||
|
||||
jsonResponse(res, 404, { error: `Team "${teamName}" not found` });
|
||||
return true;
|
||||
} catch (error) {
|
||||
jsonResponse(res, 500, { error: (error as Error).message });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ====== GET requests only from here ======
|
||||
if (req.method !== 'GET') return false;
|
||||
|
||||
// ====== GET /api/teams/:name/artifacts or /api/teams/:name/artifacts/*path ======
|
||||
const artifactsMatch = pathname.match(/^\/api\/teams\/([^/]+)\/artifacts(?:\/(.*))?$/);
|
||||
if (artifactsMatch) {
|
||||
const artifactsTeamName = decodeURIComponent(artifactsMatch[1]);
|
||||
const artifactPath = artifactsMatch[2] ? decodeURIComponent(artifactsMatch[2]) : null;
|
||||
const root = resolveProjectRoot(ctx);
|
||||
|
||||
try {
|
||||
// NEW: Session directory contains both .msg/ and artifacts
|
||||
// The team name IS the session ID now
|
||||
const sessionDir = getSessionDir(artifactsTeamName, root);
|
||||
|
||||
if (!existsSync(sessionDir)) {
|
||||
// Check if it's a legacy team with session_id
|
||||
const meta = getEffectiveTeamMeta(artifactsTeamName);
|
||||
if (meta.session_id) {
|
||||
// Legacy team with session_id - redirect to session directory
|
||||
const legacySessionDir = getSessionDir(meta.session_id, root);
|
||||
if (existsSync(legacySessionDir)) {
|
||||
serveArtifacts(legacySessionDir, meta.session_id, meta, artifactPath, res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
jsonResponse(res, 200, {
|
||||
tree: [],
|
||||
sessionId: null,
|
||||
message: 'Session directory not found'
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Direct session access - artifacts are siblings of .msg/
|
||||
const meta = getEffectiveTeamMeta(artifactsTeamName);
|
||||
serveArtifacts(sessionDir, artifactsTeamName, meta, artifactPath, res);
|
||||
return true;
|
||||
} catch (error) {
|
||||
jsonResponse(res, 500, { error: (error as Error).message });
|
||||
@@ -142,8 +462,6 @@ export async function handleTeamRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
}
|
||||
|
||||
// ====== GET /api/teams/:name/messages or /api/teams/:name/status ======
|
||||
if (req.method !== 'GET') return false;
|
||||
|
||||
const match = pathname.match(/^\/api\/teams\/([^/]+)\/(messages|status)$/);
|
||||
if (!match) return false;
|
||||
|
||||
@@ -208,3 +526,117 @@ export async function handleTeamRoutes(ctx: RouteContext): Promise<boolean> {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve artifacts from session directory
|
||||
*/
|
||||
function serveArtifacts(
|
||||
sessionDir: string,
|
||||
sessionId: string,
|
||||
meta: TeamMeta,
|
||||
artifactPath: string | null,
|
||||
res: import('http').ServerResponse
|
||||
): void {
|
||||
// If specific file path requested
|
||||
if (artifactPath) {
|
||||
const filePath = join(sessionDir, artifactPath);
|
||||
|
||||
if (!existsSync(filePath)) {
|
||||
jsonResponse(res, 404, {
|
||||
error: 'Artifact not found',
|
||||
path: artifactPath
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const stat = statSync(filePath);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
// Return directory listing
|
||||
const children = scanArtifactsDirectory(filePath, artifactPath);
|
||||
jsonResponse(res, 200, {
|
||||
type: 'directory',
|
||||
name: artifactPath.split('/').pop() || '',
|
||||
path: artifactPath,
|
||||
children,
|
||||
modifiedAt: stat.mtime.toISOString()
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Return file content
|
||||
const content = readFileSync(filePath, 'utf-8');
|
||||
const contentType = detectContentType(artifactPath.split('/').pop() || '');
|
||||
|
||||
jsonResponse(res, 200, {
|
||||
type: 'file',
|
||||
name: artifactPath.split('/').pop() || '',
|
||||
path: artifactPath,
|
||||
contentType,
|
||||
size: stat.size,
|
||||
modifiedAt: stat.mtime.toISOString(),
|
||||
content
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Return full artifacts tree
|
||||
const tree = scanArtifactsDirectory(sessionDir, '');
|
||||
|
||||
jsonResponse(res, 200, {
|
||||
teamName: sessionId,
|
||||
sessionId: sessionId,
|
||||
sessionPath: sessionDir,
|
||||
pipelineMode: meta.pipeline_mode,
|
||||
tree,
|
||||
totalFiles: countFiles(tree),
|
||||
totalDirectories: countDirectories(tree),
|
||||
totalSize: countTotalSize(tree)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Count total files in artifact tree
|
||||
*/
|
||||
function countFiles(nodes: ArtifactNode[]): number {
|
||||
let count = 0;
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'file') {
|
||||
count++;
|
||||
} else if (node.children) {
|
||||
count += countFiles(node.children);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count total directories in artifact tree
|
||||
*/
|
||||
function countDirectories(nodes: ArtifactNode[]): number {
|
||||
let count = 0;
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'directory') {
|
||||
count++;
|
||||
if (node.children) {
|
||||
count += countDirectories(node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count total size of all files in artifact tree
|
||||
*/
|
||||
function countTotalSize(nodes: ArtifactNode[]): number {
|
||||
let totalSize = 0;
|
||||
for (const node of nodes) {
|
||||
if (node.type === 'file' && node.size) {
|
||||
totalSize += node.size;
|
||||
} else if (node.children) {
|
||||
totalSize += countTotalSize(node.children);
|
||||
}
|
||||
}
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
@@ -788,17 +788,22 @@ export class JsonLinesParser implements IOutputParser {
|
||||
|
||||
// Default: treat as stdout/stderr based on fallback
|
||||
if (json.content || json.message || json.text) {
|
||||
const rawContent = json.content || json.message || json.text;
|
||||
// Safely convert to string: content may be an array (e.g. Claude API content blocks) or object
|
||||
const contentStr = typeof rawContent === 'string'
|
||||
? rawContent
|
||||
: JSON.stringify(rawContent);
|
||||
this.debugLog('mapJsonToIR_fallback_stdout', {
|
||||
type: json.type,
|
||||
fallbackType: fallbackStreamType,
|
||||
hasContent: !!json.content,
|
||||
hasMessage: !!json.message,
|
||||
hasText: !!json.text,
|
||||
contentPreview: (json.content || json.message || json.text || '').substring(0, 100)
|
||||
contentPreview: contentStr.substring(0, 100)
|
||||
});
|
||||
return {
|
||||
type: fallbackStreamType,
|
||||
content: json.content || json.message || json.text,
|
||||
content: contentStr,
|
||||
timestamp
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface TeamMeta {
|
||||
updated_at: string;
|
||||
archived_at?: string;
|
||||
pipeline_mode?: string;
|
||||
session_id?: string; // Links to .workflow/.team/{session-id}/ artifacts directory
|
||||
}
|
||||
|
||||
export function getMetaPath(team: string): string {
|
||||
@@ -106,7 +107,7 @@ export interface StatusEntry {
|
||||
|
||||
const ParamsSchema = z.object({
|
||||
operation: z.enum(['log', 'read', 'list', 'status', 'delete', 'clear']).describe('Operation to perform'),
|
||||
team: z.string().describe('Team name (maps to .workflow/.team-msg/{team}/messages.jsonl)'),
|
||||
team: z.string().describe('Session ID (new: .workflow/.team/{session-id}/.msg/) or team name (legacy: .workflow/.team-msg/{team}/)'),
|
||||
|
||||
// log params
|
||||
from: z.string().optional().describe('[log/list] Sender role name'),
|
||||
@@ -121,6 +122,9 @@ const ParamsSchema = z.object({
|
||||
|
||||
// list params
|
||||
last: z.number().min(1).max(100).optional().describe('[list] Return last N messages (default: 20)'),
|
||||
|
||||
// session_id for artifact discovery
|
||||
session_id: z.string().optional().describe('[log] Session ID for artifact discovery (links team to .workflow/.team/{session-id}/)'),
|
||||
});
|
||||
|
||||
type Params = z.infer<typeof ParamsSchema>;
|
||||
@@ -131,14 +135,21 @@ export const schema: ToolSchema = {
|
||||
name: 'team_msg',
|
||||
description: `Team message bus - persistent JSONL log for Agent Team communication.
|
||||
|
||||
Directory Structure (NEW):
|
||||
.workflow/.team/{session-id}/.msg/messages.jsonl
|
||||
|
||||
Directory Structure (LEGACY):
|
||||
.workflow/.team-msg/{team-name}/messages.jsonl
|
||||
|
||||
Operations:
|
||||
team_msg(operation="log", team="my-team", from="planner", to="coordinator", type="plan_ready", summary="Plan ready: 3 tasks", ref=".workflow/.team-plan/my-team/plan.json")
|
||||
team_msg(operation="read", team="my-team", id="MSG-003")
|
||||
team_msg(operation="list", team="my-team")
|
||||
team_msg(operation="list", team="my-team", from="tester", last=5)
|
||||
team_msg(operation="status", team="my-team")
|
||||
team_msg(operation="delete", team="my-team", id="MSG-003")
|
||||
team_msg(operation="clear", team="my-team")
|
||||
team_msg(operation="log", team="TLS-my-team-2026-02-15", from="planner", to="coordinator", type="plan_ready", summary="Plan ready: 3 tasks", ref=".workflow/.team-plan/my-team/plan.json")
|
||||
team_msg(operation="log", team="TLS-my-team-2026-02-15", from="coordinator", to="implementer", type="task_unblocked", summary="Task ready")
|
||||
team_msg(operation="read", team="TLS-my-team-2026-02-15", id="MSG-003")
|
||||
team_msg(operation="list", team="TLS-my-team-2026-02-15")
|
||||
team_msg(operation="list", team="TLS-my-team-2026-02-15", from="tester", last=5)
|
||||
team_msg(operation="status", team="TLS-my-team-2026-02-15")
|
||||
team_msg(operation="delete", team="TLS-my-team-2026-02-15", id="MSG-003")
|
||||
team_msg(operation="clear", team="TLS-my-team-2026-02-15")
|
||||
|
||||
Message types: plan_ready, plan_approved, plan_revision, task_unblocked, impl_complete, impl_progress, test_result, review_result, fix_required, error, shutdown`,
|
||||
inputSchema: {
|
||||
@@ -161,6 +172,7 @@ Message types: plan_ready, plan_approved, plan_revision, task_unblocked, impl_co
|
||||
data: { type: 'object', description: '[log] Optional structured data' },
|
||||
id: { type: 'string', description: '[read] Message ID (e.g. MSG-003)' },
|
||||
last: { type: 'number', description: '[list] Last N messages (default 20)', minimum: 1, maximum: 100 },
|
||||
session_id: { type: 'string', description: '[log] Session ID for artifact discovery' },
|
||||
},
|
||||
required: ['operation', 'team'],
|
||||
},
|
||||
@@ -168,9 +180,26 @@ Message types: plan_ready, plan_approved, plan_revision, task_unblocked, impl_co
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
export function getLogDir(team: string): string {
|
||||
/**
|
||||
* Get the log directory for a session.
|
||||
* New structure: .workflow/.team/{session-id}/.msg/
|
||||
*/
|
||||
export function getLogDir(sessionId: string): string {
|
||||
const root = getProjectRoot();
|
||||
return join(root, '.workflow', '.team-msg', team);
|
||||
return join(root, '.workflow', '.team', sessionId, '.msg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy support: Check both new (.team/{id}/.msg) and old (.team-msg/{id}) locations
|
||||
*/
|
||||
export function getLogDirWithFallback(sessionId: string): string {
|
||||
const newPath = getLogDir(sessionId);
|
||||
if (existsSync(newPath)) {
|
||||
return newPath;
|
||||
}
|
||||
// Fallback to old location for backward compatibility
|
||||
const root = getProjectRoot();
|
||||
return join(root, '.workflow', '.team-msg', sessionId);
|
||||
}
|
||||
|
||||
function getLogPath(team: string): string {
|
||||
@@ -241,6 +270,14 @@ function opLog(params: Params): ToolResult {
|
||||
|
||||
appendFileSync(logPath, JSON.stringify(msg) + '\n', 'utf-8');
|
||||
|
||||
// Update meta with session_id if provided
|
||||
if (params.session_id) {
|
||||
const meta = getEffectiveTeamMeta(params.team);
|
||||
meta.session_id = params.session_id;
|
||||
meta.updated_at = nowISO();
|
||||
writeTeamMeta(params.team, meta);
|
||||
}
|
||||
|
||||
return { success: true, result: { id, message: `Logged ${id}: [${msg.from} → ${msg.to}] ${msg.summary}` } };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user