@hasna/todos 0.9.35 → 0.9.38

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 CHANGED
@@ -3101,6 +3101,57 @@ var init_recurrence = __esm(() => {
3101
3101
  };
3102
3102
  });
3103
3103
 
3104
+ // src/db/webhooks.ts
3105
+ var exports_webhooks = {};
3106
+ __export(exports_webhooks, {
3107
+ listWebhooks: () => listWebhooks,
3108
+ getWebhook: () => getWebhook,
3109
+ dispatchWebhook: () => dispatchWebhook,
3110
+ deleteWebhook: () => deleteWebhook,
3111
+ createWebhook: () => createWebhook
3112
+ });
3113
+ function rowToWebhook(row) {
3114
+ return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
3115
+ }
3116
+ function createWebhook(input, db) {
3117
+ const d = db || getDatabase();
3118
+ const id = uuid();
3119
+ d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
3120
+ return getWebhook(id, d);
3121
+ }
3122
+ function getWebhook(id, db) {
3123
+ const d = db || getDatabase();
3124
+ const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
3125
+ return row ? rowToWebhook(row) : null;
3126
+ }
3127
+ function listWebhooks(db) {
3128
+ const d = db || getDatabase();
3129
+ return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
3130
+ }
3131
+ function deleteWebhook(id, db) {
3132
+ const d = db || getDatabase();
3133
+ return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
3134
+ }
3135
+ async function dispatchWebhook(event, payload, db) {
3136
+ const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
3137
+ for (const wh of webhooks) {
3138
+ try {
3139
+ const body = JSON.stringify({ event, payload, timestamp: now() });
3140
+ const headers = { "Content-Type": "application/json" };
3141
+ if (wh.secret) {
3142
+ const encoder = new TextEncoder;
3143
+ const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
3144
+ const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
3145
+ headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
3146
+ }
3147
+ fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
3148
+ } catch {}
3149
+ }
3150
+ }
3151
+ var init_webhooks = __esm(() => {
3152
+ init_database();
3153
+ });
3154
+
3104
3155
  // src/db/tasks.ts
3105
3156
  function rowToTask(row) {
3106
3157
  return {
@@ -3163,7 +3214,9 @@ function createTask(input, db) {
3163
3214
  if (tags.length > 0) {
3164
3215
  insertTaskTags(id, tags, d);
3165
3216
  }
3166
- return getTask(id, d);
3217
+ const task = getTask(id, d);
3218
+ dispatchWebhook("task.created", { id: task.id, short_id: task.short_id, title: task.title, status: task.status, priority: task.priority, project_id: task.project_id, assigned_to: task.assigned_to }, d).catch(() => {});
3219
+ return task;
3167
3220
  }
3168
3221
  function getTask(id, db) {
3169
3222
  const d = db || getDatabase();
@@ -3436,6 +3489,12 @@ function updateTask(id, input, db) {
3436
3489
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
3437
3490
  if (input.approved_by !== undefined)
3438
3491
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
3492
+ if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
3493
+ dispatchWebhook("task.assigned", { id, assigned_to: input.assigned_to, title: task.title }, d).catch(() => {});
3494
+ }
3495
+ if (input.status !== undefined && input.status !== task.status) {
3496
+ dispatchWebhook("task.status_changed", { id, old_status: task.status, new_status: input.status, title: task.title }, d).catch(() => {});
3497
+ }
3439
3498
  return {
3440
3499
  ...task,
3441
3500
  ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
@@ -3487,6 +3546,7 @@ function startTask(id, agentId, db) {
3487
3546
  }
3488
3547
  }
3489
3548
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
3549
+ dispatchWebhook("task.started", { id, agent_id: agentId, title: task.title }, d).catch(() => {});
3490
3550
  return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
3491
3551
  }
3492
3552
  function completeTask(id, agentId, db, options) {
@@ -3508,6 +3568,7 @@ function completeTask(id, agentId, db, options) {
3508
3568
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
3509
3569
  WHERE id = ?`, [timestamp, timestamp, id]);
3510
3570
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
3571
+ dispatchWebhook("task.completed", { id, agent_id: agentId, title: task.title, completed_at: timestamp }, d).catch(() => {});
3511
3572
  let spawnedTask = null;
3512
3573
  if (task.recurrence_rule && !options?.skip_recurrence) {
3513
3574
  spawnedTask = spawnNextRecurrence(task, d);
@@ -3786,6 +3847,7 @@ function failTask(id, agentId, reason, options, db) {
3786
3847
  d.run(`UPDATE tasks SET status = 'failed', locked_by = NULL, locked_at = NULL, metadata = ?, version = version + 1, updated_at = ?
3787
3848
  WHERE id = ?`, [JSON.stringify(meta), timestamp, id]);
3788
3849
  logTaskChange(id, "fail", "status", task.status, "failed", agentId || null, d);
3850
+ dispatchWebhook("task.failed", { id, reason, error_code: options?.error_code, agent_id: agentId, title: task.title }, d).catch(() => {});
3789
3851
  const failedTask = {
3790
3852
  ...task,
3791
3853
  status: "failed",
@@ -3838,6 +3900,37 @@ function getStaleTasks(staleMinutes = 30, filters, db) {
3838
3900
  const rows = d.query(`SELECT * FROM tasks WHERE ${where} ORDER BY updated_at ASC`).all(...params);
3839
3901
  return rows.map(rowToTask);
3840
3902
  }
3903
+ function getStatus(filters, agentId, db) {
3904
+ const d = db || getDatabase();
3905
+ const pending = countTasks({ ...filters, status: "pending" }, d);
3906
+ const in_progress = countTasks({ ...filters, status: "in_progress" }, d);
3907
+ const completed = countTasks({ ...filters, status: "completed" }, d);
3908
+ const total = countTasks(filters || {}, d);
3909
+ const active_work = getActiveWork(filters, d);
3910
+ const next_task = getNextTask(agentId, filters, d);
3911
+ const stale = getStaleTasks(30, filters, d);
3912
+ const conditions = ["recurrence_rule IS NOT NULL", "status = 'pending'", "due_at < ?"];
3913
+ const params = [now()];
3914
+ if (filters?.project_id) {
3915
+ conditions.push("project_id = ?");
3916
+ params.push(filters.project_id);
3917
+ }
3918
+ if (filters?.task_list_id) {
3919
+ conditions.push("task_list_id = ?");
3920
+ params.push(filters.task_list_id);
3921
+ }
3922
+ const overdueRow = d.query(`SELECT COUNT(*) as count FROM tasks WHERE ${conditions.join(" AND ")}`).get(...params);
3923
+ return {
3924
+ pending,
3925
+ in_progress,
3926
+ completed,
3927
+ total,
3928
+ active_work,
3929
+ next_task,
3930
+ stale_count: stale.length,
3931
+ overdue_recurring: overdueRow.count
3932
+ };
3933
+ }
3841
3934
  function wouldCreateCycle(taskId, dependsOn, db) {
3842
3935
  const visited = new Set;
3843
3936
  const queue = [dependsOn];
@@ -3947,6 +4040,7 @@ var init_tasks = __esm(() => {
3947
4040
  init_completion_guard();
3948
4041
  init_audit();
3949
4042
  init_recurrence();
4043
+ init_webhooks();
3950
4044
  });
3951
4045
 
3952
4046
  // src/db/agents.ts
@@ -8905,57 +8999,6 @@ var init_zod = __esm(() => {
8905
8999
  init_external();
8906
9000
  });
8907
9001
 
8908
- // src/db/webhooks.ts
8909
- var exports_webhooks = {};
8910
- __export(exports_webhooks, {
8911
- listWebhooks: () => listWebhooks,
8912
- getWebhook: () => getWebhook,
8913
- dispatchWebhook: () => dispatchWebhook,
8914
- deleteWebhook: () => deleteWebhook,
8915
- createWebhook: () => createWebhook
8916
- });
8917
- function rowToWebhook(row) {
8918
- return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
8919
- }
8920
- function createWebhook(input, db) {
8921
- const d = db || getDatabase();
8922
- const id = uuid();
8923
- d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
8924
- return getWebhook(id, d);
8925
- }
8926
- function getWebhook(id, db) {
8927
- const d = db || getDatabase();
8928
- const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
8929
- return row ? rowToWebhook(row) : null;
8930
- }
8931
- function listWebhooks(db) {
8932
- const d = db || getDatabase();
8933
- return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
8934
- }
8935
- function deleteWebhook(id, db) {
8936
- const d = db || getDatabase();
8937
- return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
8938
- }
8939
- async function dispatchWebhook(event, payload, db) {
8940
- const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
8941
- for (const wh of webhooks) {
8942
- try {
8943
- const body = JSON.stringify({ event, payload, timestamp: now() });
8944
- const headers = { "Content-Type": "application/json" };
8945
- if (wh.secret) {
8946
- const encoder = new TextEncoder;
8947
- const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
8948
- const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
8949
- headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
8950
- }
8951
- fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
8952
- } catch {}
8953
- }
8954
- }
8955
- var init_webhooks = __esm(() => {
8956
- init_database();
8957
- });
8958
-
8959
9002
  // src/mcp/index.ts
8960
9003
  var exports_mcp = {};
8961
9004
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -10376,6 +10419,47 @@ ${text}` }] };
10376
10419
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
10377
10420
  }
10378
10421
  });
10422
+ server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
10423
+ agent_id: exports_external.string().optional(),
10424
+ project_id: exports_external.string().optional(),
10425
+ task_list_id: exports_external.string().optional()
10426
+ }, async ({ agent_id, project_id, task_list_id }) => {
10427
+ try {
10428
+ const filters = {};
10429
+ if (project_id)
10430
+ filters.project_id = resolveId(project_id, "projects");
10431
+ if (task_list_id)
10432
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
10433
+ const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
10434
+ const lines = [
10435
+ `Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
10436
+ ];
10437
+ if (status.stale_count > 0)
10438
+ lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
10439
+ if (status.overdue_recurring > 0)
10440
+ lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
10441
+ if (status.active_work.length > 0) {
10442
+ lines.push(`
10443
+ Active (${status.active_work.length}):`);
10444
+ for (const w of status.active_work.slice(0, 5)) {
10445
+ const id = w.short_id || w.id.slice(0, 8);
10446
+ lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
10447
+ }
10448
+ }
10449
+ if (status.next_task) {
10450
+ lines.push(`
10451
+ Next up:`);
10452
+ lines.push(` ${formatTask(status.next_task)}`);
10453
+ } else {
10454
+ lines.push(`
10455
+ No pending tasks available.`);
10456
+ }
10457
+ return { content: [{ type: "text", text: lines.join(`
10458
+ `) }] };
10459
+ } catch (e) {
10460
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
10461
+ }
10462
+ });
10379
10463
  server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
10380
10464
  const all = [
10381
10465
  "create_task",
@@ -10434,6 +10518,7 @@ ${text}` }] };
10434
10518
  "get_active_work",
10435
10519
  "get_tasks_changed_since",
10436
10520
  "get_stale_tasks",
10521
+ "get_status",
10437
10522
  "search_tools",
10438
10523
  "describe_tools"
10439
10524
  ];
@@ -10601,6 +10686,9 @@ ${text}` }] };
10601
10686
  get_stale_tasks: `Find stale in_progress tasks with no recent activity.
10602
10687
  Params: stale_minutes(number, default:30), project_id(string, optional), task_list_id(string, optional)
10603
10688
  Example: {stale_minutes: 60, project_id: 'a1b2c3d4'}`,
10689
+ get_status: `Get a full project health snapshot \u2014 pending/in_progress/completed counts, active work, next recommended task, stale task count, overdue recurring tasks. Saves 4+ round trips at session start.
10690
+ Params: agent_id(string, optional \u2014 prefers tasks assigned to this agent for next_task), project_id(string, optional), task_list_id(string, optional)
10691
+ Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
10604
10692
  search_tools: `List all tool names or filter by substring.
10605
10693
  Params: query(string, optional)
10606
10694
  Example: {query: 'task'}`,
@@ -13882,6 +13970,131 @@ program2.command("interactive").description("Launch interactive TUI").action(asy
13882
13970
  const projectId = autoProject(globalOpts);
13883
13971
  renderApp2(projectId);
13884
13972
  });
13973
+ program2.command("next").description("Show the best pending task to work on next").option("--agent <id>", "Prefer tasks assigned to this agent").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
13974
+ const db = getDatabase();
13975
+ const filters = {};
13976
+ if (opts.project)
13977
+ filters.project_id = opts.project;
13978
+ const task = getNextTask(opts.agent, Object.keys(filters).length ? filters : undefined, db);
13979
+ if (!task) {
13980
+ console.log(chalk.dim("No tasks available."));
13981
+ return;
13982
+ }
13983
+ if (opts.json) {
13984
+ console.log(JSON.stringify(task, null, 2));
13985
+ return;
13986
+ }
13987
+ console.log(chalk.bold("Next task:"));
13988
+ console.log(` ${chalk.cyan(task.short_id || task.id.slice(0, 8))} ${chalk.yellow(task.priority)} ${task.title}`);
13989
+ if (task.description)
13990
+ console.log(chalk.dim(` ${task.description.slice(0, 100)}`));
13991
+ });
13992
+ program2.command("claim <agent>").description("Atomically claim the best pending task for an agent").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (agent, opts) => {
13993
+ const db = getDatabase();
13994
+ const filters = {};
13995
+ if (opts.project)
13996
+ filters.project_id = opts.project;
13997
+ const task = claimNextTask(agent, Object.keys(filters).length ? filters : undefined, db);
13998
+ if (!task) {
13999
+ console.log(chalk.dim("No tasks available to claim."));
14000
+ return;
14001
+ }
14002
+ if (opts.json) {
14003
+ console.log(JSON.stringify(task, null, 2));
14004
+ return;
14005
+ }
14006
+ console.log(chalk.green(`Claimed: ${task.short_id || task.id.slice(0, 8)} | ${task.priority} | ${task.title}`));
14007
+ });
14008
+ program2.command("status").description("Show full project health snapshot").option("--agent <id>", "Include next task for this agent").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
14009
+ const db = getDatabase();
14010
+ const filters = {};
14011
+ if (opts.project)
14012
+ filters.project_id = opts.project;
14013
+ const s = getStatus(Object.keys(filters).length ? filters : undefined, opts.agent, db);
14014
+ if (opts.json) {
14015
+ console.log(JSON.stringify(s, null, 2));
14016
+ return;
14017
+ }
14018
+ console.log(`Tasks: ${chalk.yellow(s.pending)} pending | ${chalk.blue(s.in_progress)} active | ${chalk.green(s.completed)} done | ${s.total} total`);
14019
+ if (s.stale_count > 0)
14020
+ console.log(chalk.red(`\u26A0\uFE0F ${s.stale_count} stale tasks (stuck in_progress)`));
14021
+ if (s.overdue_recurring > 0)
14022
+ console.log(chalk.yellow(`\uD83D\uDD01 ${s.overdue_recurring} overdue recurring`));
14023
+ if (s.active_work.length > 0) {
14024
+ console.log(chalk.bold(`
14025
+ Active:`));
14026
+ for (const w of s.active_work.slice(0, 5)) {
14027
+ const id = w.short_id || w.id.slice(0, 8);
14028
+ console.log(` ${chalk.cyan(id)} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
14029
+ }
14030
+ }
14031
+ if (s.next_task) {
14032
+ console.log(chalk.bold(`
14033
+ Next up:`));
14034
+ const t = s.next_task;
14035
+ console.log(` ${chalk.cyan(t.short_id || t.id.slice(0, 8))} ${chalk.yellow(t.priority)} ${t.title}`);
14036
+ }
14037
+ });
14038
+ program2.command("fail <id>").description("Mark a task as failed with optional reason and retry").option("--reason <text>", "Why it failed").option("--agent <id>", "Agent reporting the failure").option("--retry", "Auto-create a retry copy").option("--json", "Output as JSON").action(async (id, opts) => {
14039
+ const db = getDatabase();
14040
+ const resolvedId = resolvePartialId(db, "tasks", id);
14041
+ if (!resolvedId) {
14042
+ console.error(chalk.red(`Task not found: ${id}`));
14043
+ process.exit(1);
14044
+ }
14045
+ const result = failTask(resolvedId, opts.agent, opts.reason, { retry: opts.retry }, db);
14046
+ if (opts.json) {
14047
+ console.log(JSON.stringify(result, null, 2));
14048
+ return;
14049
+ }
14050
+ console.log(chalk.red(`Failed: ${result.task.short_id || result.task.id.slice(0, 8)} | ${result.task.title}`));
14051
+ if (opts.reason)
14052
+ console.log(chalk.dim(`Reason: ${opts.reason}`));
14053
+ if (result.retryTask)
14054
+ console.log(chalk.yellow(`Retry created: ${result.retryTask.short_id || result.retryTask.id.slice(0, 8)} | ${result.retryTask.title}`));
14055
+ });
14056
+ program2.command("active").description("Show all currently in-progress tasks").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
14057
+ const db = getDatabase();
14058
+ const filters = {};
14059
+ if (opts.project)
14060
+ filters.project_id = opts.project;
14061
+ const work = getActiveWork(Object.keys(filters).length ? filters : undefined, db);
14062
+ if (opts.json) {
14063
+ console.log(JSON.stringify(work, null, 2));
14064
+ return;
14065
+ }
14066
+ if (work.length === 0) {
14067
+ console.log(chalk.dim("No active work."));
14068
+ return;
14069
+ }
14070
+ console.log(chalk.bold(`Active work (${work.length}):`));
14071
+ for (const w of work) {
14072
+ const id = w.short_id || w.id.slice(0, 8);
14073
+ const agent = w.assigned_to || w.locked_by || "unassigned";
14074
+ console.log(` ${chalk.cyan(id)} | ${chalk.yellow(w.priority)} | ${agent.padEnd(12)} | ${w.title}`);
14075
+ }
14076
+ });
14077
+ program2.command("stale").description("Find tasks stuck in_progress with no recent activity").option("--minutes <n>", "Stale threshold in minutes", "30").option("--project <id>", "Filter to project").option("--json", "Output as JSON").action(async (opts) => {
14078
+ const db = getDatabase();
14079
+ const filters = {};
14080
+ if (opts.project)
14081
+ filters.project_id = opts.project;
14082
+ const tasks = getStaleTasks(parseInt(opts.minutes, 10), Object.keys(filters).length ? filters : undefined, db);
14083
+ if (opts.json) {
14084
+ console.log(JSON.stringify(tasks, null, 2));
14085
+ return;
14086
+ }
14087
+ if (tasks.length === 0) {
14088
+ console.log(chalk.dim("No stale tasks."));
14089
+ return;
14090
+ }
14091
+ console.log(chalk.bold(`Stale tasks (${tasks.length}):`));
14092
+ for (const t of tasks) {
14093
+ const id = t.short_id || t.id.slice(0, 8);
14094
+ const staleMin = Math.round((Date.now() - new Date(t.updated_at).getTime()) / 60000);
14095
+ console.log(` ${chalk.cyan(id)} | ${t.locked_by || t.assigned_to || "?"} | ${t.title} ${chalk.dim(`(${staleMin}min stale)`)}`);
14096
+ }
14097
+ });
13885
14098
  program2.action(async () => {
13886
14099
  if (process.stdout.isTTY) {
13887
14100
  try {
@@ -84,6 +84,20 @@ export declare function getStaleTasks(staleMinutes?: number, filters?: {
84
84
  project_id?: string;
85
85
  task_list_id?: string;
86
86
  }, db?: Database): Task[];
87
+ export interface StatusSummary {
88
+ pending: number;
89
+ in_progress: number;
90
+ completed: number;
91
+ total: number;
92
+ active_work: ActiveWorkItem[];
93
+ next_task: Task | null;
94
+ stale_count: number;
95
+ overdue_recurring: number;
96
+ }
97
+ export declare function getStatus(filters?: {
98
+ project_id?: string;
99
+ task_list_id?: string;
100
+ }, agentId?: string, db?: Database): StatusSummary;
87
101
  export declare function getTaskStats(filters?: {
88
102
  project_id?: string;
89
103
  task_list_id?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/db/tasks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/db/tasks.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { getDatabase, closeDatabase, resetDatabase, resolvePartialId, now, uuid } from "./db/database.js";
2
- export { createTask, getTask, getTaskWithRelations, listTasks, countTasks, updateTask, deleteTask, startTask, completeTask, lockTask, unlockTask, addDependency, removeDependency, getTaskDependencies, getTaskDependents, getBlockingDeps, bulkUpdateTasks, bulkCreateTasks, cloneTask, getTaskStats, getTaskGraph, moveTask, getNextTask, claimNextTask, getActiveWork, failTask, getTasksChangedSince, getStaleTasks, } from "./db/tasks.js";
3
- export type { TaskGraphNode, TaskGraph, BulkCreateTaskInput, ActiveWorkItem } from "./db/tasks.js";
2
+ export { createTask, getTask, getTaskWithRelations, listTasks, countTasks, updateTask, deleteTask, startTask, completeTask, lockTask, unlockTask, addDependency, removeDependency, getTaskDependencies, getTaskDependents, getBlockingDeps, bulkUpdateTasks, bulkCreateTasks, cloneTask, getTaskStats, getTaskGraph, moveTask, getNextTask, claimNextTask, getActiveWork, failTask, getTasksChangedSince, getStaleTasks, getStatus, } from "./db/tasks.js";
3
+ export type { TaskGraphNode, TaskGraph, BulkCreateTaskInput, ActiveWorkItem, StatusSummary } from "./db/tasks.js";
4
4
  export { createProject, getProject, getProjectByPath, listProjects, updateProject, deleteProject, ensureProject, nextTaskShortId, slugify, } from "./db/projects.js";
5
5
  export { createPlan, getPlan, listPlans, updatePlan, deletePlan, } from "./db/plans.js";
6
6
  export { addComment, getComment, listComments, deleteComment, } from "./db/comments.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1015,6 +1015,46 @@ function nextOccurrence(rule, from) {
1015
1015
  throw new Error(`Cannot calculate next occurrence for rule: "${rule}"`);
1016
1016
  }
1017
1017
 
1018
+ // src/db/webhooks.ts
1019
+ function rowToWebhook(row) {
1020
+ return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
1021
+ }
1022
+ function createWebhook(input, db) {
1023
+ const d = db || getDatabase();
1024
+ const id = uuid();
1025
+ d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
1026
+ return getWebhook(id, d);
1027
+ }
1028
+ function getWebhook(id, db) {
1029
+ const d = db || getDatabase();
1030
+ const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
1031
+ return row ? rowToWebhook(row) : null;
1032
+ }
1033
+ function listWebhooks(db) {
1034
+ const d = db || getDatabase();
1035
+ return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
1036
+ }
1037
+ function deleteWebhook(id, db) {
1038
+ const d = db || getDatabase();
1039
+ return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
1040
+ }
1041
+ async function dispatchWebhook(event, payload, db) {
1042
+ const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
1043
+ for (const wh of webhooks) {
1044
+ try {
1045
+ const body = JSON.stringify({ event, payload, timestamp: now() });
1046
+ const headers = { "Content-Type": "application/json" };
1047
+ if (wh.secret) {
1048
+ const encoder = new TextEncoder;
1049
+ const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
1050
+ const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
1051
+ headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
1052
+ }
1053
+ fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
1054
+ } catch {}
1055
+ }
1056
+ }
1057
+
1018
1058
  // src/db/tasks.ts
1019
1059
  function rowToTask(row) {
1020
1060
  return {
@@ -1077,7 +1117,9 @@ function createTask(input, db) {
1077
1117
  if (tags.length > 0) {
1078
1118
  insertTaskTags(id, tags, d);
1079
1119
  }
1080
- return getTask(id, d);
1120
+ const task = getTask(id, d);
1121
+ dispatchWebhook("task.created", { id: task.id, short_id: task.short_id, title: task.title, status: task.status, priority: task.priority, project_id: task.project_id, assigned_to: task.assigned_to }, d).catch(() => {});
1122
+ return task;
1081
1123
  }
1082
1124
  function getTask(id, db) {
1083
1125
  const d = db || getDatabase();
@@ -1350,6 +1392,12 @@ function updateTask(id, input, db) {
1350
1392
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
1351
1393
  if (input.approved_by !== undefined)
1352
1394
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
1395
+ if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
1396
+ dispatchWebhook("task.assigned", { id, assigned_to: input.assigned_to, title: task.title }, d).catch(() => {});
1397
+ }
1398
+ if (input.status !== undefined && input.status !== task.status) {
1399
+ dispatchWebhook("task.status_changed", { id, old_status: task.status, new_status: input.status, title: task.title }, d).catch(() => {});
1400
+ }
1353
1401
  return {
1354
1402
  ...task,
1355
1403
  ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
@@ -1401,6 +1449,7 @@ function startTask(id, agentId, db) {
1401
1449
  }
1402
1450
  }
1403
1451
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
1452
+ dispatchWebhook("task.started", { id, agent_id: agentId, title: task.title }, d).catch(() => {});
1404
1453
  return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
1405
1454
  }
1406
1455
  function completeTask(id, agentId, db, options) {
@@ -1422,6 +1471,7 @@ function completeTask(id, agentId, db, options) {
1422
1471
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
1423
1472
  WHERE id = ?`, [timestamp, timestamp, id]);
1424
1473
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
1474
+ dispatchWebhook("task.completed", { id, agent_id: agentId, title: task.title, completed_at: timestamp }, d).catch(() => {});
1425
1475
  let spawnedTask = null;
1426
1476
  if (task.recurrence_rule && !options?.skip_recurrence) {
1427
1477
  spawnedTask = spawnNextRecurrence(task, d);
@@ -1704,6 +1754,7 @@ function failTask(id, agentId, reason, options, db) {
1704
1754
  d.run(`UPDATE tasks SET status = 'failed', locked_by = NULL, locked_at = NULL, metadata = ?, version = version + 1, updated_at = ?
1705
1755
  WHERE id = ?`, [JSON.stringify(meta), timestamp, id]);
1706
1756
  logTaskChange(id, "fail", "status", task.status, "failed", agentId || null, d);
1757
+ dispatchWebhook("task.failed", { id, reason, error_code: options?.error_code, agent_id: agentId, title: task.title }, d).catch(() => {});
1707
1758
  const failedTask = {
1708
1759
  ...task,
1709
1760
  status: "failed",
@@ -1756,6 +1807,37 @@ function getStaleTasks(staleMinutes = 30, filters, db) {
1756
1807
  const rows = d.query(`SELECT * FROM tasks WHERE ${where} ORDER BY updated_at ASC`).all(...params);
1757
1808
  return rows.map(rowToTask);
1758
1809
  }
1810
+ function getStatus(filters, agentId, db) {
1811
+ const d = db || getDatabase();
1812
+ const pending = countTasks({ ...filters, status: "pending" }, d);
1813
+ const in_progress = countTasks({ ...filters, status: "in_progress" }, d);
1814
+ const completed = countTasks({ ...filters, status: "completed" }, d);
1815
+ const total = countTasks(filters || {}, d);
1816
+ const active_work = getActiveWork(filters, d);
1817
+ const next_task = getNextTask(agentId, filters, d);
1818
+ const stale = getStaleTasks(30, filters, d);
1819
+ const conditions = ["recurrence_rule IS NOT NULL", "status = 'pending'", "due_at < ?"];
1820
+ const params = [now()];
1821
+ if (filters?.project_id) {
1822
+ conditions.push("project_id = ?");
1823
+ params.push(filters.project_id);
1824
+ }
1825
+ if (filters?.task_list_id) {
1826
+ conditions.push("task_list_id = ?");
1827
+ params.push(filters.task_list_id);
1828
+ }
1829
+ const overdueRow = d.query(`SELECT COUNT(*) as count FROM tasks WHERE ${conditions.join(" AND ")}`).get(...params);
1830
+ return {
1831
+ pending,
1832
+ in_progress,
1833
+ completed,
1834
+ total,
1835
+ active_work,
1836
+ next_task,
1837
+ stale_count: stale.length,
1838
+ overdue_recurring: overdueRow.count
1839
+ };
1840
+ }
1759
1841
  function wouldCreateCycle(taskId, dependsOn, db) {
1760
1842
  const visited = new Set;
1761
1843
  const queue = [dependsOn];
@@ -2206,45 +2288,6 @@ function deleteSession(id, db) {
2206
2288
  const result = d.run("DELETE FROM sessions WHERE id = ?", [id]);
2207
2289
  return result.changes > 0;
2208
2290
  }
2209
- // src/db/webhooks.ts
2210
- function rowToWebhook(row) {
2211
- return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
2212
- }
2213
- function createWebhook(input, db) {
2214
- const d = db || getDatabase();
2215
- const id = uuid();
2216
- d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
2217
- return getWebhook(id, d);
2218
- }
2219
- function getWebhook(id, db) {
2220
- const d = db || getDatabase();
2221
- const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
2222
- return row ? rowToWebhook(row) : null;
2223
- }
2224
- function listWebhooks(db) {
2225
- const d = db || getDatabase();
2226
- return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
2227
- }
2228
- function deleteWebhook(id, db) {
2229
- const d = db || getDatabase();
2230
- return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
2231
- }
2232
- async function dispatchWebhook(event, payload, db) {
2233
- const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
2234
- for (const wh of webhooks) {
2235
- try {
2236
- const body = JSON.stringify({ event, payload, timestamp: now() });
2237
- const headers = { "Content-Type": "application/json" };
2238
- if (wh.secret) {
2239
- const encoder = new TextEncoder;
2240
- const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
2241
- const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
2242
- headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
2243
- }
2244
- fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
2245
- } catch {}
2246
- }
2247
- }
2248
2291
  // src/db/templates.ts
2249
2292
  function rowToTemplate(row) {
2250
2293
  return {
@@ -2996,6 +3039,7 @@ export {
2996
3039
  getTaskDependents,
2997
3040
  getTaskDependencies,
2998
3041
  getTask,
3042
+ getStatus,
2999
3043
  getStaleTasks,
3000
3044
  getSession,
3001
3045
  getRecentActivity,
package/dist/mcp/index.js CHANGED
@@ -557,6 +557,57 @@ var init_audit = __esm(() => {
557
557
  init_database();
558
558
  });
559
559
 
560
+ // src/db/webhooks.ts
561
+ var exports_webhooks = {};
562
+ __export(exports_webhooks, {
563
+ listWebhooks: () => listWebhooks,
564
+ getWebhook: () => getWebhook,
565
+ dispatchWebhook: () => dispatchWebhook,
566
+ deleteWebhook: () => deleteWebhook,
567
+ createWebhook: () => createWebhook
568
+ });
569
+ function rowToWebhook(row) {
570
+ return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
571
+ }
572
+ function createWebhook(input, db) {
573
+ const d = db || getDatabase();
574
+ const id = uuid();
575
+ d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
576
+ return getWebhook(id, d);
577
+ }
578
+ function getWebhook(id, db) {
579
+ const d = db || getDatabase();
580
+ const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
581
+ return row ? rowToWebhook(row) : null;
582
+ }
583
+ function listWebhooks(db) {
584
+ const d = db || getDatabase();
585
+ return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
586
+ }
587
+ function deleteWebhook(id, db) {
588
+ const d = db || getDatabase();
589
+ return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
590
+ }
591
+ async function dispatchWebhook(event, payload, db) {
592
+ const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
593
+ for (const wh of webhooks) {
594
+ try {
595
+ const body = JSON.stringify({ event, payload, timestamp: now() });
596
+ const headers = { "Content-Type": "application/json" };
597
+ if (wh.secret) {
598
+ const encoder = new TextEncoder;
599
+ const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
600
+ const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
601
+ headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
602
+ }
603
+ fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
604
+ } catch {}
605
+ }
606
+ }
607
+ var init_webhooks = __esm(() => {
608
+ init_database();
609
+ });
610
+
560
611
  // src/db/agents.ts
561
612
  var exports_agents = {};
562
613
  __export(exports_agents, {
@@ -700,57 +751,6 @@ var init_agents = __esm(() => {
700
751
  init_database();
701
752
  });
702
753
 
703
- // src/db/webhooks.ts
704
- var exports_webhooks = {};
705
- __export(exports_webhooks, {
706
- listWebhooks: () => listWebhooks,
707
- getWebhook: () => getWebhook,
708
- dispatchWebhook: () => dispatchWebhook,
709
- deleteWebhook: () => deleteWebhook,
710
- createWebhook: () => createWebhook
711
- });
712
- function rowToWebhook(row) {
713
- return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
714
- }
715
- function createWebhook(input, db) {
716
- const d = db || getDatabase();
717
- const id = uuid();
718
- d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
719
- return getWebhook(id, d);
720
- }
721
- function getWebhook(id, db) {
722
- const d = db || getDatabase();
723
- const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
724
- return row ? rowToWebhook(row) : null;
725
- }
726
- function listWebhooks(db) {
727
- const d = db || getDatabase();
728
- return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
729
- }
730
- function deleteWebhook(id, db) {
731
- const d = db || getDatabase();
732
- return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
733
- }
734
- async function dispatchWebhook(event, payload, db) {
735
- const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
736
- for (const wh of webhooks) {
737
- try {
738
- const body = JSON.stringify({ event, payload, timestamp: now() });
739
- const headers = { "Content-Type": "application/json" };
740
- if (wh.secret) {
741
- const encoder = new TextEncoder;
742
- const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
743
- const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
744
- headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
745
- }
746
- fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
747
- } catch {}
748
- }
749
- }
750
- var init_webhooks = __esm(() => {
751
- init_database();
752
- });
753
-
754
754
  // src/db/templates.ts
755
755
  var exports_templates = {};
756
756
  __export(exports_templates, {
@@ -5212,6 +5212,7 @@ function nextOccurrence(rule, from) {
5212
5212
  }
5213
5213
 
5214
5214
  // src/db/tasks.ts
5215
+ init_webhooks();
5215
5216
  function rowToTask(row) {
5216
5217
  return {
5217
5218
  ...row,
@@ -5273,7 +5274,9 @@ function createTask(input, db) {
5273
5274
  if (tags.length > 0) {
5274
5275
  insertTaskTags(id, tags, d);
5275
5276
  }
5276
- return getTask(id, d);
5277
+ const task = getTask(id, d);
5278
+ dispatchWebhook("task.created", { id: task.id, short_id: task.short_id, title: task.title, status: task.status, priority: task.priority, project_id: task.project_id, assigned_to: task.assigned_to }, d).catch(() => {});
5279
+ return task;
5277
5280
  }
5278
5281
  function getTask(id, db) {
5279
5282
  const d = db || getDatabase();
@@ -5546,6 +5549,12 @@ function updateTask(id, input, db) {
5546
5549
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
5547
5550
  if (input.approved_by !== undefined)
5548
5551
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
5552
+ if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
5553
+ dispatchWebhook("task.assigned", { id, assigned_to: input.assigned_to, title: task.title }, d).catch(() => {});
5554
+ }
5555
+ if (input.status !== undefined && input.status !== task.status) {
5556
+ dispatchWebhook("task.status_changed", { id, old_status: task.status, new_status: input.status, title: task.title }, d).catch(() => {});
5557
+ }
5549
5558
  return {
5550
5559
  ...task,
5551
5560
  ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
@@ -5597,6 +5606,7 @@ function startTask(id, agentId, db) {
5597
5606
  }
5598
5607
  }
5599
5608
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
5609
+ dispatchWebhook("task.started", { id, agent_id: agentId, title: task.title }, d).catch(() => {});
5600
5610
  return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
5601
5611
  }
5602
5612
  function completeTask(id, agentId, db, options) {
@@ -5618,6 +5628,7 @@ function completeTask(id, agentId, db, options) {
5618
5628
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
5619
5629
  WHERE id = ?`, [timestamp, timestamp, id]);
5620
5630
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
5631
+ dispatchWebhook("task.completed", { id, agent_id: agentId, title: task.title, completed_at: timestamp }, d).catch(() => {});
5621
5632
  let spawnedTask = null;
5622
5633
  if (task.recurrence_rule && !options?.skip_recurrence) {
5623
5634
  spawnedTask = spawnNextRecurrence(task, d);
@@ -5896,6 +5907,7 @@ function failTask(id, agentId, reason, options, db) {
5896
5907
  d.run(`UPDATE tasks SET status = 'failed', locked_by = NULL, locked_at = NULL, metadata = ?, version = version + 1, updated_at = ?
5897
5908
  WHERE id = ?`, [JSON.stringify(meta), timestamp, id]);
5898
5909
  logTaskChange(id, "fail", "status", task.status, "failed", agentId || null, d);
5910
+ dispatchWebhook("task.failed", { id, reason, error_code: options?.error_code, agent_id: agentId, title: task.title }, d).catch(() => {});
5899
5911
  const failedTask = {
5900
5912
  ...task,
5901
5913
  status: "failed",
@@ -5948,6 +5960,37 @@ function getStaleTasks(staleMinutes = 30, filters, db) {
5948
5960
  const rows = d.query(`SELECT * FROM tasks WHERE ${where} ORDER BY updated_at ASC`).all(...params);
5949
5961
  return rows.map(rowToTask);
5950
5962
  }
5963
+ function getStatus(filters, agentId, db) {
5964
+ const d = db || getDatabase();
5965
+ const pending = countTasks({ ...filters, status: "pending" }, d);
5966
+ const in_progress = countTasks({ ...filters, status: "in_progress" }, d);
5967
+ const completed = countTasks({ ...filters, status: "completed" }, d);
5968
+ const total = countTasks(filters || {}, d);
5969
+ const active_work = getActiveWork(filters, d);
5970
+ const next_task = getNextTask(agentId, filters, d);
5971
+ const stale = getStaleTasks(30, filters, d);
5972
+ const conditions = ["recurrence_rule IS NOT NULL", "status = 'pending'", "due_at < ?"];
5973
+ const params = [now()];
5974
+ if (filters?.project_id) {
5975
+ conditions.push("project_id = ?");
5976
+ params.push(filters.project_id);
5977
+ }
5978
+ if (filters?.task_list_id) {
5979
+ conditions.push("task_list_id = ?");
5980
+ params.push(filters.task_list_id);
5981
+ }
5982
+ const overdueRow = d.query(`SELECT COUNT(*) as count FROM tasks WHERE ${conditions.join(" AND ")}`).get(...params);
5983
+ return {
5984
+ pending,
5985
+ in_progress,
5986
+ completed,
5987
+ total,
5988
+ active_work,
5989
+ next_task,
5990
+ stale_count: stale.length,
5991
+ overdue_recurring: overdueRow.count
5992
+ };
5993
+ }
5951
5994
  function wouldCreateCycle(taskId, dependsOn, db) {
5952
5995
  const visited = new Set;
5953
5996
  const queue = [dependsOn];
@@ -8206,6 +8249,47 @@ ${text}` }] };
8206
8249
  return { content: [{ type: "text", text: formatError(e) }], isError: true };
8207
8250
  }
8208
8251
  });
8252
+ server.tool("get_status", "Get a full project health snapshot \u2014 counts, active work, next task, stale/overdue summary.", {
8253
+ agent_id: exports_external.string().optional(),
8254
+ project_id: exports_external.string().optional(),
8255
+ task_list_id: exports_external.string().optional()
8256
+ }, async ({ agent_id, project_id, task_list_id }) => {
8257
+ try {
8258
+ const filters = {};
8259
+ if (project_id)
8260
+ filters.project_id = resolveId(project_id, "projects");
8261
+ if (task_list_id)
8262
+ filters.task_list_id = resolveId(task_list_id, "task_lists");
8263
+ const status = getStatus(Object.keys(filters).length > 0 ? filters : undefined, agent_id);
8264
+ const lines = [
8265
+ `Tasks: ${status.pending} pending | ${status.in_progress} active | ${status.completed} done | ${status.total} total`
8266
+ ];
8267
+ if (status.stale_count > 0)
8268
+ lines.push(`\u26A0\uFE0F ${status.stale_count} stale (stuck in_progress)`);
8269
+ if (status.overdue_recurring > 0)
8270
+ lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
8271
+ if (status.active_work.length > 0) {
8272
+ lines.push(`
8273
+ Active (${status.active_work.length}):`);
8274
+ for (const w of status.active_work.slice(0, 5)) {
8275
+ const id = w.short_id || w.id.slice(0, 8);
8276
+ lines.push(` ${id} | ${w.assigned_to || w.locked_by || "?"} | ${w.title}`);
8277
+ }
8278
+ }
8279
+ if (status.next_task) {
8280
+ lines.push(`
8281
+ Next up:`);
8282
+ lines.push(` ${formatTask(status.next_task)}`);
8283
+ } else {
8284
+ lines.push(`
8285
+ No pending tasks available.`);
8286
+ }
8287
+ return { content: [{ type: "text", text: lines.join(`
8288
+ `) }] };
8289
+ } catch (e) {
8290
+ return { content: [{ type: "text", text: formatError(e) }], isError: true };
8291
+ }
8292
+ });
8209
8293
  server.tool("search_tools", "List all tool names, optionally filtered by substring.", { query: exports_external.string().optional() }, async ({ query }) => {
8210
8294
  const all = [
8211
8295
  "create_task",
@@ -8264,6 +8348,7 @@ server.tool("search_tools", "List all tool names, optionally filtered by substri
8264
8348
  "get_active_work",
8265
8349
  "get_tasks_changed_since",
8266
8350
  "get_stale_tasks",
8351
+ "get_status",
8267
8352
  "search_tools",
8268
8353
  "describe_tools"
8269
8354
  ];
@@ -8431,6 +8516,9 @@ server.tool("describe_tools", "Get detailed parameter info for specific tools by
8431
8516
  get_stale_tasks: `Find stale in_progress tasks with no recent activity.
8432
8517
  Params: stale_minutes(number, default:30), project_id(string, optional), task_list_id(string, optional)
8433
8518
  Example: {stale_minutes: 60, project_id: 'a1b2c3d4'}`,
8519
+ get_status: `Get a full project health snapshot \u2014 pending/in_progress/completed counts, active work, next recommended task, stale task count, overdue recurring tasks. Saves 4+ round trips at session start.
8520
+ Params: agent_id(string, optional \u2014 prefers tasks assigned to this agent for next_task), project_id(string, optional), task_list_id(string, optional)
8521
+ Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
8434
8522
  search_tools: `List all tool names or filter by substring.
8435
8523
  Params: query(string, optional)
8436
8524
  Example: {query: 'task'}`,
@@ -742,6 +742,57 @@ var init_audit = __esm(() => {
742
742
  init_database();
743
743
  });
744
744
 
745
+ // src/db/webhooks.ts
746
+ var exports_webhooks = {};
747
+ __export(exports_webhooks, {
748
+ listWebhooks: () => listWebhooks,
749
+ getWebhook: () => getWebhook,
750
+ dispatchWebhook: () => dispatchWebhook,
751
+ deleteWebhook: () => deleteWebhook,
752
+ createWebhook: () => createWebhook
753
+ });
754
+ function rowToWebhook(row) {
755
+ return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
756
+ }
757
+ function createWebhook(input, db) {
758
+ const d = db || getDatabase();
759
+ const id = uuid();
760
+ d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
761
+ return getWebhook(id, d);
762
+ }
763
+ function getWebhook(id, db) {
764
+ const d = db || getDatabase();
765
+ const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
766
+ return row ? rowToWebhook(row) : null;
767
+ }
768
+ function listWebhooks(db) {
769
+ const d = db || getDatabase();
770
+ return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
771
+ }
772
+ function deleteWebhook(id, db) {
773
+ const d = db || getDatabase();
774
+ return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
775
+ }
776
+ async function dispatchWebhook(event, payload, db) {
777
+ const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
778
+ for (const wh of webhooks) {
779
+ try {
780
+ const body = JSON.stringify({ event, payload, timestamp: now() });
781
+ const headers = { "Content-Type": "application/json" };
782
+ if (wh.secret) {
783
+ const encoder = new TextEncoder;
784
+ const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
785
+ const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
786
+ headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
787
+ }
788
+ fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
789
+ } catch {}
790
+ }
791
+ }
792
+ var init_webhooks = __esm(() => {
793
+ init_database();
794
+ });
795
+
745
796
  // src/db/agents.ts
746
797
  var exports_agents = {};
747
798
  __export(exports_agents, {
@@ -950,57 +1001,6 @@ var init_orgs = __esm(() => {
950
1001
  init_database();
951
1002
  });
952
1003
 
953
- // src/db/webhooks.ts
954
- var exports_webhooks = {};
955
- __export(exports_webhooks, {
956
- listWebhooks: () => listWebhooks,
957
- getWebhook: () => getWebhook,
958
- dispatchWebhook: () => dispatchWebhook,
959
- deleteWebhook: () => deleteWebhook,
960
- createWebhook: () => createWebhook
961
- });
962
- function rowToWebhook(row) {
963
- return { ...row, events: JSON.parse(row.events || "[]"), active: !!row.active };
964
- }
965
- function createWebhook(input, db) {
966
- const d = db || getDatabase();
967
- const id = uuid();
968
- d.run(`INSERT INTO webhooks (id, url, events, secret, created_at) VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
969
- return getWebhook(id, d);
970
- }
971
- function getWebhook(id, db) {
972
- const d = db || getDatabase();
973
- const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
974
- return row ? rowToWebhook(row) : null;
975
- }
976
- function listWebhooks(db) {
977
- const d = db || getDatabase();
978
- return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
979
- }
980
- function deleteWebhook(id, db) {
981
- const d = db || getDatabase();
982
- return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
983
- }
984
- async function dispatchWebhook(event, payload, db) {
985
- const webhooks = listWebhooks(db).filter((w) => w.active && (w.events.length === 0 || w.events.includes(event)));
986
- for (const wh of webhooks) {
987
- try {
988
- const body = JSON.stringify({ event, payload, timestamp: now() });
989
- const headers = { "Content-Type": "application/json" };
990
- if (wh.secret) {
991
- const encoder = new TextEncoder;
992
- const key = await crypto.subtle.importKey("raw", encoder.encode(wh.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
993
- const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
994
- headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
995
- }
996
- fetch(wh.url, { method: "POST", headers, body }).catch(() => {});
997
- } catch {}
998
- }
999
- }
1000
- var init_webhooks = __esm(() => {
1001
- init_database();
1002
- });
1003
-
1004
1004
  // src/db/templates.ts
1005
1005
  var exports_templates = {};
1006
1006
  __export(exports_templates, {
@@ -1265,6 +1265,7 @@ function nextOccurrence(rule, from) {
1265
1265
  }
1266
1266
 
1267
1267
  // src/db/tasks.ts
1268
+ init_webhooks();
1268
1269
  function rowToTask(row) {
1269
1270
  return {
1270
1271
  ...row,
@@ -1326,7 +1327,9 @@ function createTask(input, db) {
1326
1327
  if (tags.length > 0) {
1327
1328
  insertTaskTags(id, tags, d);
1328
1329
  }
1329
- return getTask(id, d);
1330
+ const task = getTask(id, d);
1331
+ dispatchWebhook("task.created", { id: task.id, short_id: task.short_id, title: task.title, status: task.status, priority: task.priority, project_id: task.project_id, assigned_to: task.assigned_to }, d).catch(() => {});
1332
+ return task;
1330
1333
  }
1331
1334
  function getTask(id, db) {
1332
1335
  const d = db || getDatabase();
@@ -1510,6 +1513,12 @@ function updateTask(id, input, db) {
1510
1513
  logTaskChange(id, "update", "assigned_to", task.assigned_to, input.assigned_to, agentId, d);
1511
1514
  if (input.approved_by !== undefined)
1512
1515
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
1516
+ if (input.assigned_to !== undefined && input.assigned_to !== task.assigned_to) {
1517
+ dispatchWebhook("task.assigned", { id, assigned_to: input.assigned_to, title: task.title }, d).catch(() => {});
1518
+ }
1519
+ if (input.status !== undefined && input.status !== task.status) {
1520
+ dispatchWebhook("task.status_changed", { id, old_status: task.status, new_status: input.status, title: task.title }, d).catch(() => {});
1521
+ }
1513
1522
  return {
1514
1523
  ...task,
1515
1524
  ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
@@ -1561,6 +1570,7 @@ function startTask(id, agentId, db) {
1561
1570
  }
1562
1571
  }
1563
1572
  logTaskChange(id, "start", "status", "pending", "in_progress", agentId, d);
1573
+ dispatchWebhook("task.started", { id, agent_id: agentId, title: task.title }, d).catch(() => {});
1564
1574
  return { ...task, status: "in_progress", assigned_to: agentId, locked_by: agentId, locked_at: timestamp, version: task.version + 1, updated_at: timestamp };
1565
1575
  }
1566
1576
  function completeTask(id, agentId, db, options) {
@@ -1582,6 +1592,7 @@ function completeTask(id, agentId, db, options) {
1582
1592
  d.run(`UPDATE tasks SET status = 'completed', locked_by = NULL, locked_at = NULL, completed_at = ?, version = version + 1, updated_at = ?
1583
1593
  WHERE id = ?`, [timestamp, timestamp, id]);
1584
1594
  logTaskChange(id, "complete", "status", task.status, "completed", agentId || null, d);
1595
+ dispatchWebhook("task.completed", { id, agent_id: agentId, title: task.title, completed_at: timestamp }, d).catch(() => {});
1585
1596
  let spawnedTask = null;
1586
1597
  if (task.recurrence_rule && !options?.skip_recurrence) {
1587
1598
  spawnedTask = spawnNextRecurrence(task, d);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.9.35",
3
+ "version": "0.9.38",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",