Files
Claude-Code-Workflow/.claude/commands/workflow/ui-design/capture.md
catlog22 2fcd44e856 feat: Add imitate-auto-v3 workflow for high-speed multi-page UI replication
- Introduced a new workflow named `imitate-auto-v3` that allows for batch screenshot capture and optional token refinement.
- Implemented a comprehensive 5-phase execution protocol to handle initialization, screenshot capture, style extraction, token processing, and UI generation.
- Added support for multiple target URLs through a `--url-map` parameter, enhancing flexibility in design replication.
- Included optional parameters for session management and token refinement, allowing users to customize their workflow experience.
- Enhanced error handling and reporting throughout the workflow to ensure clarity and ease of debugging.
- Deprecated the previous `imitate-auto.md` workflow in favor of this more robust version.
2025-10-11 11:42:33 +08:00

21 KiB
Raw Blame History

name, description, usage, examples, allowed-tools
name description usage examples allowed-tools
capture Batch screenshot capture for UI design workflows using MCP or local fallback /workflow:ui-design:capture --url-map "<map>" [--base-path <path>] [--session <id>]
/workflow:ui-design:capture --url-map "home:https://linear.app, pricing:https://linear.app/pricing"
/workflow:ui-design:capture --session WFS-auth --url-map "dashboard:https://app.com/dash"
/workflow:ui-design:capture --base-path ".workflow/.design/run-20250110" --url-map "hero:https://example.com#hero"
TodoWrite(*), Read(*), Write(*), Bash(*), Glob(*), mcp__browser__screenshot(*)

Batch Screenshot Capture Command

Overview

独立的批量截图命令支持MCP优先策略和多级降级机制。可被其他工作流复用。

Core Philosophy

  • MCP优先优先使用mcp__browser__screenshot如果可用
  • 智能降级MCP失败 → Playwright → Chrome → 手动上传
  • 批量处理一次处理多个URL并行截图
  • 结构化输出按target_name组织截图文件
  • 错误容忍:部分失败不阻塞整体流程

Execution Protocol

Phase 0: 初始化和路径解析

# 确定base path
IF --base-path:
    base_path = {provided_base_path}
ELSE IF --session:
    # 查找session最新design run
    session_design_dirs = Glob(".workflow/WFS-{session}/design-*")
    IF session_design_dirs:
        base_path = most_recent(session_design_dirs)
    ELSE:
        # 创建新的design run
        run_id = "run-$(date +%Y%m%d-%H%M%S)"
        base_path = ".workflow/WFS-{session}/design-{run_id}"
ELSE:
    # Standalone模式
    run_id = "run-$(date +%Y%m%d-%H%M%S)"
    base_path = ".workflow/.design/{run_id}"

# 创建截图目录
screenshot_dir = "{base_path}/screenshots"
Bash(mkdir -p "{screenshot_dir}")

REPORT: "📸 Batch Screenshot Capture"
REPORT: "   Output: {screenshot_dir}/"

# 解析url-map
url_map_string = {--url-map}  # "home:https://a.com, pricing:https://a.com/pricing"

# 解析逻辑
url_entries = []
FOR pair IN split(url_map_string, ","):
    pair = pair.strip()
    IF ":" NOT IN pair:
        ERROR: "Invalid url-map format: '{pair}'"
        ERROR: "Expected format: 'target:url'"
        ERROR: "Example: 'home:https://example.com, pricing:https://example.com/pricing'"
        EXIT 1

    target, url = pair.split(":", 1)
    target = target.strip().lower().replace(" ", "-")
    url = url.strip()

    # 验证target名称
    IF NOT regex_match(target, r"^[a-z0-9][a-z0-9_-]*$"):
        ERROR: "Invalid target name: '{target}'"
        ERROR: "Target names must start with alphanumeric and contain only [a-z0-9_-]"
        EXIT 1

    # 验证URL格式
    IF NOT (url.startswith("http://") OR url.startswith("https://")):
        WARN: "URL '{url}' does not start with http:// or https://"
        WARN: "Prepending https:// automatically"
        url = f"https://{url}"

    url_entries.append({"target": target, "url": url})

VALIDATE: len(url_entries) > 0, "url-map must contain at least one target:url pair"

total_targets = len(url_entries)
REPORT: "   Targets: {total_targets}"
FOR entry IN url_entries:
    REPORT: "     • {entry.target}: {entry.url}"

TodoWrite({todos: [
  {content: "Initialize and parse url-map", status: "completed", activeForm: "Initializing"},
  {content: "MCP batch screenshot capture", status: "pending", activeForm: "Capturing via MCP"},
  {content: "Local tool fallback (if needed)", status: "pending", activeForm: "Local fallback"},
  {content: "Verify and summarize results", status: "pending", activeForm: "Verifying"}
]})

Phase 1: MCP批量截图优先策略

REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
REPORT: "🚀 Phase 1: MCP Batch Screenshot"
REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# 检测MCP工具可用性
mcp_available = check_tool_availability("mcp__browser__screenshot")

success_captures = []
failed_captures = []

IF mcp_available:
    REPORT: "✓ MCP browser screenshot tool detected"

    # 构建MCP调用参数
    urls = [entry.url for entry in url_entries]
    file_prefixes = [entry.target for entry in url_entries]

    TRY:
        # MCP批量截图调用
        REPORT: "   Calling MCP for {len(urls)} URLs..."

        result = mcp__browser__screenshot({
            urls: urls,
            output_dir: screenshot_dir,
            file_prefix: file_prefixes,
            full_page: true,
            viewport: "1920,1080",
            timeout: 30000
        })

        # 处理MCP返回结果
        success_count = len(result.success)
        failed_count = len(result.failed)

        REPORT: "   MCP capture complete: {success_count} succeeded, {failed_count} failed"

        FOR item IN result.success:
            success_captures.append(item.target)
            REPORT: "     ✓ {item.target}.png"

        FOR item IN result.failed:
            failed_captures.append({"target": item.target, "url": item.url, "error": item.error})
            REPORT: "     ✗ {item.target}: {item.error}"

        # 如果有部分失败标记需要fallback
        IF failed_count > 0:
            failed_targets = [item.target for item in result.failed]
            REPORT: "   → {failed_count} failed, proceeding to local fallback..."
            TodoWrite(mark_completed: "MCP batch screenshot capture", mark_in_progress: "Local tool fallback")
            GOTO Phase 2 (fallback_targets = failed_targets)
        ELSE:
            REPORT: "✅ All screenshots captured via MCP"
            TodoWrite(mark_completed: "MCP batch screenshot capture", mark_completed: "Local tool fallback", mark_in_progress: "Verify results")
            GOTO Phase 3 (verification)

    CATCH error:
        REPORT: "⚠️ MCP call failed: {error}"
        REPORT: "   Falling back to local tools for all targets"
        failed_targets = [entry.target for entry in url_entries]
        TodoWrite(mark_completed: "MCP batch screenshot capture", mark_in_progress: "Local tool fallback")
        GOTO Phase 2 (fallback_targets = failed_targets)

ELSE:
    REPORT: " MCP browser screenshot tool not available"
    REPORT: "   Using local tool fallback strategy"
    failed_targets = [entry.target for entry in url_entries]
    TodoWrite(mark_completed: "MCP batch screenshot capture", mark_in_progress: "Local tool fallback")
    GOTO Phase 2 (fallback_targets = failed_targets)

Phase 2: Local工具降级Playwright → Chrome

REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
REPORT: "🔧 Phase 2: Local Tool Fallback"
REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# 仅处理Phase 1失败的targets
targets_to_capture = [entry for entry in url_entries if entry.target IN fallback_targets]

REPORT: "Processing {len(targets_to_capture)} target(s) with local tools..."

# 检测本地工具(检查可执行文件,不触发安装)
playwright_path = Bash(which playwright 2>/dev/null || echo "")
chrome_path = Bash(which google-chrome 2>/dev/null || which chrome 2>/dev/null || which chromium 2>/dev/null || echo "")

playwright_available = playwright_path != ""
chrome_available = chrome_path != ""

REPORT: "   Tool availability:"
REPORT: "     Playwright: {playwright_available ? '✓ Available' : '✗ Not found'}"
REPORT: "     Chrome: {chrome_available ? '✓ Available' : '✗ Not found'}"

local_success = []
local_failed = []

FOR entry IN targets_to_capture:
    target = entry.target
    url = entry.url
    output_file = "{screenshot_dir}/{target}.png"
    captured = false
    method_used = null

    # 尝试Playwright
    IF playwright_available AND NOT captured:
        TRY:
            REPORT: "   Trying Playwright for '{target}'..."
            Bash({playwright_path} screenshot "{url}" "{output_file}" --full-page --timeout 30000)

            IF exists(output_file) AND file_size(output_file) > 1000:  # >1KB验证有效
                captured = true
                method_used = "Playwright"
                local_success.append(target)
                success_captures.append(target)
                REPORT: "     ✓ {target}.png (Playwright, {file_size(output_file)/1024:.1f} KB)"
        CATCH error:
            REPORT: "     ⚠️ Playwright failed: {error}"

    # 尝试Chrome
    IF chrome_available AND NOT captured:
        TRY:
            REPORT: "   Trying Chrome for '{target}'..."
            Bash({chrome_path} --headless --disable-gpu --screenshot="{output_file}" --window-size=1920,1080 "{url}")

            IF exists(output_file) AND file_size(output_file) > 1000:
                captured = true
                method_used = "Chrome"
                local_success.append(target)
                success_captures.append(target)
                REPORT: "     ✓ {target}.png (Chrome, {file_size(output_file)/1024:.1f} KB)"
        CATCH error:
            REPORT: "     ⚠️ Chrome failed: {error}"

    # 标记彻底失败
    IF NOT captured:
        local_failed.append(entry)
        failed_captures.append({"target": target, "url": url, "error": "All local tools failed"})
        REPORT: "     ✗ {target}: All tools failed"

REPORT: ""
REPORT: "Local fallback summary:"
REPORT: "   Succeeded: {len(local_success)}/{len(targets_to_capture)}"
REPORT: "   Failed: {len(local_failed)}/{len(targets_to_capture)}"

# 如果仍有失败,进入手动模式
IF len(local_failed) > 0:
    TodoWrite(mark_completed: "Local tool fallback", mark_in_progress: "Manual upload mode")
    GOTO Phase 2.5 (manual_upload)
ELSE:
    TodoWrite(mark_completed: "Local tool fallback", mark_in_progress: "Verify results")
    GOTO Phase 3 (verification)

Phase 2.5: 手动上传降级

REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
REPORT: "⚠️ Phase 2.5: Manual Screenshot Required"
REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

REPORT: "Failed to auto-capture {len(local_failed)} target(s):"
REPORT: ""

FOR entry IN local_failed:
    REPORT: "  {entry.target}:"
    REPORT: "    URL: {entry.url}"
    REPORT: "    Save to: {screenshot_dir}/{entry.target}.png"
    REPORT: ""

REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
REPORT: "📋 Manual Steps:"
REPORT: "  1. Visit each URL above in your browser"
REPORT: "  2. Take full-page screenshot (Browser DevTools or extensions)"
REPORT: "  3. Save screenshot to specified path"
REPORT: "  4. Return here and choose an option"
REPORT: ""
REPORT: "Options:"
REPORT: "  • Type 'ready' : I've uploaded screenshot(s), continue workflow"
REPORT: "  • Type 'skip'  : Skip failed screenshots, continue with available"
REPORT: "  • Type 'abort' : Cancel entire capture workflow"
REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

user_response = WAIT_FOR_USER_INPUT()

MATCH user_response.lower():
    "ready" | "done" | "ok" | "continue":
        # 验证手动上传的文件
        manual_uploaded = []
        REPORT: "🔍 Checking for manual screenshots..."

        FOR entry IN local_failed:
            expected_path = f"{screenshot_dir}/{entry.target}.png"

            IF exists(expected_path) AND file_size(expected_path) > 1000:
                manual_uploaded.append(entry.target)
                success_captures.append(entry.target)
                REPORT: "  ✓ {entry.target}.png (manual, {file_size(expected_path)/1024:.1f} KB)"
            ELSE:
                REPORT: "  ✗ {entry.target}.png not found or invalid"

        IF len(manual_uploaded) > 0:
            REPORT: "✅ Detected {len(manual_uploaded)} manual screenshot(s)"

            # 移除已上传的targets from failed list
            failed_captures = [f for f in failed_captures if f.target NOT IN manual_uploaded]
        ELSE:
            REPORT: "⚠️ No valid manual screenshots detected"
            REPORT: "   Proceeding with available screenshots only"

        TodoWrite(mark_completed: "Manual upload mode", mark_in_progress: "Verify results")
        GOTO Phase 3

    "skip" | "s":
        REPORT: "⏭️ Skipping {len(local_failed)} failed screenshot(s)"
        REPORT: "   Proceeding with {len(success_captures)} available screenshot(s)"
        TodoWrite(mark_completed: "Manual upload mode", mark_in_progress: "Verify results")
        GOTO Phase 3

    "abort" | "cancel" | "exit":
        ERROR: "Workflow aborted by user"
        TodoWrite(mark_failed: "Manual upload mode")
        EXIT 1

    _:
        REPORT: "⚠️ Invalid input '{user_response}', interpreting as 'skip'"
        REPORT: "   Proceeding with available screenshots"
        TodoWrite(mark_completed: "Manual upload mode", mark_in_progress: "Verify results")
        GOTO Phase 3

Phase 3: 验证和总结

REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
REPORT: "✅ Phase 3: Verification & Summary"
REPORT: "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# 扫描实际截图文件
captured_files = Glob("{screenshot_dir}/*.{png,jpg,jpeg,webp}")
captured_targets_actual = [extract_basename_without_ext(f) for f in captured_files]

# 与请求对比
total_requested = len(url_entries)
total_captured = len(captured_targets_actual)
missing_targets = [entry.target for entry in url_entries if entry.target NOT IN captured_targets_actual]

REPORT: "📊 Capture Summary:"
REPORT: "   Total requested: {total_requested}"
REPORT: "   Successfully captured: {total_captured}"
REPORT: "   Success rate: {(total_captured / total_requested * 100):.1f}%"

IF missing_targets:
    REPORT: ""
    REPORT: "⚠️ Missing screenshots ({len(missing_targets)}):"
    FOR target IN missing_targets:
        REPORT: "     • {target}"
ELSE:
    REPORT: ""
    REPORT: "✅ All requested screenshots captured!"

# 生成capture元数据
metadata = {
    "timestamp": current_timestamp(),
    "total_requested": total_requested,
    "total_captured": total_captured,
    "success_rate": round(total_captured / total_requested * 100, 2),
    "screenshots": []
}

FOR entry IN url_entries:
    target = entry.target
    url = entry.url
    is_captured = target IN captured_targets_actual

    screenshot_info = {
        "target": target,
        "url": url,
        "captured": is_captured
    }

    IF is_captured:
        file_path = f"{screenshot_dir}/{target}.png"
        screenshot_info["path"] = file_path
        screenshot_info["size_kb"] = round(file_size(file_path) / 1024, 2)

        # 查找捕获方法
        IF target IN [s.target for s in result.success if mcp_available]:
            screenshot_info["method"] = "MCP"
        ELSE IF target IN local_success:
            screenshot_info["method"] = "Local (Playwright/Chrome)"
        ELSE:
            screenshot_info["method"] = "Manual"
    ELSE:
        screenshot_info["path"] = null
        screenshot_info["error"] = next((f.error for f in failed_captures if f.target == target), "Unknown error")

    metadata.screenshots.append(screenshot_info)

# 写入元数据文件
metadata_path = "{screenshot_dir}/capture-metadata.json"
Write(metadata_path, JSON.stringify(metadata, null, 2))

REPORT: ""
REPORT: "📁 Output:"
REPORT: "   Directory: {screenshot_dir}/"
REPORT: "   Metadata: capture-metadata.json"

TodoWrite(mark_completed: "Verify and summarize results")

Phase 4: 完成

TodoWrite({todos: [
  {content: "Initialize and parse url-map", status: "completed", activeForm: "Initializing"},
  {content: "MCP batch screenshot capture", status: "completed", activeForm: "MCP capture"},
  {content: "Local tool fallback (if needed)", status: "completed", activeForm: "Local fallback"},
  {content: "Verify and summarize results", status: "completed", activeForm: "Verifying"}
]});

Completion Message:

✅ Batch Screenshot Capture Complete!

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 Capture Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Requested: {total_requested} screenshots
Captured: {total_captured} screenshots
Success Rate: {success_rate}%

Capture Methods:
{IF mcp_available AND mcp_success_count > 0:
  • MCP: {mcp_success_count} screenshot(s)
}
{IF local_success_count > 0:
  • Local tools: {local_success_count} screenshot(s)
}
{IF manual_count > 0:
  • Manual upload: {manual_count} screenshot(s)
}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📂 Output Structure
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

{screenshot_dir}/
├── {target1}.png ({size1} KB)
├── {target2}.png ({size2} KB)
{FOR each captured screenshot}
└── capture-metadata.json

{IF missing_targets:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️ Missing Screenshots ({len(missing_targets)})
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

{FOR target IN missing_targets:
  • {target} - {get_error_for_target(target)}
}

💡 Tip: Re-run capture command for failed targets:
   /workflow:ui-design:capture --base-path "{base_path}" --url-map "{build_failed_url_map()}"
}

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🚀 Next Steps
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1. Extract design style from captured screenshots:
   /workflow:ui-design:extract --base-path "{base_path}" --images "{screenshot_dir}/*.png" --mode imitate --variants 1

2. Or continue with full imitate workflow:
   (capture is automatically called by /workflow:ui-design:imitate-auto-v3)

MCP API设计假设实现

// 假设的MCP工具签名
mcp__browser__screenshot({
  urls: string[],              // 要截图的URL列表
  output_dir: string,          // 输出目录
  file_prefix: string[],       // 文件名前缀列表对应urls
  full_page?: boolean,         // 全页截图默认true
  viewport?: string,           // 视口大小(默认"1920,1080"
  timeout?: number             // 每个URL的超时默认30000ms
}): {
  success: Array<{
    target: string,            // file_prefix对应的target名称
    url: string,              // 成功截图的URL
    path: string              // 生成的截图文件路径
  }>,
  failed: Array<{
    target: string,            // file_prefix对应的target名称
    url: string,              // 失败的URL
    error: string             // 错误信息
  }>
}

注意事项

  • 如果mcp__browser__screenshot工具不存在,命令会自动降级到本地工具
  • MCP工具应该支持并行截图以提高性能
  • 返回结果应该区分成功和失败,允许部分失败

Error Handling

  • 无效url-map格式:清晰的错误信息+格式示例
  • MCP不可用静默降级到本地工具Playwright/Chrome
  • 所有自动工具失败:进入交互式手动上传模式
  • 部分失败:记录成功项,继续流程(非阻塞),提供重试建议

Key Features

  1. MCP优先策略 - 使用托管工具提高可靠性和一致性
  2. 多级降级机制 - MCP → Playwright → Chrome → 手动上传
  3. 批量并行处理 - 一次处理多个URLMCP支持并行
  4. 错误容忍设计 - 部分失败不阻塞整体流程
  5. 高度可复用 - 可被imitate-auto-v3、explore-auto等工作流调用
  6. 结构化输出 - capture-metadata.json记录完整捕获信息
  7. 交互式降级 - 手动模式提供清晰的用户指导
  8. 智能路径管理 - 支持session集成和standalone模式

Integration Points

  • Input: --url-map (target:url pairs) + --base-path or --session
  • Output: {base_path}/screenshots/*.png + capture-metadata.json
  • Callers: /workflow:ui-design:imitate-auto-v3, potentially /workflow:ui-design:explore-auto-v2
  • Next Command: /workflow:ui-design:extract --images "{screenshot_dir}/*.png"