Files
Claude-Code-Workflow/.claude/skills/workflow-lite-plan/phases/02-lite-execute.md
catlog22 48a6a1f2aa Add comprehensive tests for ast-grep and tree-sitter relationship extraction
- Introduced test suite for AstGrepPythonProcessor covering pattern definitions, parsing, and relationship extraction.
- Added comparison tests between tree-sitter and ast-grep for consistency in relationship extraction.
- Implemented tests for ast-grep binding module to verify functionality and availability.
- Ensured tests cover various scenarios including inheritance, function calls, and imports.
2026-02-15 21:14:14 +08:00

31 KiB
Raw Blame History

Phase 2: Lite-Execute

Complete execution engine: multi-mode input, task grouping, batch execution, code review, and development index update.


Overview

Flexible task execution command supporting three input modes: in-memory plan (from lite-plan), direct prompt description, or file content. Handles execution orchestration, progress tracking, and optional code review.

Core capabilities:

  • Multi-mode input (in-memory plan, prompt description, or file path)
  • Execution orchestration (Agent or Codex) with full context
  • Live progress tracking via TodoWrite at execution call level
  • Optional code review with selected tool (Gemini, Agent, or custom)
  • Context continuity across multiple executions
  • Intelligent format detection (Enhanced Task JSON vs plain text)

Usage

Input

<input>                    Task description string, or path to file (required)

Mode 1 (In-Memory) is triggered by lite-plan direct handoff when executionContext is available. Workflow preferences (autoYes) are passed from SKILL.md via workflowPreferences context variable.

Input Modes

Mode 1: In-Memory Plan

Trigger: Called by lite-plan direct handoff after Phase 4 approval (executionContext available)

Input Source: executionContext global variable set by lite-plan

Content: Complete execution context (see Data Structures section)

Behavior:

  • Skip execution method selection (already set by lite-plan)
  • Directly proceed to execution with full context
  • All planning artifacts available (exploration, clarifications, plan)

Mode 2: Prompt Description

Trigger: User calls with task description string

Input: Simple task description (e.g., "Add unit tests for auth module")

Behavior:

  • Store prompt as originalUserInput
  • Create simple execution plan from prompt
  • AskUserQuestion: Select execution method (Agent/Codex/Auto)
  • AskUserQuestion: Select code review tool (Skip/Gemini/Agent/Other)
  • Proceed to execution with originalUserInput included

User Interaction:

const autoYes = workflowPreferences.autoYes

let userSelection

if (autoYes) {
  // Auto mode: Use defaults
  console.log(`[Auto] Auto-confirming execution:`)
  console.log(`  - Execution method: Auto`)
  console.log(`  - Code review: Skip`)

  userSelection = {
    execution_method: "Auto",
    code_review_tool: "Skip"
  }
} else {
  // Interactive mode: Ask user
  userSelection = AskUserQuestion({
    questions: [
      {
        question: "Select execution method:",
        header: "Execution",
        multiSelect: false,
        options: [
          { label: "Agent", description: "@code-developer agent" },
          { label: "Codex", description: "codex CLI tool" },
          { label: "Auto", description: "Auto-select based on complexity" }
        ]
      },
      {
        question: "Enable code review after execution?",
        header: "Code Review",
        multiSelect: false,
        options: [
          { label: "Skip", description: "No review" },
          { label: "Gemini Review", description: "Gemini CLI tool" },
          { label: "Codex Review", description: "Git-aware review (prompt OR --uncommitted)" },
          { label: "Agent Review", description: "Current agent review" }
        ]
      }
    ]
  })
}

Mode 3: File Content

Trigger: User calls with file path

Input: Path to file containing task description or plan.json

Step 1: Read and Detect Format

fileContent = Read(filePath)

// Attempt JSON parsing
try {
  jsonData = JSON.parse(fileContent)

  // Check if plan.json from lite-plan session (two-layer format: task_ids[])
  if (jsonData.summary && jsonData.approach && jsonData.task_ids) {
    planObject = jsonData
    originalUserInput = jsonData.summary
    isPlanJson = true

    // Load tasks from .task/*.json files
    const planDir = filePath.replace(/[/\\][^/\\]+$/, '')  // parent directory
    planObject._loadedTasks = loadTaskFiles(planDir, jsonData.task_ids)
  } else {
    // Valid JSON but not plan.json - treat as plain text
    originalUserInput = fileContent
    isPlanJson = false
  }
} catch {
  // Not valid JSON - treat as plain text prompt
  originalUserInput = fileContent
  isPlanJson = false
}

Step 2: Create Execution Plan

If isPlanJson === true:

  • Use planObject directly
  • User selects execution method and code review

If isPlanJson === false:

  • Treat file content as prompt (same behavior as Mode 2)
  • Create simple execution plan from content

Step 3: User Interaction

  • AskUserQuestion: Select execution method (Agent/Codex/Auto)
  • AskUserQuestion: Select code review tool
  • Proceed to execution with full context

Helper Functions

// Load task files from .task/ directory (two-layer format)
function loadTaskFiles(planDir, taskIds) {
  return taskIds.map(id => {
    const taskPath = `${planDir}/.task/${id}.json`
    return JSON.parse(Read(taskPath))
  })
}

// Get tasks array from loaded .task/*.json files
function getTasks(planObject) {
  return planObject._loadedTasks || []
}

Execution Process

Input Parsing:
   └─ Decision (mode detection):
      ├─ executionContext exists → Mode 1: Load executionContext → Skip user selection
      ├─ Ends with .md/.json/.txt → Mode 3: Read file → Detect format
      │   ├─ Valid plan.json → Use planObject → User selects method + review
      │   └─ Not plan.json → Treat as prompt → User selects method + review
      └─ Other → Mode 2: Prompt description → User selects method + review

Execution:
   ├─ Step 1: Initialize result tracking (previousExecutionResults = [])
   ├─ Step 2: Task grouping & batch creation
   │   ├─ Extract explicit depends_on (no file/keyword inference)
   │   ├─ Group: independent tasks → per-executor parallel batches (one CLI per batch)
   │   ├─ Group: dependent tasks → sequential phases (respect dependencies)
   │   └─ Create TodoWrite list for batches
   ├─ Step 3: Launch execution
   │   ├─ Phase 1: Independent tasks (⚡ per-executor batches, multi-CLI concurrent)
   │   └─ Phase 2+: Dependent tasks by dependency order
   ├─ Step 4: Track progress (TodoWrite updates per batch)
   └─ Step 5: Code review (if codeReviewTool ≠ "Skip")

Output:
   └─ Execution complete with results in previousExecutionResults[]

Detailed Execution Steps

Step 1: Initialize Execution Tracking

Operations:

  • Initialize result tracking for multi-execution scenarios
  • Set up previousExecutionResults array for context continuity
  • In-Memory Mode: Echo execution strategy from lite-plan for transparency
// Initialize result tracking
previousExecutionResults = []

// In-Memory Mode: Echo execution strategy (transparency before execution)
if (executionContext) {
  console.log(`
📋 Execution Strategy (from lite-plan):
   Method: ${executionContext.executionMethod}
   Review: ${executionContext.codeReviewTool}
   Tasks: ${getTasks(executionContext.planObject).length}
   Complexity: ${executionContext.planObject.complexity}
${executionContext.executorAssignments ? `   Assignments: ${JSON.stringify(executionContext.executorAssignments)}` : ''}
  `)
}

Step 2: Task Grouping & Batch Creation

Dependency Analysis & Grouping Algorithm:

// Use explicit depends_on from plan.json (no inference from file/keywords)
function extractDependencies(tasks) {
  const taskIdToIndex = {}
  tasks.forEach((t, i) => { taskIdToIndex[t.id] = i })

  return tasks.map((task, i) => {
    // Only use explicit depends_on from plan.json
    const deps = (task.depends_on || [])
      .map(depId => taskIdToIndex[depId])
      .filter(idx => idx !== undefined && idx < i)
    return { ...task, taskIndex: i, dependencies: deps }
  })
}

// Executor Resolution (used by task grouping below)
// 获取任务的 executor优先使用 executorAssignmentsfallback 到全局 executionMethod
function getTaskExecutor(task) {
  const assignments = executionContext?.executorAssignments || {}
  if (assignments[task.id]) {
    return assignments[task.id].executor  // 'gemini' | 'codex' | 'agent'
  }
  // Fallback: 全局 executionMethod 映射
  const method = executionContext?.executionMethod || 'Auto'
  if (method === 'Agent') return 'agent'
  if (method === 'Codex') return 'codex'
  // Auto: 根据复杂度
  return planObject.complexity === 'Low' ? 'agent' : 'codex'
}

// 按 executor 分组任务(核心分组组件)
function groupTasksByExecutor(tasks) {
  const groups = { gemini: [], codex: [], agent: [] }
  tasks.forEach(task => {
    const executor = getTaskExecutor(task)
    groups[executor].push(task)
  })
  return groups
}

// Group into batches: per-executor parallel batches (one CLI per batch)
function createExecutionCalls(tasks, executionMethod) {
  const tasksWithDeps = extractDependencies(tasks)
  const processed = new Set()
  const calls = []

  // Phase 1: Independent tasks → per-executor batches (multi-CLI concurrent)
  const independentTasks = tasksWithDeps.filter(t => t.dependencies.length === 0)
  if (independentTasks.length > 0) {
    const executorGroups = groupTasksByExecutor(independentTasks)
    let parallelIndex = 1

    for (const [executor, tasks] of Object.entries(executorGroups)) {
      if (tasks.length === 0) continue
      tasks.forEach(t => processed.add(t.taskIndex))
      calls.push({
        method: executionMethod,
        executor: executor,          // 明确指定 executor
        executionType: "parallel",
        groupId: `P${parallelIndex++}`,
        taskSummary: tasks.map(t => t.title).join(' | '),
        tasks: tasks
      })
    }
  }

  // Phase 2: Dependent tasks → sequential/parallel batches (respect dependencies)
  let sequentialIndex = 1
  let remaining = tasksWithDeps.filter(t => !processed.has(t.taskIndex))

  while (remaining.length > 0) {
    // Find tasks whose dependencies are all satisfied
    const ready = remaining.filter(t =>
      t.dependencies.every(d => processed.has(d))
    )

    if (ready.length === 0) {
      console.warn('Circular dependency detected, forcing remaining tasks')
      ready.push(...remaining)
    }

    if (ready.length > 1) {
      // Multiple ready tasks → per-executor batches (parallel within this phase)
      const executorGroups = groupTasksByExecutor(ready)
      for (const [executor, tasks] of Object.entries(executorGroups)) {
        if (tasks.length === 0) continue
        tasks.forEach(t => processed.add(t.taskIndex))
        calls.push({
          method: executionMethod,
          executor: executor,
          executionType: "parallel",
          groupId: `P${calls.length + 1}`,
          taskSummary: tasks.map(t => t.title).join(' | '),
          tasks: tasks
        })
      }
    } else {
      // Single ready task → sequential batch
      ready.forEach(t => processed.add(t.taskIndex))
      calls.push({
        method: executionMethod,
        executor: getTaskExecutor(ready[0]),
        executionType: "sequential",
        groupId: `S${sequentialIndex++}`,
        taskSummary: ready[0].title,
        tasks: ready
      })
    }

    remaining = remaining.filter(t => !processed.has(t.taskIndex))
  }

  return calls
}

executionCalls = createExecutionCalls(getTasks(planObject), executionMethod).map(c => ({ ...c, id: `[${c.groupId}]` }))

TodoWrite({
  todos: executionCalls.map(c => ({
    content: `${c.executionType === "parallel" ? "⚡" : "→"} ${c.id} (${c.tasks.length} tasks)`,
    status: "pending",
    activeForm: `Executing ${c.id}`
  }))
})

Step 3: Launch Execution

Executor Resolution: getTaskExecutor() and groupTasksByExecutor() defined in Step 2 (Task Grouping).

Batch Execution Routing (根据 batch.executor 字段路由):

// executeBatch 根据 batch 自身的 executor 字段决定调用哪个 CLI
function executeBatch(batch) {
  const executor = batch.executor || getTaskExecutor(batch.tasks[0])
  const sessionId = executionContext?.session?.id || 'standalone'
  const fixedId = `${sessionId}-${batch.groupId}`

  if (executor === 'agent') {
    // Agent execution (synchronous)
    return Task({
      subagent_type: "code-developer",
      run_in_background: false,
      description: batch.taskSummary,
      prompt: buildExecutionPrompt(batch)
    })
  } else if (executor === 'codex') {
    // Codex CLI (background)
    return Bash(`ccw cli -p "${buildExecutionPrompt(batch)}" --tool codex --mode write --id ${fixedId}`, { run_in_background: true })
  } else if (executor === 'gemini') {
    // Gemini CLI (background)
    return Bash(`ccw cli -p "${buildExecutionPrompt(batch)}" --tool gemini --mode write --id ${fixedId}`, { run_in_background: true })
  }
}

并行执行原则:

  • 每个 batch 对应一个独立的 CLI 实例或 Agent 调用
  • 并行 = 多个 Bash(run_in_background=true) 或多个 Task() 同时发出
  • 绝不将多个独立任务合并到同一个 CLI prompt 中
  • Agent 任务不可后台执行run_in_background=false但多个 Agent 任务可通过单条消息中的多个 Task() 调用并发

Execution Flow: Parallel batches concurrently → Sequential batches in order

const parallel = executionCalls.filter(c => c.executionType === "parallel")
const sequential = executionCalls.filter(c => c.executionType === "sequential")

// Phase 1: Launch all parallel batches (single message with multiple tool calls)
if (parallel.length > 0) {
  TodoWrite({ todos: executionCalls.map(c => ({ status: c.executionType === "parallel" ? "in_progress" : "pending" })) })
  parallelResults = await Promise.all(parallel.map(c => executeBatch(c)))
  previousExecutionResults.push(...parallelResults)
  TodoWrite({ todos: executionCalls.map(c => ({ status: parallel.includes(c) ? "completed" : "pending" })) })
}

// Phase 2: Execute sequential batches one by one
for (const call of sequential) {
  TodoWrite({ todos: executionCalls.map(c => ({ status: c === call ? "in_progress" : "..." })) })
  result = await executeBatch(call)
  previousExecutionResults.push(result)
  TodoWrite({ todos: executionCalls.map(c => ({ status: "completed" or "pending" })) })
}

Unified Task Prompt Builder

Task Formatting Principle: Each task is a self-contained checklist. The executor only needs to know what THIS task requires. Same template for Agent and CLI.

function buildExecutionPrompt(batch) {
  // Task template (6 parts: Files → Why → How → Reference → Risks → Done)
  const formatTask = (t) => `
## ${t.title}

**Scope**: \`${t.scope}\`  |  **Action**: ${t.action}

### Files
${(t.files || []).map(f => `- **${f.path}** → \`${f.target || ''}\`: ${f.change || (f.changes || []).join(', ') || ''}`).join('\n')}

${t.rationale ? `
### Why this approach (Medium/High)
${t.rationale.chosen_approach}
${t.rationale.decision_factors?.length > 0 ? `\nKey factors: ${t.rationale.decision_factors.join(', ')}` : ''}
${t.rationale.tradeoffs ? `\nTradeoffs: ${t.rationale.tradeoffs}` : ''}
` : ''}

### How to do it
${t.description}

${t.implementation.map(step => `- ${step}`).join('\n')}

${t.code_skeleton ? `
### Code skeleton (High)
${t.code_skeleton.interfaces?.length > 0 ? `**Interfaces**: ${t.code_skeleton.interfaces.map(i => `\`${i.name}\` - ${i.purpose}`).join(', ')}` : ''}
${t.code_skeleton.key_functions?.length > 0 ? `\n**Functions**: ${t.code_skeleton.key_functions.map(f => `\`${f.signature}\` - ${f.purpose}`).join(', ')}` : ''}
${t.code_skeleton.classes?.length > 0 ? `\n**Classes**: ${t.code_skeleton.classes.map(c => `\`${c.name}\` - ${c.purpose}`).join(', ')}` : ''}
` : ''}

### Reference
- Pattern: ${t.reference?.pattern || 'N/A'}
- Files: ${t.reference?.files?.join(', ') || 'N/A'}
${t.reference?.examples ? `- Notes: ${t.reference.examples}` : ''}

${t.risks?.length > 0 ? `
### Risk mitigations (High)
${t.risks.map(r => `- ${r.description} → **${r.mitigation}**`).join('\n')}
` : ''}

### Done when
${(t.convergence?.criteria || []).map(c => `- [ ] ${c}`).join('\n')}
${(t.test?.success_metrics || []).length > 0 ? `\n**Success metrics**: ${t.test.success_metrics.join(', ')}` : ''}`

  // Build prompt
  const sections = []

  if (originalUserInput) sections.push(`## Goal\n${originalUserInput}`)

  sections.push(`## Tasks\n${batch.tasks.map(formatTask).join('\n\n---\n')}`)

  // Context (reference only)
  const context = []
  if (previousExecutionResults.length > 0) {
    context.push(`### Previous Work\n${previousExecutionResults.map(r => `- ${r.tasksSummary}: ${r.status}`).join('\n')}`)
  }
  if (clarificationContext) {
    context.push(`### Clarifications\n${Object.entries(clarificationContext).map(([q, a]) => `- ${q}: ${a}`).join('\n')}`)
  }
  if (executionContext?.planObject?.data_flow?.diagram) {
    context.push(`### Data Flow\n${executionContext.planObject.data_flow.diagram}`)
  }
  if (executionContext?.session?.artifacts?.plan) {
    context.push(`### Artifacts\nPlan: ${executionContext.session.artifacts.plan}`)
  }
  // Project guidelines (user-defined constraints from /workflow:session:solidify)
  context.push(`### Project Guidelines\n@.workflow/project-guidelines.json`)
  if (context.length > 0) sections.push(`## Context\n${context.join('\n\n')}`)

  sections.push(`Complete each task according to its "Done when" checklist.`)

  return sections.join('\n\n')
}

Option A: Agent Execution

When to use:

  • getTaskExecutor(task) === "agent"
  • executionMethod = "Agent" (全局 fallback)
  • executionMethod = "Auto" AND complexity = "Low" (全局 fallback)
Task(
  subagent_type="code-developer",
  run_in_background=false,
  description=batch.taskSummary,
  prompt=buildExecutionPrompt(batch)
)

Result Collection: After completion, collect result following executionResult structure (see Data Structures section)

Option B: CLI Execution (Codex)

When to use:

  • getTaskExecutor(task) === "codex"
  • executionMethod = "Codex" (全局 fallback)
  • executionMethod = "Auto" AND complexity = "Medium/High" (全局 fallback)
ccw cli -p "${buildExecutionPrompt(batch)}" --tool codex --mode write

Execution with fixed IDs (predictable ID pattern):

// Launch CLI in background, wait for task hook callback
// Generate fixed execution ID: ${sessionId}-${groupId}
const sessionId = executionContext?.session?.id || 'standalone'
const fixedExecutionId = `${sessionId}-${batch.groupId}`  // e.g., "implement-auth-2025-12-13-P1"

// Check if resuming from previous failed execution
const previousCliId = batch.resumeFromCliId || null

// Build command with fixed ID (and optional resume for continuation)
const cli_command = previousCliId
  ? `ccw cli -p "${buildExecutionPrompt(batch)}" --tool codex --mode write --id ${fixedExecutionId} --resume ${previousCliId}`
  : `ccw cli -p "${buildExecutionPrompt(batch)}" --tool codex --mode write --id ${fixedExecutionId}`

// Execute in background, stop output and wait for task hook callback
Bash(
  command=cli_command,
  run_in_background=true
)
// STOP HERE - CLI executes in background, task hook will notify on completion

Resume on Failure (with fixed ID):

// If execution failed or timed out, offer resume option
if (bash_result.status === 'failed' || bash_result.status === 'timeout') {
  console.log(`
⚠️ Execution incomplete. Resume available:
   Fixed ID: ${fixedExecutionId}
   Lookup: ccw cli detail ${fixedExecutionId}
   Resume: ccw cli -p "Continue tasks" --resume ${fixedExecutionId} --tool codex --mode write --id ${fixedExecutionId}-retry
`)

  // Store for potential retry in same session
  batch.resumeFromCliId = fixedExecutionId
}

Result Collection: After completion, analyze output and collect result following executionResult structure (include cliExecutionId for resume capability)

Option C: CLI Execution (Gemini)

When to use: getTaskExecutor(task) === "gemini" (分析类任务)

# 使用统一的 buildExecutionPrompt切换 tool 和 mode
ccw cli -p "${buildExecutionPrompt(batch)}" --tool gemini --mode analysis --id ${sessionId}-${batch.groupId}

Step 4: Progress Tracking

Progress tracked at batch level (not individual task level). Icons: (parallel, concurrent), → (sequential, one-by-one)

Step 5: Code Review (Optional)

Skip Condition: Only run if codeReviewTool ≠ "Skip"

Review Focus: Verify implementation against plan convergence criteria and test requirements

  • Read plan.json + .task/*.json for task convergence criteria and test checklist
  • Check each convergence criterion is fulfilled
  • Verify success metrics from test field (Medium/High complexity)
  • Run unit/integration tests specified in test field
  • Validate code quality and identify issues
  • Ensure alignment with planned approach and risk mitigations

Operations:

  • Agent Review: Current agent performs direct review
  • Gemini Review: Execute gemini CLI with review prompt
  • Codex Review: Two options - (A) with prompt for complex reviews, (B) --uncommitted flag only for quick reviews
  • Custom tool: Execute specified CLI tool (qwen, etc.)

Unified Review Template (All tools use same standard):

Review Criteria:

  • Convergence Criteria: Verify each criterion from task convergence.criteria
  • Test Checklist (Medium/High): Check unit, integration, success_metrics from task test
  • Code Quality: Analyze quality, identify issues, suggest improvements
  • Plan Alignment: Validate implementation matches planned approach and risk mitigations

Shared Prompt Template (used by all CLI tools):

PURPOSE: Code review for implemented changes against plan convergence criteria and test requirements
TASK: • Verify plan convergence criteria fulfillment • Check test requirements (unit, integration, success_metrics) • Analyze code quality • Identify issues • Suggest improvements • Validate plan adherence and risk mitigations
MODE: analysis
CONTEXT: @**/* @{plan.json} @{.task/*.json} [@{exploration.json}] | Memory: Review lite-execute changes against plan requirements including test checklist
EXPECTED: Quality assessment with:
  - Convergence criteria verification (all tasks from .task/*.json)
  - Test checklist validation (Medium/High: unit, integration, success_metrics)
  - Issue identification
  - Recommendations
  Explicitly check each convergence criterion and test item from .task/*.json files.
CONSTRAINTS: Focus on plan convergence criteria, test requirements, and plan adherence | analysis=READ-ONLY

Tool-Specific Execution (Apply shared prompt template above):

# Method 1: Agent Review (current agent)
# - Read plan.json: ${executionContext.session.artifacts.plan}
# - Apply unified review criteria (see Shared Prompt Template)
# - Report findings directly

# Method 2: Gemini Review (recommended)
ccw cli -p "[Shared Prompt Template with artifacts]" --tool gemini --mode analysis
# CONTEXT includes: @**/* @${plan.json} [@${exploration.json}]

# Method 3: Qwen Review (alternative)
ccw cli -p "[Shared Prompt Template with artifacts]" --tool qwen --mode analysis
# Same prompt as Gemini, different execution engine

# Method 4: Codex Review (git-aware) - Two mutually exclusive options:

# Option A: With custom prompt (reviews uncommitted by default)
ccw cli -p "[Shared Prompt Template with artifacts]" --tool codex --mode review
# Use for complex reviews with specific focus areas

# Option B: Target flag only (no prompt allowed)
ccw cli --tool codex --mode review --uncommitted
# Quick review of uncommitted changes without custom instructions

# ⚠️ IMPORTANT: -p prompt and target flags (--uncommitted/--base/--commit) are MUTUALLY EXCLUSIVE

Multi-Round Review with Fixed IDs:

// Generate fixed review ID
const reviewId = `${sessionId}-review`

// First review pass with fixed ID
const reviewResult = Bash(`ccw cli -p "[Review prompt]" --tool gemini --mode analysis --id ${reviewId}`)

// If issues found, continue review dialog with fixed ID chain
if (hasUnresolvedIssues(reviewResult)) {
  // Resume with follow-up questions
  Bash(`ccw cli -p "Clarify the security concerns you mentioned" --resume ${reviewId} --tool gemini --mode analysis --id ${reviewId}-followup`)
}

Implementation Note: Replace [Shared Prompt Template with artifacts] placeholder with actual template content, substituting:

  • @{plan.json}@${executionContext.session.artifacts.plan}
  • [@{exploration.json}] → exploration files from artifacts (if exists)

Step 6: Update Development Index

Trigger: After all executions complete (regardless of code review)

Skip Condition: Skip if .workflow/project-tech.json does not exist

Operations:

const projectJsonPath = '.workflow/project-tech.json'
if (!fileExists(projectJsonPath)) return  // Silent skip

const projectJson = JSON.parse(Read(projectJsonPath))

// Initialize if needed
if (!projectJson.development_index) {
  projectJson.development_index = { feature: [], enhancement: [], bugfix: [], refactor: [], docs: [] }
}

// Detect category from keywords
function detectCategory(text) {
  text = text.toLowerCase()
  if (/\b(fix|bug|error|issue|crash)\b/.test(text)) return 'bugfix'
  if (/\b(refactor|cleanup|reorganize)\b/.test(text)) return 'refactor'
  if (/\b(doc|readme|comment)\b/.test(text)) return 'docs'
  if (/\b(add|new|create|implement)\b/.test(text)) return 'feature'
  return 'enhancement'
}

// Detect sub_feature from task file paths
function detectSubFeature(tasks) {
  const dirs = tasks.map(t => t.file?.split('/').slice(-2, -1)[0]).filter(Boolean)
  const counts = dirs.reduce((a, d) => { a[d] = (a[d] || 0) + 1; return a }, {})
  return Object.entries(counts).sort((a, b) => b[1] - a[1])[0]?.[0] || 'general'
}

const category = detectCategory(`${planObject.summary} ${planObject.approach}`)
const entry = {
  title: planObject.summary.slice(0, 60),
  sub_feature: detectSubFeature(getTasks(planObject)),
  date: new Date().toISOString().split('T')[0],
  description: planObject.approach.slice(0, 100),
  status: previousExecutionResults.every(r => r.status === 'completed') ? 'completed' : 'partial',
  session_id: executionContext?.session?.id || null
}

projectJson.development_index[category].push(entry)
projectJson.statistics.last_updated = new Date().toISOString()
Write(projectJsonPath, JSON.stringify(projectJson, null, 2))

console.log(`✓ Development index: [${category}] ${entry.title}`)

Best Practices

Input Modes: In-memory (lite-plan), prompt (standalone), file (JSON/text) Task Grouping: Based on explicit depends_on only; independent tasks split by executor, each batch runs as separate CLI instance Execution: Independent task batches launch concurrently via single Claude message with multiple tool calls (one tool call per batch)

Error Handling

Error Cause Resolution
Missing executionContext In-memory mode without context Error: "No execution context found. Only available when called by lite-plan."
File not found File path doesn't exist Error: "File not found: {path}. Check file path."
Empty file File exists but no content Error: "File is empty: {path}. Provide task description."
Invalid Enhanced Task JSON JSON missing required fields Warning: "Missing required fields. Treating as plain text."
Malformed JSON JSON parsing fails Treat as plain text (expected for non-JSON files)
Execution failure Agent/Codex crashes Display error, use fixed ID ${sessionId}-${groupId} for resume: ccw cli -p "Continue" --resume <fixed-id> --id <fixed-id>-retry
Execution timeout CLI exceeded timeout Use fixed ID for resume with extended timeout
Codex unavailable Codex not installed Show installation instructions, offer Agent execution
Fixed ID not found Custom ID lookup failed Check ccw cli history, verify date directories

Data Structures

executionContext (Input - Mode 1)

Passed from lite-plan via global variable:

{
  planObject: {
    summary: string,
    approach: string,
    task_ids: string[],                          // Task IDs referencing .task/*.json files
    task_count: number,                          // Number of tasks
    _loadedTasks: [...],                         // Populated at runtime from .task/*.json files
    estimated_time: string,
    recommended_execution: string,
    complexity: string
  },
  // Task file paths (populated for two-layer format)
  taskFiles: [{id: string, path: string}] | null,
  explorationsContext: {...} | null,       // Multi-angle explorations
  explorationAngles: string[],             // List of exploration angles
  explorationManifest: {...} | null,       // Exploration manifest
  clarificationContext: {...} | null,
  executionMethod: "Agent" | "Codex" | "Auto",  // 全局默认
  codeReviewTool: "Skip" | "Gemini Review" | "Agent Review" | string,
  originalUserInput: string,

  // 任务级 executor 分配(优先于 executionMethod
  executorAssignments: {
    [taskId]: { executor: "gemini" | "codex" | "agent", reason: string }
  },

  // Session artifacts location (saved by lite-plan)
  session: {
    id: string,                        // Session identifier: {taskSlug}-{shortTimestamp}
    folder: string,                    // Session folder path: .workflow/.lite-plan/{session-id}
    artifacts: {
      explorations: [{angle, path}],   // exploration-{angle}.json paths
      explorations_manifest: string,   // explorations-manifest.json path
      plan: string                     // plan.json path (always present)
    }
  }
}

Artifact Usage:

  • Artifact files contain detailed planning context
  • Pass artifact paths to CLI tools and agents for enhanced context
  • See execution options below for usage examples

executionResult (Output)

Collected after each execution call completes:

{
  executionId: string,                 // e.g., "[Agent-1]", "[Codex-1]"
  status: "completed" | "partial" | "failed",
  tasksSummary: string,                // Brief description of tasks handled
  completionSummary: string,           // What was completed
  keyOutputs: string,                  // Files created/modified, key changes
  notes: string,                       // Important context for next execution
  fixedCliId: string | null            // Fixed CLI execution ID (e.g., "implement-auth-2025-12-13-P1")
}

Appended to previousExecutionResults array for context continuity in multi-execution scenarios.

Post-Completion Expansion

完成后询问用户是否扩展为issue(test/enhance/refactor/doc),选中项调用 /issue:new "{summary} - {dimension}"

Fixed ID Pattern: ${sessionId}-${groupId} enables predictable lookup without auto-generated timestamps.

Resume Usage: If status is "partial" or "failed", use fixedCliId to resume:

# Lookup previous execution
ccw cli detail ${fixedCliId}

# Resume with new fixed ID for retry
ccw cli -p "Continue from where we left off" --resume ${fixedCliId} --tool codex --mode write --id ${fixedCliId}-retry