Skill: qiukui666/openclaw-skill-parallel-tasks (version 1.0.0)

--- name: parallel-tasks description: Execute multiple tasks in parallel with timeout protection, error isolation, and real-time progress feedback. Use when user says "run these in parallel", "parallel execution", "concurrent tasks", or wants multiple independent tasks done simultaneously with proper error handling and timeout control. --- # Parallel Tasks Skill Execute multiple tasks **in parallel** with enterprise-grade reliability: timeout protection, error isolation, and real-time progress feedback. ## When to Use Use this skill when: - User says "run these in parallel" or "do these simultaneously" - Multiple independent tasks need to be executed at once - User wants faster results by running tasks concurrently - Tasks are slow and user wants to avoid waiting sequentially ## Core Concept **Serial vs Parallel**: ``` SERIAL (slow): Task 1 → Task 2 → Task 3 (5min + 5min + 5min = 15min) PARALLEL (fast): Task 1 ─┬─> (5min total, not 15min) Task 2 ─┼─> Task 3 ─┘ ``` ## Usage ### Basic Parallel Execution ``` /parallel - Task 1: Search for docs - Task 2: Search for code - Task 3: Search for examples ``` ### Named Tasks with Custom Timeout ``` /parallel timeout=300 - [search-docs] Search for relevant documentation - [search-code] Find similar implementations - [analyze] Analyze the results ``` ### CLI Usage (scripts/executor.ts) ```bash # Simple usage node scripts/executor.ts "Research AI trends" "Research market analysis" # Named tasks with custom timeout node scripts/executor.ts --timeout 600 \ --task "[research] Research AI trends" \ --task "[implement] Build the feature" # Read from file (one task per line) node scripts/executor.ts --tasks-file my-tasks.txt --max-concurrent 3 # Named task formats (all equivalent): # - [name] description # - - description (auto-named as task-1, task-2, ...) # - 1. description ``` ## Implementation ### Core Execution Pattern The executor uses a **semaphore pattern** with configurable concurrency: ```typescript // 1. Parse tasks from input const tasks = parseTaskInput(input) // 2. Execute tasks with concurrency control const results: TaskResult[] = [] const executing: Promise<void>[] = [] for (const task of tasks) { // Wait if at max concurrency if (executing.length >= maxConcurrent) { await Promise.race(executing) } const promise = runTask(task).then(result => { results.push(result) // Remove from executing list const idx = executing.indexOf(promise) if (idx > -1) executing.splice(idx, 1) }) executing.push(promise) } await Promise.all(executing) ``` ### Task Execution via hermes cli ```typescript async function executeTaskViaSpawn( task: Task, timeoutSeconds: number ): Promise<TaskResult> { const taskId = `parallel-${Date.now()}-${randomId()}` return new Promise((resolve) => { const proc = spawn('hermes', [ 'cli', '--', 'sessions_spawn', '--task', `"${task.description}"`, '--label', `"${task.name}"`, '--timeout', String(timeoutSeconds), '--session-id', taskId ], { stdio: ['ignore', 'pipe', 'pipe'] }) // Timeout handling const timeoutId = setTimeout(() => { proc.kill('SIGTERM') resolve({ name: task.name, status: 'timeout', duration: Date.now() - startTime, error: `Exceeded ${timeoutSeconds}s timeout` }) }, timeoutSeconds * 1000) proc.on('close', (code, signal) => { clearTimeout(timeoutId) if (signal === 'SIGTERM') { resolve({ name: task.name, status: 'timeout', ... }) } else if (code === 0) { resolve({ name: task.name, status: 'fulfilled', ... }) } else { resolve({ name: task.name, status: 'rejected', ... }) } }) }) } ``` ### Timeout Protection | Option | Default | Description | |--------|---------|-------------| | `timeout` | 300 | Default timeout per task (seconds) | | Per-task timeout | - | Override global timeout for specific tasks | **Behavior**: Task auto-terminates after timeout, other tasks continue. ### Error Isolation Each task runs in **complete isolation**: | Problem | Serial | Parallel (This Skill) | |---------|--------|----------------------| | One task fails | All others stop | Only failed task affected | | One task hangs | Blocks entire flow | Others continue normally | | One task times out | May cascade | Contained, others finish | ### Concurrency Control | Option | Default | Description | |--------|---------|-------------| | `maxConcurrent` | 5 | Maximum tasks running simultaneously | **Pattern**: Semaphore-style - starts N tasks, when one completes, starts next. ### Progress Feedback Real-time terminal output with colored status: ``` 🚀 Starting 3 tasks in parallel (max 5 concurrent)... 🔄 [task-1] Starting (timeout: 300s)... 🔄 [task-2] Starting (timeout: 300s)... 🔄 [task-3] Starting (timeout: 300s)... ✅ [1/3] [task-1] Complete (23.5s) ✅ [2/3] [task-2] Complete (45.2s) ⏱️ [3/3] [task-3] Timeout after 300s ``` ## Task Input Formats ### 1. Named Tasks (Recommended) ``` [research] Research AI trends and write report [implement] Build the feature [test] Write comprehensive tests ``` ### 2. Bullet List ``` - Search for API documentation - Find relevant code examples - Check for existing implementations ``` ### 3. Numbered List ``` 1. Research authentication patterns 2. Design database schema 3. Implement API endpoints ``` ### 4. Plain Text (auto-named) ``` Research AI trends Build the feature Write tests ``` → Auto-named: task-1, task-2, task-3 ## Output Format ### Success Case ``` ✅ Parallel Execution Complete 3 tasks: 2 succeeded, 1 failed (45.2s total) ┌─────────────────────┬────────────┬────────────┐ │ Task │ Status │ Duration │ ├─────────────────────┼────────────┼────────────┤ │ research │ ✅ fulfilled│ 23.5s │ │ implement │ ✅ fulfilled│ 45.2s │ │ test │ ⏱️ timeout │ 300.0s │ └─────────────────────┴────────────┴────────────┘ ❌ Failed Tasks: • test: Exceeded 300s timeout ``` ### Exit Codes | Code | Meaning | |------|---------| | 0 | All tasks succeeded | | 1 | Some tasks failed or timed out | ## Options ### CLI Options | Option | Default | Description | |--------|---------|-------------| | `--timeout, -to` | 300 | Timeout per task (seconds) | | `--max-concurrent, -m` | 5 | Max concurrent tasks | | `--stop-on-error` | false | Stop all if one fails | | `--no-progress` | false | Suppress progress output | | `--tasks-file, -f` | - | Read tasks from file | | `--parse` | - | Parse stdin to JSON | ### Per-Task Options (in task description) ``` [name] description (timeout=600) ``` ## Error Handling ### Error Types | Status | Cause | Behavior | |--------|-------|----------| | `fulfilled` | Task succeeded | Returns result value | | `timeout` | Exceeded timeout | Task terminated, others continue | | `rejected` | Process error | Error captured, others continue | | `cancelled` | User cancelled | All running tasks terminate | | `no_reply` | No output | Reported as warning | ## Best Practices 1. **Independent tasks first**: Tasks should not depend on each other 2. **Set reasonable timeouts**: Don't set 5min if task should take 30s 3. **Use named tasks**: Easier to debug when something fails 4. **Keep tasks focused**: One clear goal per task 5. **Mind concurrency**: Don't set maxConcurrent higher than system can handle ## Examples ### Example 1: Research Multiple Topics ```bash node scripts/executor.ts \ "Research Claude Code best practices" \ "Find OpenClaw skill examples" \ "Search for agent design patterns" ``` ### Example 2: Named Tasks from File ```bash # tasks.txt: # [research] Research AI trends # [implement] Build the feature # [test] Write tests node scripts/executor.ts --tasks-file tasks.txt --timeout 600 ``` ### Example 3: Parallel Implementation ```bash node scripts/executor.ts --timeout 600 \ --task "[backend] Implement user authentication API" \ --task "[frontend] Build login form component" \ --task "[database] Create users table migration" ``` ### Example 4: Web Scraping ```bash node scripts/executor.ts \ --task "[store1] Fetch product data from store1.com" \ --task "[store2] Fetch product data from store2.com" \ --task "[store3] Fetch product data from store3.com" ``` ## Anti-Patterns ❌ **Don't use for dependent tasks**: ```bash # WRONG - second task depends on first! node scripts/executor.ts \ "Create user account" \ "Send welcome email" ``` Use sequential execution instead. ❌ **Don't use for very fast tasks**: ```bash # WRONG - spawning overhead not worth it node scripts/executor.ts "Read file A" "Read file B" "Read file C" ``` The overhead of spawning parallel sessions isn't worth it for sub-second tasks. ## Related Skills - `subagents` - Background agent spawning - `batch-operations` - Bulk file operations - `workflow-orchestrator` - Complex multi-step workflows

path: README.md
content: # OpenClaw Parallel Tasks Skill

<div align="center">

**Execute multiple tasks in parallel with enterprise-grade reliability**

*Timeout protection • Error isolation • Real-time progress feedback*

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![OpenClaw](https://img.shields.io/badge/OpenClaw-Skill-blue.svg)](https://github.com/openclaw/openclaw)

</div>

---

## ✨ Features

| Feature | Description |
|---------|-------------|
| 🚀 **True Parallel Execution** | Run multiple tasks concurrently, not sequentially |
| ⏱️ **Timeout Protection** | Auto-terminate tasks after configurable timeout (default: 5 min) |
| 🛡️ **Error Isolation** | One task failure doesn't block others |
| 📊 **Real-time Progress** | Live status updates during execution |
| 📋 **Smart Output** | Numbered results mapping to each task |

---

## 📊 Performance Comparison

```
SERIAL (before):
Task 1 → Task 2 → Task 3    Total: 15 minutes

PARALLEL (after):
Task 1 ─┬─> Total: 5 minutes ⚡
Task 2 ─┼─> 200% faster!
Task 3 ─┘
```

---

## 🚀 Quick Start

### Installation

Copy the `parallel-tasks` skill folder to your OpenClaw skills directory:

```bash
# Assuming your OpenClaw workspace is ~/.openclaw/workspace
cp -r parallel-tasks ~/.openclaw/workspace/skills/
```

### Usage

#### Basic Parallel Execution

```
/parallel
- Search for documentation
- Find code examples
- Check existing implementations
```

#### Named Tasks with Custom Timeout

```
/parallel timeout=600
- [research] Research market trends
- [implement] Build feature X
- [test] Write comprehensive tests
```

#### Multi-line Input

```
/parallel
Task 1: Read all config files
Task 2: Analyze source code
Task 3: Generate test cases
```

---

## 📖 Detailed Usage

### Input Formats

#### 1. Bullet List (Recommended)

```
/parallel
- First task description
- Second task description
- Third task description
```

#### 2. Numbered List

```
/parallel
1. Research authentication patterns
2. Design database schema
3. Implement API endpoints
```

#### 3. Named Tasks

```
/parallel
[research] Gather requirements and analyze use cases
[design] Create system architecture diagram
[implement] Write production-ready code
```

#### 4. JSON (Advanced)

```json
/parallel
{
  "tasks": [
    { "name": "task1", "description": "...", "timeout": 300 },
    { "name": "task2", "description": "...", "timeout": 600 }
  ],
  "options": {
    "stopOnError": false,
    "reportProgress": true
  }
}
```

### Output Format

```
[Parallel Execution]

① [research] ✅ Complete (1m 23s)
② [design] ✅ Complete (45s)
③ [implement] ⏱️ Timeout (5m 00s)
④ [test] ❌ Failed: Connection error

┌─────────────┬──────────┬────────────┐
│ Task        │ Status   │ Duration   │
├─────────────┼──────────┼────────────┤
│ research    │ ✅ Done  │ 1m 23s    │
│ design      │ ✅ Done  │ 45s       │
│ implement   │ ⏱️ Timeout│ 5m 00s   │
│ test        │ ❌ Failed│ 12s       │
└─────────────┴──────────┴────────────┘

Summary: 2 succeeded, 1 timeout, 1 failed
```

---

## ⚙️ Configuration

### Global Options

| Option | Default | Description |
|--------|---------|-------------|
| `timeout` | 300 | Default timeout per task (seconds) |
| `stopOnError` | false | Stop all tasks if one fails |
| `reportProgress` | true | Show real-time progress |

### Per-Task Options

```markdown
[TaskName] task description (timeout=600)
```

---

## 🎯 Best Practices

### ✅ Ideal Use Cases

| Use Case | Example |
|----------|---------|
| Multi-source research | Search multiple websites simultaneously |
| Batch file operations | Read/write multiple files at once |
| API polling | Check multiple endpoints in parallel |
| Independent analysis | Analyze different aspects concurrently |

### ❌ Avoid For

| Scenario | Reason |
|----------|--------|
| Dependent tasks | Task B needs Task A's result |
| Ultra-fast tasks | Spawning overhead not worth it |
| Shared state | Tasks need to communicate |

---

## 🔧 Architecture

```
┌─────────────────────────────────────────────────────┐
│                   User Request                        │
│                  "Run these in parallel"             │
└─────────────────────┬───────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────┐
│              Parallel Tasks Skill                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  │
│  │  Task 1    │  │  Task 2    │  │  Task 3    │  │
│  │ (spawn)    │  │ (spawn)    │  │ (spawn)    │  │
│  │ timeout=300│  │ timeout=300│  │ timeout=300│  │
│  └─────┬──────┘  └─────┬──────┘  └─────┬──────┘  │
│        │               │               │          │
│        ▼               ▼               ▼          │
│  ┌─────────────────────────────────────────────┐  │
│  │         Promise.allSettled()                 │  │
│  │    (waits for all, captures results)         │  │
│  └─────────────────────┬───────────────────────┘  │
│                        │                           │
│                        ▼                           │
│  ┌─────────────────────────────────────────────┐  │
│  │           Result Aggregator                   │  │
│  │  - ✅ Completed → Return result               │  │
│  │  - ⏱️ Timeout → Terminate + report           │  │
│  │  - ❌ Error → Capture + continue             │  │
│  └─────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────┘
                      │
                      ▼
              Final Summary Report
```

---

## 📝 Skill Definition

```markdown
---
name: parallel-tasks
description: Execute multiple tasks in parallel with timeout protection, 
             error isolation, and real-time progress feedback. Use when 
             user says "run these in parallel", "parallel execution", 
             "concurrent tasks", or wants multiple independent tasks 
             done simultaneously.
---
```

---

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

---

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

---

## 👤 Author

**qiukui666**
- GitHub: [@qiukui666](https://github.com/qiukui666)

---

## 🙏 Acknowledgments

- [OpenClaw](https://github.com/openclaw/openclaw) - The multi-channel gateway for AI agents
- Built with 💜 for the OpenClaw community

---

<div align="center">

**Star ⭐ if this skill was useful to you!**

</div>


path: scripts/executor.ts
content: #!/usr/bin/env node
/**
 * Parallel Tasks Executor for OpenClaw
 * 
 * Executes multiple tasks in parallel using sessions_spawn with:
 * - Configurable timeout per task
 * - Error isolation (one failure doesn't block others)
 * - Aggregated results summary
 * 
 * Usage:
 *   node executor.ts --task "Research X" --task "Research Y" --timeout 300
 *   node executor.ts --tasks-file tasks.txt --timeout 600
 */

import * as fs from 'fs'
import * as path from 'path'

// Types
interface Task {
  name: string
  description: string
  timeout?: number
}

interface TaskResult {
  name: string
  status: 'fulfilled' | 'rejected' | 'timeout' | 'cancelled' | 'no_reply'
  duration?: number
  value?: string
  error?: string
}

interface ParallelOptions {
  timeout: number
  stopOnError: boolean
  reportProgress: boolean
  maxConcurrent: number
}

// ANSI colors for terminal output
const colors = {
  reset: '\x1b[0m',
  green: '\x1b[32m',
  red: '\x1b[31m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  cyan: '\x1b[36m',
  gray: '\x1b[90m'
}

function colorize(color: string, text: string): string {
  return `${colors[color as keyof typeof colors] || ''}${text}${colors.reset}`
}

/**
 * Parse command line arguments
 */
function parseArgs(): { tasks: Task[], options: Partial<ParallelOptions> } {
  const args = process.argv.slice(2)
  const tasks: Task[] = []
  const options: Partial<ParallelOptions> = {
    timeout: 300,
    stopOnError: false,
    reportProgress: true,
    maxConcurrent: 5
  }

  let currentTask: string | null = null
  let i = 0

  while (i < args.length) {
    const arg = args[i]

    if (arg === '--task' || arg === '-t') {
      if (currentTask) {
        tasks.push({ name: `task-${tasks.length + 1}`, description: currentTask })
      }
      currentTask = args[++i]
    } else if (arg === '--timeout' || arg === '-to') {
      options.timeout = parseInt(args[++i]) || 300
    } else if (arg === '--max-concurrent' || arg === '-m') {
      options.maxConcurrent = parseInt(args[++i]) || 5
    } else if (arg === '--stop-on-error') {
      options.stopOnError = true
    } else if (arg === '--no-progress') {
      options.reportProgress = false
    } else if (arg === '--tasks-file' || arg === '-f') {
      const filePath = args[++i]
      if (filePath && fs.existsSync(filePath)) {
        const content = fs.readFileSync(filePath, 'utf-8')
        const fileTasks = parseTaskInput(content)
        tasks.push(...fileTasks)
      }
    } else if (arg === '--help' || arg === '-h') {
      printHelp()
      process.exit(0)
    } else if (arg === '--parse') {
      // Parse mode: read tasks from stdin and print parsed output
      const stdin = fs.readFileSync('/dev/stdin', 'utf-8')
      const parsed = parseTaskInput(stdin)
      console.log(JSON.stringify(parsed, null, 2))
      process.exit(0)
    } else if (!arg.startsWith('-')) {
      // Positional argument treated as task description
      if (currentTask) {
        tasks.push({ name: `task-${tasks.length + 1}`, description: currentTask })
      }
      currentTask = arg
    }
    i++
  }

  // Don't forget the last task
  if (currentTask) {
    tasks.push({ name: `task-${tasks.length + 1}`, description: currentTask })
  }

  return { tasks, options }
}

/**
 * Parse task input from various formats
 */
function parseTaskInput(input: string): Task[] {
  const lines = input.split('\n').filter(line => line.trim())
  const tasks: Task[] = []

  for (const line of lines) {
    const trimmed = line.trim()

    // Skip empty lines and comments
    if (!trimmed || trimmed.startsWith('#')) continue

    // Named task: [name] description
    const namedMatch = trimmed.match(/^\[([^\]]+)\]\s*(.+)$/)
    if (namedMatch) {
      tasks.push({
        name: namedMatch[1],
        description: namedMatch[2]
      })
      continue
    }

    // Bullet list or numbered: - task or 1. task
    const listMatch = trimmed.match(/^[-*\d.]\s*(.+)$/)
    if (listMatch) {
      tasks.push({
        name: `task-${tasks.length + 1}`,
        description: listMatch[1]
      })
      continue
    }

    // Plain text: treat as description
    if (trimmed.length > 0) {
      tasks.push({
        name: `task-${tasks.length + 1}`,
        description: trimmed
      })
    }
  }

  return tasks
}

/**
 * Execute a single task via sessions_spawn ACP command
 */
async function executeTaskViaSpawn(
  task: Task,
  timeoutSeconds: number
): Promise<TaskResult> {
  const startTime = Date.now()
  const taskId = `parallel-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`

  return new Promise((resolve) => {
    // Build the ACP spawn command
    const spawnCmd = [
      'sessions_spawn',
      '--task', `"${task.description}"`,
      '--label', `"${task.name}"`,
      '--timeout', String(timeoutSeconds),
      '--session-id', taskId
    ].join(' ')

    if (process.env.DEBUG) {
      console.error(`[DEBUG] Spawning: ${spawnCmd}`)
    }

    // Use Hermes CLI to spawn session
    const { spawn } = require('child_process')
    const proc = spawn('hermes', ['cli', '--', ...spawnCmd.split(' ').filter(Boolean)], {
      stdio: ['ignore', 'pipe', 'pipe'],
      env: { ...process.env }
    })

    let stdout = ''
    let stderr = ''

    proc.stdout?.on('data', (data: Buffer) => {
      stdout += data.toString()
    })

    proc.stderr?.on('data', (data: Buffer) => {
      stderr += data.toString()
    })

    // Set up timeout
    const timeoutId = setTimeout(() => {
      try {
        proc.kill('SIGTERM')
      } catch (e) {
        // Ignore
      }
      resolve({
        name: task.name,
        status: 'timeout',
        duration: Date.now() - startTime,
        error: `Exceeded ${timeoutSeconds}s timeout`
      })
    }, timeoutSeconds * 1000)

    proc.on('close', (code: number | null, signal: string | null) => {
      clearTimeout(timeoutId)

      if (signal === 'SIGTERM') {
        resolve({
          name: task.name,
          status: 'timeout',
          duration: Date.now() - startTime,
          error: `Exceeded ${timeoutSeconds}s timeout`
        })
        return
      }

      if (code === 0) {
        resolve({
          name: task.name,
          status: 'fulfilled',
          duration: Date.now() - startTime,
          value: stdout.trim() || 'Task completed successfully'
        })
      } else {
        resolve({
          name: task.name,
          status: 'rejected',
          duration: Date.now() - startTime,
          error: stderr.trim() || `Process exited with code ${code}`
        })
      }
    })

    proc.on('error', (err: Error) => {
      clearTimeout(timeoutId)
      resolve({
        name: task.name,
        status: 'rejected',
        duration: Date.now() - startTime,
        error: err.message
      })
    })
  })
}

/**
 * Execute multiple tasks with concurrency control
 */
async function executeParallel(
  tasks: Task[],
  options: Partial<ParallelOptions> = {}
): Promise<TaskResult[]> {
  const opts: ParallelOptions = {
    timeout: options.timeout || 300,
    stopOnError: options.stopOnError || false,
    reportProgress: options.reportProgress !== false,
    maxConcurrent: options.maxConcurrent || 5
  }

  console.log(colorize('cyan', `\n🚀 Starting ${tasks.length} tasks in parallel (max ${opts.maxConcurrent} concurrent)...\n`))

  const results: TaskResult[] = []
  const executing: Promise<void>[] = []
  let completed = 0

  // Use semaphore pattern for concurrency control
  async function runTask(task: Task): Promise<TaskResult> {
    const timeout = (task.timeout || opts.timeout)!

    if (opts.reportProgress) {
      console.log(colorize('blue', `🔄 [${task.name}] Starting (timeout: ${timeout}s)...`))
    }

    const result = await executeTaskViaSpawn(task, timeout)
    
    completed++
    const progress = `[${completed}/${tasks.length}]`
    
    if (result.status === 'fulfilled') {
      if (opts.reportProgress) {
        const duration = result.duration ? `(${(result.duration / 1000).toFixed(1)}s)` : ''
        console.log(colorize('green', `✅ ${progress} [${task.name}] Complete ${duration}`))
      }
    } else if (result.status === 'timeout') {
      console.log(colorize('yellow', `⏱️  ${progress} [${task.name}] Timeout after ${timeout}s`))
    } else {
      console.log(colorize('red', `❌ ${progress} [${task.name}] Failed: ${result.error}`))
    }

    return result
  }

  // Process tasks with concurrency limit
  for (let i = 0; i < tasks.length; i++) {
    const task = tasks[i]
    
    if (opts.stopOnError && results.some(r => r.status !== 'fulfilled')) {
      // Stop remaining tasks if stopOnError is enabled and a task failed
      while (executing.length > 0) {
        await Promise.race(executing)
      }
      break
    }

    const promise = runTask(task).then(result => {
      results.push(result)
      // Remove from executing list
      const idx = executing.indexOf(promise)
      if (idx > -1) executing.splice(idx, 1)
    })

    executing.push(promise)

    // If we've hit max concurrent, wait for one to complete
    if (executing.length >= opts.maxConcurrent) {
      await Promise.race(executing)
    }
  }

  // Wait for all remaining tasks
  await Promise.all(executing)

  return results
}

/**
 * Format results as a summary table
 */
function formatResults(results: TaskResult[]): string {
  const succeeded = results.filter(r => r.status === 'fulfilled').length
  const failed = results.filter(r => r.status !== 'fulfilled').length
  const totalDuration = results.reduce((sum, r) => sum + (r.duration || 0), 0)

  let output = '\n'
  output += colorize('green', '✅ Parallel Execution Complete') + '\n'
  output += `   ${results.length} tasks: ${colorize('green', `${succeeded} succeeded`)}, ${colorize('red', `${failed} failed`)}`
  if (totalDuration > 0) {
    output += ` (${(totalDuration / 1000).toFixed(1)}s total)`
  }
  output += '\n\n'

  // Table header
  const col1 = 'Task'
  const col2 = 'Status'
  const col3 = 'Duration'
  
  output += '┌' + '─'.repeat(21) + '┬' + '─'.repeat(12) + '┬' + '─'.repeat(12) + '┐\n'
  output += `│ ${col1.padEnd(19)} │ ${col2.padEnd(10)} │ ${col3.padEnd(10)} │\n`
  output += '├' + '─'.repeat(21) + '┼' + '─'.repeat(12) + '┼' + '─'.repeat(12) + '┤\n'

  for (const result of results) {
    const statusIcon = result.status === 'fulfilled' ? '✅' : 
                      result.status === 'timeout' ? '⏱️' : '❌'
    const statusColor = result.status === 'fulfilled' ? 'green' : 
                        result.status === 'timeout' ? 'yellow' : 'red'
    const duration = result.duration ? `${(result.status === 'timeout' ? result.duration / 1000 : result.duration / 1000).toFixed(1)}s` : '-'
    
    const name = result.name.slice(0, 19).padEnd(19)
    const status = `${statusIcon} ${result.status}`.padEnd(10)
    
    output += `│ ${colorize(statusColor as any, name)} │ ${colorize(statusColor as any, status)} │ ${duration.padStart(10)} │\n`
  }

  output += '└' + '─'.repeat(21) + '┴' + '─'.repeat(12) + '┴' + '─'.repeat(12) + '┘\n'

  // Error details
  const errors = results.filter(r => r.status !== 'fulfilled')
  if (errors.length > 0) {
    output += `\n${colorize('red', '❌ Failed Tasks:')}\n`
    for (const error of errors) {
      const errorMsg = error.error || 'Unknown error'
      output += `   • ${error.name}: ${errorMsg.slice(0, 100)}\n`
    }
  }

  return output
}

/**
 * Print help message
 */
function printHelp(): void {
  console.log(`
${colorize('cyan', '📋 Parallel Tasks Executor for OpenClaw')}

${colorize('yellow', 'Usage:')}
  node executor.ts [options] --task "Task 1" --task "Task 2" ...
  node executor.ts [options] "Task 1" "Task 2" "Task 3"

${colorize('yellow', 'Options:')}
  --task, -t <desc>      Task description (can be specified multiple times)
  --timeout, -to <sec>   Timeout per task in seconds (default: 300)
  --max-concurrent, -m   Maximum concurrent tasks (default: 5)
  --stop-on-error        Stop all tasks if one fails
  --no-progress          Suppress progress output
  --tasks-file, -f <file> Read tasks from file (one per line)
  --parse                Parse tasks from stdin and print JSON
  --help, -h            Show this help message

${colorize('yellow', 'Task Input Formats:')}
  Named:     [my-task] This is my task description
  Bullet:    - This is my task description
  Numbered:  1. This is my task description
  Plain:     This is my task description (auto-named as task-1, task-2, ...)

${colorize('yellow', 'Examples:')}
  # Simple usage
  node executor.ts "Research AI trends" "Research market analysis"

  # Named tasks with custom timeout
  node executor.ts --timeout 600 \\
    --task "[research] Research AI trends" \\
    --task "[implement] Build the feature"

  # Read from file
  node executor.ts --tasks-file my-tasks.txt --max-concurrent 3

${colorize('yellow', 'Environment Variables:')}
  DEBUG=true    Enable debug output

${colorize('yellow', 'Exit Codes:')}
  0   All tasks succeeded
  1   Some tasks failed or timed out
`)
}

// Main entry point
async function main() {
  try {
    const { tasks, options } = parseArgs()

    if (tasks.length === 0) {
      console.error(colorize('red', 'Error: No tasks specified. Use --help for usage information.'))
      process.exit(1)
    }

    if (process.env.DEBUG) {
      console.error(`[DEBUG] Parsed ${tasks.length} tasks`)
      console.error(`[DEBUG] Options:`, JSON.stringify(options, null, 2))
    }

    const results = await executeParallel(tasks, options)
    const failedCount = results.filter(r => r.status !== 'fulfilled').length

    console.log(formatResults(results))

    if (failedCount > 0) {
      console.log(colorize('yellow', `\n⚠️  ${failedCount} task(s) failed or timed out\n`))
      process.exit(1)
    } else {
      console.log(colorize('green', '\n✨ All tasks completed successfully!\n'))
      process.exit(0)
    }
  } catch (error) {
    console.error(colorize('red', `\nFatal error: ${error instanceof Error ? error.message : String(error)}\n`))
    if (process.env.DEBUG) {
      console.error(error)
    }
    process.exit(1)
  }
}

main()


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)