Files
Claude-Code-Workflow/.claude/commands/issue/execute.md
catlog22 0157e36344 feat: add CLI Stream Viewer component for real-time output monitoring
- Implemented a new CLI Stream Viewer to display real-time output from CLI executions.
- Added state management for CLI executions, including handling of start, output, completion, and errors.
- Introduced UI rendering for stream tabs and content, with auto-scroll functionality.
- Integrated keyboard shortcuts for toggling the viewer and handling user interactions.

feat: create Issue Manager view for managing issues and execution queue

- Developed the Issue Manager view to manage issues, solutions, and execution queue.
- Implemented data loading functions for fetching issues and queue data from the API.
- Added filtering and rendering logic for issues and queue items, including drag-and-drop functionality.
- Created detail panel for viewing and editing issue details, including tasks and solutions.
2025-12-27 09:46:12 +08:00

11 KiB

name, description, argument-hint, allowed-tools
name description argument-hint allowed-tools
execute Execute queue with codex using endpoint-driven task fetching (single task per codex instance) [--parallel <n>] [--executor codex|gemini] TodoWrite(*), Bash(*), Read(*), AskUserQuestion(*)

Issue Execute Command (/issue:execute)

Overview

Execution orchestrator that coordinates codex instances. Each task is executed by an independent codex instance that fetches its task via CLI endpoint. Codex does NOT read task files - it calls ccw issue next to get task data dynamically.

Core design:

  • Single task per codex instance (not loop mode)
  • Endpoint-driven: ccw issue next → execute → ccw issue complete
  • No file reading in codex
  • Orchestrator manages parallelism

Storage Structure (Flat JSONL)

.workflow/issues/
├── issues.jsonl              # All issues (one per line)
├── queue.json                # Execution queue
└── solutions/
    ├── {issue-id}.jsonl      # Solutions for issue
    └── ...

Usage

/issue:execute [FLAGS]

# Examples
/issue:execute                    # Execute all ready tasks
/issue:execute --parallel 3       # Execute up to 3 tasks in parallel
/issue:execute --executor codex   # Force codex executor

# Flags
--parallel <n>        Max parallel codex instances (default: 1)
--executor <type>     Force executor: codex|gemini|agent
--dry-run             Show what would execute without running

Execution Process

Phase 1: Queue Loading
   ├─ Load queue.json
   ├─ Count pending/ready tasks
   └─ Initialize TodoWrite tracking

Phase 2: Ready Task Detection
   ├─ Find tasks with satisfied dependencies
   ├─ Group by execution_group (parallel batches)
   └─ Determine execution order

Phase 3: Codex Coordination
   ├─ For each ready task:
   │   ├─ Launch independent codex instance
   │   ├─ Codex calls: ccw issue next
   │   ├─ Codex receives task data (NOT file)
   │   ├─ Codex executes task
   │   ├─ Codex calls: ccw issue complete <queue-id>
   │   └─ Update TodoWrite
   └─ Parallel execution based on --parallel flag

Phase 4: Completion
   ├─ Generate execution summary
   ├─ Update issue statuses in issues.jsonl
   └─ Display results

Implementation

Phase 1: Queue Loading

// Load queue
const queuePath = '.workflow/issues/queue.json';
if (!Bash(`test -f "${queuePath}" && echo exists`).includes('exists')) {
  console.log('No queue found. Run /issue:queue first.');
  return;
}

const queue = JSON.parse(Read(queuePath));

// Count by status
const pending = queue.queue.filter(q => q.status === 'pending');
const executing = queue.queue.filter(q => q.status === 'executing');
const completed = queue.queue.filter(q => q.status === 'completed');

console.log(`
## Execution Queue Status

- Pending: ${pending.length}
- Executing: ${executing.length}
- Completed: ${completed.length}
- Total: ${queue.queue.length}
`);

if (pending.length === 0 && executing.length === 0) {
  console.log('All tasks completed!');
  return;
}

Phase 2: Ready Task Detection

// Find ready tasks (dependencies satisfied)
function getReadyTasks() {
  const completedIds = new Set(
    queue.queue.filter(q => q.status === 'completed').map(q => q.queue_id)
  );

  return queue.queue.filter(item => {
    if (item.status !== 'pending') return false;
    return item.depends_on.every(depId => completedIds.has(depId));
  });
}

const readyTasks = getReadyTasks();

if (readyTasks.length === 0) {
  if (executing.length > 0) {
    console.log('Tasks are currently executing. Wait for completion.');
  } else {
    console.log('No ready tasks. Check for blocked dependencies.');
  }
  return;
}

console.log(`Found ${readyTasks.length} ready tasks`);

// Sort by execution order
readyTasks.sort((a, b) => a.execution_order - b.execution_order);

// Initialize TodoWrite
TodoWrite({
  todos: readyTasks.slice(0, parallelLimit).map(t => ({
    content: `[${t.queue_id}] ${t.issue_id}:${t.task_id}`,
    status: 'pending',
    activeForm: `Executing ${t.queue_id}`
  }))
});

Phase 3: Codex Coordination (Single Task Mode)

// Execute tasks - single codex instance per task
async function executeTask(queueItem) {
  const codexPrompt = `
## Single Task Execution

You are executing ONE task from the issue queue. Follow these steps exactly:

### Step 1: Fetch Task
Run this command to get your task:
\`\`\`bash
ccw issue next
\`\`\`

This returns JSON with:
- queue_id: Queue item ID
- task: Task definition with implementation steps
- context: Exploration context
- execution_hints: Executor and time estimate

### Step 2: Execute Task
Read the returned task object and:
1. Follow task.implementation steps in order
2. Meet all task.acceptance criteria
3. Use provided context.relevant_files for reference
4. Use context.patterns for code style

### Step 3: Report Completion
When done, run:
\`\`\`bash
ccw issue complete <queue_id> --result '{"files_modified": ["path1", "path2"], "summary": "What was done"}'
\`\`\`

If task fails, run:
\`\`\`bash
ccw issue fail <queue_id> --reason "Why it failed"
\`\`\`

### Rules
- NEVER read task files directly - use ccw issue next
- Execute the FULL task before marking complete
- Do NOT loop - execute ONE task only
- Report accurate files_modified in result

### Start Now
Begin by running: ccw issue next
`;

  // Execute codex
  const executor = queueItem.assigned_executor || flags.executor || 'codex';

  if (executor === 'codex') {
    Bash(
      `ccw cli -p "${escapePrompt(codexPrompt)}" --tool codex --mode write --id exec-${queueItem.queue_id}`,
      timeout=3600000  // 1 hour timeout
    );
  } else if (executor === 'gemini') {
    Bash(
      `ccw cli -p "${escapePrompt(codexPrompt)}" --tool gemini --mode write --id exec-${queueItem.queue_id}`,
      timeout=1800000  // 30 min timeout
    );
  } else {
    // Agent execution
    Task(
      subagent_type="code-developer",
      run_in_background=false,
      description=`Execute ${queueItem.queue_id}`,
      prompt=codexPrompt
    );
  }
}

// Execute with parallelism
const parallelLimit = flags.parallel || 1;

for (let i = 0; i < readyTasks.length; i += parallelLimit) {
  const batch = readyTasks.slice(i, i + parallelLimit);

  console.log(`\n### Executing Batch ${Math.floor(i / parallelLimit) + 1}`);
  console.log(batch.map(t => `- ${t.queue_id}: ${t.issue_id}:${t.task_id}`).join('\n'));

  if (parallelLimit === 1) {
    // Sequential execution
    for (const task of batch) {
      updateTodo(task.queue_id, 'in_progress');
      await executeTask(task);
      updateTodo(task.queue_id, 'completed');
    }
  } else {
    // Parallel execution - launch all at once
    const executions = batch.map(task => {
      updateTodo(task.queue_id, 'in_progress');
      return executeTask(task);
    });
    await Promise.all(executions);
    batch.forEach(task => updateTodo(task.queue_id, 'completed'));
  }

  // Refresh ready tasks after batch
  const newReady = getReadyTasks();
  if (newReady.length > 0) {
    console.log(`${newReady.length} more tasks now ready`);
  }
}

Codex Task Fetch Response

When codex calls ccw issue next, it receives:

{
  "queue_id": "Q-001",
  "issue_id": "GH-123",
  "solution_id": "SOL-001",
  "task": {
    "id": "T1",
    "title": "Create auth middleware",
    "scope": "src/middleware/",
    "action": "Create",
    "description": "Create JWT validation middleware",
    "modification_points": [
      { "file": "src/middleware/auth.ts", "target": "new file", "change": "Create middleware" }
    ],
    "implementation": [
      "Create auth.ts file in src/middleware/",
      "Implement JWT token validation using jsonwebtoken",
      "Add error handling for invalid/expired tokens",
      "Export middleware function"
    ],
    "acceptance": [
      "Middleware validates JWT tokens successfully",
      "Returns 401 for invalid or missing tokens",
      "Passes token payload to request context"
    ]
  },
  "context": {
    "relevant_files": ["src/config/auth.ts", "src/types/auth.d.ts"],
    "patterns": "Follow existing middleware pattern in src/middleware/logger.ts"
  },
  "execution_hints": {
    "executor": "codex",
    "estimated_minutes": 30
  }
}

Phase 4: Completion Summary

// Reload queue for final status
const finalQueue = JSON.parse(Read(queuePath));

const summary = {
  completed: finalQueue.queue.filter(q => q.status === 'completed').length,
  failed: finalQueue.queue.filter(q => q.status === 'failed').length,
  pending: finalQueue.queue.filter(q => q.status === 'pending').length,
  total: finalQueue.queue.length
};

console.log(`
## Execution Complete

**Completed**: ${summary.completed}/${summary.total}
**Failed**: ${summary.failed}
**Pending**: ${summary.pending}

### Task Results
${finalQueue.queue.map(q => {
  const icon = q.status === 'completed' ? '✓' :
               q.status === 'failed' ? '✗' :
               q.status === 'executing' ? '⟳' : '○';
  return `${icon} ${q.queue_id} [${q.issue_id}:${q.task_id}] - ${q.status}`;
}).join('\n')}
`);

// Update issue statuses in issues.jsonl
const issuesPath = '.workflow/issues/issues.jsonl';
const allIssues = Bash(`cat "${issuesPath}"`)
  .split('\n')
  .filter(line => line.trim())
  .map(line => JSON.parse(line));

const issueIds = [...new Set(finalQueue.queue.map(q => q.issue_id))];
for (const issueId of issueIds) {
  const issueTasks = finalQueue.queue.filter(q => q.issue_id === issueId);

  if (issueTasks.every(q => q.status === 'completed')) {
    console.log(`\n✓ Issue ${issueId} fully completed!`);

    // Update issue status
    const issueIndex = allIssues.findIndex(i => i.id === issueId);
    if (issueIndex !== -1) {
      allIssues[issueIndex].status = 'completed';
      allIssues[issueIndex].completed_at = new Date().toISOString();
      allIssues[issueIndex].updated_at = new Date().toISOString();
    }
  }
}

// Write updated issues.jsonl
Write(issuesPath, allIssues.map(i => JSON.stringify(i)).join('\n'));

if (summary.pending > 0) {
  console.log(`
### Continue Execution
Run \`/issue:execute\` again to execute remaining tasks.
`);
}

Dry Run Mode

if (flags.dryRun) {
  console.log(`
## Dry Run - Would Execute

${readyTasks.map((t, i) => `
${i + 1}. ${t.queue_id}
   Issue: ${t.issue_id}
   Task: ${t.task_id}
   Executor: ${t.assigned_executor}
   Group: ${t.execution_group}
`).join('')}

No changes made. Remove --dry-run to execute.
`);
  return;
}

Error Handling

Error Resolution
Queue not found Display message, suggest /issue:queue
No ready tasks Check dependencies, show blocked tasks
Codex timeout Mark as failed, allow retry
ccw issue next empty All tasks done or blocked
Task execution failure Marked via ccw issue fail

Endpoint Contract

ccw issue next

  • Returns next ready task as JSON
  • Marks task as 'executing'
  • Returns { status: 'empty' } when no tasks

ccw issue complete <queue-id>

  • Marks task as 'completed'
  • Updates queue.json
  • Checks if issue is fully complete

ccw issue fail <queue-id>

  • Marks task as 'failed'
  • Records failure reason
  • Allows retry via /issue:execute
  • /issue:plan - Plan issues with solutions
  • /issue:queue - Form execution queue
  • ccw issue queue list - View queue status
  • ccw issue retry - Retry failed tasks