@hasna/todos 0.9.18 → 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 +570 -25
- package/dist/index.js +124 -34
- package/dist/mcp/index.js +1950 -1538
- package/dist/server/index.js +117 -17
- 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)
|
|
@@ -2138,29 +2153,20 @@ function runMigrations(db) {
|
|
|
2138
2153
|
const result = db.query("SELECT MAX(id) as max_id FROM _migrations").get();
|
|
2139
2154
|
const currentLevel = result?.max_id ?? 0;
|
|
2140
2155
|
for (let i = currentLevel;i < MIGRATIONS.length; i++) {
|
|
2141
|
-
|
|
2156
|
+
try {
|
|
2157
|
+
db.exec(MIGRATIONS[i]);
|
|
2158
|
+
} catch {}
|
|
2142
2159
|
}
|
|
2143
2160
|
} catch {
|
|
2144
2161
|
for (const migration of MIGRATIONS) {
|
|
2145
|
-
|
|
2162
|
+
try {
|
|
2163
|
+
db.exec(migration);
|
|
2164
|
+
} catch {}
|
|
2146
2165
|
}
|
|
2147
2166
|
}
|
|
2148
|
-
|
|
2167
|
+
ensureSchema(db);
|
|
2149
2168
|
}
|
|
2150
|
-
function
|
|
2151
|
-
try {
|
|
2152
|
-
const hasAgents = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='agents'").get();
|
|
2153
|
-
if (!hasAgents) {
|
|
2154
|
-
db.exec(MIGRATIONS[4]);
|
|
2155
|
-
}
|
|
2156
|
-
} catch {}
|
|
2157
|
-
try {
|
|
2158
|
-
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
2159
|
-
} catch {
|
|
2160
|
-
try {
|
|
2161
|
-
db.exec(MIGRATIONS[5]);
|
|
2162
|
-
} catch {}
|
|
2163
|
-
}
|
|
2169
|
+
function ensureSchema(db) {
|
|
2164
2170
|
const ensureColumn = (table, column, type) => {
|
|
2165
2171
|
try {
|
|
2166
2172
|
db.query(`SELECT ${column} FROM ${table} LIMIT 0`).get();
|
|
@@ -2170,6 +2176,76 @@ function ensureTableMigrations(db) {
|
|
|
2170
2176
|
} catch {}
|
|
2171
2177
|
}
|
|
2172
2178
|
};
|
|
2179
|
+
const ensureTable = (name, sql) => {
|
|
2180
|
+
try {
|
|
2181
|
+
const exists = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(name);
|
|
2182
|
+
if (!exists)
|
|
2183
|
+
db.exec(sql);
|
|
2184
|
+
} catch {}
|
|
2185
|
+
};
|
|
2186
|
+
const ensureIndex = (sql) => {
|
|
2187
|
+
try {
|
|
2188
|
+
db.exec(sql);
|
|
2189
|
+
} catch {}
|
|
2190
|
+
};
|
|
2191
|
+
ensureTable("agents", `
|
|
2192
|
+
CREATE TABLE agents (
|
|
2193
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT,
|
|
2194
|
+
role TEXT DEFAULT 'agent', permissions TEXT DEFAULT '["*"]',
|
|
2195
|
+
metadata TEXT DEFAULT '{}',
|
|
2196
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2197
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2198
|
+
)`);
|
|
2199
|
+
ensureTable("task_lists", `
|
|
2200
|
+
CREATE TABLE task_lists (
|
|
2201
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2202
|
+
slug TEXT NOT NULL, name TEXT NOT NULL, description TEXT,
|
|
2203
|
+
metadata TEXT DEFAULT '{}',
|
|
2204
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2205
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2206
|
+
UNIQUE(project_id, slug)
|
|
2207
|
+
)`);
|
|
2208
|
+
ensureTable("plans", `
|
|
2209
|
+
CREATE TABLE plans (
|
|
2210
|
+
id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
|
|
2211
|
+
task_list_id TEXT, agent_id TEXT,
|
|
2212
|
+
name TEXT NOT NULL, description TEXT,
|
|
2213
|
+
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
|
|
2214
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2215
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2216
|
+
)`);
|
|
2217
|
+
ensureTable("task_tags", `
|
|
2218
|
+
CREATE TABLE task_tags (
|
|
2219
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2220
|
+
tag TEXT NOT NULL, PRIMARY KEY (task_id, tag)
|
|
2221
|
+
)`);
|
|
2222
|
+
ensureTable("task_history", `
|
|
2223
|
+
CREATE TABLE task_history (
|
|
2224
|
+
id TEXT PRIMARY KEY, task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
2225
|
+
action TEXT NOT NULL, field TEXT, old_value TEXT, new_value TEXT, agent_id TEXT,
|
|
2226
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2227
|
+
)`);
|
|
2228
|
+
ensureTable("webhooks", `
|
|
2229
|
+
CREATE TABLE webhooks (
|
|
2230
|
+
id TEXT PRIMARY KEY, url TEXT NOT NULL, events TEXT NOT NULL DEFAULT '[]',
|
|
2231
|
+
secret TEXT, active INTEGER NOT NULL DEFAULT 1,
|
|
2232
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2233
|
+
)`);
|
|
2234
|
+
ensureTable("task_templates", `
|
|
2235
|
+
CREATE TABLE task_templates (
|
|
2236
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL, title_pattern TEXT NOT NULL,
|
|
2237
|
+
description TEXT, priority TEXT DEFAULT 'medium', tags TEXT DEFAULT '[]',
|
|
2238
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
2239
|
+
plan_id TEXT REFERENCES plans(id) ON DELETE SET NULL,
|
|
2240
|
+
metadata TEXT DEFAULT '{}',
|
|
2241
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
2242
|
+
)`);
|
|
2243
|
+
ensureColumn("projects", "task_list_id", "TEXT");
|
|
2244
|
+
ensureColumn("projects", "task_prefix", "TEXT");
|
|
2245
|
+
ensureColumn("projects", "task_counter", "INTEGER NOT NULL DEFAULT 0");
|
|
2246
|
+
ensureColumn("tasks", "plan_id", "TEXT REFERENCES plans(id) ON DELETE SET NULL");
|
|
2247
|
+
ensureColumn("tasks", "task_list_id", "TEXT REFERENCES task_lists(id) ON DELETE SET NULL");
|
|
2248
|
+
ensureColumn("tasks", "short_id", "TEXT");
|
|
2173
2249
|
ensureColumn("tasks", "due_at", "TEXT");
|
|
2174
2250
|
ensureColumn("tasks", "estimated_minutes", "INTEGER");
|
|
2175
2251
|
ensureColumn("tasks", "requires_approval", "INTEGER NOT NULL DEFAULT 0");
|
|
@@ -2179,6 +2255,21 @@ function ensureTableMigrations(db) {
|
|
|
2179
2255
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
2180
2256
|
ensureColumn("plans", "task_list_id", "TEXT");
|
|
2181
2257
|
ensureColumn("plans", "agent_id", "TEXT");
|
|
2258
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_plan ON tasks(plan_id)");
|
|
2259
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id)");
|
|
2260
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_tasks_due_at ON tasks(due_at)");
|
|
2261
|
+
ensureIndex("CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL");
|
|
2262
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_agents_name ON agents(name)");
|
|
2263
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_project ON task_lists(project_id)");
|
|
2264
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug)");
|
|
2265
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_tag ON task_tags(tag)");
|
|
2266
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_tags_task ON task_tags(task_id)");
|
|
2267
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
|
|
2268
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
|
|
2269
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
|
|
2270
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id)");
|
|
2271
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_task ON task_history(task_id)");
|
|
2272
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_agent ON task_history(agent_id)");
|
|
2182
2273
|
}
|
|
2183
2274
|
function backfillTaskTags(db) {
|
|
2184
2275
|
try {
|
|
@@ -2819,6 +2910,33 @@ var init_completion_guard = __esm(() => {
|
|
|
2819
2910
|
init_projects();
|
|
2820
2911
|
});
|
|
2821
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
|
+
|
|
2822
2940
|
// src/db/tasks.ts
|
|
2823
2941
|
function rowToTask(row) {
|
|
2824
2942
|
return {
|
|
@@ -3069,6 +3187,17 @@ function updateTask(id, input, db) {
|
|
|
3069
3187
|
if (input.tags !== undefined) {
|
|
3070
3188
|
replaceTaskTags(id, input.tags, d);
|
|
3071
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);
|
|
3072
3201
|
return getTask(id, d);
|
|
3073
3202
|
}
|
|
3074
3203
|
function deleteTask(id, db) {
|
|
@@ -3090,6 +3219,7 @@ function startTask(id, agentId, db) {
|
|
|
3090
3219
|
throw new LockError(id, current.locked_by);
|
|
3091
3220
|
}
|
|
3092
3221
|
}
|
|
3222
|
+
logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
|
|
3093
3223
|
return getTask(id, d);
|
|
3094
3224
|
}
|
|
3095
3225
|
function completeTask(id, agentId, db) {
|
|
@@ -3104,6 +3234,7 @@ function completeTask(id, agentId, db) {
|
|
|
3104
3234
|
const timestamp = now();
|
|
3105
3235
|
d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
|
|
3106
3236
|
WHERE id = ?`, [timestamp, timestamp, id]);
|
|
3237
|
+
logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
|
|
3107
3238
|
return getTask(id, d);
|
|
3108
3239
|
}
|
|
3109
3240
|
function lockTask(id, agentId, db) {
|
|
@@ -3184,6 +3315,7 @@ var init_tasks = __esm(() => {
|
|
|
3184
3315
|
init_database();
|
|
3185
3316
|
init_projects();
|
|
3186
3317
|
init_completion_guard();
|
|
3318
|
+
init_audit();
|
|
3187
3319
|
});
|
|
3188
3320
|
|
|
3189
3321
|
// src/db/agents.ts
|
|
@@ -4020,6 +4152,73 @@ var init_sync = __esm(() => {
|
|
|
4020
4152
|
init_config();
|
|
4021
4153
|
});
|
|
4022
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
|
+
|
|
4023
4222
|
// node_modules/zod/v3/helpers/util.js
|
|
4024
4223
|
var util, objectUtil, ZodParsedType, getParsedType = (data) => {
|
|
4025
4224
|
const t = typeof data;
|
|
@@ -7986,6 +8185,57 @@ var init_zod = __esm(() => {
|
|
|
7986
8185
|
init_external();
|
|
7987
8186
|
});
|
|
7988
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
|
+
|
|
7989
8239
|
// src/mcp/index.ts
|
|
7990
8240
|
var exports_mcp = {};
|
|
7991
8241
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -8094,7 +8344,9 @@ var init_mcp = __esm(() => {
|
|
|
8094
8344
|
plan_id: exports_external.string().optional().describe("Plan ID to assign task to"),
|
|
8095
8345
|
task_list_id: exports_external.string().optional().describe("Task list ID to assign task to"),
|
|
8096
8346
|
tags: exports_external.array(exports_external.string()).optional().describe("Task tags"),
|
|
8097
|
-
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")
|
|
8098
8350
|
}, async (params) => {
|
|
8099
8351
|
try {
|
|
8100
8352
|
const resolved = { ...params };
|
|
@@ -8376,12 +8628,16 @@ ${text}` }] };
|
|
|
8376
8628
|
name: exports_external.string().describe("Plan name"),
|
|
8377
8629
|
project_id: exports_external.string().optional().describe("Project ID"),
|
|
8378
8630
|
description: exports_external.string().optional().describe("Plan description"),
|
|
8379
|
-
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")
|
|
8380
8634
|
}, async (params) => {
|
|
8381
8635
|
try {
|
|
8382
8636
|
const resolved = { ...params };
|
|
8383
8637
|
if (resolved.project_id)
|
|
8384
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");
|
|
8385
8641
|
const plan = createPlan(resolved);
|
|
8386
8642
|
return {
|
|
8387
8643
|
content: [{
|
|
@@ -8442,11 +8698,16 @@ ${text}` }] };
|
|
|
8442
8698
|
id: exports_external.string().describe("Plan ID (full or partial)"),
|
|
8443
8699
|
name: exports_external.string().optional().describe("New name"),
|
|
8444
8700
|
description: exports_external.string().optional().describe("New description"),
|
|
8445
|
-
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")
|
|
8446
8704
|
}, async ({ id, ...rest }) => {
|
|
8447
8705
|
try {
|
|
8448
8706
|
const resolvedId = resolveId(id, "plans");
|
|
8449
|
-
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);
|
|
8450
8711
|
return {
|
|
8451
8712
|
content: [{
|
|
8452
8713
|
type: "text",
|
|
@@ -8717,6 +8978,160 @@ Slug: ${list.slug}`
|
|
|
8717
8978
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
8718
8979
|
}
|
|
8719
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
|
+
});
|
|
8720
9135
|
server.resource("tasks", "todos://tasks", { description: "All active tasks", mimeType: "application/json" }, async () => {
|
|
8721
9136
|
const tasks = listTasks({ status: ["pending", "in_progress"] });
|
|
8722
9137
|
return { contents: [{ uri: "todos://tasks", text: JSON.stringify(tasks, null, 2), mimeType: "application/json" }] };
|
|
@@ -10372,7 +10787,7 @@ function formatTaskLine(t) {
|
|
|
10372
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}`;
|
|
10373
10788
|
}
|
|
10374
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");
|
|
10375
|
-
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) => {
|
|
10376
10791
|
const globalOpts = program2.opts();
|
|
10377
10792
|
const projectId = autoProject(globalOpts);
|
|
10378
10793
|
opts.tags = opts.tags || opts.tag;
|
|
@@ -10407,7 +10822,9 @@ program2.command("add <title>").description("Create a new task").option("-d, --d
|
|
|
10407
10822
|
agent_id: globalOpts.agent,
|
|
10408
10823
|
session_id: globalOpts.session,
|
|
10409
10824
|
project_id: projectId,
|
|
10410
|
-
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
|
|
10411
10828
|
});
|
|
10412
10829
|
if (globalOpts.json) {
|
|
10413
10830
|
output(task, true);
|
|
@@ -10535,6 +10952,12 @@ program2.command("show <id>").description("Show full task details").action((id)
|
|
|
10535
10952
|
console.log(` ${chalk.dim("Session:")} ${task.session_id}`);
|
|
10536
10953
|
if (task.locked_by)
|
|
10537
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`);
|
|
10538
10961
|
if (task.project_id)
|
|
10539
10962
|
console.log(` ${chalk.dim("Project:")} ${task.project_id}`);
|
|
10540
10963
|
if (task.plan_id)
|
|
@@ -10579,7 +11002,29 @@ program2.command("show <id>").description("Show full task details").action((id)
|
|
|
10579
11002
|
}
|
|
10580
11003
|
}
|
|
10581
11004
|
});
|
|
10582
|
-
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) => {
|
|
10583
11028
|
const globalOpts = program2.opts();
|
|
10584
11029
|
opts.tags = opts.tags || opts.tag;
|
|
10585
11030
|
opts.list = opts.list || opts.taskList;
|
|
@@ -10608,7 +11053,9 @@ program2.command("update <id>").description("Update a task").option("--title <te
|
|
|
10608
11053
|
priority: opts.priority,
|
|
10609
11054
|
assigned_to: opts.assign,
|
|
10610
11055
|
tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
|
|
10611
|
-
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
|
|
10612
11059
|
});
|
|
10613
11060
|
} catch (e) {
|
|
10614
11061
|
handleError(e);
|
|
@@ -10636,6 +11083,34 @@ program2.command("done <id>").description("Mark a task as completed").action((id
|
|
|
10636
11083
|
console.log(formatTaskLine(task));
|
|
10637
11084
|
}
|
|
10638
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
|
+
});
|
|
10639
11114
|
program2.command("start <id>").description("Claim, lock, and start a task").action((id) => {
|
|
10640
11115
|
const globalOpts = program2.opts();
|
|
10641
11116
|
const agentId = globalOpts.agent || "cli";
|
|
@@ -10843,6 +11318,76 @@ program2.command("plans").description("List and manage plans").option("--add <na
|
|
|
10843
11318
|
console.log(`${chalk.dim(p.id.slice(0, 8))} ${chalk.bold(p.name)} ${chalk.cyan(`[${p.status}]`)}${desc}`);
|
|
10844
11319
|
}
|
|
10845
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
|
+
});
|
|
10846
11391
|
program2.command("comment <id> <text>").description("Add a comment to a task").action((id, text) => {
|
|
10847
11392
|
const globalOpts = program2.opts();
|
|
10848
11393
|
const resolvedId = resolveTaskId(id);
|