feat(v0.5.0): weekly update 2026-03-16 — 48 new skills (total 387)

This commit is contained in:
MyClaw AI
2026-03-23 02:03:45 +00:00
parent 43d9a8c788
commit 708f572924
496 changed files with 83579 additions and 10 deletions
@@ -0,0 +1,7 @@
{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "openclaw-todoist",
"installedVersion": "1.2.0",
"installedAt": 1773626662009
}
+121
View File
@@ -0,0 +1,121 @@
---
name: todoist
description: "Todoist task management for OpenClaw. Unified todo API with multi-agent identity, scheduled checks and reminders."
metadata:
openclaw:
emoji: "✅"
requires: {}
---
# Todoist Skill
为 OpenClaw 提供统一的待办管理能力,支持多 Agent 身份识别。
## 身份系统
每个 OpenClaw 实例有唯一 ID,每个 Agent 有独立标签:
```
实例 ID: 8259c9d1 (自动生成)
Agent 标签: agent-8259c9d1-main
```
## 命令
| 命令 | 说明 |
|------|------|
| `list [filter]` | 列出任务 (today/personal/agent/overdue) |
| `add <内容> [type] [日期]` | 添加任务 |
| `subtask <父任务> <内容>` | 添加子任务 |
| `show <关键词>` | 查看任务详情 |
| `update <任务> <字段> <值>` | 更新任务 |
| `claim <任务>` | 认领任务 |
| `complete <任务>` | 完成任务 |
| `delete <任务>` | 删除任务 |
| `projects` | 列出项目 |
| `labels` | 列出标签 |
## 配置
| 命令 | 说明 |
|------|------|
| `show` | 显示配置 |
| `set-time HH:MM` | 设置每日提醒时间 |
| `set-interval N` | 设置心跳检查间隔(小时) |
| `set-agent <name>` | 切换当前 Agent |
| `add-agent <name>` | 添加新 Agent |
## 🔄 自动同步任务到 TASK.md
心跳时会自动同步 Todoist 任务到 `~/.openclaw/workspace/TASK.md`
```bash
~/.openclaw/workspace/skills/openclaw-todoist/scripts/sync-to-task.sh
```
生成的 TASK.md 格式:
```markdown
# 当前任务
_自动同步自 Todoist (2026-03-16 10:00)_
## ⚠️ 逾期任务
- [ ] 任务名 (逾期: 2026-03-15)
## 📅 今日任务
- [ ] 任务名
## 📌 待办(无日期)
- [ ] 任务名
```
### 心跳配置
`HEARTBEAT.md` 中添加:
```markdown
# 心跳任务
## 每次心跳自动执行
1. **同步 Todoist 任务到 TASK.md**
```bash
~/.openclaw/workspace/skills/openclaw-todoist/scripts/sync-to-task.sh
```
## 静默条件
- 无任务时回复 HEARTBEAT_OK
- 有逾期任务时主动提醒
```
### 一键配置
```bash
~/.openclaw/workspace/skills/openclaw-todoist/scripts/setup-heartbeat.sh
```
### 提醒逻辑
- ✅ 自动同步所有任务到 TASK.md
- ⚠️ **仅逾期任务**时主动提醒用户
- 无任务或无逾期 → 静默(HEARTBEAT_OK
## 发布内容
```
skills/todoist/
├── SKILL.md
└── todoist.sh
scripts/
├── agent-config.sh
└── heartbeat-tasks.sh
```
## 用户配置文件(不包含在发布中)
```
~/.openclaw/workspace/
├── .todoist-token # 用户 API token
└── .agent-identity.json # 用户身份配置
```
+6
View File
@@ -0,0 +1,6 @@
{
"ownerId": "kn7ehs23ek43b5tw6yt3hy35rh82w875",
"slug": "openclaw-todoist",
"version": "1.2.0",
"publishedAt": 1773626429583
}
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
# Agent Identity Configuration Tool
IDENTITY_FILE="$HOME/.openclaw/workspace/.agent-identity.json"
# Ensure file exists
if [ ! -f "$IDENTITY_FILE" ]; then
INSTANCE_ID=$(hostname | shasum -a 256 | cut -c1-8)
cat > "$IDENTITY_FILE" << EOF
{
"instance_id": "$INSTANCE_ID",
"instance_name": "$(hostname)",
"user": "$(whoami)",
"current_agent": "main",
"todoist": {
"daily_reminder_time": "09:00",
"heartbeat_check_interval_hours": 4
},
"agents": {
"main": {
"name": "main",
"full_id": "$INSTANCE_ID:main",
"todoist_label": "agent-$INSTANCE_ID-main"
}
}
}
EOF
echo "✅ 已创建身份配置: $IDENTITY_FILE"
fi
show_config() {
echo "📋 当前配置:"
echo ""
cat "$IDENTITY_FILE" | jq -C '.'
}
set_reminder_time() {
local time="$1"
if [[ ! "$time" =~ ^[0-2][0-9]:[0-5][0-9]$ ]]; then
echo "❌ 时间格式错误,应为 HH:MM (如 09:00)"
exit 1
fi
cat "$IDENTITY_FILE" | jq ".todoist.daily_reminder_time = \"$time\"" > /tmp/identity.tmp
mv /tmp/identity.tmp "$IDENTITY_FILE"
echo "✅ 每日提醒时间已设置为: $time"
}
set_heartbeat_interval() {
local hours="$1"
if [[ ! "$hours" =~ ^[0-9]+$ ]]; then
echo "❌ 间隔应为小时数 (如 4)"
exit 1
fi
cat "$IDENTITY_FILE" | jq ".todoist.heartbeat_check_interval_hours = $hours" > /tmp/identity.tmp
mv /tmp/identity.tmp "$IDENTITY_FILE"
echo "✅ 心跳检查间隔已设置为: ${hours} 小时"
}
set_current_agent() {
local agent="$1"
# Check if agent exists
if ! cat "$IDENTITY_FILE" | jq -e ".agents.$agent" > /dev/null 2>&1; then
echo "❌ Agent 不存在: $agent"
echo "可用 agents: $(cat "$IDENTITY_FILE" | jq -r '.agents | keys[]' | tr '\n' ' ')"
exit 1
fi
cat "$IDENTITY_FILE" | jq ".current_agent = \"$agent\"" > /tmp/identity.tmp
mv /tmp/identity.tmp "$IDENTITY_FILE"
echo "✅ 当前 Agent 已切换为: $agent"
}
add_agent() {
local name="$1"
local instance_id=$(cat "$IDENTITY_FILE" | jq -r '.instance_id')
local label="agent-$instance_id-$name"
cat "$IDENTITY_FILE" | jq ".agents.$name = {\"name\": \"$name\", \"full_id\": \"$instance_id:$name\", \"todoist_label\": \"$label\"}" > /tmp/identity.tmp
mv /tmp/identity.tmp "$IDENTITY_FILE"
echo "✅ 已添加 Agent: $name (标签: $label)"
# Create label in Todoist
TOKEN=$(cat ~/.openclaw/workspace/.todoist-token 2>/dev/null)
if [ -n "$TOKEN" ]; then
curl -s -X POST "https://api.todoist.com/api/v1/labels" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\": \"$label\"}" > /dev/null 2>&1
echo " 已在 Todoist 创建标签"
fi
}
case "${1:-show}" in
show|config)
show_config
;;
set-time)
set_reminder_time "$2"
;;
set-interval)
set_heartbeat_interval "$2"
;;
set-agent)
set_current_agent "$2"
;;
add-agent)
add_agent "$2"
;;
*)
echo "用法: agent-config.sh <command> [args]"
echo ""
echo "Commands:"
echo " show 显示当前配置"
echo " set-time HH:MM 设置每日提醒时间"
echo " set-interval N 设置心跳检查间隔(小时)"
echo " set-agent <name> 切换当前 Agent"
echo " add-agent <name> 添加新 Agent"
;;
esac
@@ -0,0 +1,66 @@
#!/bin/bash
# Heartbeat task check - runs periodically
IDENTITY_FILE="$HOME/.openclaw/workspace/.agent-identity.json"
TOKEN_FILE="$HOME/.openclaw/workspace/.todoist-token"
LOG_FILE="$HOME/.openclaw/workspace/memory/heartbeat-tasks.log"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') $1" | tee -a "$LOG_FILE"
}
# Check if enabled
if [ ! -f "$TOKEN_FILE" ]; then
log "⚠️ Todoist token not found"
exit 0
fi
TOKEN=$(cat "$TOKEN_FILE")
TODAY=$(date +%Y-%m-%d)
# Get identity
INSTANCE_ID=$(cat "$IDENTITY_FILE" | jq -r '.instance_id')
CURRENT_AGENT=$(cat "$IDENTITY_FILE" | jq -r '.current_agent')
AGENT_LABEL=$(cat "$IDENTITY_FILE" | jq -r ".agents.$CURRENT_AGENT.todoist_label")
log "🔍 检查任务 (实例: $INSTANCE_ID, Agent: $CURRENT_AGENT)"
# Get tasks due today
TODAY_TASKS=$(curl -s "https://api.todoist.com/api/v1/tasks" \
-H "Authorization: Bearer $TOKEN" | \
jq -r ".results[] | select(.due.date == \"$TODAY\") | .content")
if [ -n "$TODAY_TASKS" ]; then
log "📅 今日任务:"
echo "$TODAY_TASKS" | while read task; do
log "$task"
done
else
log "✅ 今日无待办任务"
fi
# Get agent-specific tasks
AGENT_TASKS=$(curl -s "https://api.todoist.com/api/v1/tasks" \
-H "Authorization: Bearer $TOKEN" | \
jq -r ".results[] | select(.labels | index(\"$AGENT_LABEL\")) | .content")
if [ -n "$AGENT_TASKS" ]; then
log ""
log "🤖 Agent 任务 ($CURRENT_AGENT):"
echo "$AGENT_TASKS" | while read task; do
log "$task"
done
fi
# Get overdue tasks
OVERDUE=$(curl -s "https://api.todoist.com/api/v1/tasks" \
-H "Authorization: Bearer $TOKEN" | \
jq -r ".results[] | select(.due.date < \"$TODAY\") | .content")
if [ -n "$OVERDUE" ]; then
log ""
log "⚠️ 逾期任务:"
echo "$OVERDUE" | while read task; do
log " 🔴 $task"
done
fi
@@ -0,0 +1,52 @@
#!/bin/bash
# Setup Todoist heartbeat checks in HEARTBEAT.md
HEARTBEAT_FILE="$HOME/.openclaw/workspace/HEARTBEAT.md"
TODOIST_CHECK='~/.openclaw/workspace/skills/todoist/todoist.sh'
# Check if already configured
if grep -q "todoist.sh list" "$HEARTBEAT_FILE" 2>/dev/null; then
echo "✅ Todoist 心跳检查已配置"
exit 0
fi
# Create or append to HEARTBEAT.md
if [ ! -f "$HEARTBEAT_FILE" ]; then
cat > "$HEARTBEAT_FILE" << 'EOF'
# 心跳任务
## 每次心跳检查
1. **Todoist 任务**
- 检查今日任务
- 检查过期任务
- 有紧急事项时提醒用户
```bash
~/.openclaw/workspace/skills/todoist/todoist.sh list today
~/.openclaw/workspace/skills/todoist/todoist.sh list overdue
```
## 静默条件
- 无任务时回复 HEARTBEAT_OK
- 有过期任务或重要截止日期时主动提醒
EOF
echo "✅ 已创建 HEARTBEAT.md 并配置 Todoist 心跳检查"
else
# Append to existing file
cat >> "$HEARTBEAT_FILE" << 'EOF'
## Todoist 任务检查
```bash
~/.openclaw/workspace/skills/todoist/todoist.sh list today
~/.openclaw/workspace/skills/todoist/todoist.sh list overdue
```
有紧急事项时主动提醒,否则静默。
EOF
echo "✅ 已添加 Todoist 心跳检查到现有 HEARTBEAT.md"
fi
echo ""
echo "📌 下次心跳时会自动检查 Todoist 任务"
@@ -0,0 +1,79 @@
#!/bin/bash
# Sync Todoist tasks to TASK.md
# Called by heartbeat to keep tasks in sync
set -e
TASK_FILE="$HOME/.openclaw/workspace/TASK.md"
TODOIST_SCRIPT="$HOME/.openclaw/workspace/skills/openclaw-todoist/todoist.sh"
TOKEN_FILE="$HOME/.openclaw/workspace/.todoist-token"
# Skip if Todoist not configured
[ ! -f "$TOKEN_FILE" ] && exit 0
# Get tasks from Todoist API
TOKEN=$(cat "$TOKEN_FILE")
API_BASE="https://api.todoist.com/api/v1"
# Fetch today's tasks and overdue tasks
today=$(date +%Y-%m-%d)
all_tasks=$(curl -s "$API_BASE/tasks" -H "Authorization: Bearer $TOKEN")
today_tasks=$(echo "$all_tasks" | jq -r ".results[] | select(.due.date == \"$today\") | .content" 2>/dev/null || true)
overdue_tasks=$(echo "$all_tasks" | jq -r ".results[] | select(.due.date < \"$today\" and .due.date != null) | \"\(.content) (逾期: \(.due.date))\"" 2>/dev/null || true)
no_due_tasks=$(echo "$all_tasks" | jq -r ".results[] | select(.due.date == null) | .content" 2>/dev/null || true)
# Check if we have any tasks
has_today=$(echo "$today_tasks" | grep -v '^$' | head -1 || true)
has_overdue=$(echo "$overdue_tasks" | grep -v '^$' | head -1 || true)
has_nodue=$(echo "$no_due_tasks" | grep -v '^$' | head -1 || true)
# No tasks at all - remove TASK.md if exists
if [ -z "$has_today" ] && [ -z "$has_overdue" ] && [ -z "$has_nodue" ]; then
[ -f "$TASK_FILE" ] && rm "$TASK_FILE"
exit 0
fi
# Build TASK.md content
{
echo "# 当前任务"
echo ""
echo "_自动同步自 Todoist ($(date '+%Y-%m-%d %H:%M'))_"
echo ""
if [ -n "$has_overdue" ]; then
echo "## ⚠️ 逾期任务"
echo ""
echo "$overdue_tasks" | while read -r line; do
[ -n "$line" ] && echo "- [ ] $line"
done
echo ""
fi
if [ -n "$has_today" ]; then
echo "## 📅 今日任务"
echo ""
echo "$today_tasks" | while read -r line; do
[ -n "$line" ] && echo "- [ ] $line"
done
echo ""
fi
if [ -n "$has_nodue" ]; then
echo "## 📌 待办(无日期)"
echo ""
echo "$no_due_tasks" | while read -r line; do
[ -n "$line" ] && echo "- [ ] $line"
done
echo ""
fi
echo "---"
echo "完成或修改任务请使用 Todoist 命令或 App"
} > "$TASK_FILE"
# Return status for heartbeat
if [ -n "$has_overdue" ]; then
echo "⚠️ 有 $(echo "$overdue_tasks" | grep -v '^$' | wc -l | tr -d ' ') 个逾期任务"
exit 0
fi
+7
View File
@@ -0,0 +1,7 @@
{
"name": "todoist",
"version": "1.0.0",
"description": "Todoist task management for OpenClaw with multi-agent identity support",
"author": "kings0527",
"license": "MIT"
}
+210
View File
@@ -0,0 +1,210 @@
#!/bin/bash
# Todoist CLI wrapper for OpenClaw
set -e
TOKEN_FILE="$HOME/.openclaw/workspace/.todoist-token"
IDENTITY_FILE="$HOME/.openclaw/workspace/.agent-identity.json"
API_BASE="https://api.todoist.com/api/v1"
# Load token
if [ ! -f "$TOKEN_FILE" ]; then
echo "Token not found: $TOKEN_FILE"
exit 1
fi
TOKEN=$(cat "$TOKEN_FILE")
# Load identity
load_identity() {
if [ -f "$IDENTITY_FILE" ]; then
INSTANCE_ID=$(jq -r .instance_id "$IDENTITY_FILE")
CURRENT_AGENT=$(jq -r .current_agent "$IDENTITY_FILE")
AGENT_LABEL=$(jq -r ".agents.$CURRENT_AGENT.todoist_label" "$IDENTITY_FILE")
else
INSTANCE_ID=$(hostname | shasum -a 256 | cut -c1-8)
CURRENT_AGENT="main"
AGENT_LABEL="agent-$INSTANCE_ID-main"
fi
}
# API helpers
api_get() { curl -s "$API_BASE/$1" -H "Authorization: Bearer $TOKEN"; }
api_post() { curl -s -X POST "$API_BASE/$1" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$2"; }
get_task_id() {
api_get "tasks" | jq -r ".results[] | select(.content | test(\"$1\"; \"i\")) | .id" | head -1
}
get_project_id() {
api_get "projects" | jq -r ".results[] | select(.name | contains(\"$1\")) | .id" | head -1
}
# Commands
cmd_list() {
local filter="${1:-all}"
load_identity
echo "Tasks (Instance: $INSTANCE_ID, Agent: $CURRENT_AGENT)"
echo ""
local tasks=$(api_get "tasks")
local today=$(date +%Y-%m-%d)
case "$filter" in
today)
echo "Today:"
echo "$tasks" | jq -r ".results[] | select(.due.date == \"$today\") | \" [ ] \(.content)\""
;;
overdue)
echo "Overdue:"
echo "$tasks" | jq -r ".results[] | select(.due.date < \"$today\") | \" [!] \(.content) (\(.due.date))\""
;;
personal)
local pid=$(get_project_id "个人事务")
echo "Personal:"
echo "$tasks" | jq -r ".results[] | select(.project_id == \"$pid\") | \" [ ] \(.content)\""
;;
agent)
echo "Agent ($CURRENT_AGENT):"
echo "$tasks" | jq -r ".results[] | select(.labels | index(\"$AGENT_LABEL\")) | \" [ ] \(.content)\""
;;
*)
local pid=$(get_project_id "个人事务")
echo "Personal:"
echo "$tasks" | jq -r ".results[] | select(.project_id == \"$pid\") | \" [ ] \(.content)\""
echo ""
echo "Agent ($CURRENT_AGENT):"
echo "$tasks" | jq -r ".results[] | select(.labels | index(\"$AGENT_LABEL\")) | \" [ ] \(.content)\""
;;
esac
}
cmd_add() {
local content="$1"
local type="${2:-personal}"
local due="${3:-}"
load_identity
local pid
local labels="[]"
if [ "$type" = "agent" ]; then
pid=$(get_project_id "Agent 任务")
labels="[\"$AGENT_LABEL\"]"
echo "Adding Agent task [$CURRENT_AGENT]: $content"
else
pid=$(get_project_id "个人事务")
echo "Adding personal task: $content"
fi
local payload="{\"content\":\"$content\",\"project_id\":\"$pid\",\"labels\":$labels"
[ -n "$due" ] && payload="$payload,\"due_string\":\"$due\""
payload="$payload}"
api_post "tasks" "$payload" | jq -r '.content // "Added"'
}
cmd_subtask() {
local parent="$1"
local content="$2"
local pid=$(get_task_id "$parent")
[ -z "$pid" ] && { echo "Parent not found: $parent"; exit 1; }
echo "Adding subtask to: $parent"
api_post "tasks" "{\"content\":\"$content\",\"parent_id\":\"$pid\"}" | jq -r '.content // "Added"'
}
cmd_show() {
local q="$1"
local id=$(get_task_id "$q")
[ -z "$id" ] && { echo "Task not found: $q"; exit 1; }
local t=$(api_get "tasks" | jq -r ".results[] | select(.id == \"$id\")")
echo "Task: $(echo "$t" | jq -r .content)"
echo "Due: $(echo "$t" | jq -r '.due.string // "none"')"
echo "Labels: $(echo "$t" | jq -r '.labels | join(", ") // "none"')"
echo ""
echo "Subtasks:"
api_get "tasks" | jq -r ".results[] | select(.parent_id == \"$id\") | \" - \(.content)\""
}
cmd_update() {
local q="$1"
local field="$2"
local val="$3"
local id=$(get_task_id "$q")
[ -z "$id" ] && { echo "Task not found: $q"; exit 1; }
local payload
case "$field" in
date|due) payload="{\"due_string\":\"$val\"}" ;;
content) payload="{\"content\":\"$val\"}" ;;
priority) payload="{\"priority\":$val}" ;;
*) echo "Unknown field: $field"; exit 1 ;;
esac
curl -s -X POST "$API_BASE/tasks/$id" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "$payload" > /dev/null
echo "Updated: $q ($field = $val)"
}
cmd_claim() {
local q="$1"
load_identity
local id=$(get_task_id "$q")
[ -z "$id" ] && { echo "Task not found: $q"; exit 1; }
local t=$(api_get "tasks" | jq -r ".results[] | select(.id == \"$id\")")
local lbls=$(echo "$t" | jq -r '.labels')
local new=$(echo "[$lbls, \"$AGENT_LABEL\"]" | jq -s 'add | unique')
curl -s -X POST "$API_BASE/tasks/$id" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d "{\"labels\":$new}" > /dev/null
echo "Claimed: $(echo "$t" | jq -r .content) -> $CURRENT_AGENT"
}
cmd_complete() {
local q="$1"
local id=$(get_task_id "$q")
[ -z "$id" ] && { echo "Task not found: $q"; exit 1; }
local name=$(api_get "tasks" | jq -r ".results[] | select(.id == \"$id\") | .content")
api_post "tasks/$id/close" "{}" > /dev/null
echo "Completed: $name"
}
cmd_delete() {
local q="$1"
local id=$(get_task_id "$q")
[ -z "$id" ] && { echo "Task not found: $q"; exit 1; }
local name=$(api_get "tasks" | jq -r ".results[] | select(.id == \"$id\") | .content")
curl -s -X DELETE "$API_BASE/tasks/$id" -H "Authorization: Bearer $TOKEN" > /dev/null
echo "Deleted: $name"
}
cmd_projects() { api_get "projects" | jq -r '.results[] | " \(.name)"'; }
cmd_labels() { api_get "labels" | jq -r '.results[] | " \(.name)"'; }
cmd_config() { jq -C . "$IDENTITY_FILE"; }
# Main
case "${1:-help}" in
list|ls) cmd_list "$2" ;;
add) cmd_add "$2" "$3" "$4" ;;
subtask) cmd_subtask "$2" "$3" ;;
show) cmd_show "$2" ;;
update) cmd_update "$2" "$3" "$4" ;;
claim) cmd_claim "$2" ;;
complete|done) cmd_complete "$2" ;;
delete|rm) cmd_delete "$2" ;;
projects) cmd_projects ;;
labels) cmd_labels ;;
config) cmd_config ;;
*)
echo "Todoist CLI"
echo ""
echo "Commands:"
echo " list [today|personal|agent|overdue] List tasks"
echo " add <content> [type] [due] Add task"
echo " subtask <parent> <content> Add subtask"
echo " show <keyword> Show task details"
echo " update <task> <field> <value> Update task"
echo " claim <task> Claim task for current agent"
echo " complete <task> Complete task"
echo " delete <task> Delete task"
echo " projects List projects"
echo " labels List labels"
echo " config Show config"
;;
esac