@hasna/todos 0.9.19 → 0.9.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +477 -8
- package/dist/index.js +31 -17
- package/dist/mcp/index.js +3099 -2763
- package/dist/server/index.js +24 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4,6 +4,7 @@ var __create = Object.create;
|
|
|
4
4
|
var __getProtoOf = Object.getPrototypeOf;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
9
|
var __toESM = (mod, isNodeMode, target) => {
|
|
9
10
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
@@ -16,6 +17,20 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
16
17
|
});
|
|
17
18
|
return to;
|
|
18
19
|
};
|
|
20
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
21
|
+
var __toCommonJS = (from) => {
|
|
22
|
+
var entry = __moduleCache.get(from), desc;
|
|
23
|
+
if (entry)
|
|
24
|
+
return entry;
|
|
25
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
26
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
27
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
28
|
+
get: () => from[key],
|
|
29
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
30
|
+
}));
|
|
31
|
+
__moduleCache.set(from, entry);
|
|
32
|
+
return entry;
|
|
33
|
+
};
|
|
19
34
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
20
35
|
var __export = (target, all) => {
|
|
21
36
|
for (var name in all)
|
|
@@ -2895,6 +2910,33 @@ var init_completion_guard = __esm(() => {
|
|
|
2895
2910
|
init_projects();
|
|
2896
2911
|
});
|
|
2897
2912
|
|
|
2913
|
+
// src/db/audit.ts
|
|
2914
|
+
var exports_audit = {};
|
|
2915
|
+
__export(exports_audit, {
|
|
2916
|
+
logTaskChange: () => logTaskChange,
|
|
2917
|
+
getTaskHistory: () => getTaskHistory,
|
|
2918
|
+
getRecentActivity: () => getRecentActivity
|
|
2919
|
+
});
|
|
2920
|
+
function logTaskChange(taskId, action, field, oldValue, newValue, agentId, db) {
|
|
2921
|
+
const d = db || getDatabase();
|
|
2922
|
+
const id = uuid();
|
|
2923
|
+
const timestamp = now();
|
|
2924
|
+
d.run(`INSERT INTO task_history (id, task_id, action, field, old_value, new_value, agent_id, created_at)
|
|
2925
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, taskId, action, field || null, oldValue ?? null, newValue ?? null, agentId || null, timestamp]);
|
|
2926
|
+
return { id, task_id: taskId, action, field: field || null, old_value: oldValue ?? null, new_value: newValue ?? null, agent_id: agentId || null, created_at: timestamp };
|
|
2927
|
+
}
|
|
2928
|
+
function getTaskHistory(taskId, db) {
|
|
2929
|
+
const d = db || getDatabase();
|
|
2930
|
+
return d.query("SELECT * FROM task_history WHERE task_id = ? ORDER BY created_at DESC").all(taskId);
|
|
2931
|
+
}
|
|
2932
|
+
function getRecentActivity(limit = 50, db) {
|
|
2933
|
+
const d = db || getDatabase();
|
|
2934
|
+
return d.query("SELECT * FROM task_history ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
2935
|
+
}
|
|
2936
|
+
var init_audit = __esm(() => {
|
|
2937
|
+
init_database();
|
|
2938
|
+
});
|
|
2939
|
+
|
|
2898
2940
|
// src/db/tasks.ts
|
|
2899
2941
|
function rowToTask(row) {
|
|
2900
2942
|
return {
|
|
@@ -3145,6 +3187,17 @@ function updateTask(id, input, db) {
|
|
|
3145
3187
|
if (input.tags !== undefined) {
|
|
3146
3188
|
replaceTaskTags(id, input.tags, d);
|
|
3147
3189
|
}
|
|
3190
|
+
const agentId = task.assigned_to || task.agent_id || null;
|
|
3191
|
+
if (input.status !== undefined && input.status !== task.status)
|
|
3192
|
+
logTaskChange(id, "update", "status", task.status, input.status, agentId, d);
|
|
3193
|
+
if (input.priority !== undefined && input.priority !== task.priority)
|
|
3194
|
+
logTaskChange(id, "update", "priority", task.priority, input.priority, agentId, d);
|
|
3195
|
+
if (input.title !== undefined && input.title !== task.title)
|
|
3196
|
+
logTaskChange(id, "update", "title", task.title, input.title, agentId, d);
|
|
3197
|
+
if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to)
|
|
3198
|
+
logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
|
|
3199
|
+
if (input.approved_by !== undefined)
|
|
3200
|
+
logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
|
|
3148
3201
|
return getTask(id, d);
|
|
3149
3202
|
}
|
|
3150
3203
|
function deleteTask(id, db) {
|
|
@@ -3166,6 +3219,7 @@ function startTask(id, agentId, db) {
|
|
|
3166
3219
|
throw new LockError(id, current.locked_by);
|
|
3167
3220
|
}
|
|
3168
3221
|
}
|
|
3222
|
+
logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
|
|
3169
3223
|
return getTask(id, d);
|
|
3170
3224
|
}
|
|
3171
3225
|
function completeTask(id, agentId, db) {
|
|
@@ -3180,6 +3234,7 @@ function completeTask(id, agentId, db) {
|
|
|
3180
3234
|
const timestamp = now();
|
|
3181
3235
|
d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
|
|
3182
3236
|
WHERE id = ?`, [timestamp, timestamp, id]);
|
|
3237
|
+
logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
|
|
3183
3238
|
return getTask(id, d);
|
|
3184
3239
|
}
|
|
3185
3240
|
function lockTask(id, agentId, db) {
|
|
@@ -3260,6 +3315,7 @@ var init_tasks = __esm(() => {
|
|
|
3260
3315
|
init_database();
|
|
3261
3316
|
init_projects();
|
|
3262
3317
|
init_completion_guard();
|
|
3318
|
+
init_audit();
|
|
3263
3319
|
});
|
|
3264
3320
|
|
|
3265
3321
|
// src/db/agents.ts
|
|
@@ -4096,6 +4152,73 @@ var init_sync = __esm(() => {
|
|
|
4096
4152
|
init_config();
|
|
4097
4153
|
});
|
|
4098
4154
|
|
|
4155
|
+
// src/db/templates.ts
|
|
4156
|
+
var exports_templates = {};
|
|
4157
|
+
__export(exports_templates, {
|
|
4158
|
+
taskFromTemplate: () => taskFromTemplate,
|
|
4159
|
+
listTemplates: () => listTemplates,
|
|
4160
|
+
getTemplate: () => getTemplate,
|
|
4161
|
+
deleteTemplate: () => deleteTemplate,
|
|
4162
|
+
createTemplate: () => createTemplate
|
|
4163
|
+
});
|
|
4164
|
+
function rowToTemplate(row) {
|
|
4165
|
+
return {
|
|
4166
|
+
...row,
|
|
4167
|
+
tags: JSON.parse(row.tags || "[]"),
|
|
4168
|
+
metadata: JSON.parse(row.metadata || "{}"),
|
|
4169
|
+
priority: row.priority || "medium"
|
|
4170
|
+
};
|
|
4171
|
+
}
|
|
4172
|
+
function createTemplate(input, db) {
|
|
4173
|
+
const d = db || getDatabase();
|
|
4174
|
+
const id = uuid();
|
|
4175
|
+
d.run(`INSERT INTO task_templates (id, name, title_pattern, description, priority, tags, project_id, plan_id, metadata, created_at)
|
|
4176
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
4177
|
+
id,
|
|
4178
|
+
input.name,
|
|
4179
|
+
input.title_pattern,
|
|
4180
|
+
input.description || null,
|
|
4181
|
+
input.priority || "medium",
|
|
4182
|
+
JSON.stringify(input.tags || []),
|
|
4183
|
+
input.project_id || null,
|
|
4184
|
+
input.plan_id || null,
|
|
4185
|
+
JSON.stringify(input.metadata || {}),
|
|
4186
|
+
now()
|
|
4187
|
+
]);
|
|
4188
|
+
return getTemplate(id, d);
|
|
4189
|
+
}
|
|
4190
|
+
function getTemplate(id, db) {
|
|
4191
|
+
const d = db || getDatabase();
|
|
4192
|
+
const row = d.query("SELECT * FROM task_templates WHERE id = ?").get(id);
|
|
4193
|
+
return row ? rowToTemplate(row) : null;
|
|
4194
|
+
}
|
|
4195
|
+
function listTemplates(db) {
|
|
4196
|
+
const d = db || getDatabase();
|
|
4197
|
+
return d.query("SELECT * FROM task_templates ORDER BY name").all().map(rowToTemplate);
|
|
4198
|
+
}
|
|
4199
|
+
function deleteTemplate(id, db) {
|
|
4200
|
+
const d = db || getDatabase();
|
|
4201
|
+
return d.run("DELETE FROM task_templates WHERE id = ?", [id]).changes > 0;
|
|
4202
|
+
}
|
|
4203
|
+
function taskFromTemplate(templateId, overrides = {}, db) {
|
|
4204
|
+
const t = getTemplate(templateId, db);
|
|
4205
|
+
if (!t)
|
|
4206
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
4207
|
+
return {
|
|
4208
|
+
title: overrides.title || t.title_pattern,
|
|
4209
|
+
description: overrides.description ?? t.description ?? undefined,
|
|
4210
|
+
priority: overrides.priority ?? t.priority,
|
|
4211
|
+
tags: overrides.tags ?? t.tags,
|
|
4212
|
+
project_id: overrides.project_id ?? t.project_id ?? undefined,
|
|
4213
|
+
plan_id: overrides.plan_id ?? t.plan_id ?? undefined,
|
|
4214
|
+
metadata: overrides.metadata ?? t.metadata,
|
|
4215
|
+
...overrides
|
|
4216
|
+
};
|
|
4217
|
+
}
|
|
4218
|
+
var init_templates = __esm(() => {
|
|
4219
|
+
init_database();
|
|
4220
|
+
});
|
|
4221
|
+
|
|
4099
4222
|
// node_modules/zod/v3/helpers/util.js
|
|
4100
4223
|
var util, objectUtil, ZodParsedType, getParsedType = (data) => {
|
|
4101
4224
|
const t = typeof data;
|
|
@@ -8062,6 +8185,57 @@ var init_zod = __esm(() => {
|
|
|
8062
8185
|
init_external();
|
|
8063
8186
|
});
|
|
8064
8187
|
|
|
8188
|
+
// src/db/webhooks.ts
|
|
8189
|
+
var exports_webhooks = {};
|
|
8190
|
+
__export(exports_webhooks, {
|
|
8191
|
+
listWebhooks: () => listWebhooks,
|
|
8192
|
+
getWebhook: () => getWebhook,
|
|
8193
|
+
dispatchWebhook: () => dispatchWebhook,
|
|
8194
|
+
deleteWebhook: () => deleteWebhook,
|
|
8195
|
+
createWebhook: () => createWebhook
|
|
8196
|
+
});
|
|
8197
|
+
function rowToWebhook(row) {
|
|
8198
|
+
return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
|
|
8199
|
+
}
|
|
8200
|
+
function createWebhook(input, db) {
|
|
8201
|
+
const d = db || getDatabase();
|
|
8202
|
+
const id = uuid();
|
|
8203
|
+
d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
|
|
8204
|
+
return getWebhook(id, d);
|
|
8205
|
+
}
|
|
8206
|
+
function getWebhook(id, db) {
|
|
8207
|
+
const d = db || getDatabase();
|
|
8208
|
+
const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
|
|
8209
|
+
return row ? rowToWebhook(row) : null;
|
|
8210
|
+
}
|
|
8211
|
+
function listWebhooks(db) {
|
|
8212
|
+
const d = db || getDatabase();
|
|
8213
|
+
return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
|
|
8214
|
+
}
|
|
8215
|
+
function deleteWebhook(id, db) {
|
|
8216
|
+
const d = db || getDatabase();
|
|
8217
|
+
return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
|
|
8218
|
+
}
|
|
8219
|
+
async function dispatchWebhook(event, payload, db) {
|
|
8220
|
+
const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
|
|
8221
|
+
for (const wh of webhooks) {
|
|
8222
|
+
try {
|
|
8223
|
+
const body = JSON.stringify({ event, payload, timestamp: now() });
|
|
8224
|
+
const headers = { "Content-Type": "application/json" };
|
|
8225
|
+
if (wh.secret) {
|
|
8226
|
+
const encoder = new TextEncoder;
|
|
8227
|
+
const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
8228
|
+
const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
|
|
8229
|
+
headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
8230
|
+
}
|
|
8231
|
+
fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
|
|
8232
|
+
} catch {}
|
|
8233
|
+
}
|
|
8234
|
+
}
|
|
8235
|
+
var init_webhooks = __esm(() => {
|
|
8236
|
+
init_database();
|
|
8237
|
+
});
|
|
8238
|
+
|
|
8065
8239
|
// src/mcp/index.ts
|
|
8066
8240
|
var exports_mcp = {};
|
|
8067
8241
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -8170,7 +8344,9 @@ var init_mcp = __esm(() => {
|
|
|
8170
8344
|
plan_id: exports_external.string().optional().describe("Plan ID to assign task to"),
|
|
8171
8345
|
task_list_id: exports_external.string().optional().describe("Task list ID to assign task to"),
|
|
8172
8346
|
tags: exports_external.array(exports_external.string()).optional().describe("Task tags"),
|
|
8173
|
-
metadata: exports_external.record(exports_external.unknown()).optional().describe("Arbitrary metadata")
|
|
8347
|
+
metadata: exports_external.record(exports_external.unknown()).optional().describe("Arbitrary metadata"),
|
|
8348
|
+
estimated_minutes: exports_external.number().optional().describe("Estimated time in minutes"),
|
|
8349
|
+
requires_approval: exports_external.boolean().optional().describe("Require approval before completion")
|
|
8174
8350
|
}, async (params) => {
|
|
8175
8351
|
try {
|
|
8176
8352
|
const resolved = { ...params };
|
|
@@ -8452,12 +8628,16 @@ ${text}` }] };
|
|
|
8452
8628
|
name: exports_external.string().describe("Plan name"),
|
|
8453
8629
|
project_id: exports_external.string().optional().describe("Project ID"),
|
|
8454
8630
|
description: exports_external.string().optional().describe("Plan description"),
|
|
8455
|
-
status: exports_external.enum(["active", "completed", "archived"]).optional().describe("Plan status")
|
|
8631
|
+
status: exports_external.enum(["active", "completed", "archived"]).optional().describe("Plan status"),
|
|
8632
|
+
task_list_id: exports_external.string().optional().describe("Task list ID"),
|
|
8633
|
+
agent_id: exports_external.string().optional().describe("Owner agent ID")
|
|
8456
8634
|
}, async (params) => {
|
|
8457
8635
|
try {
|
|
8458
8636
|
const resolved = { ...params };
|
|
8459
8637
|
if (resolved.project_id)
|
|
8460
8638
|
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
8639
|
+
if (resolved.task_list_id)
|
|
8640
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
8461
8641
|
const plan = createPlan(resolved);
|
|
8462
8642
|
return {
|
|
8463
8643
|
content: [{
|
|
@@ -8518,11 +8698,16 @@ ${text}` }] };
|
|
|
8518
8698
|
id: exports_external.string().describe("Plan ID (full or partial)"),
|
|
8519
8699
|
name: exports_external.string().optional().describe("New name"),
|
|
8520
8700
|
description: exports_external.string().optional().describe("New description"),
|
|
8521
|
-
status: exports_external.enum(["active", "completed", "archived"]).optional().describe("New status")
|
|
8701
|
+
status: exports_external.enum(["active", "completed", "archived"]).optional().describe("New status"),
|
|
8702
|
+
task_list_id: exports_external.string().optional().describe("Task list ID"),
|
|
8703
|
+
agent_id: exports_external.string().optional().describe("Owner agent ID")
|
|
8522
8704
|
}, async ({ id, ...rest }) => {
|
|
8523
8705
|
try {
|
|
8524
8706
|
const resolvedId = resolveId(id, "plans");
|
|
8525
|
-
const
|
|
8707
|
+
const resolved = { ...rest };
|
|
8708
|
+
if (resolved.task_list_id)
|
|
8709
|
+
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
8710
|
+
const plan = updatePlan(resolvedId, resolved);
|
|
8526
8711
|
return {
|
|
8527
8712
|
content: [{
|
|
8528
8713
|
type: "text",
|
|
@@ -8793,6 +8978,160 @@ Slug: ${list.slug}`
|
|
|
8793
8978
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8794
8979
|
}
|
|
8795
8980
|
});
|
|
8981
|
+
server.tool("get_task_history", "Get change history for a task (audit log)", {
|
|
8982
|
+
task_id: exports_external.string().describe("Task ID (full or partial)")
|
|
8983
|
+
}, async ({ task_id }) => {
|
|
8984
|
+
try {
|
|
8985
|
+
const resolvedId = resolveId(task_id);
|
|
8986
|
+
const { getTaskHistory: getTaskHistory2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
|
|
8987
|
+
const history = getTaskHistory2(resolvedId);
|
|
8988
|
+
if (history.length === 0)
|
|
8989
|
+
return { content: [{ type: "text", text: "No history for this task." }] };
|
|
8990
|
+
const text = history.map((h) => `${h.created_at} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.old_value ? ` from "${h.old_value}"` : ""}${h.new_value ? ` to "${h.new_value}"` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
|
|
8991
|
+
`);
|
|
8992
|
+
return { content: [{ type: "text", text: `${history.length} change(s):
|
|
8993
|
+
${text}` }] };
|
|
8994
|
+
} catch (e) {
|
|
8995
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8996
|
+
}
|
|
8997
|
+
});
|
|
8998
|
+
server.tool("get_recent_activity", "Get recent task changes across all tasks (audit log)", {
|
|
8999
|
+
limit: exports_external.number().optional().describe("Max entries (default 50)")
|
|
9000
|
+
}, async ({ limit }) => {
|
|
9001
|
+
try {
|
|
9002
|
+
const { getRecentActivity: getRecentActivity2 } = await Promise.resolve().then(() => (init_audit(), exports_audit));
|
|
9003
|
+
const activity = getRecentActivity2(limit || 50);
|
|
9004
|
+
if (activity.length === 0)
|
|
9005
|
+
return { content: [{ type: "text", text: "No recent activity." }] };
|
|
9006
|
+
const text = activity.map((h) => `${h.created_at} | ${h.task_id.slice(0, 8)} | ${h.action}${h.field ? ` ${h.field}` : ""}${h.agent_id ? ` by ${h.agent_id}` : ""}`).join(`
|
|
9007
|
+
`);
|
|
9008
|
+
return { content: [{ type: "text", text: `${activity.length} recent change(s):
|
|
9009
|
+
${text}` }] };
|
|
9010
|
+
} catch (e) {
|
|
9011
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9012
|
+
}
|
|
9013
|
+
});
|
|
9014
|
+
server.tool("create_webhook", "Register a webhook URL to receive task change notifications", {
|
|
9015
|
+
url: exports_external.string().describe("Webhook URL"),
|
|
9016
|
+
events: exports_external.array(exports_external.string()).optional().describe("Event types to subscribe to (empty = all)"),
|
|
9017
|
+
secret: exports_external.string().optional().describe("HMAC secret for signature verification")
|
|
9018
|
+
}, async (params) => {
|
|
9019
|
+
try {
|
|
9020
|
+
const { createWebhook: createWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
9021
|
+
const wh = createWebhook2(params);
|
|
9022
|
+
return { content: [{ type: "text", text: `Webhook created: ${wh.id.slice(0, 8)} | ${wh.url} | events: ${wh.events.length === 0 ? "all" : wh.events.join(",")}` }] };
|
|
9023
|
+
} catch (e) {
|
|
9024
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9025
|
+
}
|
|
9026
|
+
});
|
|
9027
|
+
server.tool("list_webhooks", "List all registered webhooks", {}, async () => {
|
|
9028
|
+
try {
|
|
9029
|
+
const { listWebhooks: listWebhooks2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
9030
|
+
const webhooks = listWebhooks2();
|
|
9031
|
+
if (webhooks.length === 0)
|
|
9032
|
+
return { content: [{ type: "text", text: "No webhooks registered." }] };
|
|
9033
|
+
const text = webhooks.map((w) => `${w.id.slice(0, 8)} | ${w.active ? "active" : "inactive"} | ${w.url} | events: ${w.events.length === 0 ? "all" : w.events.join(",")}`).join(`
|
|
9034
|
+
`);
|
|
9035
|
+
return { content: [{ type: "text", text: `${webhooks.length} webhook(s):
|
|
9036
|
+
${text}` }] };
|
|
9037
|
+
} catch (e) {
|
|
9038
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9039
|
+
}
|
|
9040
|
+
});
|
|
9041
|
+
server.tool("delete_webhook", "Delete a webhook", {
|
|
9042
|
+
id: exports_external.string().describe("Webhook ID")
|
|
9043
|
+
}, async ({ id }) => {
|
|
9044
|
+
try {
|
|
9045
|
+
const { deleteWebhook: deleteWebhook2 } = await Promise.resolve().then(() => (init_webhooks(), exports_webhooks));
|
|
9046
|
+
const deleted = deleteWebhook2(id);
|
|
9047
|
+
return { content: [{ type: "text", text: deleted ? "Webhook deleted." : "Webhook not found." }] };
|
|
9048
|
+
} catch (e) {
|
|
9049
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9050
|
+
}
|
|
9051
|
+
});
|
|
9052
|
+
server.tool("create_template", "Create a reusable task template", {
|
|
9053
|
+
name: exports_external.string().describe("Template name"),
|
|
9054
|
+
title_pattern: exports_external.string().describe("Title pattern for tasks created from this template"),
|
|
9055
|
+
description: exports_external.string().optional().describe("Default description"),
|
|
9056
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Default priority"),
|
|
9057
|
+
tags: exports_external.array(exports_external.string()).optional().describe("Default tags"),
|
|
9058
|
+
project_id: exports_external.string().optional().describe("Default project"),
|
|
9059
|
+
plan_id: exports_external.string().optional().describe("Default plan")
|
|
9060
|
+
}, async (params) => {
|
|
9061
|
+
try {
|
|
9062
|
+
const { createTemplate: createTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
9063
|
+
const t = createTemplate2(params);
|
|
9064
|
+
return { content: [{ type: "text", text: `Template created: ${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}"` }] };
|
|
9065
|
+
} catch (e) {
|
|
9066
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9067
|
+
}
|
|
9068
|
+
});
|
|
9069
|
+
server.tool("list_templates", "List all task templates", {}, async () => {
|
|
9070
|
+
try {
|
|
9071
|
+
const { listTemplates: listTemplates2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
9072
|
+
const templates = listTemplates2();
|
|
9073
|
+
if (templates.length === 0)
|
|
9074
|
+
return { content: [{ type: "text", text: "No templates." }] };
|
|
9075
|
+
const text = templates.map((t) => `${t.id.slice(0, 8)} | ${t.name} | "${t.title_pattern}" | ${t.priority}`).join(`
|
|
9076
|
+
`);
|
|
9077
|
+
return { content: [{ type: "text", text: `${templates.length} template(s):
|
|
9078
|
+
${text}` }] };
|
|
9079
|
+
} catch (e) {
|
|
9080
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9081
|
+
}
|
|
9082
|
+
});
|
|
9083
|
+
server.tool("create_task_from_template", "Create a task from a template with optional overrides", {
|
|
9084
|
+
template_id: exports_external.string().describe("Template ID"),
|
|
9085
|
+
title: exports_external.string().optional().describe("Override title"),
|
|
9086
|
+
description: exports_external.string().optional().describe("Override description"),
|
|
9087
|
+
priority: exports_external.enum(["low", "medium", "high", "critical"]).optional().describe("Override priority"),
|
|
9088
|
+
assigned_to: exports_external.string().optional().describe("Assign to agent"),
|
|
9089
|
+
project_id: exports_external.string().optional().describe("Override project")
|
|
9090
|
+
}, async (params) => {
|
|
9091
|
+
try {
|
|
9092
|
+
const { taskFromTemplate: taskFromTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
9093
|
+
const input = taskFromTemplate2(params.template_id, {
|
|
9094
|
+
title: params.title,
|
|
9095
|
+
description: params.description,
|
|
9096
|
+
priority: params.priority,
|
|
9097
|
+
assigned_to: params.assigned_to,
|
|
9098
|
+
project_id: params.project_id
|
|
9099
|
+
});
|
|
9100
|
+
const task = createTask(input);
|
|
9101
|
+
return { content: [{ type: "text", text: `Task created from template:
|
|
9102
|
+
${task.id.slice(0, 8)} | ${task.priority} | ${task.title}` }] };
|
|
9103
|
+
} catch (e) {
|
|
9104
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9105
|
+
}
|
|
9106
|
+
});
|
|
9107
|
+
server.tool("delete_template", "Delete a task template", { id: exports_external.string().describe("Template ID") }, async ({ id }) => {
|
|
9108
|
+
try {
|
|
9109
|
+
const { deleteTemplate: deleteTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
|
|
9110
|
+
const deleted = deleteTemplate2(id);
|
|
9111
|
+
return { content: [{ type: "text", text: deleted ? "Template deleted." : "Template not found." }] };
|
|
9112
|
+
} catch (e) {
|
|
9113
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9114
|
+
}
|
|
9115
|
+
});
|
|
9116
|
+
server.tool("approve_task", "Approve a task that requires approval before completion", {
|
|
9117
|
+
id: exports_external.string().describe("Task ID (full or partial)"),
|
|
9118
|
+
agent_id: exports_external.string().optional().describe("Agent approving the task")
|
|
9119
|
+
}, async ({ id, agent_id }) => {
|
|
9120
|
+
try {
|
|
9121
|
+
const resolvedId = resolveId(id);
|
|
9122
|
+
const task = getTaskWithRelations(resolvedId);
|
|
9123
|
+
if (!task)
|
|
9124
|
+
return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
|
|
9125
|
+
if (!task.requires_approval)
|
|
9126
|
+
return { content: [{ type: "text", text: `Task ${id} does not require approval.` }] };
|
|
9127
|
+
if (task.approved_by)
|
|
9128
|
+
return { content: [{ type: "text", text: `Task already approved by ${task.approved_by}.` }] };
|
|
9129
|
+
const updated = updateTask(resolvedId, { approved_by: agent_id || "system", version: task.version });
|
|
9130
|
+
return { content: [{ type: "text", text: `Task approved by ${agent_id || "system"}: ${updated.id.slice(0, 8)} | ${updated.title}` }] };
|
|
9131
|
+
} catch (e) {
|
|
9132
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9133
|
+
}
|
|
9134
|
+
});
|
|
8796
9135
|
server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
|
|
8797
9136
|
const tasks = listTasks({ status: ["pending", "in_progress"] });
|
|
8798
9137
|
return { contents: [{ uri: "todos://tasks", text: JSON.stringify(tasks, null, 2), mimeType: "application/json" }] };
|
|
@@ -10448,7 +10787,7 @@ function formatTaskLine(t) {
|
|
|
10448
10787
|
return `${chalk.dim(t.id.slice(0, 8))} ${statusFn(t.status.padEnd(11))} ${priorityFn(t.priority.padEnd(8))} ${t.title}${assigned}${lock}${tags}${plan}`;
|
|
10449
10788
|
}
|
|
10450
10789
|
program2.name("todos").description("Universal task management for AI coding agents").version(getPackageVersion()).option("--project <path>", "Project path").option("--json", "Output as JSON").option("--agent <name>", "Agent name").option("--session <id>", "Session ID");
|
|
10451
|
-
program2.command("add <title>").description("Create a new task").option("-d, --description <text>", "Task description").option("-p, --priority <level>", "Priority: low, medium, high, critical").option("--parent <id>", "Parent task ID").option("-t, --tags <tags>", "Comma-separated tags").option("--tag <tags>", "Comma-separated tags (alias for --tags)").option("--plan <id>", "Assign to a plan").option("--assign <agent>", "Assign to agent").option("--status <status>", "Initial status").option("--list <id>", "Task list ID").option("--task-list <id>", "Task list ID (alias for --list)").action((title, opts) => {
|
|
10790
|
+
program2.command("add <title>").description("Create a new task").option("-d, --description <text>", "Task description").option("-p, --priority <level>", "Priority: low, medium, high, critical").option("--parent <id>", "Parent task ID").option("-t, --tags <tags>", "Comma-separated tags").option("--tag <tags>", "Comma-separated tags (alias for --tags)").option("--plan <id>", "Assign to a plan").option("--assign <agent>", "Assign to agent").option("--status <status>", "Initial status").option("--list <id>", "Task list ID").option("--task-list <id>", "Task list ID (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--approval", "Require approval before completion").action((title, opts) => {
|
|
10452
10791
|
const globalOpts = program2.opts();
|
|
10453
10792
|
const projectId = autoProject(globalOpts);
|
|
10454
10793
|
opts.tags = opts.tags || opts.tag;
|
|
@@ -10483,7 +10822,9 @@ program2.command("add <title>").description("Create a new task").option("-d, --d
|
|
|
10483
10822
|
agent_id: globalOpts.agent,
|
|
10484
10823
|
session_id: globalOpts.session,
|
|
10485
10824
|
project_id: projectId,
|
|
10486
|
-
working_dir: process.cwd()
|
|
10825
|
+
working_dir: process.cwd(),
|
|
10826
|
+
estimated_minutes: opts.estimated ? parseInt(opts.estimated, 10) : undefined,
|
|
10827
|
+
requires_approval: opts.approval || false
|
|
10487
10828
|
});
|
|
10488
10829
|
if (globalOpts.json) {
|
|
10489
10830
|
output(task, true);
|
|
@@ -10611,6 +10952,12 @@ program2.command("show <id>").description("Show full task details").action((id)
|
|
|
10611
10952
|
console.log(` ${chalk.dim("Session:")} ${task.session_id}`);
|
|
10612
10953
|
if (task.locked_by)
|
|
10613
10954
|
console.log(` ${chalk.dim("Locked:")} ${task.locked_by} (at ${task.locked_at})`);
|
|
10955
|
+
if (task.requires_approval) {
|
|
10956
|
+
const approvalStatus = task.approved_by ? chalk.green(`approved by ${task.approved_by}`) : chalk.yellow("pending approval");
|
|
10957
|
+
console.log(` ${chalk.dim("Approval:")} ${approvalStatus}`);
|
|
10958
|
+
}
|
|
10959
|
+
if (task.estimated_minutes)
|
|
10960
|
+
console.log(` ${chalk.dim("Estimate:")} ${task.estimated_minutes} minutes`);
|
|
10614
10961
|
if (task.project_id)
|
|
10615
10962
|
console.log(` ${chalk.dim("Project:")} ${task.project_id}`);
|
|
10616
10963
|
if (task.plan_id)
|
|
@@ -10655,7 +11002,29 @@ program2.command("show <id>").description("Show full task details").action((id)
|
|
|
10655
11002
|
}
|
|
10656
11003
|
}
|
|
10657
11004
|
});
|
|
10658
|
-
program2.command("
|
|
11005
|
+
program2.command("history <id>").description("Show change history for a task (audit log)").action((id) => {
|
|
11006
|
+
const globalOpts = program2.opts();
|
|
11007
|
+
const resolvedId = resolveTaskId(id);
|
|
11008
|
+
const { getTaskHistory: getTaskHistory2 } = (init_audit(), __toCommonJS(exports_audit));
|
|
11009
|
+
const history = getTaskHistory2(resolvedId);
|
|
11010
|
+
if (globalOpts.json) {
|
|
11011
|
+
output(history, true);
|
|
11012
|
+
return;
|
|
11013
|
+
}
|
|
11014
|
+
if (history.length === 0) {
|
|
11015
|
+
console.log(chalk.dim("No history for this task."));
|
|
11016
|
+
return;
|
|
11017
|
+
}
|
|
11018
|
+
console.log(chalk.bold(`${history.length} change(s):
|
|
11019
|
+
`));
|
|
11020
|
+
for (const h of history) {
|
|
11021
|
+
const agent = h.agent_id ? chalk.cyan(` by ${h.agent_id}`) : "";
|
|
11022
|
+
const field = h.field ? chalk.yellow(` ${h.field}`) : "";
|
|
11023
|
+
const change = h.old_value && h.new_value ? ` ${chalk.red(h.old_value)} \u2192 ${chalk.green(h.new_value)}` : h.new_value ? ` \u2192 ${chalk.green(h.new_value)}` : "";
|
|
11024
|
+
console.log(` ${chalk.dim(h.created_at)} ${chalk.bold(h.action)}${field}${change}${agent}`);
|
|
11025
|
+
}
|
|
11026
|
+
});
|
|
11027
|
+
program2.command("update <id>").description("Update a task").option("--title <text>", "New title").option("-d, --description <text>", "New description").option("-s, --status <status>", "New status").option("-p, --priority <priority>", "New priority").option("--assign <agent>", "Assign to agent").option("--tags <tags>", "New tags (comma-separated)").option("--tag <tags>", "New tags (alias for --tags)").option("--list <id>", "Move to a task list").option("--task-list <id>", "Move to a task list (alias for --list)").option("--estimated <minutes>", "Estimated time in minutes").option("--approval", "Require approval before completion").action((id, opts) => {
|
|
10659
11028
|
const globalOpts = program2.opts();
|
|
10660
11029
|
opts.tags = opts.tags || opts.tag;
|
|
10661
11030
|
opts.list = opts.list || opts.taskList;
|
|
@@ -10684,7 +11053,9 @@ program2.command("update <id>").description("Update a task").option("--title <te
|
|
|
10684
11053
|
priority: opts.priority,
|
|
10685
11054
|
assigned_to: opts.assign,
|
|
10686
11055
|
tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
|
|
10687
|
-
task_list_id: taskListId
|
|
11056
|
+
task_list_id: taskListId,
|
|
11057
|
+
estimated_minutes: opts.estimated !== undefined ? parseInt(opts.estimated, 10) : undefined,
|
|
11058
|
+
requires_approval: opts.approval !== undefined ? true : undefined
|
|
10688
11059
|
});
|
|
10689
11060
|
} catch (e) {
|
|
10690
11061
|
handleError(e);
|
|
@@ -10712,6 +11083,34 @@ program2.command("done <id>").description("Mark a task as completed").action((id
|
|
|
10712
11083
|
console.log(formatTaskLine(task));
|
|
10713
11084
|
}
|
|
10714
11085
|
});
|
|
11086
|
+
program2.command("approve <id>").description("Approve a task that requires approval").action((id) => {
|
|
11087
|
+
const globalOpts = program2.opts();
|
|
11088
|
+
const resolvedId = resolveTaskId(id);
|
|
11089
|
+
const task = getTask(resolvedId);
|
|
11090
|
+
if (!task) {
|
|
11091
|
+
console.error(chalk.red(`Task not found: ${id}`));
|
|
11092
|
+
process.exit(1);
|
|
11093
|
+
}
|
|
11094
|
+
if (!task.requires_approval) {
|
|
11095
|
+
console.log(chalk.yellow("This task does not require approval."));
|
|
11096
|
+
return;
|
|
11097
|
+
}
|
|
11098
|
+
if (task.approved_by) {
|
|
11099
|
+
console.log(chalk.yellow(`Already approved by ${task.approved_by}.`));
|
|
11100
|
+
return;
|
|
11101
|
+
}
|
|
11102
|
+
try {
|
|
11103
|
+
const updated = updateTask(resolvedId, { approved_by: globalOpts.agent || "cli", version: task.version });
|
|
11104
|
+
if (globalOpts.json) {
|
|
11105
|
+
output(updated, true);
|
|
11106
|
+
} else {
|
|
11107
|
+
console.log(chalk.green(`Task approved by ${globalOpts.agent || "cli"}:`));
|
|
11108
|
+
console.log(formatTaskLine(updated));
|
|
11109
|
+
}
|
|
11110
|
+
} catch (e) {
|
|
11111
|
+
handleError(e);
|
|
11112
|
+
}
|
|
11113
|
+
});
|
|
10715
11114
|
program2.command("start <id>").description("Claim, lock, and start a task").action((id) => {
|
|
10716
11115
|
const globalOpts = program2.opts();
|
|
10717
11116
|
const agentId = globalOpts.agent || "cli";
|
|
@@ -10919,6 +11318,76 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
10919
11318
|
console.log(`${chalk.dim(p.id.slice(0, 8))} ${chalk.bold(p.name)} ${chalk.cyan(`[${p.status}]`)}${desc}`);
|
|
10920
11319
|
}
|
|
10921
11320
|
});
|
|
11321
|
+
program2.command("templates").description("List and manage task templates").option("--add <name>", "Create a template").option("--title <pattern>", "Title pattern (with --add)").option("-d, --description <text>", "Default description").option("-p, --priority <level>", "Default priority").option("-t, --tags <tags>", "Default tags (comma-separated)").option("--delete <id>", "Delete a template").option("--use <id>", "Create a task from a template").action((opts) => {
|
|
11322
|
+
const globalOpts = program2.opts();
|
|
11323
|
+
const { createTemplate: createTemplate2, listTemplates: listTemplates2, deleteTemplate: deleteTemplate2, taskFromTemplate: taskFromTemplate2 } = (init_templates(), __toCommonJS(exports_templates));
|
|
11324
|
+
if (opts.add) {
|
|
11325
|
+
if (!opts.title) {
|
|
11326
|
+
console.error(chalk.red("--title is required with --add"));
|
|
11327
|
+
process.exit(1);
|
|
11328
|
+
}
|
|
11329
|
+
const projectId = autoProject(globalOpts);
|
|
11330
|
+
const template = createTemplate2({
|
|
11331
|
+
name: opts.add,
|
|
11332
|
+
title_pattern: opts.title,
|
|
11333
|
+
description: opts.description,
|
|
11334
|
+
priority: opts.priority || "medium",
|
|
11335
|
+
tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [],
|
|
11336
|
+
project_id: projectId
|
|
11337
|
+
});
|
|
11338
|
+
if (globalOpts.json) {
|
|
11339
|
+
output(template, true);
|
|
11340
|
+
} else {
|
|
11341
|
+
console.log(chalk.green(`Template created: ${template.id.slice(0, 8)} | ${template.name} | "${template.title_pattern}"`));
|
|
11342
|
+
}
|
|
11343
|
+
return;
|
|
11344
|
+
}
|
|
11345
|
+
if (opts.delete) {
|
|
11346
|
+
const deleted = deleteTemplate2(opts.delete);
|
|
11347
|
+
if (globalOpts.json) {
|
|
11348
|
+
output({ deleted }, true);
|
|
11349
|
+
} else if (deleted) {
|
|
11350
|
+
console.log(chalk.green("Template deleted."));
|
|
11351
|
+
} else {
|
|
11352
|
+
console.error(chalk.red("Template not found."));
|
|
11353
|
+
process.exit(1);
|
|
11354
|
+
}
|
|
11355
|
+
return;
|
|
11356
|
+
}
|
|
11357
|
+
if (opts.use) {
|
|
11358
|
+
try {
|
|
11359
|
+
const input = taskFromTemplate2(opts.use, {
|
|
11360
|
+
title: opts.title,
|
|
11361
|
+
description: opts.description,
|
|
11362
|
+
priority: opts.priority
|
|
11363
|
+
});
|
|
11364
|
+
const task = createTask({ ...input, project_id: input.project_id || autoProject(globalOpts) });
|
|
11365
|
+
if (globalOpts.json) {
|
|
11366
|
+
output(task, true);
|
|
11367
|
+
} else {
|
|
11368
|
+
console.log(chalk.green("Task created from template:"));
|
|
11369
|
+
console.log(formatTaskLine(task));
|
|
11370
|
+
}
|
|
11371
|
+
} catch (e) {
|
|
11372
|
+
handleError(e);
|
|
11373
|
+
}
|
|
11374
|
+
return;
|
|
11375
|
+
}
|
|
11376
|
+
const templates = listTemplates2();
|
|
11377
|
+
if (globalOpts.json) {
|
|
11378
|
+
output(templates, true);
|
|
11379
|
+
return;
|
|
11380
|
+
}
|
|
11381
|
+
if (templates.length === 0) {
|
|
11382
|
+
console.log(chalk.dim("No templates."));
|
|
11383
|
+
return;
|
|
11384
|
+
}
|
|
11385
|
+
console.log(chalk.bold(`${templates.length} template(s):
|
|
11386
|
+
`));
|
|
11387
|
+
for (const t of templates) {
|
|
11388
|
+
console.log(` ${chalk.dim(t.id.slice(0, 8))} ${chalk.bold(t.name)} ${chalk.cyan(`"${t.title_pattern}"`)} ${chalk.yellow(t.priority)}`);
|
|
11389
|
+
}
|
|
11390
|
+
});
|
|
10922
11391
|
program2.command("comment <id> <text>").description("Add a comment to a task").action((id, text) => {
|
|
10923
11392
|
const globalOpts = program2.opts();
|
|
10924
11393
|
const resolvedId = resolveTaskId(id);
|