task-runner

ClawSkills 作者 skill-engineer

Persistent task queue system. Users add tasks at any time via natural language; tasks are stored in a single persistent queue file and executed asynchronously via subagents. A heartbeat/cron dispatcher wakes periodically to check pending tasks, spawn workers, and report completions. The system never "finishes" — it always remains ready for the next task.

安装 / 下载方式

TotalClaw CLI推荐
totalclaw install clawskills:clawskills~chunhualiao-autonomous-task-runner
cURL直接下载,无需登录
curl -fsSL https://skills.taituai.com/api/skills/clawskills%3Aclawskills~chunhualiao-autonomous-task-runner/file -o chunhualiao-autonomous-task-runner.md
# Task Runner Skill

A persistent, daemon-style task queue. Users add tasks at any time. A dispatcher runs on every
heartbeat to check the queue and execute pending work via subagents. Tasks accumulate, complete,
and are archived — the queue itself never closes.

---

## Two Operating Modes

This skill has **two distinct modes** with different triggers and behaviors:

| Mode | Trigger | Purpose |
|------|---------|---------|
| **INTAKE** | User message containing task intent | Parse message → add tasks to queue → confirm → **immediately run DISPATCHER** |
| **DISPATCHER** | After INTAKE (primary) · Heartbeat/cron (backup) | Read queue → dispatch pending tasks → report completions |

Both modes read and write the **same persistent queue file**.

---

## A1 — Triggers

### Mode 1: INTAKE (user message)

Activate INTAKE mode when the user's message matches any of the following patterns:

| Pattern | Examples |
|---------|---------|
| Explicit task add | "add task", "add these tasks", "task:", "new task" |
| Delegation | "do this for me", "do these for me", "handle these", "can you do X" |
| Framing | "I need you to", "help me with", "I need", "I want you to" |
| List framing | "task list", "my tasks", "queue these", "work on these" |
| Control commands | "skip T-03", "retry T-02", "mark T-01 done", "cancel T-04" |
| Status check | "show tasks", "task status", "what's in the queue", "what are my pending tasks" |
| Compound ask | Any message with 2+ distinct action items (bullets, numbers, "and also", "then") |

**Do NOT activate INTAKE for:**
- Pure single-question lookups answered in one sentence ("what time is it?")
- Scheduling-only requests with no actual task ("remind me in 20 min")
- Single web search requests ("google X")
- The heartbeat systemEvent (that's DISPATCHER mode)

### Mode 2: DISPATCHER (inline after INTAKE, heartbeat, or cron)

Activate DISPATCHER mode when triggered by:
- **Immediately after INTAKE** — runs in the same turn, right after tasks are queued (primary path)
- `HEARTBEAT.md` check during a heartbeat poll (backup: catches retries and completions)
- systemEvent: `"TASK_RUNNER_DISPATCH: check queue and run pending tasks"` (backup)
- Any scheduled/cron trigger registered for task-runner (backup)

---

## Configuration

| Variable | Location | Default | Description |
|----------|----------|---------|-------------|
| `TASK_RUNNER_DIR` | TOOLS.md | `~/.openclaw/tasks/` | Directory for queue file and deliverables |
| `TASK_RUNNER_MAX_CONCURRENT` | TOOLS.md | `2` | Max tasks running simultaneously |
| `TASK_RUNNER_MAX_RETRIES` | TOOLS.md or env | `3` | Max retry attempts before marking blocked |
| `TASK_RUNNER_ARCHIVE_DAYS` | TOOLS.md | `7` | Days after which done/blocked tasks are archived |

**How to configure** — add to `TOOLS.md`:
```
## Task Runner
TASK_RUNNER_DIR=~/.openclaw/tasks/
TASK_RUNNER_MAX_CONCURRENT=2
TASK_RUNNER_MAX_RETRIES=3
TASK_RUNNER_ARCHIVE_DAYS=7
```

**Queue file path:** `${TASK_RUNNER_DIR}/task-queue.json`
(single persistent file, NOT dated — accumulates all tasks over time)

---

## A3 — Outputs

| Output | Path / Channel | Description |
|--------|---------------|-------------|
| Queue file | `${TASK_RUNNER_DIR}/task-queue.json` | Single persistent queue; all tasks |
| Per-task completion message | Chat notification | Sent immediately when a task finishes (done or blocked) |
| Deliverable files | Task-specific paths | Files produced by tasks (when applicable) |
| INTAKE confirmation | Chat | Sent after adding tasks to queue |

---

## Mode 1: INTAKE — Step-by-Step

**Goal:** Convert user message into structured task objects, append to queue, confirm.

### Step 0 — First Run Setup (auto-configure on first use)

**Run this check before anything else, every INTAKE invocation:**

```
CHECK whether ${TASK_RUNNER_DIR}/task-queue.json exists
IF file does NOT exist:
  → This is the first run. Auto-configure everything silently before proceeding.

  [1] Create directory:
      exec: mkdir -p ${TASK_RUNNER_DIR}

  [2] Initialize queue file:
      WRITE ${TASK_RUNNER_DIR}/task-queue.json with default structure:
      { "lastId": null, "tasks": [], "archivedCount": 0 }

  [3] Register heartbeat entry:
      READ HEARTBEAT.md (create it if missing)
      IF "Task Runner Dispatcher" is NOT already in the file:
        APPEND the following block (with a blank line before it):

        ## Task Runner Dispatcher
        Every heartbeat: check ${TASK_RUNNER_DIR}/task-queue.json
        - If pending or running tasks exist → run DISPATCHER mode (task-runner skill)
        - If nothing pending → HEARTBEAT_OK (skip)

      WRITE the updated HEARTBEAT.md

  [4] Register backup cron job:
      CALL cron tool with:
        action: "add"
        job:
          name: "Task Runner Dispatcher"
          schedule: { kind: "every", everyMs: 900000 }
          payload: { kind: "systemEvent", text: "TASK_RUNNER_DISPATCH: check queue and run pending tasks" }
          sessionTarget: "main"
          enabled: true

  [5] Notify user:
      "⚙️ Task Runner initialized.
       Heartbeat dispatcher registered in HEARTBEAT.md.
       Backup cron job registered (runs every 15 minutes).
       Your tasks will execute automatically."

  → THEN continue with normal INTAKE steps below.

IF file already exists:
  → Skip Step 0 entirely. Proceed directly to Step 1.
```

**Idempotency rule:** Step 0 only fires on true first run (queue file absent).
It will never double-register the heartbeat entry or create duplicate cron jobs.

---

### Step 1 — Load queue

```
READ ${TASK_RUNNER_DIR}/task-queue.json
IF file does not exist:
  Initialize with default structure (see references/queue-schema.md)
  Set lastId = null
```

### Step 2 — Parse tasks from message

Split user message into individual tasks using these cues:
- Numbered lists (1., 2., 3.)
- Bulleted lists (-, *, •)
- Explicit separators ("first", "also", "and then", "next")
- Compound sentences with multiple imperatives
- Single task: entire message is one task

### Step 3 — Assign IDs

Continue from `lastId` in the queue file:
- If `lastId = "T-05"`, next task is `T-06`
- If `lastId = null`, start at `T-01`
- Format: `T-NN` (zero-padded, minimum 2 digits; expand to 3 when N > 99)

### Step 4 — Build task objects

For each parsed task, create a JSON object (schema in `references/queue-schema.md`):
- Set `id`, `description`, `goal`, `status = "pending"`, `added_at`
- Set `retries = 0`, `maxRetries` from config
- Leave execution fields null

### Step 5 — Append to queue and save

```
APPEND new task objects to queue.tasks[]
UPDATE queue.lastId to the last assigned ID
WRITE updated queue file to disk
```

### Step 6 — Confirm to user

```
Added T-06: [description]. Starting now...
```

For multiple tasks:
```
📋 Added 3 tasks to queue:
• T-06: [description]
• T-07: [description]
• T-08: [description]
Starting dispatcher now...
```

**Then immediately run DISPATCHER mode (Steps 1–5 below) in the same turn.**
Do not exit and wait for the next heartbeat. Tasks must start executing immediately.
The heartbeat/cron dispatcher is a backup for retries and completion checks — not the primary execution path.

### Step 7 — Handle control commands

| Command | Action |
|---------|--------|
| `skip T-NN` | Set status = "skipped"; save; confirm |
| `retry T-NN` | Reset status = "pending", retries = 0; save; confirm |
| `cancel T-NN` | Set status = "skipped", blocked_reason = "cancelled by user"; save; confirm |
| `mark T-NN done` | Set status = "done", completed_at = now; save; confirm |
| `show tasks` / `task status` | Read queue; render status table (see A5 templates) |

---

## Mode 2: DISPATCHER — Step-by-Step

**Goal:** Check queue, dispatch pending tasks, track running tasks, report completions.

### Step 1 — Load queue

```
READ ${TASK_RUNNER_DIR}/task-queue.json
IF file does not exist OR tasks array is empty:
  → HEARTBEAT_OK (silent, nothing to do)
  → EXIT
```

### Step 2 — Check for work

```
pending_task