Initial commit: OpenClaw Skill Collection

6 custom skills (assign-task, dispatch-webhook, daily-briefing,
task-capture, qmd-brain, tts-voice) with technical documentation.
Compatible with Claude Code, OpenClaw, Codex CLI, and OpenCode.
This commit is contained in:
2026-03-13 10:58:30 +08:00
commit 4c966a3ad2
884 changed files with 140761 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
---
name: task-capture
description: 快速將使用者說的事情記錄到 TODO.md支援優先級、標籤、截止日。
triggers:
- "記住"
- "記一下"
- "待辦"
- "todo"
- "提醒我"
- "別忘了"
- "加到清單"
tools:
- exec
- memory
---
# Task Capture Skill
## 功能說明
快速捕捉待辦事項,記錄到 `workspace/TODO.md`,支援:
- 優先級標記(🔴 緊急 / 🟡 一般 / 🟢 低優先)
- 標籤(#工作 #個人 #專案
- 截止日期(`due: 2026-02-25`
- 自動分類
## 使用方式
```
使用者:「記一下要 review PR #42明天下班前」
Agent 記錄:- [ ] 🟡 review PR #42 due:2026-02-21 #工作
回覆:「✅ 已記錄review PR #42截止明天下班前
```
## TODO.md 格式
```markdown
# TODO
## 🔴 緊急
- [ ] 修復 production 的 nginx 錯誤 due:2026-02-20 #緊急
## 🟡 進行中
- [ ] 完成 CLIProxyAPI 文件 due:2026-02-22 #工作
- [ ] 回覆客戶報告 #工作
## 🟢 稍後
- [ ] 整理 skill 庫 #個人
- [ ] 看 OpenClaw 官方文件 #學習
## ✅ 已完成
- [x] 安裝 OpenClaw ✅ 2026-02-19
- [x] 設定 Telegram Bot ✅ 2026-02-19
```
## 相關指令
```
使用者:「顯示我的待辦」 → 列出 TODO.md 未完成項目
使用者:「完成第一項」 → 將第一項標為 [x]
使用者:「清除已完成」 → 移除 [x] 項目
```

View File

@@ -0,0 +1,123 @@
/**
* task-capture skill
* 快速記錄待辦事項到 TODO.md
*/
import { readFileSync, writeFileSync, existsSync } from 'fs';
import { join } from 'path';
function getDateStr(): string {
const now = new Date();
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
}
function detectPriority(text: string): { emoji: string; label: string } {
const lower = text.toLowerCase();
if (lower.includes('緊急') || lower.includes('urgent') || lower.includes('asap') || lower.includes('立刻')) {
return { emoji: '🔴', label: '緊急' };
}
if (lower.includes('之後') || lower.includes('有空') || lower.includes('低優先') || lower.includes('不急')) {
return { emoji: '🟢', label: '低優先' };
}
return { emoji: '🟡', label: '一般' };
}
function detectDueDate(text: string): string | null {
// 明天
if (text.includes('明天')) {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return `due:${tomorrow.getFullYear()}-${String(tomorrow.getMonth() + 1).padStart(2, '0')}-${String(tomorrow.getDate()).padStart(2, '0')}`;
}
// 下週
if (text.includes('下週') || text.includes('下周')) {
const next = new Date();
next.setDate(next.getDate() + 7);
return `due:${next.getFullYear()}-${String(next.getMonth() + 1).padStart(2, '0')}-${String(next.getDate()).padStart(2, '0')}`;
}
// 明確日期 YYYY-MM-DD
const match = text.match(/(\d{4}-\d{2}-\d{2})/);
if (match) return `due:${match[1]}`;
return null;
}
function detectTags(text: string): string[] {
const tags: string[] = [];
if (text.match(/專案|project|開發|dev|code|PR|review/i)) tags.push('#工作');
if (text.match(/個人|私人|自己|家/)) tags.push('#個人');
if (text.match(/學習|看|讀|研究|study/i)) tags.push('#學習');
// 保留原有 #tag
const existing = text.match(/#[\w\u4e00-\u9fa5]+/g) || [];
tags.push(...existing);
return [...new Set(tags)];
}
function cleanTaskText(text: string): string {
return text
.replace(/^(記住|記一下|待辦|todo|提醒我|別忘了|加到清單)[:]?\s*/i, '')
.replace(/(明天|下週|下周|緊急|urgent|asap)/gi, '')
.replace(/#[\w\u4e00-\u9fa5]+/g, '')
.replace(/\s+/g, ' ')
.trim();
}
export async function handler(ctx: any) {
const workspace = ctx.env?.OPENCLAW_WORKSPACE || process.env.HOME + '/.openclaw/workspace';
const message = ctx.message?.text || ctx.message?.content || '';
const todoPath = join(workspace, 'TODO.md');
if (!message) {
return { reply: '❌ 請告訴我要記錄什麼。' };
}
const taskText = cleanTaskText(message);
if (!taskText) {
return { reply: '❌ 無法識別待辦內容,請再說清楚一點。' };
}
const priority = detectPriority(message);
const dueDate = detectDueDate(message);
const tags = detectTags(message);
// 組裝 TODO 項目
const parts = [`- [ ] ${priority.emoji} ${taskText}`];
if (dueDate) parts.push(dueDate);
if (tags.length > 0) parts.push(tags.join(' '));
const todoLine = parts.join(' ');
// 讀取或建立 TODO.md
let content = '';
if (existsSync(todoPath)) {
content = readFileSync(todoPath, 'utf-8');
} else {
content = `# TODO\n\n## 🔴 緊急\n\n## 🟡 進行中\n\n## 🟢 稍後\n\n## ✅ 已完成\n`;
}
// 插入到對應優先級區段
const sectionMap: Record<string, string> = {
'緊急': '## 🔴 緊急',
'一般': '## 🟡 進行中',
'低優先': '## 🟢 稍後',
};
const section = sectionMap[priority.label];
if (content.includes(section)) {
content = content.replace(section, `${section}\n${todoLine}`);
} else {
content += `\n${todoLine}\n`;
}
writeFileSync(todoPath, content, 'utf-8');
const dueDateDisplay = dueDate ? `(截止:${dueDate.replace('due:', '')}` : '';
return {
reply: `✅ 已記錄
${priority.emoji} **${taskText}**${dueDateDisplay}
${tags.length > 0 ? `標籤:${tags.join(' ')}` : ''}
輸入「顯示我的待辦」查看完整清單。`,
metadata: { taskText, priority: priority.label, dueDate, tags },
};
}