Files
Claude-Code-Workflow/.claude/commands/team/spec-writer.md
catlog22 6054a01b8f feat: add CLI fallback for MCP calls in team commands
- Implemented CLI fallback using `ccw team` for various team command operations in `execute.md`, `plan.md`, `review.md`, `spec-analyst.md`, `spec-coordinate.md`, `spec-discuss.md`, `spec-reviewer.md`, `spec-writer.md`, and `test.md`.
- Updated command generation templates to include CLI fallback examples.
- Enhanced validation checks to ensure CLI fallback sections are present.
- Added quality standards for CLI fallback in team command design.
- Introduced a new `GlobalGraphExpander` class for expanding search results with cross-directory relationships.
- Added tests for `GlobalGraphExpander` to verify functionality and score decay factors.
2026-02-13 12:05:48 +08:00

17 KiB
Raw Blame History

name, description, argument-hint, allowed-tools, group
name description argument-hint allowed-tools group
spec-writer Team spec writer - 产品简报/需求文档/架构文档/史诗故事撰写、模板驱动文档生成 SendMessage(*), TaskUpdate(*), TaskList(*), TaskGet(*), TodoWrite(*), Read(*), Write(*), Edit(*), Bash(*), Glob(*), Grep(*), Task(*) team

Team Spec Writer Command (/team:spec-writer)

Overview

Team spec-writer role command. Operates as a teammate within a Spec Team, responsible for generating all specification documents. Maps to spec-generator Phases 2-5 (Product Brief, Requirements, Architecture, Epics & Stories).

Core capabilities:

  • Task discovery from shared team task list (DRAFT-* tasks)
  • Complexity-adaptive writing (Low → direct, Medium/High → multi-CLI analysis)
  • Multi-perspective document generation (产品/技术/用户 parallel analysis)
  • Template-driven output following spec-generator document standards
  • Discussion feedback incorporation for iterative refinement

Role Definition

Name: spec-writer Responsibility: Load Context → Generate Document → Incorporate Feedback → Report Communication: SendMessage to coordinator only

消息总线

每次 SendMessage ,必须调用 mcp__ccw-tools__team_msg 记录消息:

mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "<type>", summary: "<摘要>", ref: "<文件路径>" })

支持的 Message Types

Type 方向 触发时机 说明
draft_ready spec-writer → coordinator 文档撰写完成 附带文档路径和类型
draft_revision spec-writer → coordinator 文档修订后重新提交 说明修改内容
impl_progress spec-writer → coordinator 长时间撰写进展 多文档阶段进度
error spec-writer → coordinator 遇到不可恢复错误 模板缺失、上下文不足等

调用示例

// 文档就绪
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "draft_ready", summary: "Product Brief 完成: 8个章节, 3视角合成", ref: ".workflow/.spec-team/session/product-brief.md" })

// 文档修订
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "draft_revision", summary: "PRD 已按讨论反馈修订: 新增2个NFR, 调整3个优先级" })

// 错误上报
mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "error", summary: "缺少 discovery-context.json, 无法生成 Product Brief" })

CLI 回退

mcp__ccw-tools__team_msg MCP 不可用时,使用 ccw team CLI 作为等效回退:

// 回退: 将 MCP 调用替换为 Bash CLI参数一一对应
Bash(`ccw team log --team "${teamName}" --from "spec-writer" --to "coordinator" --type "draft_ready" --summary "Product Brief 完成: 8个章节" --ref "${sessionFolder}/product-brief.md" --json`)

参数映射: team_msg(params)ccw team log --team <team> --from spec-writer --to coordinator --type <type> --summary "<text>" [--ref <path>] [--data '<json>'] [--json]

Execution Process

Phase 1: Task Discovery
   ├─ TaskList to find unblocked DRAFT-* tasks
   ├─ TaskGet to read full task details
   └─ TaskUpdate to mark in_progress

Phase 2: Context & Discussion Loading
   ├─ Read session config (spec-config.json)
   ├─ Read relevant prior documents and discussion records
   ├─ Determine document type from task subject (Brief/PRD/Architecture/Epics)
   └─ Load discussion feedback (discuss-*.md)

Phase 3: Document Generation (type-specific)
   ├─ DRAFT-001: Product Brief (multi-CLI parallel analysis)
   ├─ DRAFT-002: Requirements/PRD (functional + non-functional + MoSCoW)
   ├─ DRAFT-003: Architecture (ADRs + tech stack + diagrams)
   └─ DRAFT-004: Epics & Stories (decomposition + dependencies + MVP)

Phase 4: Self-Validation
   ├─ Check all template sections populated
   ├─ Verify cross-references to prior documents
   └─ Validate YAML frontmatter completeness

Phase 5: Report to Coordinator
   ├─ team_msg log + SendMessage document summary
   ├─ TaskUpdate completed
   └─ Check for next DRAFT-* task

Implementation

Phase 1: Task Discovery

// Find assigned DRAFT-* tasks
const tasks = TaskList()
const myTasks = tasks.filter(t =>
  t.subject.startsWith('DRAFT-') &&
  t.owner === 'spec-writer' &&
  t.status === 'pending' &&
  t.blockedBy.length === 0
)

if (myTasks.length === 0) return // idle

const task = TaskGet({ taskId: myTasks[0].id })
TaskUpdate({ taskId: task.id, status: 'in_progress' })

Phase 2: Context & Discussion Loading

// Extract session folder from task description
const sessionMatch = task.description.match(/Session:\s*(.+)/)
const sessionFolder = sessionMatch ? sessionMatch[1].trim() : ''

// Load session config
let specConfig = null
try { specConfig = JSON.parse(Read(`${sessionFolder}/spec-config.json`)) } catch {}

// Determine document type from task subject
const docType = task.subject.includes('Product Brief') ? 'product-brief'
  : task.subject.includes('Requirements') || task.subject.includes('PRD') ? 'requirements'
  : task.subject.includes('Architecture') ? 'architecture'
  : task.subject.includes('Epics') ? 'epics'
  : 'unknown'

// Load discussion feedback (from preceding DISCUSS task)
const discussionFiles = {
  'product-brief': 'discussions/discuss-001-scope.md',
  'requirements': 'discussions/discuss-002-brief.md',
  'architecture': 'discussions/discuss-003-requirements.md',
  'epics': 'discussions/discuss-004-architecture.md'
}
let discussionFeedback = null
try {
  discussionFeedback = Read(`${sessionFolder}/${discussionFiles[docType]}`)
} catch {}

// Load prior documents
const priorDocs = {}
if (docType !== 'product-brief') {
  try { priorDocs.discoveryContext = Read(`${sessionFolder}/discovery-context.json`) } catch {}
}
if (docType === 'requirements' || docType === 'architecture' || docType === 'epics') {
  try { priorDocs.productBrief = Read(`${sessionFolder}/product-brief.md`) } catch {}
}
if (docType === 'architecture' || docType === 'epics') {
  try { priorDocs.requirementsIndex = Read(`${sessionFolder}/requirements/_index.md`) } catch {}
}
if (docType === 'epics') {
  try { priorDocs.architectureIndex = Read(`${sessionFolder}/architecture/_index.md`) } catch {}
}

Phase 3: Document Generation

// Route to specific generation logic based on document type
switch (docType) {
  case 'product-brief':
    await generateProductBrief(sessionFolder, specConfig, discussionFeedback)
    break
  case 'requirements':
    await generateRequirements(sessionFolder, specConfig, priorDocs, discussionFeedback)
    break
  case 'architecture':
    await generateArchitecture(sessionFolder, specConfig, priorDocs, discussionFeedback)
    break
  case 'epics':
    await generateEpics(sessionFolder, specConfig, priorDocs, discussionFeedback)
    break
}

DRAFT-001: Product Brief (Multi-Perspective Analysis)

async function generateProductBrief(sessionFolder, config, discussionFeedback) {
  const discoveryContext = JSON.parse(Read(`${sessionFolder}/discovery-context.json`))
  const topic = config?.topic || discoveryContext.seed_analysis.problem_statement

  // 进展通知
  mcp__ccw-tools__team_msg({ operation: "log", team: teamName, from: "spec-writer", to: "coordinator", type: "impl_progress", summary: "开始 Product Brief 多视角分析 (1/3)" })

  // Launch 3 parallel CLI analyses for multi-perspective synthesis
  // 1. Product perspective (Gemini)
  Bash({
    command: `ccw cli -p "PURPOSE: Analyze from PRODUCT perspective for specification.
TASK: • Market fit analysis • Value proposition • Success criteria • Competitive landscape
TOPIC: ${topic}
CONTEXT: Discovery findings: ${JSON.stringify(discoveryContext.seed_analysis)}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Structured product analysis (vision, problem, goals, scope, constraints)
CONSTRAINTS: Focus on product strategy" --tool gemini --mode analysis`,
    run_in_background: true
  })

  // 2. Technical perspective (Codex)
  Bash({
    command: `ccw cli -p "PURPOSE: Analyze from TECHNICAL perspective for specification.
TASK: • Technical feasibility • Architecture constraints • Tech stack recommendations • Implementation risks
TOPIC: ${topic}
CONTEXT: Discovery findings: ${JSON.stringify(discoveryContext.seed_analysis)}
${discoveryContext.codebase_context ? `CODEBASE: ${JSON.stringify(discoveryContext.codebase_context)}` : ''}
EXPECTED: Technical feasibility assessment
CONSTRAINTS: Focus on engineering perspective" --tool codex --mode analysis`,
    run_in_background: true
  })

  // 3. User perspective (Claude)
  Bash({
    command: `ccw cli -p "PURPOSE: Analyze from USER perspective for specification.
TASK: • User personas • User journeys • UX considerations • Accessibility needs
TOPIC: ${topic}
CONTEXT: Target users: ${JSON.stringify(discoveryContext.seed_analysis.target_users)}
EXPECTED: User experience analysis (personas, journeys, pain points)
CONSTRAINTS: Focus on end-user perspective" --tool claude --mode analysis`,
    run_in_background: true
  })

  // Wait for all 3 analyses to complete, then synthesize

  // Generate product-brief.md with YAML frontmatter
  const brief = `---
session_id: ${config.session_id}
phase: 2
document_type: product-brief
status: draft
generated_at: ${new Date().toISOString()}
version: 1
dependencies:
  - discovery-context.json
  - discuss-001-scope.md
---

# Product Brief: ${topic}

## Vision
${productPerspective.vision}

## Problem Statement
${discoveryContext.seed_analysis.problem_statement}

## Target Users
${personas.map(p => `### ${p.name}\n- **Role**: ${p.role}\n- **Pain Points**: ${p.painPoints}\n- **Goals**: ${p.goals}`).join('\n\n')}

## Goals & Success Metrics
${productPerspective.goals}

## Scope
### In Scope
${productPerspective.inScope}

### Out of Scope
${productPerspective.outOfScope}

## Technical Feasibility
${technicalPerspective.summary}

## User Experience Considerations
${userPerspective.summary}

## Multi-Perspective Synthesis
### Convergent Themes
${synthesis.convergent}

### Divergent Views
${synthesis.divergent}

### Discussion Feedback Integration
${discussionFeedback ? discussionFeedback.summary : 'N/A (first draft)'}

## Constraints
${discoveryContext.seed_analysis.constraints.map(c => `- ${c}`).join('\n')}

## Open Questions
${openQuestions.map(q => `- ${q}`).join('\n')}
`
  Write(`${sessionFolder}/product-brief.md`, brief)
}

DRAFT-002: Requirements/PRD

async function generateRequirements(sessionFolder, config, priorDocs, discussionFeedback) {
  // Use Gemini CLI to expand requirements from product brief
  Bash({
    command: `ccw cli -p "PURPOSE: Generate functional and non-functional requirements from Product Brief.
TASK:
• Extract functional requirements (REQ-NNN format) with user stories and acceptance criteria
• Generate non-functional requirements (NFR-{type}-NNN) for Performance/Security/Scalability/Usability
• Apply MoSCoW prioritization (Must/Should/Could/Won't)
• Create traceability matrix to Product Brief goals
CONTEXT: Product Brief: ${priorDocs.productBrief}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Structured requirements list in JSON
CONSTRAINTS: Each requirement needs ID, title, user story, 2-4 acceptance criteria" --tool gemini --mode analysis`,
    run_in_background: true
  })

  // Generate requirements/ directory structure
  Bash(`mkdir -p ${sessionFolder}/requirements`)

  // Write _index.md + individual REQ-*.md + NFR-*.md files
  // Following spec-generator templates/requirements-prd.md format
}

DRAFT-003: Architecture

async function generateArchitecture(sessionFolder, config, priorDocs, discussionFeedback) {
  // Generate architecture via Gemini
  Bash({
    command: `ccw cli -p "PURPOSE: Design system architecture based on requirements.
TASK:
• Select architecture style with justification
• Define core components and responsibilities
• Create component interaction diagram (Mermaid)
• Choose tech stack (languages, frameworks, databases, infrastructure)
• Generate 2-4 ADRs with alternatives and pros/cons
• Design data model (Mermaid erDiagram)
• Define security architecture
CONTEXT: Requirements: ${priorDocs.requirementsIndex}
Product Brief: ${priorDocs.productBrief}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Complete architecture document
CONSTRAINTS: Include ADRs with alternatives" --tool gemini --mode analysis`,
    run_in_background: true
  })

  // Challenge architecture via Codex
  Bash({
    command: `ccw cli -p "PURPOSE: Challenge and review proposed architecture.
TASK: • Review ADR alternatives • Identify bottlenecks • Assess security gaps • Rate quality (1-5)
CONTEXT: [architecture output from above]
EXPECTED: Architecture review with ratings" --tool codex --mode analysis`,
    run_in_background: true
  })

  // Generate architecture/ directory
  Bash(`mkdir -p ${sessionFolder}/architecture`)

  // Write _index.md + ADR-*.md files
}

DRAFT-004: Epics & Stories

async function generateEpics(sessionFolder, config, priorDocs, discussionFeedback) {
  // Decompose via Gemini
  Bash({
    command: `ccw cli -p "PURPOSE: Decompose requirements into Epics and Stories.
TASK:
• Group 3-7 logical Epics by domain or user journey
• Generate 2-5 Stories per Epic (user story format)
• Create cross-Epic dependency map (Mermaid)
• Define MVP scope with done criteria
• Recommend execution order
CONTEXT: Requirements: ${priorDocs.requirementsIndex}
Architecture: ${priorDocs.architectureIndex}
Product Brief: ${priorDocs.productBrief}
${discussionFeedback ? `DISCUSSION FEEDBACK: ${discussionFeedback}` : ''}
EXPECTED: Epic/Story decomposition with dependencies
CONSTRAINTS: Each story needs AC, size estimate (S/M/L/XL), requirement tracing" --tool gemini --mode analysis`,
    run_in_background: true
  })

  // Generate epics/ directory
  Bash(`mkdir -p ${sessionFolder}/epics`)

  // Write _index.md + EPIC-*.md files
}

Phase 4: Self-Validation

// Validate generated document
const validationChecks = {
  has_frontmatter: false,
  sections_complete: false,
  cross_references: false,
  discussion_integrated: false
}

// Check YAML frontmatter
const docContent = Read(`${sessionFolder}/${outputPath}`)
validationChecks.has_frontmatter = /^---\n[\s\S]+?\n---/.test(docContent)

// Check required sections based on doc type
const requiredSections = {
  'product-brief': ['Vision', 'Problem Statement', 'Target Users', 'Goals', 'Scope'],
  'requirements': ['_index.md', 'REQ-'],
  'architecture': ['_index.md', 'ADR-'],
  'epics': ['_index.md', 'EPIC-']
}
// Verify all sections present

// Check cross-references to prior documents
validationChecks.cross_references = docContent.includes('session_id')

// Check discussion feedback integration
validationChecks.discussion_integrated = !discussionFeedback || docContent.includes('Discussion')

const allValid = Object.values(validationChecks).every(v => v)

Phase 5: Report to Coordinator

const docTypeLabel = {
  'product-brief': 'Product Brief',
  'requirements': 'Requirements/PRD',
  'architecture': 'Architecture Document',
  'epics': 'Epics & Stories'
}

// Log before SendMessage
mcp__ccw-tools__team_msg({
  operation: "log", team: teamName,
  from: "spec-writer", to: "coordinator",
  type: "draft_ready",
  summary: `${docTypeLabel[docType]} 完成: ${allValid ? '验证通过' : '部分验证失败'}`,
  ref: `${sessionFolder}/${outputPath}`
})

SendMessage({
  type: "message",
  recipient: "coordinator",
  content: `## 文档撰写结果

**Task**: ${task.subject}
**文档类型**: ${docTypeLabel[docType]}
**验证状态**: ${allValid ? 'PASS' : 'PARTIAL'}

### 文档摘要
${documentSummary}

### 讨论反馈整合
${discussionFeedback ? '已整合前序讨论反馈' : '首次撰写(无前序讨论反馈)'}

### 自验证结果
${Object.entries(validationChecks).map(([k, v]) => `- ${k}: ${v ? '✓' : '✗'}`).join('\n')}

### 输出位置
${sessionFolder}/${outputPath}

文档已就绪,可进入讨论轮次。`,
  summary: `${docTypeLabel[docType]} 就绪`
})

// Mark task completed
TaskUpdate({ taskId: task.id, status: 'completed' })

// Check for next DRAFT task
const nextTasks = TaskList().filter(t =>
  t.subject.startsWith('DRAFT-') &&
  t.owner === 'spec-writer' &&
  t.status === 'pending' &&
  t.blockedBy.length === 0
)

if (nextTasks.length > 0) {
  // Continue with next task -> back to Phase 1
}

Error Handling

Scenario Resolution
No DRAFT-* tasks available Idle, wait for coordinator assignment
Prior document not found Notify coordinator, request prerequisite
CLI analysis failure Retry with fallback tool, then direct generation
Template sections incomplete Generate best-effort, note gaps in report
Discussion feedback contradicts prior docs Note conflict in document, flag for next discussion
Session folder missing Notify coordinator, request session path
Unexpected error Log error via team_msg, report to coordinator