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:
73
skills/assign-task/SKILL.md
Normal file
73
skills/assign-task/SKILL.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
name: assign-task
|
||||
description: 分析使用者任務,判斷類型,分派給對應的 VPS(Claude Code 或 OpenCode),並追蹤執行狀態。
|
||||
triggers:
|
||||
- "幫我開發"
|
||||
- "建立專案"
|
||||
- "寫程式"
|
||||
- "實作功能"
|
||||
- "分派任務"
|
||||
- "assign task"
|
||||
- "新任務"
|
||||
tools:
|
||||
- web_fetch
|
||||
- exec
|
||||
- memory
|
||||
---
|
||||
|
||||
# Assign Task Skill
|
||||
|
||||
## 功能說明
|
||||
|
||||
分析使用者描述的任務,自動判斷應分派給哪個 VPS,呼叫 `dispatch-webhook` 執行,並回報狀態。
|
||||
|
||||
## 分派規則
|
||||
|
||||
| 任務類型 | 目標 | 說明 |
|
||||
|---------|------|------|
|
||||
| 主要專案開發 | VPS-A (Claude Code) | 核心業務邏輯、架構設計 |
|
||||
| 其他專案 / 實驗性功能 | VPS-B (OpenCode) | 輔助工具、腳本、研究 |
|
||||
| 文件撰寫 | VPS-B | README、技術文件 |
|
||||
| Code Review | VPS-A | 需要 Claude Opus 審查 |
|
||||
|
||||
## 執行流程
|
||||
|
||||
```
|
||||
1. 解析使用者意圖(任務類型、優先級、專案名稱)
|
||||
2. 判斷分派目標(VPS-A or VPS-B)
|
||||
3. 呼叫 dispatch-webhook skill 發送任務
|
||||
4. 等待回應(async 模式下記錄 task_id)
|
||||
5. 回報使用者:「✅ 任務已分派給 VPS-A,task_id: xxx」
|
||||
```
|
||||
|
||||
## 輸入格式
|
||||
|
||||
使用者自然語言描述,例如:
|
||||
- 「幫我開發一個 Python API 來處理 webhook」
|
||||
- 「建立一個 React 前端,連接現有的後端 API」
|
||||
|
||||
## 輸出格式
|
||||
|
||||
```
|
||||
✅ 任務已分派
|
||||
|
||||
📋 任務摘要:[任務描述摘要]
|
||||
🎯 分派目標:VPS-A / VPS-B
|
||||
🤖 執行模型:Claude Code Opus / OpenCode Codex
|
||||
🆔 Task ID:[uuid]
|
||||
⏱️ 預計完成:[估計時間]
|
||||
|
||||
執行中,完成後會通知你。
|
||||
```
|
||||
|
||||
## 設定(環境變數)
|
||||
|
||||
在 `~/.openclaw/workspace/.env` 或 openclaw.json env 區塊設定:
|
||||
|
||||
```
|
||||
VPS_A_WEBHOOK_URL=https://vps-a.example.com/webhook/openclaw
|
||||
VPS_A_WEBHOOK_TOKEN=<shared-secret>
|
||||
VPS_B_WEBHOOK_URL=https://vps-b.example.com/webhook/openclaw
|
||||
VPS_B_WEBHOOK_TOKEN=<shared-secret>
|
||||
CALLBACK_BASE_URL=https://oclaw.nature.edu.kg/webhook/callback
|
||||
```
|
||||
94
skills/assign-task/handler.ts
Normal file
94
skills/assign-task/handler.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* assign-task skill
|
||||
* 分析任務,判斷目標 VPS,呼叫 dispatch-webhook 執行
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
// 任務分派規則
|
||||
const DISPATCH_RULES = [
|
||||
{
|
||||
target: 'vps-a',
|
||||
label: 'VPS-A (Claude Code)',
|
||||
model: 'Claude Code Opus',
|
||||
keywords: ['主要專案', '核心功能', '架構', 'code review', 'review', '審查', 'API', '後端', 'backend'],
|
||||
},
|
||||
{
|
||||
target: 'vps-b',
|
||||
label: 'VPS-B (OpenCode)',
|
||||
model: 'OpenCode Codex',
|
||||
keywords: ['腳本', 'script', '工具', 'tool', '文件', 'doc', 'readme', '實驗', '前端', 'frontend', 'react', 'vue'],
|
||||
},
|
||||
];
|
||||
|
||||
function determineTarget(description: string): typeof DISPATCH_RULES[0] {
|
||||
const lower = description.toLowerCase();
|
||||
for (const rule of DISPATCH_RULES) {
|
||||
if (rule.keywords.some(k => lower.includes(k.toLowerCase()))) {
|
||||
return rule;
|
||||
}
|
||||
}
|
||||
// 預設分派到 VPS-A
|
||||
return DISPATCH_RULES[0];
|
||||
}
|
||||
|
||||
function determinePriority(description: string): string {
|
||||
const lower = description.toLowerCase();
|
||||
if (lower.includes('緊急') || lower.includes('urgent') || lower.includes('asap')) return 'high';
|
||||
if (lower.includes('之後') || lower.includes('有空') || lower.includes('低優先')) return 'low';
|
||||
return 'normal';
|
||||
}
|
||||
|
||||
export async function handler(ctx: any) {
|
||||
const { message, env, callSkill } = ctx;
|
||||
const description = message.text || message.content || '';
|
||||
|
||||
if (!description) {
|
||||
return { reply: '❌ 請描述你想要執行的任務。' };
|
||||
}
|
||||
|
||||
const taskId = randomUUID();
|
||||
const target = determineTarget(description);
|
||||
const priority = determinePriority(description);
|
||||
|
||||
// 建立任務 payload
|
||||
const task = {
|
||||
task_id: taskId,
|
||||
type: 'project_development',
|
||||
description: description.slice(0, 2000),
|
||||
priority,
|
||||
target: target.target,
|
||||
callback_url: `${env.CALLBACK_BASE_URL || 'https://oclaw.nature.edu.kg/webhook/callback'}`,
|
||||
created_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// 呼叫 dispatch-webhook skill
|
||||
let dispatchResult;
|
||||
try {
|
||||
dispatchResult = await callSkill('dispatch-webhook', {
|
||||
target: target.target,
|
||||
payload: task,
|
||||
webhookUrl: target.target === 'vps-a' ? env.VPS_A_WEBHOOK_URL : env.VPS_B_WEBHOOK_URL,
|
||||
webhookToken: target.target === 'vps-a' ? env.VPS_A_WEBHOOK_TOKEN : env.VPS_B_WEBHOOK_TOKEN,
|
||||
});
|
||||
} catch (err: any) {
|
||||
return {
|
||||
reply: `❌ Webhook 發送失敗:${err.message}\n\nTask ID: \`${taskId}\`(可稍後重試)`,
|
||||
};
|
||||
}
|
||||
|
||||
const priorityLabel = { high: '🔴 緊急', normal: '🟡 一般', low: '🟢 低優先' }[priority] || priority;
|
||||
|
||||
return {
|
||||
reply: `✅ 任務已分派
|
||||
|
||||
📋 **任務摘要**:${description.slice(0, 100)}${description.length > 100 ? '...' : ''}
|
||||
🎯 **分派目標**:${target.label}
|
||||
🤖 **執行模型**:${target.model}
|
||||
⚡ **優先級**:${priorityLabel}
|
||||
🆔 **Task ID**:\`${taskId}\`
|
||||
|
||||
執行中,完成後會通知你。`,
|
||||
metadata: { taskId, target: target.target, priority },
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user