feat: Implement team artifacts feature with tree navigation and file preview

This commit is contained in:
catlog22
2026-02-15 18:14:07 +08:00
parent 0d56396710
commit 4ddd2e9f17
26 changed files with 1250 additions and 3038 deletions

View File

@@ -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' // 统一 Skillbrainstorm (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.

View File

@@ -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.

View File

@@ -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]
```

View File

@@ -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.

View File

@@ -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】|
---

View File

@@ -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)

View File

@@ -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选择CRDTui-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.

View File

@@ -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/`

View File

@@ -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

View File

@@ -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

View File

@@ -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 |
---

View File

@@ -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": {

View File

@@ -4,7 +4,7 @@
"level": 1,
"steps": [
{
"cmd": "/workflow:lite-fix",
"cmd": "/workflow:lite-plan",
"args": "--hotfix \"{{goal}}\"",
"execution": {
"type": "slash-command",

View File

@@ -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",

View File

@@ -4,7 +4,7 @@
"level": 4,
"steps": [
{
"cmd": "/workflow:brainstorm:auto-parallel",
"cmd": "/brainstorm",
"args": "\"{{goal}}\"",
"execution": {
"type": "slash-command",

View File

@@ -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",

View File

@@ -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"
}
]
}

View File

@@ -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": {

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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);

View File

@@ -101,7 +101,7 @@ export function TeamPage() {
{/* Artifacts Tab */}
{detailTab === 'artifacts' && (
<TeamArtifacts messages={messages} />
<TeamArtifacts teamName={selectedTeam} />
)}
{/* Messages Tab */}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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
};
}

View File

@@ -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}` } };
}