jpskill.com
📄 ドキュメント コミュニティ

taskflow

OpenClawエージェント向けに、マークダウンでの作成やSQLiteでのクエリ、双方向同期、CLI、Apple Notes連携などを通じて、構造化されたプロジェクトやタスクを管理するためのSkill。

📜 元の英語説明(参考)

Structured project/task management for OpenClaw agents — markdown-first authoring, SQLite-backed querying, bidirectional sync, CLI, Apple Notes integration.

🇯🇵 日本人クリエイター向け解説

一言でいうと

OpenClawエージェント向けに、マークダウンでの作成やSQLiteでのクエリ、双方向同期、CLI、Apple Notes連携などを通じて、構造化されたプロジェクトやタスクを管理するためのSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o taskflow.zip https://jpskill.com/download/5461.zip && unzip -o taskflow.zip && rm taskflow.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/5461.zip -OutFile "$d\taskflow.zip"; Expand-Archive "$d\taskflow.zip" -DestinationPath $d -Force; ri "$d\taskflow.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して taskflow.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → taskflow フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-17
取得日時
2026-05-17
同梱ファイル
7

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

TaskFlow — エージェントスキルリファレンス

TaskFlow は、あらゆる OpenClaw エージェントに、Markdown ファーストのオーサリング、SQLite をバックエンドとするクエリ、双方向同期を備えた構造化されたプロジェクト/タスク/計画システムを提供します。

原則: Markdown が正典です。tasks/*.md を直接編集してください。SQLite DB は派生インデックスであり、真の情報源ではありません。


セキュリティ

OPENCLAW_WORKSPACE 信頼境界

OPENCLAW_WORKSPACE高信頼値です。すべての TaskFlow スクリプトはここからファイルパスを解決し、CLI と同期デーモンはこれを使用して SQLite データベース、Markdown タスクファイル、およびログディレクトリを特定します。

安全な使用のためのルール:

  1. 信頼できる、管理されたソースからのみ設定してください。 値は以下から取得する必要があります。

    • ご自身のシェルプロファイル (.zshrc.bashrc/etc/environment)
    • ご自身で管理するテンプレート内の systemd ユーザーユニット Environment= ディレクティブ
    • ご自身でインストールした macOS LaunchAgent EnvironmentVariables ディクショナリ

    決して OPENCLAW_WORKSPACE を以下から受け入れないでください。

    • ユーザーが提供する CLI 引数または HTTP リクエストパラメータ
    • 実行時に読み込まれる信頼できない設定ファイル
    • 明示的に検証されていない外部入力
  2. 使用前にパスが存在することを確認してください。 OPENCLAW_WORKSPACE を読み取るスクリプトは、続行する前にディレクトリが存在することを確認する必要があります。

    import { existsSync } from 'node:fs'
    import path from 'node:path'
    
    const workspace = process.env.OPENCLAW_WORKSPACE
    if (!workspace) {
      console.error('OPENCLAW_WORKSPACE is not set. Aborting.')
      process.exit(1)
    }
    if (!existsSync(workspace)) {
      console.error(`OPENCLAW_WORKSPACE path does not exist: ${workspace}`)
      process.exit(1)
    }
    // 相対パスのトリックを無効にするため、絶対パスに解決します
    const safeWorkspace = path.resolve(workspace)
  3. 信頼できない入力からパスを構築しないでください。 有効な OPENCLAW_WORKSPACE があっても、検証されていないユーザー入力をそれに連結しないでください (例: path.join(workspace, userSlug, '../../../etc/passwd'))。path.resolve() を使用し、解決されたパスがワークスペースルートで始まることを確認してください。

    function safeJoin(base, ...parts) {
      const resolved = path.resolve(base, ...parts)
      if (!resolved.startsWith(path.resolve(base) + path.sep)) {
        throw new Error(`Path traversal attempt detected: ${resolved}`)
      }
      return resolved
    }
  4. OPENCLAW_WORKSPACE はローカルシステムパスとしてのみ扱ってください。 ローカルファイルシステム上のディレクトリを指す必要があります。リモートパス (NFS マウント、ネットワーク共有) は機能する可能性がありますが、テスト済みの構成外であり、TOCTOU (time-of-check/time-of-use) 競合状態を引き起こす可能性があります。


セットアップ

1. 環境変数を設定する

シェルプロファイル (.zshrc.bashrc など) に追加してください。

export OPENCLAW_WORKSPACE="/path/to/your/.openclaw/workspace"

すべての TaskFlow スクリプトと CLI は、この変数からパスを解決します。これがない場合、process.cwd() にフォールバックしますが、これはほとんどの場合、望ましいものではありません。

参照: セキュリティ要件については、上記の OPENCLAW_WORKSPACE 信頼境界 を参照してください。

2. CLI をリンクする

ln -sf {baseDir}/scripts/taskflow-cli.mjs /opt/homebrew/bin/taskflow  # macOS (Apple Silicon)
# または: ln -sf {baseDir}/scripts/taskflow-cli.mjs /usr/local/bin/taskflow

3. セットアップウィザードを実行する

taskflow setup

ウィザードが残りの処理を行います。ワークスペースディレクトリを作成し、最初のプロジェクトの追加を案内し、データベースを初期化し、同期を行い、オプションで定期同期のための macOS LaunchAgent をインストールします。

代替 — 手動セットアップ:

<details> <summary>手動手順 (明示的な制御を好む場合)</summary>

# ワークスペースディレクトリを作成
mkdir -p "$OPENCLAW_WORKSPACE/tasks" "$OPENCLAW_WORKSPACE/plans" "$OPENCLAW_WORKSPACE/memory" "$OPENCLAW_WORKSPACE/logs"

# DB スキーマをブートストラップ
taskflow init

# PROJECTS.md と tasks/<slug>-tasks.md を手動で作成 (templates/ を参照)

# Markdown → DB を同期
taskflow sync files-to-db

# 確認
taskflow status

</details>


初回実行

エージェント (OpenClaw / AI) 向け

ユーザーが TaskFlow のセットアップを依頼した場合、または初期化されていないことを検出した場合:

  1. 状態を検出します。 $OPENCLAW_WORKSPACE/PROJECTS.md$OPENCLAW_WORKSPACE/memory/taskflow.sqlite を確認します。
  2. クリーンな状態の場合: ユーザーに最初のプロジェクト名と説明を尋ね、以下を実行します。
    taskflow setup --name "Project Name" --desc "One-liner description"

    その後、taskflow status を実行して確認します。

  3. PROJECTS.md は存在するが DB がない場合: taskflow setup を実行します (状態を自動的に検出し、初期化と同期を提案します)。
  4. 両方存在する場合: taskflow status を実行します — すでにセットアップ済みです。
  5. セットアップ後、AGENTS.md を新しいプロジェクトスラッグで更新し、将来のセッションで cat PROJECTS.md を介して検出できるようにします。

人間 (CLI) 向け

taskflow setup

対話型ウィザードは以下を行います。

  • 既存のワークスペース状態を検出します
  • 最初のプロジェクトの名前付けを案内します
  • テンプレートから PROJECTS.mdtasks/<slug>-tasks.md を作成します
  • SQLite データベースを初期化し、同期します
  • 自動 60 秒同期のための定期同期デーモン (macOS では LaunchAgent、Linux では systemd タイマー) のインストールを提案します

非対話型 (スクリプトによるインストール):

taskflow setup --name "My Project" --desc "What it does"

--name を渡すと、すべての対話型プロンプトがスキップされます (デーモンインストールも非対話型モードではスキップされます)。


ディレクトリレイアウト

<workspace>/
├── PROJECTS.md                      # プロジェクトレジストリ (プロジェクトごとに ## ブロックが1つ)
├── tasks/<slug>-tasks.md            # プロジェクトごとのタスクリスト
├── plans/<slug>-plan.md             # オプション: プロジェクトごとのアーキテクチャ/設計ドキュメント
└── taskflow/
    ├── SKILL.md                     # このファイル
    ├── scripts/
    │   ├── taskflow-cli.mjs         # CLI エントリポイント (シンボリックリンクターゲット)
    │   ├── task-sync.mjs            # 双方向 Markdown ↔ SQLite 同期
    │   ├── init-db.mjs              # SQLite スキーマのブートストラップ (冪等)
    │   ├── export-projec
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

TaskFlow — Agent Skill Reference

TaskFlow gives any OpenClaw agent a structured project/task/plan system with markdown-first authoring, SQLite-backed querying, and bidirectional sync.

Principle: Markdown is canonical. Edit tasks/*.md directly. The SQLite DB is a derived index, not the source of truth.


Security

OPENCLAW_WORKSPACE Trust Boundary

OPENCLAW_WORKSPACE is a high-trust value. All TaskFlow scripts resolve file paths from it, and the CLI and sync daemon use it to locate the SQLite database, markdown task files, and log directory.

Rules for safe use:

  1. Set it only from trusted, controlled sources. The value must come from:

    • Your own shell profile (.zshrc, .bashrc, /etc/environment)
    • The systemd user unit Environment= directive in a template you control
    • The macOS LaunchAgent EnvironmentVariables dictionary you installed

    Never accept OPENCLAW_WORKSPACE from:

    • User-supplied CLI arguments or HTTP request parameters
    • Untrusted config files read at runtime
    • Any external input that has not been explicitly validated
  2. Validate the path exists before use. Any script that reads OPENCLAW_WORKSPACE should confirm the directory exists before proceeding:

    import { existsSync } from 'node:fs'
    import path from 'node:path'
    
    const workspace = process.env.OPENCLAW_WORKSPACE
    if (!workspace) {
      console.error('OPENCLAW_WORKSPACE is not set. Aborting.')
      process.exit(1)
    }
    if (!existsSync(workspace)) {
      console.error(`OPENCLAW_WORKSPACE path does not exist: ${workspace}`)
      process.exit(1)
    }
    // Resolve to absolute path to neutralize any relative-path tricks
    const safeWorkspace = path.resolve(workspace)
  3. Do not construct paths from untrusted input. Even with a valid OPENCLAW_WORKSPACE, never concatenate unvalidated user input onto it (e.g. path.join(workspace, userSlug, '../../../etc/passwd')). Use path.resolve() and check that the resolved path starts with the workspace root:

    function safeJoin(base, ...parts) {
      const resolved = path.resolve(base, ...parts)
      if (!resolved.startsWith(path.resolve(base) + path.sep)) {
        throw new Error(`Path traversal attempt detected: ${resolved}`)
      }
      return resolved
    }
  4. Treat OPENCLAW_WORKSPACE as a local system path only. It must point to a directory on the local filesystem. Remote paths (NFS mounts, network shares) may work but are outside the tested configuration and could introduce TOCTOU (time-of-check/time-of-use) race conditions.


Setup

1. Set environment variable

Add to your shell profile (.zshrc, .bashrc, etc.):

export OPENCLAW_WORKSPACE="/path/to/your/.openclaw/workspace"

All TaskFlow scripts and the CLI resolve paths from this variable. Without it, they fall back to process.cwd(), which is almost never what you want.

See also: OPENCLAW_WORKSPACE Trust Boundary above for security requirements.

2. Link the CLI

ln -sf {baseDir}/scripts/taskflow-cli.mjs /opt/homebrew/bin/taskflow  # macOS (Apple Silicon)
# or: ln -sf {baseDir}/scripts/taskflow-cli.mjs /usr/local/bin/taskflow

3. Run the setup wizard

taskflow setup

The wizard handles the rest: creates workspace directories, walks you through adding your first project(s), initializes the database, syncs, and optionally installs the macOS LaunchAgent for periodic sync.

Alternative — manual setup:

<details> <summary>Manual steps (if you prefer explicit control)</summary>

# Create workspace dirs
mkdir -p "$OPENCLAW_WORKSPACE/tasks" "$OPENCLAW_WORKSPACE/plans" "$OPENCLAW_WORKSPACE/memory" "$OPENCLAW_WORKSPACE/logs"

# Bootstrap the DB schema
taskflow init

# Create PROJECTS.md and tasks/<slug>-tasks.md manually (see templates/)

# Sync markdown → DB
taskflow sync files-to-db

# Verify
taskflow status

</details>


First Run

For agents (OpenClaw / AI)

When a user asks you to set up TaskFlow or you detect it has not been initialized:

  1. Detect state. Check for $OPENCLAW_WORKSPACE/PROJECTS.md and $OPENCLAW_WORKSPACE/memory/taskflow.sqlite.
  2. If clean slate: Ask the user for their first project name and description, then run:
    taskflow setup --name "Project Name" --desc "One-liner description"

    Follow up by running taskflow status to confirm.

  3. If PROJECTS.md exists but no DB: Run taskflow setup (it detects the state automatically and offers to init + sync).
  4. If both exist: Run taskflow status — already set up.
  5. After setup, update AGENTS.md with the new project slug so future sessions discover it via cat PROJECTS.md.

For humans (CLI)

taskflow setup

The interactive wizard will:

  • Detect your existing workspace state
  • Walk you through naming your first project(s)
  • Create PROJECTS.md and tasks/<slug>-tasks.md from templates
  • Initialize the SQLite database and sync
  • Offer to install the periodic-sync daemon (LaunchAgent on macOS, systemd timer on Linux) for automatic 60s sync

Non-interactive (scripted installs):

taskflow setup --name "My Project" --desc "What it does"

Passing --name skips all interactive prompts (daemon install is also skipped in non-interactive mode).


Directory Layout

<workspace>/
├── PROJECTS.md                      # Project registry (one ## block per project)
├── tasks/<slug>-tasks.md            # Task list per project
├── plans/<slug>-plan.md             # Optional: architecture/design doc per project
└── taskflow/
    ├── SKILL.md                     # This file
    ├── scripts/
    │   ├── taskflow-cli.mjs         # CLI entry point (symlink target)
    │   ├── task-sync.mjs            # Bidirectional markdown ↔ SQLite sync
    │   ├── init-db.mjs              # Bootstrap SQLite schema (idempotent)
    │   ├── export-projects-overview.mjs  # JSON export of project/task state
    │   └── apple-notes-export.mjs   # Optional: project state → Apple Notes (macOS only)
    ├── templates/                   # Starter files for new projects
    ├── schema/
    │   └── taskflow.sql             # Full DDL
    └── system/
        ├── com.taskflow.sync.plist.xml  # Periodic sync (macOS LaunchAgent)
        ├── taskflow-sync.service        # Periodic sync (Linux systemd user unit)
        └── taskflow-sync.timer          # Systemd timer (60s interval)
<workspace>/
└── taskflow.config.json                 # Apple Notes config (auto-created on first notes run)

Creating a Project

Follow this full checklist when creating a new project:

1. Add a block to PROJECTS.md

## <slug>
- Name: <Human-Readable Name>
- Status: active
- Description: One-sentence description of the project.
  • slug is lowercase, hyphenated (e.g., my-project). It becomes the canonical project ID everywhere.
  • Valid status values: active, paused, done.

2. Create the task file

Copy taskflow/templates/tasks-template.mdtasks/<slug>-tasks.md and update the project name in the heading.

The file must contain these five section headers in this order:

# <Project Name> — Tasks

## In Progress
## Pending Validation
## Backlog
## Blocked
## Done

3. Optionally create a plan file

Copy taskflow/templates/plan-template.mdplans/<slug>-plan.md for architecture docs, design decisions, and phased roadmaps. Plan files are not synced to SQLite — they are reference-only for the agent.

4. DB row (auto-created on first sync)

You do not need to manually insert into the projects table. The sync engine auto-creates the project row from PROJECTS.md on the next files-to-db run. If you want to be explicit via Node.js, use a parameterized statement:

// Safe: parameterized insert — no string interpolation in the SQL
db.prepare(`INSERT INTO projects (id, name, description, status)
            VALUES (:id, :name, :description, 'active')`)
  .run({ id: slug, name: projectName, description: projectDesc })

Task Line Format

Every task line follows this exact format:

- [x| ] (task:<id>) [<priority>] [<owner>] <title>
Field Details
[ ] / [x] Open / completed. Sync drives status from section header, not this checkbox.
(task:<id>) Task ID. Format: <slug>-NNN (zero-padded 3-digit). Sequential per project.
[<priority>] Required. Must come before owner tag. See priority table below.
[<owner>] Optional. Agent/model tag (e.g., codex, sonnet, claude).
<title> Human-readable task title.

⚠️ Tag Order Rule

Priority tag MUST come before owner tag. The sync parser is positional — it reads the first [Px] bracket as priority, and the next [tag] as owner. Swapping them will misparse the task.

⚠️ Title Sanitization Rules

Task titles must be plain text only. Before writing any user-supplied string as a task title, apply the following rules:

  1. Reject lines that look like section headers. A title may not start with one or more # characters followed by a space (e.g. # My heading, ## Done). These would corrupt the sync parser's section detection.

  2. Reject the exact section header strings even without leading whitespace:

    • In Progress, Pending Validation, Backlog, Blocked, Done
    • Comparison must be case-insensitive.
  3. Escape or strip markdown special characters that have structural meaning in the task file:

    Character Risk Safe action
    # Looks like a header Strip or reject
    - (dash + space at line start) Looks like a list item / task Strip leading -
    [ ] / [x] Looks like a checkbox Escape brackets: \[ \]
    ] / [ alone Can corrupt (task:id) parse Escape: \[ \]
    Newlines (\n, \r) Creates multi-line titles Strip / reject
  4. Maximum length. Titles should be ≤ 200 characters. Truncate or reject longer strings.

Example sanitization (Node.js):

// Safe: sanitize a user-supplied task title before writing to markdown
function sanitizeTitle(raw) {
  if (typeof raw !== 'string') throw new TypeError('title must be a string')

  // Strip newlines
  let title = raw.replace(/[\r\n]+/g, ' ').trim()

  // Reject lines that look like section headers (# Heading or bare header words)
  if (/^#{1,6}\s/.test(title)) {
    throw new Error('Title may not start with a markdown heading (#)')
  }
  const BANNED_HEADERS = /^(in progress|pending validation|backlog|blocked|done)$/i
  if (BANNED_HEADERS.test(title)) {
    throw new Error('Title may not be a reserved section header name')
  }

  // Escape structural markdown characters
  title = title
    .replace(/\[/g, '\\[')
    .replace(/\]/g, '\\]')

  // Enforce length limit
  if (title.length > 200) {
    throw new Error('Title exceeds 200 character limit')
  }

  return title
}

These rules apply whenever a task title comes from any external or user-supplied source (CLI args, API payloads, file imports). Titles hard-coded by agents in their own sessions are low-risk but should still avoid structural characters.

✅ Correct: - [ ] (task:myproject-007) [P1] [codex] Implement search ❌ Wrong: - [ ] (task:myproject-007) [codex] [P1] Implement search

Priority Levels (Configurable)

Tag Default Meaning
P0 Critical — must do now, blocks everything
P1 High — important, do soon
P2 Normal — standard priority (default)
P3 Low — nice to have
P9 Someday — no urgency, parking lot

Priorities are configurable per-installation but the tags themselves (P0P3, P9) are what the sync engine validates.

Optional Note Lines

A note can follow a task line as an indented - note: line:

- [ ] (task:myproject-003) [P1] [codex] Implement auth flow
  - note: blocked on API key from vendor

Known limitation (v1): Notes are one-way. Removing or editing a note in markdown does not propagate to the DB. This is tracked for a post-MVP fix.

Example Task File Section

## In Progress
- [ ] (task:myproject-001) [P1] [codex] Wire up OAuth login
  - note: PR open, needs review

## Backlog
- [ ] (task:myproject-002) [P2] Add rate limiting middleware
- [ ] (task:myproject-003) [P3] Write integration tests

Adding a New Task

  1. Determine the next ID. Scan the task file for the highest existing <slug>-NNN and increment by 1. Or query SQLite using a parameterized statement (never interpolate the slug into SQL strings):

    // Node.js — safe, parameterized
    const db = new DatabaseSync(dbPath)
    const row = db
      .prepare(`SELECT MAX(CAST(SUBSTR(id, LENGTH(:slug) + 2) AS INTEGER)) AS max_seq
                FROM tasks_v2
                WHERE project_id = :slug`)
      .get({ slug: projectSlug })
    const nextSeq = (row.max_seq ?? 0) + 1
    const nextId  = `${projectSlug}-${String(nextSeq).padStart(3, '0')}`

    ⚠️ Never construct SQL by string interpolation. Use db.prepare() with named or positional parameters (? or :name) for all values that come from external input. This applies even for read-only queries.

  2. Append the task line to the correct section (## Backlog for new work, ## In Progress if starting immediately).

  3. Format the line using the exact format above. No trailing spaces. Priority tag before owner tag.


Updating Task Status

Move the task line from its current section to the target section in the markdown file.

Target State Move to Section
Started / picked up ## In Progress
Needs human review ## Pending Validation
Not started yet ## Backlog
Waiting on dependency ## Blocked
Finished ## Done

Also flip the checkbox: [ ] for active states, [x] for Done (and optionally Pending Validation).

The periodic sync (60s) will pick up the change and update SQLite automatically. To force an immediate sync:

node taskflow/scripts/task-sync.mjs files-to-db

Querying Tasks

Simple: Read the markdown file directly

cat tasks/<slug>-tasks.md

For a quick in-session view, just read the relevant section.

Advanced: Query SQLite

⚠️ SQL Safety Rule: Any query that incorporates a variable value (project slug, task ID, status string, etc.) must use parameterized statements — not string interpolation. The sqlite3 CLI examples below use only static, hardcoded literal values and are shown as diagnostic/inspection tools only. For programmatic use, always use the Node.js db.prepare() API with bound parameters.

sqlite3 CLI (static queries — for manual inspection only)

# All in-progress tasks across all projects (by priority)
# Safe: 'in_progress' is a static literal, not a variable
sqlite3 "$OPENCLAW_WORKSPACE/memory/taskflow.sqlite" \
  "SELECT id, project_id, priority, title
   FROM tasks_v2
   WHERE status = 'in_progress'
   ORDER BY priority, project_id;"

# Task count by status per project (no variables — safe for CLI)
sqlite3 "$OPENCLAW_WORKSPACE/memory/taskflow.sqlite" \
  "SELECT project_id, status, COUNT(*) AS count
   FROM tasks_v2
   GROUP BY project_id, status
   ORDER BY project_id, status;"

Do not embed shell variables directly in the SQL string (e.g. WHERE project_id = '$SLUG'). That pattern is SQL injection waiting to happen. Use the Node.js API with parameters instead.

Node.js API — parameterized queries (required for programmatic use)

import { DatabaseSync } from 'node:sqlite'
import path from 'node:path'

const dbPath = path.join(process.env.OPENCLAW_WORKSPACE, 'memory', 'taskflow.sqlite')
const db = new DatabaseSync(dbPath)
db.exec('PRAGMA foreign_keys = ON')

// ── Backlog for a specific project ─────────────────────────────
// :slug is a named parameter — never interpolated into the SQL string
const backlog = db
  .prepare(`SELECT id, priority, title
            FROM tasks_v2
            WHERE project_id = :slug AND status = 'backlog'
            ORDER BY priority`)
  .all({ slug: 'my-project' })  // value bound at runtime, never in SQL string

// ── Audit trail for a specific task ────────────────────────────
const transitions = db
  .prepare(`SELECT from_status, to_status, actor, at
            FROM task_transitions_v2
            WHERE task_id = ?
            ORDER BY at`)
  .all('my-project-007')  // positional parameter — also safe

// ── Write: update task status ───────────────────────────────────
// NEVER: db.exec(`UPDATE tasks_v2 SET status='${newStatus}' WHERE id='${id}'`)
// ALWAYS:
db.prepare(`UPDATE tasks_v2 SET status = :status, updated_at = datetime('now')
            WHERE id = :id`)
  .run({ status: 'done', id: 'my-project-007' })

CLI Quick Reference

# Terminal summary: all projects + task counts by status
taskflow status

# Add a task in markdown with automatic next ID
taskflow add taskflow "Implement quick add command" --priority P1 --owner codex

# List current tasks for a project (excludes done by default)
taskflow list taskflow
taskflow list --project "TaskFlow" --all
taskflow list task --status backlog,pending_validation --json

# JSON export of full project/task state (for dashboards, integrations)
node taskflow/scripts/export-projects-overview.mjs

# Detect drift between markdown and DB (exit 1 if mismatch)
node taskflow/scripts/task-sync.mjs check

# Sync markdown → DB (normal direction; run after editing task files)
node taskflow/scripts/task-sync.mjs files-to-db

# Sync DB → markdown (run after programmatic DB updates)
node taskflow/scripts/task-sync.mjs db-to-files

Apple Notes Export (Optional — macOS Only)

TaskFlow can maintain a live Apple Note with your current project status. The note is rendered as rich HTML and written via AppleScript.

# Push current status to Apple Notes (creates note on first run)
taskflow notes

On first run (or during taskflow setup), a new note is created in the configured folder and its Core Data ID is saved to:

$OPENCLAW_WORKSPACE/taskflow.config.json

Config schema:

{
  "appleNotesId":     "x-coredata://...",
  "appleNotesFolder": "Notes",
  "appleNotesTitle":  "TaskFlow - Project Status"
}

Important — never delete the shared note. The note is always edited in-place. Deleting and recreating it generates a new Core Data ID and breaks any existing share links. If the note is accidentally deleted, taskflow notes will create a new one and update the config automatically.

For hourly auto-refresh, add a cron entry:

# Run: crontab -e
0 * * * * OPENCLAW_WORKSPACE=/path/to/workspace /path/to/node /path/to/taskflow/scripts/apple-notes-export.mjs

Or install a dedicated LaunchAgent (macOS) targeting apple-notes-export.mjs with an hourly StartInterval of 3600.

This feature is entirely optional and macOS-specific. On other platforms, taskflow notes exits gracefully with a message.


Memory Integration Rules

These rules keep daily memory logs clean and prevent duplication.

✅ Do

  • Reference task IDs in daily memory logs when you complete or advance work:
    Completed `myproject-007` (OAuth login). Moved `myproject-008` to In Progress.
  • Keep memory entries narrative — what happened, what you decided, what's next.

❌ Do Not

  • Never duplicate the backlog in daily memory files. tasks/<slug>-tasks.md is the single source of truth for all pending work. Memory files should not list what's left to do.
  • Do not track task state changes in memory (e.g., "Task 007 is now in progress"). Only note meaningful progress events or decisions.
  • Do not create new tasks in memory files. Add them to the task file directly.

Pattern: Loading Project Context

At the start of a session involving a project:

  1. cat PROJECTS.md — identify the project slug and status
  2. cat tasks/<slug>-tasks.md — load current task state
  3. cat plans/<slug>-plan.md — load architecture context (if it exists)
  4. Begin work. Record task ID references in memory at session end.

Periodic Sync Daemon

The sync daemon runs task-sync.mjs files-to-db every 60 seconds in the background. This means markdown edits are automatically reflected in SQLite within a minute.

  • Logs: logs/taskflow-sync.stdout.log and logs/taskflow-sync.stderr.log (relative to workspace)
  • Lock: Advisory TTL lock in sync_state table prevents concurrent syncs
  • Conflict resolution: Last-write-wins per sync direction

Quickest install (auto-detects OS)

taskflow install-daemon

This detects your platform and installs the appropriate unit. On macOS it installs and loads the LaunchAgent; on Linux it writes systemd user units and enables the timer.

macOS — LaunchAgent (manual steps)

Templates: taskflow/system/com.taskflow.sync.plist.xml

  1. Copy taskflow/system/com.taskflow.sync.plist.xml~/Library/LaunchAgents/com.taskflow.sync.plist
  2. Replace {{workspace}} with the absolute path to your workspace (no trailing slash)
  3. Replace {{node}} with the path to your node binary (which node)
  4. Load: launchctl load ~/Library/LaunchAgents/com.taskflow.sync.plist
  5. Verify: launchctl list | grep taskflow

Uninstall:

launchctl unload ~/Library/LaunchAgents/com.taskflow.sync.plist
rm ~/Library/LaunchAgents/com.taskflow.sync.plist

Linux — systemd user timer (manual steps)

Templates: taskflow/system/taskflow-sync.service and taskflow/system/taskflow-sync.timer

# Create the user unit directory
mkdir -p ~/.config/systemd/user

# Copy templates, replacing placeholders
sed -e "s|{{workspace}}|$OPENCLAW_WORKSPACE|g" \
    -e "s|{{node}}|$(which node)|g" \
    taskflow/system/taskflow-sync.service > ~/.config/systemd/user/taskflow-sync.service

sed -e "s|{{workspace}}|$OPENCLAW_WORKSPACE|g" \
    -e "s|{{node}}|$(which node)|g" \
    taskflow/system/taskflow-sync.timer  > ~/.config/systemd/user/taskflow-sync.timer

# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now taskflow-sync.timer

Verify:

systemctl --user status taskflow-sync.timer
journalctl --user -u taskflow-sync.service

Uninstall:

systemctl --user disable --now taskflow-sync.timer
rm ~/.config/systemd/user/taskflow-sync.{service,timer}
systemctl --user daemon-reload

Note: systemd user units require a login session. To run them without an interactive session (e.g. on a server), enable lingering: loginctl enable-linger $USER


Section Header → DB Status Map

Markdown Header DB status value
## In Progress in_progress
## Pending Validation pending_validation
## Backlog backlog
## Blocked blocked
## Done done

Section headers are fixed. Do not rename them. The sync parser maps these exact strings.


Known Quirks

Things that work but might trip you up:

  • MAX(id) is lexicographic. Task IDs are text, so SELECT MAX(id) works only because IDs are zero-padded (-001, -002). If you create -1 instead of -001, sequencing breaks. Always zero-pad to 3 digits.
  • Checkbox state is decorative. Status comes from which ## section a task lives under, not whether it's [x] or [ ]. The sync engine ignores the checkbox on read. On write-back, done tasks get [x], everything else gets [ ].
  • Notes survive deletion. If you remove a - note: line from markdown, the old note stays in the DB (COALESCE preserves it). This is intentional for v1 -- notes are one-way display. To truly clear a note, update the DB directly.
  • Lock TTL is 60 seconds. If a sync crashes without releasing the lock, the next run will be blocked for up to 60s. The SIGTERM/SIGINT handlers try to clean up, but a kill -9 won't. The lock auto-expires.
  • Auto-project creation derives names from slugs. If sync encounters a task file with no matching projects row, it creates one with a name like "My Project" from slug "my-project". The name might not be what you want -- fix it in PROJECTS.md and re-sync.
  • Tag order is strict. [P1] [codex] works. [codex] [P1] silently assigns codex as... nothing useful. Priority tag must come first.

Known Limitations (v1)

  • Notes are one-way (markdown → DB). Removing a note in markdown does not clear it in DB.
  • db-to-files rewrites all project task files, even unchanged ones.
  • One task file per project (1:1 mapping). Multiple files per project is post-MVP.
  • Periodic sync daemon: macOS (LaunchAgent) and Linux (systemd user timer) are supported. Run taskflow install-daemon to install.
  • Node.js 22.5+ required (node:sqlite). No Python fallback in v1.

Quick Cheat Sheet

New project:   PROJECTS.md block + tasks/<slug>-tasks.md + optional plans/<slug>-plan.md
New task:      taskflow add <project> "title" (or append manually to section)
Update status: Move line to correct ## section, flip checkbox if needed
Query simple:  cat tasks/<slug>-tasks.md
Query complex: Use db.prepare('SELECT ... WHERE id = ?').all(id) — never interpolate variables into SQL
CLI status:    taskflow status
CLI add:       taskflow add dashboard "Fix cron panel" --priority P1 --owner codex
Force sync:    node taskflow/scripts/task-sync.mjs files-to-db
Memory rule:   Reference IDs in logs; never copy backlog into memory

同梱ファイル

※ ZIPに含まれるファイル一覧。`SKILL.md` 本体に加え、参考資料・サンプル・スクリプトが入っている場合があります。