# CCW Orchestrator 无状态编排器:分析输入 → 选择工作流链 → TODO 跟踪执行 ## Architecture ``` ┌──────────────────────────────────────────────────────────────────┐ │ CCW Orchestrator (CLI-Enhanced) │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ Phase 1: Input Analysis (Rule-Based, Fast Path) │ │ ├─ Parse input (natural language / explicit command) │ │ ├─ Classify intent (bugfix / feature / issue / ui / docs) │ │ └─ Assess complexity (low / medium / high) │ │ │ │ Phase 1.5: CLI-Assisted Classification (Smart Path) │ │ ├─ Trigger: low match count / high complexity / long input │ │ ├─ Use Gemini CLI for semantic intent understanding │ │ ├─ Get confidence score and reasoning │ │ └─ Fallback to rule-based if CLI fails │ │ │ │ Phase 2: Chain Selection │ │ ├─ Load index/workflow-chains.json │ │ ├─ Match intent → chain(s) │ │ ├─ Filter by complexity │ │ └─ Select optimal chain │ │ │ │ Phase 2.5: CLI-Assisted Action Planning (Optimization) │ │ ├─ Trigger: high complexity / many steps / long request │ │ ├─ Use Gemini CLI to optimize execution strategy │ │ ├─ Suggest step modifications or CLI injections │ │ └─ Identify risks and provide mitigations │ │ │ │ Phase 3: User Confirmation (optional) │ │ ├─ Display selected chain and steps │ │ └─ Allow modification or manual selection │ │ │ │ Phase 4: TODO Tracking Setup │ │ ├─ Create TodoWrite with chain steps │ │ └─ Mark first step as in_progress │ │ │ │ Phase 5: Execution Loop │ │ ├─ Execute current step (SlashCommand) │ │ ├─ Update TODO status (completed) │ │ ├─ Check auto_continue flag │ │ └─ Proceed to next step or wait for user │ │ │ └──────────────────────────────────────────────────────────────────┘ ``` ## CLI Enhancement Features ### Trigger Conditions | Feature | Trigger Condition | Default Tool | |---------|------------------|--------------| | CLI Classification | matchCount < 2 OR complexity = high OR input > 100 chars | gemini | | CLI Action Planning | complexity = high OR steps >= 3 OR input > 200 chars | gemini | ### Fallback Behavior - **Classification**: If CLI fails, falls back to rule-based classification - **Action Planning**: If CLI fails, uses default chain without optimization - **Tool Fallback**: Primary tool (gemini) -> Secondary tool (qwen) ### Configuration All CLI enhancement settings are in `index/intent-rules.json`: ```json { "cli_classification": { "enabled": true, "trigger_conditions": { ... }, "default_tool": "gemini", "fallback_tool": "qwen" }, "cli_action_planning": { "enabled": true, "trigger_conditions": { ... }, "allow_step_modification": true } } ``` ## Implementation ### Phase 1: Input Analysis ```javascript // Load external configuration (externalized for flexibility) const intentRules = JSON.parse(Read('.claude/skills/ccw/index/intent-rules.json')) const capabilities = JSON.parse(Read('.claude/skills/ccw/index/command-capabilities.json')) function analyzeInput(userInput) { const input = userInput.trim() // Check for explicit command passthrough if (input.match(/^\/(?:workflow|issue|memory|task):/)) { return { type: 'explicit', command: input, passthrough: true } } // Classify intent using external rules const intent = classifyIntent(input, intentRules.intent_patterns) // Assess complexity using external indicators const complexity = assessComplexity(input, intentRules.complexity_indicators) // Detect tool preferences using external triggers const toolPreference = detectToolPreference(input, intentRules.cli_tool_triggers) return { type: 'natural', text: input, intent, complexity, toolPreference, passthrough: false } } function classifyIntent(text, patterns) { // Sort by priority const sorted = Object.entries(patterns) .sort((a, b) => a[1].priority - b[1].priority) for (const [intentType, config] of sorted) { // Handle variants (bugfix, ui, docs) if (config.variants) { for (const [variant, variantConfig] of Object.entries(config.variants)) { const variantPatterns = variantConfig.patterns || variantConfig.triggers || [] if (matchesAnyPattern(text, variantPatterns)) { // For bugfix, check if standard patterns also match if (intentType === 'bugfix') { const standardMatch = matchesAnyPattern(text, config.variants.standard?.patterns || []) if (standardMatch) { return { type: intentType, variant, workflow: variantConfig.workflow } } } else { return { type: intentType, variant, workflow: variantConfig.workflow } } } } // Check default variant if (config.variants.standard) { if (matchesAnyPattern(text, config.variants.standard.patterns)) { return { type: intentType, variant: 'standard', workflow: config.variants.standard.workflow } } } } // Handle simple patterns (exploration, tdd, review) if (config.patterns && !config.require_both) { if (matchesAnyPattern(text, config.patterns)) { return { type: intentType, workflow: config.workflow } } } // Handle dual-pattern matching (issue_batch) if (config.require_both && config.patterns) { const matchBatch = matchesAnyPattern(text, config.patterns.batch_keywords) const matchAction = matchesAnyPattern(text, config.patterns.action_keywords) if (matchBatch && matchAction) { return { type: intentType, workflow: config.workflow } } } } // Default to feature return { type: 'feature' } } function matchesAnyPattern(text, patterns) { if (!Array.isArray(patterns)) return false const lowerText = text.toLowerCase() return patterns.some(p => lowerText.includes(p.toLowerCase())) } function assessComplexity(text, indicators) { let score = 0 for (const [level, config] of Object.entries(indicators)) { if (config.patterns) { for (const [category, patternConfig] of Object.entries(config.patterns)) { if (matchesAnyPattern(text, patternConfig.keywords)) { score += patternConfig.weight || 1 } } } } if (score >= indicators.high.score_threshold) return 'high' if (score >= indicators.medium.score_threshold) return 'medium' return 'low' } function detectToolPreference(text, triggers) { for (const [tool, config] of Object.entries(triggers)) { // Check explicit triggers if (matchesAnyPattern(text, config.explicit)) return tool // Check semantic triggers if (matchesAnyPattern(text, config.semantic)) return tool } return null } // Calculate match count for confidence assessment function calculateMatchCount(text, patterns) { let count = 0 for (const [intentType, config] of Object.entries(patterns)) { if (config.variants) { for (const [variant, variantConfig] of Object.entries(config.variants)) { const variantPatterns = variantConfig.patterns || variantConfig.triggers || [] if (matchesAnyPattern(text, variantPatterns)) count++ } } if (config.patterns && !config.require_both) { if (matchesAnyPattern(text, config.patterns)) count++ } } return count } ``` ### Phase 1.5: CLI-Assisted Classification For ambiguous or complex inputs, use CLI tools for semantic understanding. ```javascript // CLI-assisted classification for ambiguous inputs async function cliAssistedClassification(input, ruleBasedResult, intentRules) { const cliConfig = intentRules.cli_classification // Skip if CLI classification is disabled if (!cliConfig || !cliConfig.enabled) { return { ...ruleBasedResult, source: 'rules', matchCount: 0 } } // Calculate match count for confidence assessment const matchCount = calculateMatchCount(input, intentRules.intent_patterns) // Determine if CLI classification should be triggered const triggers = cliConfig.trigger_conditions const shouldUseCli = matchCount < triggers.low_match_count || ruleBasedResult.complexity === triggers.complexity_trigger || input.length > triggers.min_input_length || matchesAnyPattern(input, triggers.ambiguous_patterns) if (!shouldUseCli) { return { ...ruleBasedResult, source: 'rules', matchCount } } console.log('### CLI-Assisted Intent Classification\n') console.log('> Using CLI for semantic understanding of ambiguous input...\n') // Build CLI prompt for intent classification const cliPrompt = ` PURPOSE: Classify user request intent and recommend optimal workflow TASK: - Analyze the semantic meaning of the user request - Classify into one of: bugfix, feature, exploration, ui, issue, tdd, review, docs - Assess complexity: low, medium, high - Recommend workflow chain based on intent and complexity - Provide confidence score (0-1) MODE: analysis CONTEXT: User request analysis for workflow orchestration EXPECTED: JSON output with structure: { "intent": { "type": "bugfix|feature|exploration|ui|issue|tdd|review|docs", "variant": "optional variant like hotfix, imitate, incremental", "confidence": 0.0-1.0, "reasoning": "brief explanation of classification" }, "complexity": { "level": "low|medium|high", "factors": ["factor1", "factor2"], "confidence": 0.0-1.0 }, "recommended_workflow": { "chain_id": "rapid|full|coupled|bugfix|issue|tdd|ui|review-fix|docs", "reasoning": "why this workflow is optimal" }, "tool_preference": { "suggested": "gemini|qwen|codex|null", "reasoning": "optional reasoning" } } USER REQUEST: ${input} RULES: Output ONLY valid JSON without markdown code blocks. Be concise but accurate. ` // Select CLI tool (default or fallback) const tool = cliConfig.default_tool || 'gemini' const timeout = cliConfig.timeout_ms || 60000 try { // Execute CLI call synchronously for classification const escapedPrompt = cliPrompt.replace(/"/g, '\\"').replace(/\n/g, '\\n') const cliResult = Bash({ command: `ccw cli -p "${escapedPrompt}" --tool ${tool} --mode analysis`, run_in_background: false, timeout: timeout }) // Parse CLI result - extract JSON from response const jsonMatch = cliResult.match(/\{[\s\S]*\}/) if (!jsonMatch) { throw new Error('No JSON found in CLI response') } const parsed = JSON.parse(jsonMatch[0]) console.log(` **CLI Classification Result**: - **Intent**: ${parsed.intent.type}${parsed.intent.variant ? ` (${parsed.intent.variant})` : ''} - **Complexity**: ${parsed.complexity.level} - **Confidence**: ${(parsed.intent.confidence * 100).toFixed(0)}% - **Reasoning**: ${parsed.intent.reasoning} - **Recommended Chain**: ${parsed.recommended_workflow.chain_id} `) return { type: 'natural', text: input, intent: { type: parsed.intent.type, variant: parsed.intent.variant, workflow: parsed.recommended_workflow.chain_id }, complexity: parsed.complexity.level, toolPreference: parsed.tool_preference?.suggested || ruleBasedResult.toolPreference, confidence: parsed.intent.confidence, source: 'cli', cliReasoning: parsed.intent.reasoning, passthrough: false } } catch (error) { console.log(`> CLI classification failed: ${error.message}`) console.log('> Falling back to rule-based classification\n') // Try fallback tool if available if (cliConfig.fallback_tool && cliConfig.fallback_tool !== tool) { console.log(`> Attempting fallback with ${cliConfig.fallback_tool}...`) // Could recursively call with fallback tool, but for simplicity, just return rule-based } return { ...ruleBasedResult, source: 'rules', matchCount } } } ``` ### Phase 2: Chain Selection ```javascript // Load workflow chains index const chains = JSON.parse(Read('.claude/skills/ccw/index/workflow-chains.json')) function selectChain(analysis) { const { intent, complexity } = analysis // Map intent type (from intent-rules.json) to chain ID (from workflow-chains.json) const chainMapping = { 'bugfix': 'bugfix', 'issue_batch': 'issue', // intent-rules.json key → chains.json chain ID 'exploration': 'full', 'ui_design': 'ui', // intent-rules.json key → chains.json chain ID 'tdd': 'tdd', 'review': 'review-fix', 'documentation': 'docs', // intent-rules.json key → chains.json chain ID 'feature': null // Use complexity fallback } let chainId = chainMapping[intent.type] // Fallback to complexity-based selection if (!chainId) { chainId = chains.chain_selection_rules.complexity_fallback[complexity] } const chain = chains.chains[chainId] // Handle variants let steps = chain.steps if (chain.variants) { const variant = intent.variant || Object.keys(chain.variants)[0] steps = chain.variants[variant].steps } return { id: chainId, name: chain.name, description: chain.description, steps, complexity: chain.complexity, estimated_time: chain.estimated_time } } ``` ### Phase 2.5: CLI-Assisted Action Planning For high complexity tasks, use CLI to plan optimal execution strategy. ```javascript // CLI-assisted action planning for complex tasks async function cliAssistedPlanning(analysis, selectedChain, intentRules) { const planConfig = intentRules.cli_action_planning // Skip if action planning is disabled if (!planConfig || !planConfig.enabled) { return { useDefaultChain: true, chain: selectedChain } } // Determine if CLI planning should be triggered const triggers = planConfig.trigger_conditions const shouldUseCli = analysis.complexity === triggers.complexity_threshold || selectedChain.steps.length >= triggers.step_count_threshold || (analysis.text && analysis.text.length > 200) if (!shouldUseCli) { return { useDefaultChain: true, chain: selectedChain } } console.log('### CLI-Assisted Action Planning\n') console.log('> Using CLI to optimize execution strategy for complex task...\n') // Build CLI prompt for action planning const planningPrompt = ` PURPOSE: Plan optimal workflow execution strategy for complex task TASK: - Review the selected workflow chain and its steps - Consider task complexity, dependencies, and potential risks - Suggest step modifications, additions, or reordering if beneficial - Identify potential risks and provide mitigations - Recommend CLI tool injection points for efficiency MODE: analysis CONTEXT: - User Intent: ${analysis.intent.type}${analysis.intent.variant ? ` (${analysis.intent.variant})` : ''} - Complexity: ${analysis.complexity} - Selected Chain: ${selectedChain.name} - Current Steps: ${selectedChain.steps.map((s, i) => `${i + 1}. ${s.command}`).join(', ')} - User Request: ${analysis.text ? analysis.text.substring(0, 200) : 'N/A'} EXPECTED: JSON output with structure: { "recommendation": "use_default|modify|upgrade", "modified_steps": [ { "command": "/workflow:xxx", "optional": false, "auto_continue": true, "reason": "why this step" } ], "cli_injections": [ { "before_step": 1, "tool": "gemini", "mode": "analysis", "purpose": "pre-analysis" } ], "reasoning": "explanation of recommendations", "risks": ["risk1", "risk2"], "mitigations": ["mitigation1", "mitigation2"], "suggestions": ["suggestion1", "suggestion2"] } RULES: Output ONLY valid JSON. If no modifications needed, set recommendation to "use_default" and leave modified_steps empty. ` const tool = planConfig.default_tool || 'gemini' const timeout = planConfig.timeout_ms || 60000 try { const escapedPrompt = planningPrompt.replace(/"/g, '\\"').replace(/\n/g, '\\n') const cliResult = Bash({ command: `ccw cli -p "${escapedPrompt}" --tool ${tool} --mode analysis`, run_in_background: false, timeout: timeout }) // Parse CLI result const jsonMatch = cliResult.match(/\{[\s\S]*\}/) if (!jsonMatch) { throw new Error('No JSON found in CLI response') } const parsed = JSON.parse(jsonMatch[0]) // Display planning results console.log(` **CLI Planning Result**: - **Recommendation**: ${parsed.recommendation} - **Reasoning**: ${parsed.reasoning} ${parsed.risks && parsed.risks.length > 0 ? `- **Risks**: ${parsed.risks.join(', ')}` : ''} ${parsed.suggestions && parsed.suggestions.length > 0 ? `- **Suggestions**: ${parsed.suggestions.join(', ')}` : ''} `) // Handle step modification if (parsed.recommendation === 'modify' && parsed.modified_steps && parsed.modified_steps.length > 0) { if (planConfig.allow_step_modification) { console.log('> Applying modified execution plan\n') return { useDefaultChain: false, chain: { ...selectedChain, steps: parsed.modified_steps }, reasoning: parsed.reasoning, risks: parsed.risks, cliInjections: parsed.cli_injections, source: 'cli-planned' } } else { console.log('> Step modification disabled, using default chain with suggestions\n') } } // Handle upgrade recommendation if (parsed.recommendation === 'upgrade') { console.log('> CLI recommends upgrading to a more comprehensive workflow\n') // Could select a more complex chain here } return { useDefaultChain: true, chain: selectedChain, suggestions: parsed.suggestions, risks: parsed.risks, cliInjections: parsed.cli_injections, source: 'cli-reviewed' } } catch (error) { console.log(`> CLI planning failed: ${error.message}`) console.log('> Using default chain without optimization\n') return { useDefaultChain: true, chain: selectedChain, source: 'default' } } } ``` ### Phase 3: User Confirmation ```javascript function confirmChain(selectedChain, analysis) { // Skip confirmation for simple chains if (selectedChain.steps.length <= 2 && analysis.complexity === 'low') { return selectedChain } console.log(` ## CCW Workflow Selection **Task**: ${analysis.text.substring(0, 80)}... **Intent**: ${analysis.intent.type}${analysis.intent.variant ? ` (${analysis.intent.variant})` : ''} **Complexity**: ${analysis.complexity} **Selected Chain**: ${selectedChain.name} **Description**: ${selectedChain.description} **Estimated Time**: ${selectedChain.estimated_time} **Steps**: ${selectedChain.steps.map((s, i) => `${i + 1}. ${s.command}${s.optional ? ' (optional)' : ''}`).join('\n')} `) const response = AskUserQuestion({ questions: [{ question: `Proceed with ${selectedChain.name}?`, header: "Confirm", multiSelect: false, options: [ { label: "Proceed", description: `Execute ${selectedChain.steps.length} steps` }, { label: "Rapid", description: "Use lite-plan → lite-execute" }, { label: "Full", description: "Use brainstorm → plan → execute" }, { label: "Manual", description: "Specify commands manually" } ] }] }) // Handle alternative selection if (response.Confirm === 'Rapid') { return selectChain({ intent: { type: 'feature' }, complexity: 'low' }) } if (response.Confirm === 'Full') { return chains.chains['full'] } if (response.Confirm === 'Manual') { return null // User will specify } return selectedChain } ``` ### Phase 4: TODO Tracking Setup ```javascript function setupTodoTracking(chain, analysis) { const todos = chain.steps.map((step, index) => ({ content: `[${index + 1}/${chain.steps.length}] ${step.command}`, status: index === 0 ? 'in_progress' : 'pending', activeForm: `Executing ${step.command}` })) // Add header todo todos.unshift({ content: `CCW: ${chain.name} (${chain.steps.length} steps)`, status: 'in_progress', activeForm: `Running ${chain.name} workflow` }) TodoWrite({ todos }) return { chain, currentStep: 0, todos } } ``` ### Phase 5: Execution Loop ```javascript async function executeChain(execution, analysis) { const { chain, todos } = execution let currentStep = 0 while (currentStep < chain.steps.length) { const step = chain.steps[currentStep] // Update TODO: mark current as in_progress const updatedTodos = todos.map((t, i) => ({ ...t, status: i === 0 ? 'in_progress' : i === currentStep + 1 ? 'in_progress' : i <= currentStep ? 'completed' : 'pending' })) TodoWrite({ todos: updatedTodos }) console.log(`\n### Step ${currentStep + 1}/${chain.steps.length}: ${step.command}\n`) // Check for confirmation requirement if (step.confirm_before) { const proceed = AskUserQuestion({ questions: [{ question: `Ready to execute ${step.command}?`, header: "Step", multiSelect: false, options: [ { label: "Execute", description: "Run this step" }, { label: "Skip", description: "Skip to next step" }, { label: "Abort", description: "Stop workflow" } ] }] }) if (proceed.Step === 'Skip') { currentStep++ continue } if (proceed.Step === 'Abort') { break } } // Execute the command const args = analysis.text SlashCommand(step.command, { args }) // Mark step as completed updatedTodos[currentStep + 1].status = 'completed' TodoWrite({ todos: updatedTodos }) currentStep++ // Check auto_continue if (!step.auto_continue && currentStep < chain.steps.length) { console.log(` Step completed. Next: ${chain.steps[currentStep].command} Type "continue" to proceed or specify different action. `) // Wait for user input before continuing break } } // Final status if (currentStep >= chain.steps.length) { const finalTodos = todos.map(t => ({ ...t, status: 'completed' })) TodoWrite({ todos: finalTodos }) console.log(`\n✓ ${chain.name} workflow completed (${chain.steps.length} steps)`) } return { completed: currentStep, total: chain.steps.length } } ``` ## Main Orchestration Entry ```javascript async function ccwOrchestrate(userInput) { console.log('## CCW Orchestrator\n') // Phase 1: Analyze input (rule-based, fast path) const ruleBasedAnalysis = analyzeInput(userInput) // Handle explicit command passthrough if (ruleBasedAnalysis.passthrough) { console.log(`Direct command: ${ruleBasedAnalysis.command}`) return SlashCommand(ruleBasedAnalysis.command) } // Phase 1.5: CLI-Assisted Classification (smart path for ambiguous inputs) const analysis = await cliAssistedClassification(userInput, ruleBasedAnalysis, intentRules) // Display classification source if (analysis.source === 'cli') { console.log(` ### Classification Summary - **Source**: CLI-Assisted (${analysis.confidence ? (analysis.confidence * 100).toFixed(0) + '% confidence' : 'semantic analysis'}) - **Intent**: ${analysis.intent.type}${analysis.intent.variant ? ` (${analysis.intent.variant})` : ''} - **Complexity**: ${analysis.complexity} ${analysis.cliReasoning ? `- **Reasoning**: ${analysis.cliReasoning}` : ''} `) } else { console.log(` ### Classification Summary - **Source**: Rule-Based (fast path) - **Intent**: ${analysis.intent.type}${analysis.intent.variant ? ` (${analysis.intent.variant})` : ''} - **Complexity**: ${analysis.complexity} `) } // Phase 2: Select chain const selectedChain = selectChain(analysis) // Phase 2.5: CLI-Assisted Action Planning (for high complexity) const planningResult = await cliAssistedPlanning(analysis, selectedChain, intentRules) const optimizedChain = planningResult.chain // Display planning result if CLI was used if (planningResult.source === 'cli-planned' || planningResult.source === 'cli-reviewed') { console.log(` ### Planning Summary - **Source**: CLI-Assisted ${planningResult.reasoning ? `- **Reasoning**: ${planningResult.reasoning}` : ''} ${planningResult.risks && planningResult.risks.length > 0 ? `- **Identified Risks**: ${planningResult.risks.join(', ')}` : ''} ${planningResult.suggestions && planningResult.suggestions.length > 0 ? `- **Suggestions**: ${planningResult.suggestions.join(', ')}` : ''} `) } // Phase 3: Confirm (for complex workflows) const confirmedChain = confirmChain(optimizedChain, analysis) if (!confirmedChain) { console.log('Manual mode selected. Specify commands directly.') return } // Phase 4: Setup TODO tracking const execution = setupTodoTracking(confirmedChain, analysis) // Phase 5: Execute const result = await executeChain(execution, analysis) return result } ``` ## Decision Matrix | Intent | Complexity | Chain | Steps | |--------|------------|-------|-------| | bugfix (standard) | * | bugfix | lite-fix | | bugfix (hotfix) | * | bugfix | lite-fix --hotfix | | issue | * | issue | plan → queue → execute | | exploration | * | full | brainstorm → plan → execute | | ui (explore) | * | ui | ui-design:explore → sync → plan → execute | | ui (imitate) | * | ui | ui-design:imitate → sync → plan → execute | | tdd | * | tdd | tdd-plan → execute → tdd-verify | | review | * | review-fix | review-session-cycle → review-fix | | docs | low | docs | update-related | | docs | medium+ | docs | docs → execute | | feature | low | rapid | lite-plan → lite-execute | | feature | medium | coupled | plan → verify → execute | | feature | high | full | brainstorm → plan → execute | ## Continuation Commands After each step pause: | User Input | Action | |------------|--------| | `continue` | Execute next step | | `skip` | Skip current step | | `abort` | Stop workflow | | `/workflow:*` | Execute specific command | | Natural language | Re-analyze and potentially switch chains |