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.
95 lines
2.9 KiB
TypeScript
95 lines
2.9 KiB
TypeScript
/**
|
||
* 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 },
|
||
};
|
||
}
|