@hasna/todos 0.9.69 → 0.9.70
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/dashboard/dist/assets/index-DWpVlvWb.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/cli/index.js +311 -115
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.js +194 -82
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/mcp/index.js +311 -115
- package/dist/server/index.js +159 -89
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/index-BXQ39iMX.css +0 -1
- /package/dashboard/dist/assets/{index-B-w1tUlm.js → index-DVzieYOj.js} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -2599,6 +2599,58 @@ var init_database = __esm(() => {
|
|
|
2599
2599
|
ALTER TABLE task_comments ADD COLUMN type TEXT DEFAULT 'comment' CHECK(type IN ('comment', 'progress', 'note'));
|
|
2600
2600
|
ALTER TABLE task_comments ADD COLUMN progress_pct INTEGER CHECK(progress_pct IS NULL OR (progress_pct >= 0 AND progress_pct <= 100));
|
|
2601
2601
|
INSERT OR IGNORE INTO _migrations (id) VALUES (14);
|
|
2602
|
+
`,
|
|
2603
|
+
`
|
|
2604
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS tasks_fts USING fts5(
|
|
2605
|
+
task_id UNINDEXED,
|
|
2606
|
+
title,
|
|
2607
|
+
description,
|
|
2608
|
+
tags,
|
|
2609
|
+
tokenize='unicode61 remove_diacritics 2'
|
|
2610
|
+
);
|
|
2611
|
+
|
|
2612
|
+
INSERT INTO tasks_fts(rowid, task_id, title, description, tags)
|
|
2613
|
+
SELECT t.rowid, t.id, t.title, COALESCE(t.description, ''),
|
|
2614
|
+
COALESCE((SELECT GROUP_CONCAT(tag, ' ') FROM task_tags WHERE task_id = t.id), '')
|
|
2615
|
+
FROM tasks t;
|
|
2616
|
+
|
|
2617
|
+
CREATE TRIGGER IF NOT EXISTS tasks_fts_ai AFTER INSERT ON tasks BEGIN
|
|
2618
|
+
INSERT INTO tasks_fts(rowid, task_id, title, description, tags)
|
|
2619
|
+
VALUES (new.rowid, new.id, new.title, COALESCE(new.description, ''), '');
|
|
2620
|
+
END;
|
|
2621
|
+
|
|
2622
|
+
CREATE TRIGGER IF NOT EXISTS tasks_fts_ad AFTER DELETE ON tasks BEGIN
|
|
2623
|
+
DELETE FROM tasks_fts WHERE rowid = old.rowid;
|
|
2624
|
+
END;
|
|
2625
|
+
|
|
2626
|
+
CREATE TRIGGER IF NOT EXISTS tasks_fts_au AFTER UPDATE OF title, description ON tasks BEGIN
|
|
2627
|
+
DELETE FROM tasks_fts WHERE rowid = old.rowid;
|
|
2628
|
+
INSERT INTO tasks_fts(rowid, task_id, title, description, tags)
|
|
2629
|
+
SELECT new.rowid, new.id, new.title, COALESCE(new.description, ''),
|
|
2630
|
+
COALESCE((SELECT GROUP_CONCAT(tag, ' ') FROM task_tags WHERE task_id = new.id), '');
|
|
2631
|
+
END;
|
|
2632
|
+
|
|
2633
|
+
CREATE TRIGGER IF NOT EXISTS task_tags_fts_ai AFTER INSERT ON task_tags BEGIN
|
|
2634
|
+
DELETE FROM tasks_fts WHERE rowid = (SELECT rowid FROM tasks WHERE id = new.task_id);
|
|
2635
|
+
INSERT INTO tasks_fts(rowid, task_id, title, description, tags)
|
|
2636
|
+
SELECT t.rowid, t.id, t.title, COALESCE(t.description, ''),
|
|
2637
|
+
COALESCE((SELECT GROUP_CONCAT(tag, ' ') FROM task_tags WHERE task_id = t.id), '')
|
|
2638
|
+
FROM tasks t WHERE t.id = new.task_id;
|
|
2639
|
+
END;
|
|
2640
|
+
|
|
2641
|
+
CREATE TRIGGER IF NOT EXISTS task_tags_fts_ad AFTER DELETE ON task_tags BEGIN
|
|
2642
|
+
DELETE FROM tasks_fts WHERE rowid = (SELECT rowid FROM tasks WHERE id = old.task_id);
|
|
2643
|
+
INSERT INTO tasks_fts(rowid, task_id, title, description, tags)
|
|
2644
|
+
SELECT t.rowid, t.id, t.title, COALESCE(t.description, ''),
|
|
2645
|
+
COALESCE((SELECT GROUP_CONCAT(tag, ' ') FROM task_tags WHERE task_id = t.id), '')
|
|
2646
|
+
FROM tasks t WHERE t.id = old.task_id;
|
|
2647
|
+
END;
|
|
2648
|
+
|
|
2649
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (15);
|
|
2650
|
+
`,
|
|
2651
|
+
`
|
|
2652
|
+
ALTER TABLE tasks ADD COLUMN spawns_template_id TEXT REFERENCES task_templates(id) ON DELETE SET NULL;
|
|
2653
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (16);
|
|
2602
2654
|
`
|
|
2603
2655
|
];
|
|
2604
2656
|
});
|
|
@@ -3184,6 +3236,73 @@ var init_webhooks = __esm(() => {
|
|
|
3184
3236
|
init_database();
|
|
3185
3237
|
});
|
|
3186
3238
|
|
|
3239
|
+
// src/db/templates.ts
|
|
3240
|
+
var exports_templates = {};
|
|
3241
|
+
__export(exports_templates, {
|
|
3242
|
+
taskFromTemplate: () => taskFromTemplate,
|
|
3243
|
+
listTemplates: () => listTemplates,
|
|
3244
|
+
getTemplate: () => getTemplate,
|
|
3245
|
+
deleteTemplate: () => deleteTemplate,
|
|
3246
|
+
createTemplate: () => createTemplate
|
|
3247
|
+
});
|
|
3248
|
+
function rowToTemplate(row) {
|
|
3249
|
+
return {
|
|
3250
|
+
...row,
|
|
3251
|
+
tags: JSON.parse(row.tags || "[]"),
|
|
3252
|
+
metadata: JSON.parse(row.metadata || "{}"),
|
|
3253
|
+
priority: row.priority || "medium"
|
|
3254
|
+
};
|
|
3255
|
+
}
|
|
3256
|
+
function createTemplate(input, db) {
|
|
3257
|
+
const d = db || getDatabase();
|
|
3258
|
+
const id = uuid();
|
|
3259
|
+
d.run(`INSERT INTO task_templates (id, name, title_pattern, description, priority, tags, project_id, plan_id, metadata, created_at)
|
|
3260
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
3261
|
+
id,
|
|
3262
|
+
input.name,
|
|
3263
|
+
input.title_pattern,
|
|
3264
|
+
input.description || null,
|
|
3265
|
+
input.priority || "medium",
|
|
3266
|
+
JSON.stringify(input.tags || []),
|
|
3267
|
+
input.project_id || null,
|
|
3268
|
+
input.plan_id || null,
|
|
3269
|
+
JSON.stringify(input.metadata || {}),
|
|
3270
|
+
now()
|
|
3271
|
+
]);
|
|
3272
|
+
return getTemplate(id, d);
|
|
3273
|
+
}
|
|
3274
|
+
function getTemplate(id, db) {
|
|
3275
|
+
const d = db || getDatabase();
|
|
3276
|
+
const row = d.query("SELECT * FROM task_templates WHERE id = ?").get(id);
|
|
3277
|
+
return row ? rowToTemplate(row) : null;
|
|
3278
|
+
}
|
|
3279
|
+
function listTemplates(db) {
|
|
3280
|
+
const d = db || getDatabase();
|
|
3281
|
+
return d.query("SELECT * FROM task_templates ORDER BY name").all().map(rowToTemplate);
|
|
3282
|
+
}
|
|
3283
|
+
function deleteTemplate(id, db) {
|
|
3284
|
+
const d = db || getDatabase();
|
|
3285
|
+
return d.run("DELETE FROM task_templates WHERE id = ?", [id]).changes > 0;
|
|
3286
|
+
}
|
|
3287
|
+
function taskFromTemplate(templateId, overrides = {}, db) {
|
|
3288
|
+
const t = getTemplate(templateId, db);
|
|
3289
|
+
if (!t)
|
|
3290
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
3291
|
+
return {
|
|
3292
|
+
title: overrides.title || t.title_pattern,
|
|
3293
|
+
description: overrides.description ?? t.description ?? undefined,
|
|
3294
|
+
priority: overrides.priority ?? t.priority,
|
|
3295
|
+
tags: overrides.tags ?? t.tags,
|
|
3296
|
+
project_id: overrides.project_id ?? t.project_id ?? undefined,
|
|
3297
|
+
plan_id: overrides.plan_id ?? t.plan_id ?? undefined,
|
|
3298
|
+
metadata: overrides.metadata ?? t.metadata,
|
|
3299
|
+
...overrides
|
|
3300
|
+
};
|
|
3301
|
+
}
|
|
3302
|
+
var init_templates = __esm(() => {
|
|
3303
|
+
init_database();
|
|
3304
|
+
});
|
|
3305
|
+
|
|
3187
3306
|
// src/db/tasks.ts
|
|
3188
3307
|
var exports_tasks = {};
|
|
3189
3308
|
__export(exports_tasks, {
|
|
@@ -3250,8 +3369,8 @@ function createTask(input, db) {
|
|
|
3250
3369
|
const tags = input.tags || [];
|
|
3251
3370
|
const shortId = input.project_id ? nextTaskShortId(input.project_id, d) : null;
|
|
3252
3371
|
const title = shortId ? `${shortId}: ${input.title}` : input.title;
|
|
3253
|
-
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id)
|
|
3254
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
3372
|
+
d.run(`INSERT INTO tasks (id, short_id, project_id, parent_id, plan_id, task_list_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at, due_at, estimated_minutes, requires_approval, approved_by, approved_at, recurrence_rule, recurrence_parent_id, spawns_template_id)
|
|
3373
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
3255
3374
|
id,
|
|
3256
3375
|
shortId,
|
|
3257
3376
|
input.project_id || null,
|
|
@@ -3276,7 +3395,8 @@ function createTask(input, db) {
|
|
|
3276
3395
|
null,
|
|
3277
3396
|
null,
|
|
3278
3397
|
input.recurrence_rule || null,
|
|
3279
|
-
input.recurrence_parent_id || null
|
|
3398
|
+
input.recurrence_parent_id || null,
|
|
3399
|
+
input.spawns_template_id || null
|
|
3280
3400
|
]);
|
|
3281
3401
|
if (tags.length > 0) {
|
|
3282
3402
|
insertTaskTags(id, tags, d);
|
|
@@ -3383,19 +3503,25 @@ function listTasks(filter = {}, db) {
|
|
|
3383
3503
|
} else if (filter.has_recurrence === false) {
|
|
3384
3504
|
conditions.push("recurrence_rule IS NULL");
|
|
3385
3505
|
}
|
|
3506
|
+
const PRIORITY_RANK = `CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END`;
|
|
3507
|
+
if (filter.cursor) {
|
|
3508
|
+
try {
|
|
3509
|
+
const decoded = JSON.parse(Buffer.from(filter.cursor, "base64").toString("utf8"));
|
|
3510
|
+
conditions.push(`(${PRIORITY_RANK} > ? OR (${PRIORITY_RANK} = ? AND created_at < ?) OR (${PRIORITY_RANK} = ? AND created_at = ? AND id > ?))`);
|
|
3511
|
+
params.push(decoded.p, decoded.p, decoded.c, decoded.p, decoded.c, decoded.i);
|
|
3512
|
+
} catch {}
|
|
3513
|
+
}
|
|
3386
3514
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3387
3515
|
let limitClause = "";
|
|
3388
3516
|
if (filter.limit) {
|
|
3389
3517
|
limitClause = " LIMIT ?";
|
|
3390
3518
|
params.push(filter.limit);
|
|
3391
|
-
if (filter.offset) {
|
|
3519
|
+
if (!filter.cursor && filter.offset) {
|
|
3392
3520
|
limitClause += " OFFSET ?";
|
|
3393
3521
|
params.push(filter.offset);
|
|
3394
3522
|
}
|
|
3395
3523
|
}
|
|
3396
|
-
const rows = d.query(`SELECT * FROM tasks ${where} ORDER BY
|
|
3397
|
-
CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
3398
|
-
created_at DESC${limitClause}`).all(...params);
|
|
3524
|
+
const rows = d.query(`SELECT * FROM tasks ${where} ORDER BY ${PRIORITY_RANK}, created_at DESC${limitClause}`).all(...params);
|
|
3399
3525
|
return rows.map(rowToTask);
|
|
3400
3526
|
}
|
|
3401
3527
|
function countTasks(filter = {}, db) {
|
|
@@ -3640,10 +3766,25 @@ function completeTask(id, agentId, db, options) {
|
|
|
3640
3766
|
if (task.recurrence_rule && !options?.skip_recurrence) {
|
|
3641
3767
|
spawnedTask = spawnNextRecurrence(task, d);
|
|
3642
3768
|
}
|
|
3769
|
+
let spawnedFromTemplate = null;
|
|
3770
|
+
if (task.spawns_template_id) {
|
|
3771
|
+
try {
|
|
3772
|
+
const input = taskFromTemplate(task.spawns_template_id, {
|
|
3773
|
+
project_id: task.project_id ?? undefined,
|
|
3774
|
+
plan_id: task.plan_id ?? undefined,
|
|
3775
|
+
task_list_id: task.task_list_id ?? undefined,
|
|
3776
|
+
assigned_to: task.assigned_to ?? undefined
|
|
3777
|
+
}, d);
|
|
3778
|
+
spawnedFromTemplate = createTask(input, d);
|
|
3779
|
+
} catch {}
|
|
3780
|
+
}
|
|
3643
3781
|
const meta = hasEvidence ? { ...task.metadata, _evidence: evidence } : task.metadata;
|
|
3644
3782
|
if (spawnedTask) {
|
|
3645
3783
|
meta._next_recurrence = { id: spawnedTask.id, short_id: spawnedTask.short_id, due_at: spawnedTask.due_at };
|
|
3646
3784
|
}
|
|
3785
|
+
if (spawnedFromTemplate) {
|
|
3786
|
+
meta._spawned_task = { id: spawnedFromTemplate.id, short_id: spawnedFromTemplate.short_id, title: spawnedFromTemplate.title };
|
|
3787
|
+
}
|
|
3647
3788
|
return { ...task, status: "completed", locked_by: null, locked_at: null, completed_at: timestamp, version: task.version + 1, updated_at: timestamp, metadata: meta };
|
|
3648
3789
|
}
|
|
3649
3790
|
function lockTask(id, agentId, db) {
|
|
@@ -3855,11 +3996,21 @@ function getNextTask(agentId, filters, db) {
|
|
|
3855
3996
|
}
|
|
3856
3997
|
conditions.push("id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')");
|
|
3857
3998
|
const where = conditions.join(" AND ");
|
|
3999
|
+
let recentProjectIds = [];
|
|
4000
|
+
if (agentId) {
|
|
4001
|
+
const recentRows = d.query(`SELECT DISTINCT project_id FROM tasks WHERE assigned_to = ? AND status = 'completed' AND project_id IS NOT NULL ORDER BY completed_at DESC LIMIT 3`).all(agentId);
|
|
4002
|
+
recentProjectIds = recentRows.map((r) => r.project_id);
|
|
4003
|
+
}
|
|
3858
4004
|
let sql = `SELECT * FROM tasks WHERE ${where} ORDER BY `;
|
|
3859
4005
|
if (agentId) {
|
|
3860
4006
|
sql += `CASE WHEN assigned_to = ? THEN 0 WHEN assigned_to IS NULL THEN 1 ELSE 2 END, `;
|
|
3861
4007
|
params.push(agentId);
|
|
3862
4008
|
}
|
|
4009
|
+
if (recentProjectIds.length > 0) {
|
|
4010
|
+
const placeholders = recentProjectIds.map(() => "?").join(",");
|
|
4011
|
+
sql += `CASE WHEN project_id IN (${placeholders}) THEN 0 ELSE 1 END, `;
|
|
4012
|
+
params.push(...recentProjectIds);
|
|
4013
|
+
}
|
|
3863
4014
|
sql += `CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END, created_at ASC LIMIT 1`;
|
|
3864
4015
|
const row = d.query(sql).get(...params);
|
|
3865
4016
|
return row ? rowToTask(row) : null;
|
|
@@ -4195,6 +4346,7 @@ var init_tasks = __esm(() => {
|
|
|
4195
4346
|
init_audit();
|
|
4196
4347
|
init_recurrence();
|
|
4197
4348
|
init_webhooks();
|
|
4349
|
+
init_templates();
|
|
4198
4350
|
});
|
|
4199
4351
|
|
|
4200
4352
|
// src/db/agents.ts
|
|
@@ -4541,68 +4693,95 @@ function rowToTask2(row) {
|
|
|
4541
4693
|
requires_approval: Boolean(row.requires_approval)
|
|
4542
4694
|
};
|
|
4543
4695
|
}
|
|
4696
|
+
function hasFts(db) {
|
|
4697
|
+
try {
|
|
4698
|
+
const result = db.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name='tasks_fts'").get();
|
|
4699
|
+
return result !== null;
|
|
4700
|
+
} catch {
|
|
4701
|
+
return false;
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
function escapeFtsQuery(q) {
|
|
4705
|
+
return q.replace(/["*^()]/g, " ").trim().split(/\s+/).filter(Boolean).map((token) => `"${token}"*`).join(" ");
|
|
4706
|
+
}
|
|
4544
4707
|
function searchTasks(options, projectId, taskListId, db) {
|
|
4545
4708
|
const opts = typeof options === "string" ? { query: options, project_id: projectId, task_list_id: taskListId } : options;
|
|
4546
4709
|
const d = db || getDatabase();
|
|
4547
4710
|
clearExpiredLocks(d);
|
|
4548
|
-
const
|
|
4549
|
-
let sql
|
|
4550
|
-
|
|
4711
|
+
const params = [];
|
|
4712
|
+
let sql;
|
|
4713
|
+
if (hasFts(d) && opts.query.trim()) {
|
|
4714
|
+
const ftsQuery = escapeFtsQuery(opts.query);
|
|
4715
|
+
sql = `SELECT t.* FROM tasks t
|
|
4716
|
+
INNER JOIN tasks_fts fts ON fts.rowid = t.rowid
|
|
4717
|
+
WHERE tasks_fts MATCH ?`;
|
|
4718
|
+
params.push(ftsQuery);
|
|
4719
|
+
} else {
|
|
4720
|
+
const pattern = `%${opts.query}%`;
|
|
4721
|
+
sql = `SELECT * FROM tasks t WHERE (t.title LIKE ? OR t.description LIKE ? OR EXISTS (SELECT 1 FROM task_tags WHERE task_tags.task_id = t.id AND tag LIKE ?))`;
|
|
4722
|
+
params.push(pattern, pattern, pattern);
|
|
4723
|
+
}
|
|
4551
4724
|
if (opts.project_id) {
|
|
4552
|
-
sql += " AND project_id = ?";
|
|
4725
|
+
sql += " AND t.project_id = ?";
|
|
4553
4726
|
params.push(opts.project_id);
|
|
4554
4727
|
}
|
|
4555
4728
|
if (opts.task_list_id) {
|
|
4556
|
-
sql += " AND task_list_id = ?";
|
|
4729
|
+
sql += " AND t.task_list_id = ?";
|
|
4557
4730
|
params.push(opts.task_list_id);
|
|
4558
4731
|
}
|
|
4559
4732
|
if (opts.status) {
|
|
4560
4733
|
if (Array.isArray(opts.status)) {
|
|
4561
|
-
sql += ` AND status IN (${opts.status.map(() => "?").join(",")})`;
|
|
4734
|
+
sql += ` AND t.status IN (${opts.status.map(() => "?").join(",")})`;
|
|
4562
4735
|
params.push(...opts.status);
|
|
4563
4736
|
} else {
|
|
4564
|
-
sql += " AND status = ?";
|
|
4737
|
+
sql += " AND t.status = ?";
|
|
4565
4738
|
params.push(opts.status);
|
|
4566
4739
|
}
|
|
4567
4740
|
}
|
|
4568
4741
|
if (opts.priority) {
|
|
4569
4742
|
if (Array.isArray(opts.priority)) {
|
|
4570
|
-
sql += ` AND priority IN (${opts.priority.map(() => "?").join(",")})`;
|
|
4743
|
+
sql += ` AND t.priority IN (${opts.priority.map(() => "?").join(",")})`;
|
|
4571
4744
|
params.push(...opts.priority);
|
|
4572
4745
|
} else {
|
|
4573
|
-
sql += " AND priority = ?";
|
|
4746
|
+
sql += " AND t.priority = ?";
|
|
4574
4747
|
params.push(opts.priority);
|
|
4575
4748
|
}
|
|
4576
4749
|
}
|
|
4577
4750
|
if (opts.assigned_to) {
|
|
4578
|
-
sql += " AND assigned_to = ?";
|
|
4751
|
+
sql += " AND t.assigned_to = ?";
|
|
4579
4752
|
params.push(opts.assigned_to);
|
|
4580
4753
|
}
|
|
4581
4754
|
if (opts.agent_id) {
|
|
4582
|
-
sql += " AND agent_id = ?";
|
|
4755
|
+
sql += " AND t.agent_id = ?";
|
|
4583
4756
|
params.push(opts.agent_id);
|
|
4584
4757
|
}
|
|
4585
4758
|
if (opts.created_after) {
|
|
4586
|
-
sql += " AND created_at > ?";
|
|
4759
|
+
sql += " AND t.created_at > ?";
|
|
4587
4760
|
params.push(opts.created_after);
|
|
4588
4761
|
}
|
|
4589
4762
|
if (opts.updated_after) {
|
|
4590
|
-
sql += " AND updated_at > ?";
|
|
4763
|
+
sql += " AND t.updated_at > ?";
|
|
4591
4764
|
params.push(opts.updated_after);
|
|
4592
4765
|
}
|
|
4593
4766
|
if (opts.has_dependencies === true) {
|
|
4594
|
-
sql += " AND id IN (SELECT task_id FROM task_dependencies)";
|
|
4767
|
+
sql += " AND t.id IN (SELECT task_id FROM task_dependencies)";
|
|
4595
4768
|
} else if (opts.has_dependencies === false) {
|
|
4596
|
-
sql += " AND id NOT IN (SELECT task_id FROM task_dependencies)";
|
|
4769
|
+
sql += " AND t.id NOT IN (SELECT task_id FROM task_dependencies)";
|
|
4597
4770
|
}
|
|
4598
4771
|
if (opts.is_blocked === true) {
|
|
4599
|
-
sql += " AND id IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
|
|
4772
|
+
sql += " AND t.id IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
|
|
4600
4773
|
} else if (opts.is_blocked === false) {
|
|
4601
|
-
sql += " AND id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
|
|
4774
|
+
sql += " AND t.id NOT IN (SELECT td.task_id FROM task_dependencies td JOIN tasks dep ON dep.id = td.depends_on WHERE dep.status != 'completed')";
|
|
4775
|
+
}
|
|
4776
|
+
if (hasFts(d) && opts.query.trim()) {
|
|
4777
|
+
sql += ` ORDER BY bm25(tasks_fts),
|
|
4778
|
+
CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
4779
|
+
t.created_at DESC`;
|
|
4780
|
+
} else {
|
|
4781
|
+
sql += ` ORDER BY
|
|
4782
|
+
CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
4783
|
+
t.created_at DESC`;
|
|
4602
4784
|
}
|
|
4603
|
-
sql += ` ORDER BY
|
|
4604
|
-
CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
4605
|
-
created_at DESC`;
|
|
4606
4785
|
const rows = d.query(sql).all(...params);
|
|
4607
4786
|
return rows.map(rowToTask2);
|
|
4608
4787
|
}
|
|
@@ -5142,73 +5321,6 @@ var init_sync = __esm(() => {
|
|
|
5142
5321
|
init_config();
|
|
5143
5322
|
});
|
|
5144
5323
|
|
|
5145
|
-
// src/db/templates.ts
|
|
5146
|
-
var exports_templates = {};
|
|
5147
|
-
__export(exports_templates, {
|
|
5148
|
-
taskFromTemplate: () => taskFromTemplate,
|
|
5149
|
-
listTemplates: () => listTemplates,
|
|
5150
|
-
getTemplate: () => getTemplate,
|
|
5151
|
-
deleteTemplate: () => deleteTemplate,
|
|
5152
|
-
createTemplate: () => createTemplate
|
|
5153
|
-
});
|
|
5154
|
-
function rowToTemplate(row) {
|
|
5155
|
-
return {
|
|
5156
|
-
...row,
|
|
5157
|
-
tags: JSON.parse(row.tags || "[]"),
|
|
5158
|
-
metadata: JSON.parse(row.metadata || "{}"),
|
|
5159
|
-
priority: row.priority || "medium"
|
|
5160
|
-
};
|
|
5161
|
-
}
|
|
5162
|
-
function createTemplate(input, db) {
|
|
5163
|
-
const d = db || getDatabase();
|
|
5164
|
-
const id = uuid();
|
|
5165
|
-
d.run(`INSERT INTO task_templates (id, name, title_pattern, description, priority, tags, project_id, plan_id, metadata, created_at)
|
|
5166
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
5167
|
-
id,
|
|
5168
|
-
input.name,
|
|
5169
|
-
input.title_pattern,
|
|
5170
|
-
input.description || null,
|
|
5171
|
-
input.priority || "medium",
|
|
5172
|
-
JSON.stringify(input.tags || []),
|
|
5173
|
-
input.project_id || null,
|
|
5174
|
-
input.plan_id || null,
|
|
5175
|
-
JSON.stringify(input.metadata || {}),
|
|
5176
|
-
now()
|
|
5177
|
-
]);
|
|
5178
|
-
return getTemplate(id, d);
|
|
5179
|
-
}
|
|
5180
|
-
function getTemplate(id, db) {
|
|
5181
|
-
const d = db || getDatabase();
|
|
5182
|
-
const row = d.query("SELECT * FROM task_templates WHERE id = ?").get(id);
|
|
5183
|
-
return row ? rowToTemplate(row) : null;
|
|
5184
|
-
}
|
|
5185
|
-
function listTemplates(db) {
|
|
5186
|
-
const d = db || getDatabase();
|
|
5187
|
-
return d.query("SELECT * FROM task_templates ORDER BY name").all().map(rowToTemplate);
|
|
5188
|
-
}
|
|
5189
|
-
function deleteTemplate(id, db) {
|
|
5190
|
-
const d = db || getDatabase();
|
|
5191
|
-
return d.run("DELETE FROM task_templates WHERE id = ?", [id]).changes > 0;
|
|
5192
|
-
}
|
|
5193
|
-
function taskFromTemplate(templateId, overrides = {}, db) {
|
|
5194
|
-
const t = getTemplate(templateId, db);
|
|
5195
|
-
if (!t)
|
|
5196
|
-
throw new Error(`Template not found: ${templateId}`);
|
|
5197
|
-
return {
|
|
5198
|
-
title: overrides.title || t.title_pattern,
|
|
5199
|
-
description: overrides.description ?? t.description ?? undefined,
|
|
5200
|
-
priority: overrides.priority ?? t.priority,
|
|
5201
|
-
tags: overrides.tags ?? t.tags,
|
|
5202
|
-
project_id: overrides.project_id ?? t.project_id ?? undefined,
|
|
5203
|
-
plan_id: overrides.plan_id ?? t.plan_id ?? undefined,
|
|
5204
|
-
metadata: overrides.metadata ?? t.metadata,
|
|
5205
|
-
...overrides
|
|
5206
|
-
};
|
|
5207
|
-
}
|
|
5208
|
-
var init_templates = __esm(() => {
|
|
5209
|
-
init_database();
|
|
5210
|
-
});
|
|
5211
|
-
|
|
5212
5324
|
// node_modules/zod/v3/helpers/util.js
|
|
5213
5325
|
var util, objectUtil, ZodParsedType, getParsedType = (data) => {
|
|
5214
5326
|
const t = typeof data;
|
|
@@ -9256,15 +9368,17 @@ function formatTask(task) {
|
|
|
9256
9368
|
const recur = task.recurrence_rule ? ` [\u21BB]` : "";
|
|
9257
9369
|
return `${id} ${task.status.padEnd(11)} ${task.priority.padEnd(8)} ${task.title}${assigned}${lock}${recur}`;
|
|
9258
9370
|
}
|
|
9259
|
-
function formatTaskDetail(task) {
|
|
9371
|
+
function formatTaskDetail(task, maxDescriptionChars) {
|
|
9260
9372
|
const parts = [
|
|
9261
9373
|
`ID: ${task.id}`,
|
|
9262
9374
|
`Title: ${task.title}`,
|
|
9263
9375
|
`Status: ${task.status}`,
|
|
9264
9376
|
`Priority: ${task.priority}`
|
|
9265
9377
|
];
|
|
9266
|
-
if (task.description)
|
|
9267
|
-
|
|
9378
|
+
if (task.description) {
|
|
9379
|
+
const desc = maxDescriptionChars && task.description.length > maxDescriptionChars ? task.description.slice(0, maxDescriptionChars) + "\u2026" : task.description;
|
|
9380
|
+
parts.push(`Description: ${desc}`);
|
|
9381
|
+
}
|
|
9268
9382
|
if (task.assigned_to)
|
|
9269
9383
|
parts.push(`Assigned to: ${task.assigned_to}`);
|
|
9270
9384
|
if (task.agent_id)
|
|
@@ -9322,7 +9436,9 @@ var init_mcp = __esm(() => {
|
|
|
9322
9436
|
"get_task",
|
|
9323
9437
|
"start_task",
|
|
9324
9438
|
"add_comment",
|
|
9325
|
-
"get_next_task"
|
|
9439
|
+
"get_next_task",
|
|
9440
|
+
"bootstrap",
|
|
9441
|
+
"get_tasks_changed_since"
|
|
9326
9442
|
]);
|
|
9327
9443
|
STANDARD_EXCLUDED = new Set([
|
|
9328
9444
|
"get_org_chart",
|
|
@@ -9356,7 +9472,8 @@ var init_mcp = __esm(() => {
|
|
|
9356
9472
|
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
9357
9473
|
estimated_minutes: exports_external.number().optional(),
|
|
9358
9474
|
requires_approval: exports_external.boolean().optional(),
|
|
9359
|
-
recurrence_rule: exports_external.string().optional()
|
|
9475
|
+
recurrence_rule: exports_external.string().optional(),
|
|
9476
|
+
spawns_template_id: exports_external.string().optional().describe("Template ID to auto-create as next task when this task is completed (pipeline/handoff chains)")
|
|
9360
9477
|
}, async (params) => {
|
|
9361
9478
|
try {
|
|
9362
9479
|
const resolved = { ...params };
|
|
@@ -9376,7 +9493,7 @@ var init_mcp = __esm(() => {
|
|
|
9376
9493
|
});
|
|
9377
9494
|
}
|
|
9378
9495
|
if (shouldRegisterTool("list_tasks")) {
|
|
9379
|
-
server.tool("list_tasks", "List tasks with optional filters and pagination.", {
|
|
9496
|
+
server.tool("list_tasks", "List tasks with optional filters and pagination. Default limit is 50 \u2014 use offset to page through results.", {
|
|
9380
9497
|
project_id: exports_external.string().optional(),
|
|
9381
9498
|
status: exports_external.union([
|
|
9382
9499
|
exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]),
|
|
@@ -9394,11 +9511,15 @@ var init_mcp = __esm(() => {
|
|
|
9394
9511
|
due_today: exports_external.boolean().optional(),
|
|
9395
9512
|
overdue: exports_external.boolean().optional(),
|
|
9396
9513
|
limit: exports_external.number().optional(),
|
|
9397
|
-
offset: exports_external.number().optional()
|
|
9514
|
+
offset: exports_external.number().optional(),
|
|
9515
|
+
summary_only: exports_external.boolean().optional().describe("When true, return only id, short_id, title, status, priority \u2014 minimal tokens for navigation"),
|
|
9516
|
+
cursor: exports_external.string().optional().describe("Opaque cursor from a prior response for stable pagination. Use next_cursor from the previous page. Mutually exclusive with offset.")
|
|
9398
9517
|
}, async (params) => {
|
|
9399
9518
|
try {
|
|
9400
|
-
const { due_today, overdue, ...rest } = params;
|
|
9519
|
+
const { due_today, overdue, summary_only, ...rest } = params;
|
|
9401
9520
|
const resolved = { ...rest };
|
|
9521
|
+
if (resolved.limit === undefined)
|
|
9522
|
+
resolved.limit = 50;
|
|
9402
9523
|
if (resolved.project_id)
|
|
9403
9524
|
resolved.project_id = resolveId(resolved.project_id, "projects");
|
|
9404
9525
|
if (resolved.plan_id)
|
|
@@ -9420,6 +9541,9 @@ var init_mcp = __esm(() => {
|
|
|
9420
9541
|
return { content: [{ type: "text", text: total > 0 ? `No tasks in this page (total: ${total}).` : "No tasks found." }] };
|
|
9421
9542
|
}
|
|
9422
9543
|
const text = tasks.map((t) => {
|
|
9544
|
+
if (summary_only) {
|
|
9545
|
+
return `${t.short_id || t.id.slice(0, 8)} [${t.status}] ${t.priority} ${t.title}`;
|
|
9546
|
+
}
|
|
9423
9547
|
const lock = t.locked_by ? ` [locked by ${t.locked_by}]` : "";
|
|
9424
9548
|
const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
|
|
9425
9549
|
const due = t.due_at ? ` due:${t.due_at.slice(0, 10)}` : "";
|
|
@@ -9427,10 +9551,23 @@ var init_mcp = __esm(() => {
|
|
|
9427
9551
|
return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${lock}${due}${recur}`;
|
|
9428
9552
|
}).join(`
|
|
9429
9553
|
`);
|
|
9430
|
-
const
|
|
9431
|
-
|
|
9554
|
+
const currentOffset = resolved.offset || 0;
|
|
9555
|
+
const hasMore = total > (resolved.cursor ? tasks.length : currentOffset + tasks.length);
|
|
9556
|
+
let paginationNote = `
|
|
9557
|
+
(showing ${tasks.length} of ${total}`;
|
|
9558
|
+
if (hasMore) {
|
|
9559
|
+
if (resolved.cursor || tasks.length > 0) {
|
|
9560
|
+
const last = tasks[tasks.length - 1];
|
|
9561
|
+
const priorityRank = { critical: 0, high: 1, medium: 2, low: 3 }[last.priority] ?? 3;
|
|
9562
|
+
const cursorPayload = Buffer.from(JSON.stringify({ p: priorityRank, c: last.created_at, i: last.id })).toString("base64");
|
|
9563
|
+
paginationNote += ` \u2014 next_cursor: ${cursorPayload}`;
|
|
9564
|
+
} else {
|
|
9565
|
+
paginationNote += ` \u2014 use offset: ${currentOffset + tasks.length} to get next page`;
|
|
9566
|
+
}
|
|
9567
|
+
}
|
|
9568
|
+
paginationNote += ")";
|
|
9432
9569
|
return { content: [{ type: "text", text: `${tasks.length} task(s):
|
|
9433
|
-
${text}${
|
|
9570
|
+
${text}${paginationNote}` }] };
|
|
9434
9571
|
} catch (e) {
|
|
9435
9572
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
9436
9573
|
}
|
|
@@ -9438,14 +9575,15 @@ ${text}${pagination}` }] };
|
|
|
9438
9575
|
}
|
|
9439
9576
|
if (shouldRegisterTool("get_task")) {
|
|
9440
9577
|
server.tool("get_task", "Get full task details with subtasks, deps, and comments.", {
|
|
9441
|
-
id: exports_external.string()
|
|
9442
|
-
|
|
9578
|
+
id: exports_external.string(),
|
|
9579
|
+
max_description_chars: exports_external.number().optional().describe("Truncate description to this many characters (default: unlimited). Use 300-500 for quick checks.")
|
|
9580
|
+
}, async ({ id, max_description_chars }) => {
|
|
9443
9581
|
try {
|
|
9444
9582
|
const resolvedId = resolveId(id);
|
|
9445
9583
|
const task = getTaskWithRelations(resolvedId);
|
|
9446
9584
|
if (!task)
|
|
9447
9585
|
return { content: [{ type: "text", text: `Task not found: ${id}` }], isError: true };
|
|
9448
|
-
const parts = [formatTaskDetail(task)];
|
|
9586
|
+
const parts = [formatTaskDetail(task, max_description_chars)];
|
|
9449
9587
|
if (task.subtasks.length > 0) {
|
|
9450
9588
|
parts.push(`
|
|
9451
9589
|
Subtasks (${task.subtasks.length}):`);
|
|
@@ -10673,7 +10811,7 @@ ${lines.join(`
|
|
|
10673
10811
|
return { content: [{ type: "text", text: "No tasks available \u2014 all pending tasks are blocked, locked, or none exist." }] };
|
|
10674
10812
|
}
|
|
10675
10813
|
return { content: [{ type: "text", text: `next: ${formatTask(task)}
|
|
10676
|
-
${formatTaskDetail(task)}` }] };
|
|
10814
|
+
${formatTaskDetail(task, 300)}` }] };
|
|
10677
10815
|
} catch (e) {
|
|
10678
10816
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10679
10817
|
}
|
|
@@ -10759,7 +10897,7 @@ ${text}` }] };
|
|
|
10759
10897
|
return { content: [{ type: "text", text: "No tasks available to claim." }] };
|
|
10760
10898
|
}
|
|
10761
10899
|
return { content: [{ type: "text", text: `claimed: ${formatTask(task)}
|
|
10762
|
-
${formatTaskDetail(task)}` }] };
|
|
10900
|
+
${formatTaskDetail(task, 300)}` }] };
|
|
10763
10901
|
} catch (e) {
|
|
10764
10902
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
10765
10903
|
}
|
|
@@ -10844,6 +10982,8 @@ No pending tasks available.`);
|
|
|
10844
10982
|
}
|
|
10845
10983
|
}
|
|
10846
10984
|
}
|
|
10985
|
+
lines.push(`
|
|
10986
|
+
as_of: ${new Date().toISOString()} (pass to get_tasks_changed_since for incremental polling)`);
|
|
10847
10987
|
return { content: [{ type: "text", text: lines.join(`
|
|
10848
10988
|
`) }] };
|
|
10849
10989
|
} catch (e) {
|
|
@@ -10955,6 +11095,58 @@ ${checks.map((c) => ` ${c.status === "ok" ? "\u2713" : "\u26A0"} ${c.name}: ${c
|
|
|
10955
11095
|
}
|
|
10956
11096
|
if (next)
|
|
10957
11097
|
lines.push(`Next up: ${next.short_id || next.id.slice(0, 8)} [${next.priority}] ${next.title}`);
|
|
11098
|
+
lines.push(`as_of: ${new Date().toISOString()}`);
|
|
11099
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
11100
|
+
`) }] };
|
|
11101
|
+
} catch (e) {
|
|
11102
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
11103
|
+
}
|
|
11104
|
+
});
|
|
11105
|
+
}
|
|
11106
|
+
if (shouldRegisterTool("bootstrap")) {
|
|
11107
|
+
server.tool("bootstrap", "Single call for session start. Returns agent's in-progress task (if resuming), next claimable task, and project health \u2014 no side effects. Replaces 3-4 round trips at cold start.", {
|
|
11108
|
+
agent_id: exports_external.string().optional().describe("Your agent ID \u2014 used to find your active tasks and preferred next task"),
|
|
11109
|
+
project_id: exports_external.string().optional()
|
|
11110
|
+
}, async ({ agent_id, project_id }) => {
|
|
11111
|
+
try {
|
|
11112
|
+
const filters = {};
|
|
11113
|
+
if (project_id)
|
|
11114
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
11115
|
+
const f = Object.keys(filters).length > 0 ? filters : undefined;
|
|
11116
|
+
const status = getStatus(f, agent_id);
|
|
11117
|
+
const next = getNextTask(agent_id, f);
|
|
11118
|
+
const lines = [];
|
|
11119
|
+
const myActive = agent_id ? status.active_work.filter((w) => w.assigned_to === agent_id || w.locked_by === agent_id) : [];
|
|
11120
|
+
if (myActive.length > 0) {
|
|
11121
|
+
lines.push(`## Resuming`);
|
|
11122
|
+
for (const w of myActive) {
|
|
11123
|
+
lines.push(`[${w.short_id || w.id.slice(0, 8)}] ${w.priority} \u2014 ${w.title}`);
|
|
11124
|
+
}
|
|
11125
|
+
lines.push("");
|
|
11126
|
+
}
|
|
11127
|
+
if (next) {
|
|
11128
|
+
lines.push(`## Next task to claim`);
|
|
11129
|
+
lines.push(`[${next.short_id || next.id.slice(0, 8)}] ${next.priority} \u2014 ${next.title}`);
|
|
11130
|
+
if (next.description)
|
|
11131
|
+
lines.push(next.description.slice(0, 300) + (next.description.length > 300 ? "\u2026" : ""));
|
|
11132
|
+
lines.push(` call: claim_next_task(agent_id: "${agent_id || "<your-id>"}")`);
|
|
11133
|
+
lines.push("");
|
|
11134
|
+
} else {
|
|
11135
|
+
lines.push(`## No tasks available to claim`);
|
|
11136
|
+
lines.push("");
|
|
11137
|
+
}
|
|
11138
|
+
lines.push(`## Health`);
|
|
11139
|
+
lines.push(`${status.pending} pending | ${status.in_progress} active | ${status.completed} done`);
|
|
11140
|
+
if (status.stale_count > 0)
|
|
11141
|
+
lines.push(`\u26A0 ${status.stale_count} stale task(s)`);
|
|
11142
|
+
if (status.overdue_recurring > 0)
|
|
11143
|
+
lines.push(`\uD83D\uDD01 ${status.overdue_recurring} overdue recurring`);
|
|
11144
|
+
if (status.active_work.length > 0) {
|
|
11145
|
+
const others = agent_id ? status.active_work.filter((w) => w.assigned_to !== agent_id && w.locked_by !== agent_id) : status.active_work;
|
|
11146
|
+
if (others.length > 0) {
|
|
11147
|
+
lines.push(`Other agents active: ${others.slice(0, 3).map((w) => `${w.short_id || w.id.slice(0, 8)} (${w.assigned_to || "?"})`).join(", ")}`);
|
|
11148
|
+
}
|
|
11149
|
+
}
|
|
10958
11150
|
return { content: [{ type: "text", text: lines.join(`
|
|
10959
11151
|
`) }] };
|
|
10960
11152
|
} catch (e) {
|
|
@@ -11025,6 +11217,7 @@ ${checks.map((c) => ` ${c.status === "ok" ? "\u2713" : "\u26A0"} ${c.name}: ${c
|
|
|
11025
11217
|
"get_status",
|
|
11026
11218
|
"get_context",
|
|
11027
11219
|
"get_health",
|
|
11220
|
+
"bootstrap",
|
|
11028
11221
|
"decompose_task",
|
|
11029
11222
|
"set_task_status",
|
|
11030
11223
|
"set_task_priority",
|
|
@@ -11042,9 +11235,9 @@ ${checks.map((c) => ` ${c.status === "ok" ? "\u2713" : "\u26A0"} ${c.name}: ${c
|
|
|
11042
11235
|
create_task: `Create a new task.
|
|
11043
11236
|
Params: title(string, req), description(string), priority(low|medium|high|critical, default:medium), status(pending|in_progress|completed|failed|cancelled, default:pending), project_id(string), parent_id(string \u2014 creates subtask), plan_id(string), task_list_id(string), agent_id(string), assigned_to(string), tags(string[]), metadata(object), estimated_minutes(number), requires_approval(boolean), recurrence_rule(string \u2014 e.g. 'every day', 'every weekday', 'every 2 weeks', 'every monday'), session_id(string), working_dir(string)
|
|
11044
11237
|
Example: {title: 'Daily standup', recurrence_rule: 'every weekday', priority: 'medium'}`,
|
|
11045
|
-
list_tasks: `List tasks with optional filters.
|
|
11046
|
-
Params: status(string|string[]), priority(string|string[]), project_id(string), plan_id(string), task_list_id(string), assigned_to(string), tags(string[]), has_recurrence(boolean \u2014 true=only recurring, false=only non-recurring), limit(number), offset(number)
|
|
11047
|
-
Example: {status: ['pending', 'in_progress'],
|
|
11238
|
+
list_tasks: `List tasks with optional filters. Default limit is 50 to avoid context overflow \u2014 always paginate with offset for large lists.
|
|
11239
|
+
Params: status(string|string[]), priority(string|string[]), project_id(string), plan_id(string), task_list_id(string), assigned_to(string), tags(string[]), has_recurrence(boolean \u2014 true=only recurring, false=only non-recurring), limit(number, default 50), offset(number)
|
|
11240
|
+
Example: {status: ['pending', 'in_progress'], limit: 50, offset: 0}`,
|
|
11048
11241
|
get_task: `Get full task details with subtasks, deps, and comments.
|
|
11049
11242
|
Params: id(string, req \u2014 task ID, short_id like 'APP-00001', or partial ID)
|
|
11050
11243
|
Example: {id: 'a1b2c3d4'}`,
|
|
@@ -11194,8 +11387,8 @@ ${checks.map((c) => ` ${c.status === "ok" ? "\u2713" : "\u26A0"} ${c.name}: ${c
|
|
|
11194
11387
|
get_active_work: `See all in-progress tasks and who is working on them.
|
|
11195
11388
|
Params: project_id(string, optional), task_list_id(string, optional)
|
|
11196
11389
|
Example: {project_id: 'a1b2c3d4'}`,
|
|
11197
|
-
get_tasks_changed_since: `Get tasks modified after a timestamp \u2014
|
|
11198
|
-
Params: since(string, req \u2014 ISO date), project_id(string, optional), task_list_id(string, optional)
|
|
11390
|
+
get_tasks_changed_since: `PREFERRED POLLING PATTERN: Get only tasks modified after a timestamp \u2014 much cheaper than re-fetching everything. Save the as_of timestamp from bootstrap/get_status/get_context and pass it here on your next check.
|
|
11391
|
+
Params: since(string, req \u2014 ISO date from prior as_of), project_id(string, optional), task_list_id(string, optional)
|
|
11199
11392
|
Example: {since: '2026-03-14T10:00:00Z'}`,
|
|
11200
11393
|
get_stale_tasks: `Find stale in_progress tasks with no recent activity.
|
|
11201
11394
|
Params: stale_minutes(number, default:30), project_id(string, optional), task_list_id(string, optional)
|
|
@@ -11203,6 +11396,9 @@ ${checks.map((c) => ` ${c.status === "ok" ? "\u2713" : "\u26A0"} ${c.name}: ${c
|
|
|
11203
11396
|
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.
|
|
11204
11397
|
Params: agent_id(string, optional \u2014 prefers tasks assigned to this agent for next_task), project_id(string, optional), task_list_id(string, optional)
|
|
11205
11398
|
Example: {agent_id: 'a1b2c3d4', project_id: 'e5f6g7h8'}`,
|
|
11399
|
+
bootstrap: `CALL THIS FIRST at session start. Returns your in-progress task (if resuming), next claimable task with description, and project health \u2014 all in one call, no side effects. Eliminates 3-4 round trips.
|
|
11400
|
+
Params: agent_id(string, optional but recommended), project_id(string, optional)
|
|
11401
|
+
Example: {agent_id: 'a1b2c3d4'}`,
|
|
11206
11402
|
decompose_task: `Break a task into subtasks in one call. Subtasks inherit project/plan/list from parent.
|
|
11207
11403
|
Params: parent_id(string, req), subtasks(array, req \u2014 [{title, description, priority, assigned_to, estimated_minutes, tags}]), depends_on_prev(boolean \u2014 chain subtasks sequentially)
|
|
11208
11404
|
Example: {parent_id: 'a1b2c3d4', subtasks: [{title: 'Research'}, {title: 'Implement'}, {title: 'Test'}], depends_on_prev: true}`,
|