
Feishu Task
- 2 installs
- 20 repo stars
- Updated April 11, 2026
- autogame-17/feishu-skills
feishu-task is a Claude skill that manages native Feishu (Lark) Tasks via the Open Platform API, creating, completing, and deleting tasks with due dates and assignees.
About
feishu-task manages native Feishu (Lark) Tasks through the Open Platform API. It creates tasks with a summary, description, due date, origin, and optional assignee, and can complete or delete tasks by id. Developers use it when an agent needs to create and manage collaborative tasks in Feishu. It targets Feishu's built-in Tasks feature rather than a Bitable.
- Manages native Feishu (Lark) Tasks via the Open Platform API
- Create tasks with summary, description, due date, origin, and assignee
- Complete and delete tasks by task_id
Feishu Task by the numbers
- 2 all-time installs (skills.sh)
- Ranked #2,409 of 3,280 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
feishu-task capabilities & compatibility
Free skill; requires Feishu Open Platform credentials (FEISHU_APP_ID / FEISHU_APP_SECRET).
- Capabilities
- feishu pm · feishu doc
- Works with
- notion
- Use cases
- project management
What feishu-task says it does
Manage Feishu (Lark) Tasks. Create tasks, set due dates, and assign users for collaboration.
A skill to manage Feishu (Lark) Tasks directly via the Open Platform API.
npx skills add https://github.com/autogame-17/feishu-skills --skill feishu-taskAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 20 |
| Last updated | April 11, 2026 |
| Repository | autogame-17/feishu-skills ↗ |
What it does
Create, complete, and delete native Feishu Tasks with due dates and assignees from the CLI.
Who is it for?
Creating and managing native Feishu Tasks with due dates and assignees from an agent.
Skip if: Task management in Jira, Asana, or non-Feishu tools, or record-based Bitable tracking.
When should I use this skill?
An agent needs to create, complete, or delete a native Feishu (Lark) task.
What you get
Native Feishu Tasks are created, assigned, completed, and deleted programmatically.
- A created, completed, or deleted native Feishu task
By the numbers
- 3 commands (create, complete, delete)
Files
feishu-task
A skill to manage Feishu (Lark) Tasks directly via the Open Platform API.
Usage
# Create a task
node skills/feishu-task/index.js create --summary "Review PR #123" --due "2023-12-31T18:00:00Z" --origin "OpenClaw"
# Complete a task
node skills/feishu-task/index.js complete --task_id "t_12345"
# Delete a task
node skills/feishu-task/index.js delete --task_id "t_12345"Options
create:--summary: Task title/content.--description: Detailed description (optional).--due: Due date/time (ISO 8601 or timestamp).--origin: Origin source description.--user_id: Assignee User ID (optional).complete:--task_id: The ID of the task to complete.delete:--task_id: The ID of the task to delete.
{
"ownerId": "kn7apafdj4thknczrgxdzfd2v1808svf",
"slug": "feishu-task",
"version": "1.0.0",
"publishedAt": 1770118141325
}{
"version": 1,
"registry": "https://clawhub.ai",
"slug": "feishu-task",
"installedVersion": "1.0.0",
"installedAt": 1770639849782
}
const { program } = require('commander');
const Lark = require('@larksuiteoapi/node-sdk');
require('dotenv').config({ path: require('path').resolve(__dirname, '../../.env') });
const APP_ID = process.env.FEISHU_APP_ID;
const APP_SECRET = process.env.FEISHU_APP_SECRET;
if (!APP_ID || !APP_SECRET) {
console.error('Error: FEISHU_APP_ID or FEISHU_APP_SECRET not set.');
process.exit(1);
}
const client = new Lark.Client({
appId: APP_ID,
appSecret: APP_SECRET,
});
program
.requiredOption('--summary <text>', 'Task Title')
.option('--desc <text>', 'Task Description')
.option('--content <text>', 'Task Description (alias)')
.option('--due <time>', 'Due time (YYYY-MM-DD HH:mm)')
.option('--assignees <ids>', 'Comma-separated OpenIDs of executors')
.option('--origin <text>', 'Origin info (optional)')
.parse(process.argv);
const options = program.opts();
// Alias mapping
if (options.content && !options.desc) options.desc = options.content;
async function createTask() {
try {
// 1. Create Task
const taskData = {
summary: options.summary,
description: options.desc || '',
origin: {
platform_i18n_name: JSON.stringify({ "zh_cn": "OpenClaw Assistant", "en_us": "OpenClaw Assistant" }),
href: {
url: options.origin || "https://open.feishu.cn",
title: options.summary
}
}
};
if (options.due) {
// Fix: Handle Timezone correctly.
// Environment is UTC. User input usually implies Shanghai Time (UTC+8).
// new Date("2023-01-01 10:00") -> 10:00 UTC.
// We want 10:00 Shanghai -> 02:00 UTC.
// So we subtract 8 hours if no timezone is explicitly provided.
let dateObj = new Date(options.due);
let dueTs = Math.floor(dateObj.getTime() / 1000);
if (isNaN(dueTs)) {
console.error(`❌ Invalid Date format: ${options.due}`);
process.exit(1);
}
// Heuristic: If string doesn't contain "+" or "Z", assume local (Shanghai) intent.
if (!options.due.includes('+') && !options.due.includes('Z')) {
dueTs -= 8 * 3600;
}
taskData.due = {
time: String(dueTs),
timezone: 'Asia/Shanghai',
is_all_day: false
};
// Debug log to confirm interpretation
const checkDate = new Date(dueTs * 1000);
console.log(` Parsed Due Date: ${checkDate.toISOString()} (UTC) => ${options.due} (Shanghai/Local)`);
}
console.log(`Creating task: "${options.summary}"...`);
const createRes = await client.task.task.create({
data: taskData
});
if (createRes.code !== 0) {
console.error(`❌ Failed to create task: ${createRes.msg}`);
process.exit(1);
}
const task = createRes.data.task;
const taskId = task.id;
console.log(`✅ Task Created: ${taskId}`);
console.log(` Link: ${task.app_link}`);
// 2. Add Assignees (Collaborators) - Parallelized
if (options.assignees) {
const assignees = options.assignees.split(',').map(s => s.trim()).filter(s => s);
console.log(` Adding ${assignees.length} assignees...`);
const results = await Promise.allSettled(assignees.map(async (userId) => {
const collabRes = await client.task.taskCollaborator.create({
path: { task_id: taskId },
params: { user_id_type: 'open_id' },
data: { id: userId }
});
if (collabRes.code !== 0) {
throw new Error(`${userId}: ${collabRes.msg}`);
}
return userId;
}));
results.forEach((res, idx) => {
if (res.status === 'fulfilled') {
console.log(` ✅ Added assignee: ${res.value}`);
} else {
console.error(` ❌ Failed to add assignee: ${res.reason.message}`);
}
});
}
console.log(`🎉 Task Setup Complete!`);
} catch (e) {
console.error('Error:', e.message);
if (e.response) console.error('Data:', JSON.stringify(e.response.data));
}
}
createTask();
const fs = require('fs');
const path = require('path');
const https = require('https');
// Helper to get Feishu Tenant Access Token (Internal)
// In a real OpenClaw env, this might reuse a shared token manager.
// For simplicity and robustness, we implement a basic fetcher using env vars.
async function getTenantAccessToken() {
const appId = process.env.FEISHU_APP_ID;
const appSecret = process.env.FEISHU_APP_SECRET;
if (!appId || !appSecret) {
throw new Error('Missing FEISHU_APP_ID or FEISHU_APP_SECRET in environment.');
}
return new Promise((resolve, reject) => {
const req = https.request({
hostname: 'open.feishu.cn',
path: '/open-apis/auth/v3/tenant_access_token/internal',
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
const json = JSON.parse(data);
if (json.code === 0) resolve(json.tenant_access_token);
else reject(new Error(`Token Error: ${json.msg}`));
} catch (e) { reject(e); }
});
});
req.on('error', reject);
req.write(JSON.stringify({ app_id: appId, app_secret: appSecret }));
req.end();
});
}
// Generic Feishu API Request
async function feishuRequest(method, endpoint, body = null) {
const token = await getTenantAccessToken();
return new Promise((resolve, reject) => {
const options = {
hostname: 'open.feishu.cn',
path: endpoint,
method: method,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json; charset=utf-8'
}
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
const json = JSON.parse(data);
if (json.code === 0) resolve(json.data);
else reject(new Error(`API Error [${json.code}]: ${json.msg}`));
} catch (e) { reject(e); }
});
});
req.on('error', reject);
if (body) req.write(JSON.stringify(body));
req.end();
});
}
// --- Actions ---
async function createTask(args) {
const summary = args.summary;
if (!summary) throw new Error('Missing --summary');
// v2 Tasks API: POST /open-apis/task/v2/tasks
// https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/task-v2/task/create
const payload = {
summary: summary,
description: args.description || '',
due: args.due ? { timestamp: String(new Date(args.due).getTime()) } : undefined,
origin: { platform_i18n_name: JSON.stringify({ "zh_cn": args.origin || "OpenClaw" }) }
};
if (args.user_id) {
payload.members = [{ id: args.user_id, type: 'user' }];
}
const result = await feishuRequest('POST', '/open-apis/task/v2/tasks', payload);
console.log(JSON.stringify({ status: 'success', task: result.task }));
}
async function completeTask(args) {
const taskId = args.task_id;
if (!taskId) throw new Error('Missing --task_id');
// POST /open-apis/task/v2/tasks/:task_guid/complete
const result = await feishuRequest('POST', `/open-apis/task/v2/tasks/${taskId}/complete`);
console.log(JSON.stringify({ status: 'success', result }));
}
async function deleteTask(args) {
const taskId = args.task_id;
if (!taskId) throw new Error('Missing --task_id');
// DELETE /open-apis/task/v2/tasks/:task_guid
const result = await feishuRequest('DELETE', `/open-apis/task/v2/tasks/${taskId}`);
console.log(JSON.stringify({ status: 'success', result }));
}
// --- Main ---
async function main() {
const args = process.argv.slice(2);
const command = args[0];
const parsedArgs = {};
for (let i = 1; i < args.length; i += 2) {
if (args[i].startsWith('--')) {
parsedArgs[args[i].replace(/^--/, '')] = args[i + 1];
}
}
try {
switch (command) {
case 'create':
await createTask(parsedArgs);
break;
case 'complete':
await completeTask(parsedArgs);
break;
case 'delete':
await deleteTask(parsedArgs);
break;
default:
// If required as a module, export functions
if (require.main !== module) return;
console.log('Usage: node index.js [create|complete|delete] --arg val ...');
break;
}
} catch (error) {
console.error(JSON.stringify({ status: 'error', message: error.message }));
process.exit(1);
}
}
// Allow importing
module.exports = { main, createTask, completeTask, deleteTask };
if (require.main === module) {
main();
}
const { program } = require('commander');
const Lark = require('@larksuiteoapi/node-sdk');
const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') });
const APP_ID = process.env.FEISHU_APP_ID;
const APP_SECRET = process.env.FEISHU_APP_SECRET;
if (!APP_ID || !APP_SECRET) {
console.error('Error: FEISHU_APP_ID or FEISHU_APP_SECRET not set.');
process.exit(1);
}
const client = new Lark.Client({
appId: APP_ID,
appSecret: APP_SECRET,
});
program
.option('-l, --limit <number>', 'Number of tasks to list', '20')
.option('--json', 'Output as JSON')
.parse(process.argv);
const options = program.opts();
async function listTasks() {
try {
const res = await client.task.task.list({
params: {
page_size: parseInt(options.limit),
user_id_type: 'open_id'
}
});
if (res.code !== 0) {
console.error(`❌ API Error: ${res.msg}`);
process.exit(1);
}
const tasks = res.data.items || [];
if (options.json) {
console.log(JSON.stringify(tasks, null, 2));
return;
}
if (tasks.length === 0) {
console.log('No tasks found.');
return;
}
console.log(`Found ${tasks.length} tasks:`);
tasks.forEach(task => {
const status = task.completed_at ? '✅' : '⬜';
let dueStr = '';
if (task.due && task.due.time) {
const d = new Date(parseInt(task.due.time) * 1000);
dueStr = `(Due: ${d.toISOString().replace('T', ' ').substring(0, 16)})`;
}
console.log(`${status} [${task.id}] ${task.summary} ${dueStr}`);
console.log(` Link: ${task.app_link}`);
});
} catch (e) {
console.error('Error:', e.message);
if (e.response) console.error('Data:', JSON.stringify(e.response.data));
}
}
listTasks();
{
"name": "feishu-task",
"version": "1.0.0",
"description": "Manage Feishu (Lark) Tasks via API.",
"main": "index.js",
"dependencies": {
"axios": "^1.6.0"
}
}
Related skills
FAQ
How do I set a due date?
Pass --due with an ISO 8601 datetime or timestamp when creating the task.
How do I complete a task?
Run the complete command with --task_id set to the task's id.