@hasna/todos 0.11.19 → 0.11.21
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 +399 -21
- package/dist/db/projects.d.ts +39 -1
- package/dist/db/projects.d.ts.map +1 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/tasks.d.ts +16 -0
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +168 -9
- package/dist/lib/search.d.ts +1 -1
- package/dist/lib/search.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +264 -18
- package/dist/mcp/tools/agents.d.ts.map +1 -1
- package/dist/server/index.js +166 -4
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2260,6 +2260,18 @@ function ensureSchema(db) {
|
|
|
2260
2260
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2261
2261
|
UNIQUE(source_id, source_type, target_id, target_type, relation_type)
|
|
2262
2262
|
)`);
|
|
2263
|
+
ensureTable("project_machine_paths", `
|
|
2264
|
+
CREATE TABLE project_machine_paths (
|
|
2265
|
+
id TEXT PRIMARY KEY,
|
|
2266
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
2267
|
+
machine_id TEXT NOT NULL,
|
|
2268
|
+
path TEXT NOT NULL,
|
|
2269
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2270
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
2271
|
+
UNIQUE(project_id, machine_id)
|
|
2272
|
+
)`);
|
|
2273
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_project ON project_machine_paths(project_id)");
|
|
2274
|
+
ensureIndex("CREATE INDEX IF NOT EXISTS idx_project_machine_paths_machine ON project_machine_paths(machine_id)");
|
|
2263
2275
|
ensureTable("machines", `
|
|
2264
2276
|
CREATE TABLE machines (
|
|
2265
2277
|
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, hostname TEXT, platform TEXT,
|
|
@@ -2295,6 +2307,7 @@ function ensureSchema(db) {
|
|
|
2295
2307
|
ensureColumn("tasks", "max_retries", "INTEGER DEFAULT 3");
|
|
2296
2308
|
ensureColumn("tasks", "retry_after", "TEXT");
|
|
2297
2309
|
ensureColumn("tasks", "sla_minutes", "INTEGER");
|
|
2310
|
+
ensureColumn("tasks", "archived_at", "TEXT");
|
|
2298
2311
|
ensureColumn("agents", "role", "TEXT DEFAULT 'agent'");
|
|
2299
2312
|
ensureColumn("agents", "permissions", `TEXT DEFAULT '["*"]'`);
|
|
2300
2313
|
ensureColumn("agents", "reports_to", "TEXT");
|
|
@@ -3133,6 +3146,25 @@ var init_schema = __esm(() => {
|
|
|
3133
3146
|
CREATE INDEX IF NOT EXISTS idx_agents_machine ON agents(machine_id);
|
|
3134
3147
|
|
|
3135
3148
|
INSERT OR IGNORE INTO _migrations (id) VALUES (41);
|
|
3149
|
+
`,
|
|
3150
|
+
`
|
|
3151
|
+
CREATE TABLE IF NOT EXISTS project_machine_paths (
|
|
3152
|
+
id TEXT PRIMARY KEY,
|
|
3153
|
+
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
3154
|
+
machine_id TEXT NOT NULL,
|
|
3155
|
+
path TEXT NOT NULL,
|
|
3156
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
3157
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
3158
|
+
UNIQUE(project_id, machine_id)
|
|
3159
|
+
);
|
|
3160
|
+
CREATE INDEX IF NOT EXISTS idx_project_machine_paths_project ON project_machine_paths(project_id);
|
|
3161
|
+
CREATE INDEX IF NOT EXISTS idx_project_machine_paths_machine ON project_machine_paths(machine_id);
|
|
3162
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (42);
|
|
3163
|
+
`,
|
|
3164
|
+
`
|
|
3165
|
+
ALTER TABLE tasks ADD COLUMN archived_at TEXT;
|
|
3166
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_archived ON tasks(archived_at) WHERE archived_at IS NOT NULL;
|
|
3167
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (43);
|
|
3136
3168
|
`
|
|
3137
3169
|
];
|
|
3138
3170
|
});
|
|
@@ -3517,13 +3549,18 @@ var exports_projects = {};
|
|
|
3517
3549
|
__export(exports_projects, {
|
|
3518
3550
|
updateProject: () => updateProject,
|
|
3519
3551
|
slugify: () => slugify,
|
|
3552
|
+
setMachineLocalPath: () => setMachineLocalPath,
|
|
3553
|
+
renameProject: () => renameProject,
|
|
3520
3554
|
removeProjectSource: () => removeProjectSource,
|
|
3555
|
+
removeMachineLocalPath: () => removeMachineLocalPath,
|
|
3521
3556
|
nextTaskShortId: () => nextTaskShortId,
|
|
3522
3557
|
listProjects: () => listProjects,
|
|
3523
3558
|
listProjectSources: () => listProjectSources,
|
|
3559
|
+
listMachineLocalPaths: () => listMachineLocalPaths,
|
|
3524
3560
|
getProjectWithSources: () => getProjectWithSources,
|
|
3525
3561
|
getProjectByPath: () => getProjectByPath,
|
|
3526
3562
|
getProject: () => getProject,
|
|
3563
|
+
getMachineLocalPath: () => getMachineLocalPath,
|
|
3527
3564
|
ensureProject: () => ensureProject,
|
|
3528
3565
|
deleteProject: () => deleteProject,
|
|
3529
3566
|
createProject: () => createProject,
|
|
@@ -3569,8 +3606,15 @@ function getProject(id, db) {
|
|
|
3569
3606
|
}
|
|
3570
3607
|
function getProjectByPath(path, db) {
|
|
3571
3608
|
const d = db || getDatabase();
|
|
3572
|
-
|
|
3573
|
-
|
|
3609
|
+
try {
|
|
3610
|
+
const machineId = getMachineId(d);
|
|
3611
|
+
const machineRow = d.query(`SELECT p.* FROM projects p
|
|
3612
|
+
JOIN project_machine_paths pmp ON pmp.project_id = p.id
|
|
3613
|
+
WHERE pmp.machine_id = ? AND pmp.path = ?`).get(machineId, path);
|
|
3614
|
+
if (machineRow)
|
|
3615
|
+
return machineRow;
|
|
3616
|
+
} catch {}
|
|
3617
|
+
return d.query("SELECT * FROM projects WHERE path = ?").get(path);
|
|
3574
3618
|
}
|
|
3575
3619
|
function listProjects(db) {
|
|
3576
3620
|
const d = db || getDatabase();
|
|
@@ -3595,10 +3639,40 @@ function updateProject(id, input, db) {
|
|
|
3595
3639
|
sets.push("task_list_id = ?");
|
|
3596
3640
|
params.push(input.task_list_id);
|
|
3597
3641
|
}
|
|
3642
|
+
if (input.path !== undefined) {
|
|
3643
|
+
sets.push("path = ?");
|
|
3644
|
+
params.push(input.path);
|
|
3645
|
+
}
|
|
3598
3646
|
params.push(id);
|
|
3599
3647
|
d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
3600
3648
|
return getProject(id, d);
|
|
3601
3649
|
}
|
|
3650
|
+
function renameProject(id, input, db) {
|
|
3651
|
+
const d = db || getDatabase();
|
|
3652
|
+
const project = getProject(id, d);
|
|
3653
|
+
if (!project)
|
|
3654
|
+
throw new ProjectNotFoundError(id);
|
|
3655
|
+
let taskListsUpdated = 0;
|
|
3656
|
+
const ts = now();
|
|
3657
|
+
if (input.new_slug !== undefined) {
|
|
3658
|
+
const normalised = input.new_slug.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-|-$/g, "");
|
|
3659
|
+
if (!normalised)
|
|
3660
|
+
throw new Error("Invalid slug \u2014 must be non-empty kebab-case");
|
|
3661
|
+
const conflict = d.query("SELECT id FROM projects WHERE task_list_id = ? AND id != ?").get(normalised, id);
|
|
3662
|
+
if (conflict)
|
|
3663
|
+
throw new Error(`Slug "${normalised}" is already used by another project`);
|
|
3664
|
+
const oldSlug = project.task_list_id;
|
|
3665
|
+
d.run("UPDATE projects SET task_list_id = ?, updated_at = ? WHERE id = ?", [normalised, ts, id]);
|
|
3666
|
+
if (oldSlug) {
|
|
3667
|
+
const result = d.run("UPDATE task_lists SET slug = ?, name = COALESCE(?, name), updated_at = ? WHERE project_id = ? AND slug = ?", [normalised, input.name ?? null, ts, id, oldSlug]);
|
|
3668
|
+
taskListsUpdated = result.changes;
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
if (input.name !== undefined) {
|
|
3672
|
+
d.run("UPDATE projects SET name = ?, updated_at = ? WHERE id = ?", [input.name, ts, id]);
|
|
3673
|
+
}
|
|
3674
|
+
return { project: getProject(id, d), task_lists_updated: taskListsUpdated };
|
|
3675
|
+
}
|
|
3602
3676
|
function deleteProject(id, db) {
|
|
3603
3677
|
const d = db || getDatabase();
|
|
3604
3678
|
const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
@@ -3651,6 +3725,13 @@ function nextTaskShortId(projectId, db) {
|
|
|
3651
3725
|
const project = getProject(projectId, d);
|
|
3652
3726
|
if (!project || !project.task_prefix)
|
|
3653
3727
|
return null;
|
|
3728
|
+
const prefix = project.task_prefix;
|
|
3729
|
+
const prefixLen = prefix.length + 2;
|
|
3730
|
+
const maxRow = d.query(`SELECT MAX(CAST(SUBSTR(short_id, ?) AS INTEGER)) as max_counter FROM tasks WHERE short_id LIKE ?`).get(prefixLen, `${prefix}-%`);
|
|
3731
|
+
const syncedMax = maxRow?.max_counter ?? 0;
|
|
3732
|
+
if (syncedMax >= (project.task_counter ?? 0)) {
|
|
3733
|
+
d.run("UPDATE projects SET task_counter = ?, updated_at = ? WHERE id = ?", [syncedMax, now(), projectId]);
|
|
3734
|
+
}
|
|
3654
3735
|
d.run("UPDATE projects SET task_counter = task_counter + 1, updated_at = ? WHERE id = ?", [now(), projectId]);
|
|
3655
3736
|
const updated = getProject(projectId, d);
|
|
3656
3737
|
const padded = String(updated.task_counter).padStart(5, "0");
|
|
@@ -3665,13 +3746,53 @@ function ensureProject(name, path, db) {
|
|
|
3665
3746
|
d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
|
|
3666
3747
|
return getProject(existing.id, d);
|
|
3667
3748
|
}
|
|
3749
|
+
setMachineLocalPath(existing.id, path, d);
|
|
3668
3750
|
return existing;
|
|
3669
3751
|
}
|
|
3670
|
-
|
|
3752
|
+
const project = createProject({ name, path }, d);
|
|
3753
|
+
setMachineLocalPath(project.id, path, d);
|
|
3754
|
+
return project;
|
|
3755
|
+
}
|
|
3756
|
+
function setMachineLocalPath(projectId, path, db) {
|
|
3757
|
+
const d = db || getDatabase();
|
|
3758
|
+
const machineId = getMachineId(d);
|
|
3759
|
+
const ts = now();
|
|
3760
|
+
const existing = d.query("SELECT * FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
|
|
3761
|
+
if (existing) {
|
|
3762
|
+
if (existing.path !== path) {
|
|
3763
|
+
d.run("UPDATE project_machine_paths SET path = ?, updated_at = ? WHERE id = ?", [path, ts, existing.id]);
|
|
3764
|
+
}
|
|
3765
|
+
return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(existing.id);
|
|
3766
|
+
}
|
|
3767
|
+
const id = uuid();
|
|
3768
|
+
d.run("INSERT INTO project_machine_paths (id, project_id, machine_id, path, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", [id, projectId, machineId, path, ts, ts]);
|
|
3769
|
+
return d.query("SELECT * FROM project_machine_paths WHERE id = ?").get(id);
|
|
3770
|
+
}
|
|
3771
|
+
function getMachineLocalPath(projectId, db) {
|
|
3772
|
+
const d = db || getDatabase();
|
|
3773
|
+
try {
|
|
3774
|
+
const machineId = getMachineId(d);
|
|
3775
|
+
const row = d.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(projectId, machineId);
|
|
3776
|
+
if (row)
|
|
3777
|
+
return row.path;
|
|
3778
|
+
} catch {}
|
|
3779
|
+
const project = getProject(projectId, d);
|
|
3780
|
+
return project?.path ?? null;
|
|
3781
|
+
}
|
|
3782
|
+
function listMachineLocalPaths(projectId, db) {
|
|
3783
|
+
const d = db || getDatabase();
|
|
3784
|
+
return d.query("SELECT * FROM project_machine_paths WHERE project_id = ? ORDER BY machine_id").all(projectId);
|
|
3785
|
+
}
|
|
3786
|
+
function removeMachineLocalPath(projectId, machineId, db) {
|
|
3787
|
+
const d = db || getDatabase();
|
|
3788
|
+
const mid = machineId ?? getMachineId(d);
|
|
3789
|
+
const result = d.run("DELETE FROM project_machine_paths WHERE project_id = ? AND machine_id = ?", [projectId, mid]);
|
|
3790
|
+
return result.changes > 0;
|
|
3671
3791
|
}
|
|
3672
3792
|
var init_projects = __esm(() => {
|
|
3673
3793
|
init_types();
|
|
3674
3794
|
init_database();
|
|
3795
|
+
init_machines();
|
|
3675
3796
|
});
|
|
3676
3797
|
|
|
3677
3798
|
// src/lib/sync-utils.ts
|
|
@@ -4658,6 +4779,7 @@ var exports_tasks = {};
|
|
|
4658
4779
|
__export(exports_tasks, {
|
|
4659
4780
|
updateTask: () => updateTask,
|
|
4660
4781
|
unlockTask: () => unlockTask,
|
|
4782
|
+
unarchiveTask: () => unarchiveTask,
|
|
4661
4783
|
stealTask: () => stealTask,
|
|
4662
4784
|
startTask: () => startTask,
|
|
4663
4785
|
setTaskStatus: () => setTaskStatus,
|
|
@@ -4692,6 +4814,7 @@ __export(exports_tasks, {
|
|
|
4692
4814
|
claimNextTask: () => claimNextTask,
|
|
4693
4815
|
bulkUpdateTasks: () => bulkUpdateTasks,
|
|
4694
4816
|
bulkCreateTasks: () => bulkCreateTasks,
|
|
4817
|
+
archiveTasks: () => archiveTasks,
|
|
4695
4818
|
addDependency: () => addDependency
|
|
4696
4819
|
});
|
|
4697
4820
|
function rowToTask(row) {
|
|
@@ -4884,6 +5007,9 @@ function listTasks(filter = {}, db) {
|
|
|
4884
5007
|
params.push(decoded.p, decoded.p, decoded.c, decoded.p, decoded.c, decoded.i);
|
|
4885
5008
|
} catch {}
|
|
4886
5009
|
}
|
|
5010
|
+
if (!filter.include_archived) {
|
|
5011
|
+
conditions.push("archived_at IS NULL");
|
|
5012
|
+
}
|
|
4887
5013
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
4888
5014
|
let limitClause = "";
|
|
4889
5015
|
if (filter.limit) {
|
|
@@ -5797,6 +5923,35 @@ function bulkUpdateTasks(taskIds, updates, db) {
|
|
|
5797
5923
|
tx();
|
|
5798
5924
|
return { updated, failed };
|
|
5799
5925
|
}
|
|
5926
|
+
function archiveTasks(options, db) {
|
|
5927
|
+
const d = db || getDatabase();
|
|
5928
|
+
const conditions = ["archived_at IS NULL"];
|
|
5929
|
+
const params = [];
|
|
5930
|
+
const statuses = options.status ?? ["completed", "failed", "cancelled"];
|
|
5931
|
+
conditions.push(`status IN (${statuses.map(() => "?").join(",")})`);
|
|
5932
|
+
params.push(...statuses);
|
|
5933
|
+
if (options.project_id) {
|
|
5934
|
+
conditions.push("project_id = ?");
|
|
5935
|
+
params.push(options.project_id);
|
|
5936
|
+
}
|
|
5937
|
+
if (options.task_list_id) {
|
|
5938
|
+
conditions.push("task_list_id = ?");
|
|
5939
|
+
params.push(options.task_list_id);
|
|
5940
|
+
}
|
|
5941
|
+
if (options.older_than_days !== undefined) {
|
|
5942
|
+
const cutoff = new Date(Date.now() - options.older_than_days * 86400000).toISOString();
|
|
5943
|
+
conditions.push("updated_at < ?");
|
|
5944
|
+
params.push(cutoff);
|
|
5945
|
+
}
|
|
5946
|
+
const ts = now();
|
|
5947
|
+
const result = d.run(`UPDATE tasks SET archived_at = ? WHERE ${conditions.join(" AND ")}`, [ts, ...params]);
|
|
5948
|
+
return { archived: result.changes };
|
|
5949
|
+
}
|
|
5950
|
+
function unarchiveTask(id, db) {
|
|
5951
|
+
const d = db || getDatabase();
|
|
5952
|
+
d.run("UPDATE tasks SET archived_at = NULL WHERE id = ?", [id]);
|
|
5953
|
+
return getTask(id, d);
|
|
5954
|
+
}
|
|
5800
5955
|
function getOverdueTasks(projectId, db) {
|
|
5801
5956
|
const d = db || getDatabase();
|
|
5802
5957
|
const nowStr = new Date().toISOString();
|
|
@@ -6331,21 +6486,25 @@ function escapeFtsQuery(q) {
|
|
|
6331
6486
|
return q.replace(/["*^()]/g, " ").trim().split(/\s+/).filter(Boolean).map((token) => `"${token}"*`).join(" ");
|
|
6332
6487
|
}
|
|
6333
6488
|
function searchTasks(options, projectId, taskListId, db) {
|
|
6334
|
-
const opts = typeof options === "string" ? { query: options, project_id: projectId, task_list_id: taskListId } : options;
|
|
6489
|
+
const opts = typeof options === "string" ? { query: options || undefined, project_id: projectId, task_list_id: taskListId } : options;
|
|
6335
6490
|
const d = db || getDatabase();
|
|
6336
6491
|
clearExpiredLocks(d);
|
|
6337
6492
|
const params = [];
|
|
6338
6493
|
let sql;
|
|
6339
|
-
|
|
6340
|
-
|
|
6494
|
+
const raw = opts.query?.trim() ?? "";
|
|
6495
|
+
const q = raw === "*" ? "" : raw;
|
|
6496
|
+
if (hasFts(d) && q) {
|
|
6497
|
+
const ftsQuery = escapeFtsQuery(q);
|
|
6341
6498
|
sql = `SELECT t.* FROM tasks t
|
|
6342
6499
|
INNER JOIN tasks_fts fts ON fts.rowid = t.rowid
|
|
6343
6500
|
WHERE tasks_fts MATCH ?`;
|
|
6344
6501
|
params.push(ftsQuery);
|
|
6345
|
-
} else {
|
|
6346
|
-
const pattern = `%${
|
|
6502
|
+
} else if (q) {
|
|
6503
|
+
const pattern = `%${q}%`;
|
|
6347
6504
|
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 ?))`;
|
|
6348
6505
|
params.push(pattern, pattern, pattern);
|
|
6506
|
+
} else {
|
|
6507
|
+
sql = `SELECT * FROM tasks t WHERE 1=1`;
|
|
6349
6508
|
}
|
|
6350
6509
|
if (opts.project_id) {
|
|
6351
6510
|
sql += " AND t.project_id = ?";
|
|
@@ -6399,7 +6558,7 @@ function searchTasks(options, projectId, taskListId, db) {
|
|
|
6399
6558
|
} else if (opts.is_blocked === false) {
|
|
6400
6559
|
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')";
|
|
6401
6560
|
}
|
|
6402
|
-
if (hasFts(d) &&
|
|
6561
|
+
if (hasFts(d) && q) {
|
|
6403
6562
|
sql += ` ORDER BY bm25(tasks_fts),
|
|
6404
6563
|
CASE t.priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
6405
6564
|
t.created_at DESC`;
|
|
@@ -23771,12 +23930,17 @@ ${text}` }] };
|
|
|
23771
23930
|
if (!agent) {
|
|
23772
23931
|
return { content: [{ type: "text", text: `Agent not found: ${id || name}` }], isError: true };
|
|
23773
23932
|
}
|
|
23933
|
+
const oldName = agent.name;
|
|
23774
23934
|
const updated = updateAgent(agent.id, { name: new_name });
|
|
23935
|
+
const db = getDatabase();
|
|
23936
|
+
const tasksResult = db.run("UPDATE tasks SET assigned_to = ? WHERE assigned_to = ?", [new_name, oldName]);
|
|
23937
|
+
const taskNote = tasksResult.changes > 0 ? `
|
|
23938
|
+
Updated assigned_to on ${tasksResult.changes} task(s).` : "";
|
|
23775
23939
|
return {
|
|
23776
23940
|
content: [{
|
|
23777
23941
|
type: "text",
|
|
23778
|
-
text: `Agent renamed: ${
|
|
23779
|
-
ID: ${updated.id}`
|
|
23942
|
+
text: `Agent renamed: ${oldName} -> ${updated.name}
|
|
23943
|
+
ID: ${updated.id}${taskNote}`
|
|
23780
23944
|
}]
|
|
23781
23945
|
};
|
|
23782
23946
|
} catch (e) {
|
|
@@ -25996,6 +26160,8 @@ function formatTaskDetail(task, maxDescriptionChars) {
|
|
|
25996
26160
|
parts.push(`Project: ${task.project_id}`);
|
|
25997
26161
|
if (task.plan_id)
|
|
25998
26162
|
parts.push(`Plan: ${task.plan_id}`);
|
|
26163
|
+
if (task.due_at)
|
|
26164
|
+
parts.push(`Due: ${task.due_at.slice(0, 10)}`);
|
|
25999
26165
|
if (task.tags.length > 0)
|
|
26000
26166
|
parts.push(`Tags: ${task.tags.join(", ")}`);
|
|
26001
26167
|
if (task.recurrence_rule)
|
|
@@ -26097,7 +26263,8 @@ var init_mcp = __esm(() => {
|
|
|
26097
26263
|
reason: exports_external.string().optional().describe("Why this task exists \u2014 context for agents picking it up"),
|
|
26098
26264
|
spawned_from_session: exports_external.string().optional().describe("Session ID that created this task (for tracing task lineage)"),
|
|
26099
26265
|
assigned_from_project: exports_external.string().optional().describe("Override: project ID the assigning agent is working from. Auto-detected from agent focus if omitted."),
|
|
26100
|
-
task_type: exports_external.string().optional().describe("Task type: bug, feature, chore, improvement, docs, test, security, or any custom string")
|
|
26266
|
+
task_type: exports_external.string().optional().describe("Task type: bug, feature, chore, improvement, docs, test, security, or any custom string"),
|
|
26267
|
+
due_date: exports_external.string().optional().describe("Due date as YYYY-MM-DD (e.g. '2026-04-15'). Stored as end-of-day ISO timestamp.")
|
|
26101
26268
|
}, async (params) => {
|
|
26102
26269
|
try {
|
|
26103
26270
|
if (!params.agent_id) {
|
|
@@ -26112,6 +26279,10 @@ var init_mcp = __esm(() => {
|
|
|
26112
26279
|
resolved["plan_id"] = resolveId(resolved["plan_id"], "plans");
|
|
26113
26280
|
if (resolved["task_list_id"])
|
|
26114
26281
|
resolved["task_list_id"] = resolveId(resolved["task_list_id"], "task_lists");
|
|
26282
|
+
if (resolved["due_date"]) {
|
|
26283
|
+
resolved["due_at"] = `${resolved["due_date"]}T23:59:59.000Z`;
|
|
26284
|
+
delete resolved["due_date"];
|
|
26285
|
+
}
|
|
26115
26286
|
if (!resolved["assigned_from_project"]) {
|
|
26116
26287
|
const focus = getAgentFocus(params.agent_id);
|
|
26117
26288
|
if (focus?.project_id) {
|
|
@@ -26147,7 +26318,8 @@ var init_mcp = __esm(() => {
|
|
|
26147
26318
|
limit: exports_external.number().optional(),
|
|
26148
26319
|
offset: exports_external.number().optional(),
|
|
26149
26320
|
summary_only: exports_external.boolean().optional().describe("When true, return only id, short_id, title, status, priority \u2014 minimal tokens for navigation"),
|
|
26150
|
-
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.")
|
|
26321
|
+
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."),
|
|
26322
|
+
include_archived: exports_external.boolean().optional().describe("When true, include archived tasks. Default: false.")
|
|
26151
26323
|
}, async (params) => {
|
|
26152
26324
|
try {
|
|
26153
26325
|
const { due_today, overdue, summary_only, ...rest } = params;
|
|
@@ -26182,7 +26354,8 @@ var init_mcp = __esm(() => {
|
|
|
26182
26354
|
const assigned = t.assigned_to ? ` -> ${t.assigned_to}` : "";
|
|
26183
26355
|
const due = t.due_at ? ` due:${t.due_at.slice(0, 10)}` : "";
|
|
26184
26356
|
const recur = t.recurrence_rule ? " [\u21BB]" : "";
|
|
26185
|
-
|
|
26357
|
+
const list = t.task_list_id ? ` list:${t.task_list_id.slice(0, 8)}` : "";
|
|
26358
|
+
return `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}${assigned}${list}${lock}${due}${recur}`;
|
|
26186
26359
|
}).join(`
|
|
26187
26360
|
`);
|
|
26188
26361
|
const currentOffset = resolved.offset || 0;
|
|
@@ -26269,7 +26442,7 @@ Checklist (${done}/${task.checklist.length}):`);
|
|
|
26269
26442
|
if (shouldRegisterTool("update_task")) {
|
|
26270
26443
|
server.tool("update_task", "Update task fields. Version required for optimistic locking.", {
|
|
26271
26444
|
id: exports_external.string(),
|
|
26272
|
-
version: exports_external.number(),
|
|
26445
|
+
version: exports_external.union([exports_external.number(), exports_external.string()]).transform((v) => Number(v)),
|
|
26273
26446
|
title: exports_external.string().optional(),
|
|
26274
26447
|
description: exports_external.string().optional(),
|
|
26275
26448
|
status: exports_external.enum(["pending", "in_progress", "completed", "failed", "cancelled"]).optional(),
|
|
@@ -26279,7 +26452,8 @@ Checklist (${done}/${task.checklist.length}):`);
|
|
|
26279
26452
|
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
26280
26453
|
plan_id: exports_external.string().optional(),
|
|
26281
26454
|
task_list_id: exports_external.string().optional(),
|
|
26282
|
-
task_type: exports_external.string().nullable().optional().describe("Task type: bug, feature, chore, improvement, docs, test, security, or custom. null to clear.")
|
|
26455
|
+
task_type: exports_external.string().nullable().optional().describe("Task type: bug, feature, chore, improvement, docs, test, security, or custom. null to clear."),
|
|
26456
|
+
due_date: exports_external.string().nullable().optional().describe("Due date as YYYY-MM-DD. null to clear. Stored as end-of-day ISO timestamp.")
|
|
26283
26457
|
}, async ({ id, ...rest }) => {
|
|
26284
26458
|
try {
|
|
26285
26459
|
const resolvedId = resolveId(id);
|
|
@@ -26288,6 +26462,10 @@ Checklist (${done}/${task.checklist.length}):`);
|
|
|
26288
26462
|
resolved.task_list_id = resolveId(resolved.task_list_id, "task_lists");
|
|
26289
26463
|
if (resolved.plan_id)
|
|
26290
26464
|
resolved.plan_id = resolveId(resolved.plan_id, "plans");
|
|
26465
|
+
if ("due_date" in resolved) {
|
|
26466
|
+
resolved["due_at"] = resolved["due_date"] ? `${resolved["due_date"]}T23:59:59.000Z` : null;
|
|
26467
|
+
delete resolved["due_date"];
|
|
26468
|
+
}
|
|
26291
26469
|
const task = updateTask(resolvedId, resolved);
|
|
26292
26470
|
return { content: [{ type: "text", text: `updated: ${formatTask(task)}` }] };
|
|
26293
26471
|
} catch (e) {
|
|
@@ -26523,6 +26701,29 @@ ${text}` }] };
|
|
|
26523
26701
|
}
|
|
26524
26702
|
});
|
|
26525
26703
|
}
|
|
26704
|
+
if (shouldRegisterTool("update_project")) {
|
|
26705
|
+
server.tool("update_project", "Update project fields: name, description, path, task_list_id. Use new_slug to rename the project slug (cascades to task_lists).", {
|
|
26706
|
+
id: exports_external.string().describe("Project ID"),
|
|
26707
|
+
name: exports_external.string().optional(),
|
|
26708
|
+
description: exports_external.string().optional(),
|
|
26709
|
+
path: exports_external.string().optional().describe("Update global path (use projects-path set for machine-local overrides)"),
|
|
26710
|
+
task_list_id: exports_external.string().optional(),
|
|
26711
|
+
new_slug: exports_external.string().optional().describe("Rename the project slug (kebab-case). Cascades to matching task_list slugs.")
|
|
26712
|
+
}, async ({ id, name, description, path, task_list_id, new_slug }) => {
|
|
26713
|
+
try {
|
|
26714
|
+
const resolvedId = resolveId(id, "projects");
|
|
26715
|
+
if (new_slug || name && !description && !path && !task_list_id) {
|
|
26716
|
+
const result = renameProject(resolvedId, { name, new_slug });
|
|
26717
|
+
const note = result.task_lists_updated > 0 ? ` (${result.task_lists_updated} task list(s) slug updated)` : "";
|
|
26718
|
+
return { content: [{ type: "text", text: `Project updated: ${result.project.id.slice(0, 8)} | ${result.project.name}${note}` }] };
|
|
26719
|
+
}
|
|
26720
|
+
const updated = updateProject(resolvedId, { name, description, path, task_list_id });
|
|
26721
|
+
return { content: [{ type: "text", text: `Project updated: ${updated.id.slice(0, 8)} | ${updated.name}${updated.path ? ` (${updated.path})` : ""}` }] };
|
|
26722
|
+
} catch (e) {
|
|
26723
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
26724
|
+
}
|
|
26725
|
+
});
|
|
26726
|
+
}
|
|
26526
26727
|
if (shouldRegisterTool("remove_project_source")) {
|
|
26527
26728
|
server.tool("remove_project_source", "Remove a data source from a project by source ID.", {
|
|
26528
26729
|
source_id: exports_external.string().describe("Source ID to remove")
|
|
@@ -26786,8 +26987,8 @@ ${text}` }] };
|
|
|
26786
26987
|
});
|
|
26787
26988
|
}
|
|
26788
26989
|
if (shouldRegisterTool("search_tasks")) {
|
|
26789
|
-
server.tool("search_tasks", "Full-text search across tasks with filters.", {
|
|
26790
|
-
query: exports_external.string(),
|
|
26990
|
+
server.tool("search_tasks", "Full-text search across tasks with filters. query is optional \u2014 if omitted, returns all tasks matching the given filters.", {
|
|
26991
|
+
query: exports_external.string().optional(),
|
|
26791
26992
|
project_id: exports_external.string().optional(),
|
|
26792
26993
|
task_list_id: exports_external.string().optional(),
|
|
26793
26994
|
status: exports_external.union([
|
|
@@ -26815,11 +27016,12 @@ ${text}` }] };
|
|
|
26815
27016
|
...filters
|
|
26816
27017
|
});
|
|
26817
27018
|
if (tasks.length === 0) {
|
|
26818
|
-
return { content: [{ type: "text", text: `No tasks matching "${query}".` }] };
|
|
27019
|
+
return { content: [{ type: "text", text: query ? `No tasks matching "${query}".` : "No tasks found." }] };
|
|
26819
27020
|
}
|
|
26820
27021
|
const text = tasks.map((t) => `[${t.status}] ${t.id.slice(0, 8)} | ${t.priority} | ${t.title}`).join(`
|
|
26821
27022
|
`);
|
|
26822
|
-
|
|
27023
|
+
const label = query ? `${tasks.length} result(s) for "${query}"` : `${tasks.length} task(s)`;
|
|
27024
|
+
return { content: [{ type: "text", text: `${label}:
|
|
26823
27025
|
${text}` }] };
|
|
26824
27026
|
} catch (e) {
|
|
26825
27027
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -26915,9 +27117,15 @@ Description: ${list.description}` : ""}`
|
|
|
26915
27117
|
if (lists.length === 0) {
|
|
26916
27118
|
return { content: [{ type: "text", text: "No task lists found." }] };
|
|
26917
27119
|
}
|
|
27120
|
+
const db = getDatabase();
|
|
26918
27121
|
const text = lists.map((l) => {
|
|
26919
27122
|
const project = l.project_id ? ` (project: ${l.project_id.slice(0, 8)})` : "";
|
|
26920
|
-
|
|
27123
|
+
const counts = db.query(`SELECT COUNT(*) as total,
|
|
27124
|
+
SUM(CASE WHEN status IN ('pending','in_progress') THEN 1 ELSE 0 END) as active,
|
|
27125
|
+
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as done
|
|
27126
|
+
FROM tasks WHERE task_list_id = ? AND archived_at IS NULL`).get(l.id);
|
|
27127
|
+
const taskNote = ` [${counts.active} active, ${counts.done} done / ${counts.total} total]`;
|
|
27128
|
+
return `${l.id.slice(0, 8)} | ${l.name} [${l.slug}]${taskNote}${project}${l.description ? ` - ${l.description}` : ""}`;
|
|
26921
27129
|
}).join(`
|
|
26922
27130
|
`);
|
|
26923
27131
|
return { content: [{ type: "text", text: `${lists.length} task list(s):
|
|
@@ -27505,6 +27713,52 @@ In Progress:`);
|
|
|
27505
27713
|
}
|
|
27506
27714
|
});
|
|
27507
27715
|
}
|
|
27716
|
+
if (shouldRegisterTool("archive_completed")) {
|
|
27717
|
+
server.tool("archive_completed", "Archive completed/failed/cancelled tasks to reduce clutter. Archived tasks are hidden from list_tasks and search_tasks by default.", {
|
|
27718
|
+
project_id: exports_external.string().optional().describe("Scope to a project"),
|
|
27719
|
+
task_list_id: exports_external.string().optional().describe("Scope to a task list"),
|
|
27720
|
+
older_than_days: exports_external.number().optional().describe("Only archive tasks last updated more than N days ago. Default: all matching tasks."),
|
|
27721
|
+
status: exports_external.array(exports_external.enum(["completed", "failed", "cancelled"])).optional().describe("Statuses to archive. Default: completed, failed, cancelled."),
|
|
27722
|
+
dry_run: exports_external.boolean().optional().describe("Preview count without archiving. Default: false.")
|
|
27723
|
+
}, async ({ project_id, task_list_id, older_than_days, status, dry_run }) => {
|
|
27724
|
+
try {
|
|
27725
|
+
const filters = {};
|
|
27726
|
+
if (project_id)
|
|
27727
|
+
filters.project_id = resolveId(project_id, "projects");
|
|
27728
|
+
if (task_list_id)
|
|
27729
|
+
filters.task_list_id = resolveId(task_list_id, "task_lists");
|
|
27730
|
+
if (older_than_days !== undefined)
|
|
27731
|
+
filters.older_than_days = older_than_days;
|
|
27732
|
+
if (status)
|
|
27733
|
+
filters.status = status;
|
|
27734
|
+
if (dry_run) {
|
|
27735
|
+
const db = getDatabase();
|
|
27736
|
+
const statuses = status ?? ["completed", "failed", "cancelled"];
|
|
27737
|
+
const conditions = ["archived_at IS NULL", `status IN (${statuses.map(() => "?").join(",")})`];
|
|
27738
|
+
const params = [...statuses];
|
|
27739
|
+
if (filters.project_id) {
|
|
27740
|
+
conditions.push("project_id = ?");
|
|
27741
|
+
params.push(filters.project_id);
|
|
27742
|
+
}
|
|
27743
|
+
if (filters.task_list_id) {
|
|
27744
|
+
conditions.push("task_list_id = ?");
|
|
27745
|
+
params.push(filters.task_list_id);
|
|
27746
|
+
}
|
|
27747
|
+
if (older_than_days !== undefined) {
|
|
27748
|
+
const cutoff = new Date(Date.now() - older_than_days * 86400000).toISOString();
|
|
27749
|
+
conditions.push("updated_at < ?");
|
|
27750
|
+
params.push(cutoff);
|
|
27751
|
+
}
|
|
27752
|
+
const count = db.query(`SELECT COUNT(*) as c FROM tasks WHERE ${conditions.join(" AND ")}`).get(...params).c;
|
|
27753
|
+
return { content: [{ type: "text", text: `Dry run: would archive ${count} task(s).` }] };
|
|
27754
|
+
}
|
|
27755
|
+
const result = archiveTasks(filters);
|
|
27756
|
+
return { content: [{ type: "text", text: `Archived ${result.archived} task(s).` }] };
|
|
27757
|
+
} catch (e) {
|
|
27758
|
+
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
27759
|
+
}
|
|
27760
|
+
});
|
|
27761
|
+
}
|
|
27508
27762
|
if (shouldRegisterTool("bulk_update_tasks")) {
|
|
27509
27763
|
server.tool("bulk_update_tasks", "Update multiple tasks at once. Two modes: (1) task_ids + shared fields \u2014 apply the same changes to all; (2) updates array \u2014 per-task fields, each entry has id plus any fields to update.", {
|
|
27510
27764
|
task_ids: exports_external.array(exports_external.string()).optional().describe("Task IDs to update with the same fields (mode 1)"),
|
|
@@ -27617,6 +27871,26 @@ In Progress:`);
|
|
|
27617
27871
|
if (agent_id)
|
|
27618
27872
|
filters.agent_id = agent_id;
|
|
27619
27873
|
const stats = getTaskStats(Object.keys(filters).length > 0 ? filters : undefined);
|
|
27874
|
+
if (!task_list_id) {
|
|
27875
|
+
const db = getDatabase();
|
|
27876
|
+
const listRows = db.query(`SELECT tl.id, tl.name, tl.slug,
|
|
27877
|
+
COUNT(t.id) as total,
|
|
27878
|
+
SUM(CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END) as completed,
|
|
27879
|
+
SUM(CASE WHEN t.status IN ('pending','in_progress') THEN 1 ELSE 0 END) as active
|
|
27880
|
+
FROM task_lists tl
|
|
27881
|
+
LEFT JOIN tasks t ON t.task_list_id = tl.id ${filters.project_id ? "AND t.project_id = ?" : ""}
|
|
27882
|
+
${filters.project_id ? "WHERE tl.project_id = ?" : ""}
|
|
27883
|
+
GROUP BY tl.id ORDER BY tl.name`).all(...filters.project_id ? [filters.project_id, filters.project_id] : []);
|
|
27884
|
+
stats.by_task_list = listRows.map((r) => ({
|
|
27885
|
+
id: r.id.slice(0, 8),
|
|
27886
|
+
name: r.name,
|
|
27887
|
+
slug: r.slug,
|
|
27888
|
+
total: r.total,
|
|
27889
|
+
completed: r.completed,
|
|
27890
|
+
active: r.active,
|
|
27891
|
+
completion_rate: r.total > 0 ? Math.round(r.completed / r.total * 100) : 0
|
|
27892
|
+
}));
|
|
27893
|
+
}
|
|
27620
27894
|
return { content: [{ type: "text", text: JSON.stringify(stats, null, 2) }] };
|
|
27621
27895
|
} catch (e) {
|
|
27622
27896
|
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
@@ -33513,6 +33787,10 @@ program2.command("projects").description("List and manage projects").option("--a
|
|
|
33513
33787
|
} else {
|
|
33514
33788
|
project = createProject({ name, path: projectPath, task_list_id: opts.taskListId });
|
|
33515
33789
|
}
|
|
33790
|
+
try {
|
|
33791
|
+
const { setMachineLocalPath: setMachineLocalPath2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
33792
|
+
setMachineLocalPath2(project.id, projectPath);
|
|
33793
|
+
} catch {}
|
|
33516
33794
|
if (globalOpts.json) {
|
|
33517
33795
|
output(project, true);
|
|
33518
33796
|
} else {
|
|
@@ -33538,6 +33816,106 @@ program2.command("projects").description("List and manage projects").option("--a
|
|
|
33538
33816
|
console.log(`${chalk3.dim(p.id.slice(0, 8))} ${chalk3.bold(p.name)} ${chalk3.dim(p.path)}${taskList}${p.description ? ` - ${p.description}` : ""}`);
|
|
33539
33817
|
}
|
|
33540
33818
|
});
|
|
33819
|
+
program2.command("project-rename <id-or-slug> <new-slug>").description("Rename a project slug. Cascades to matching task lists. Task prefixes (e.g. APP-00001) are unchanged.").option("--name <name>", "Also update the project display name").option("--json", "Output as JSON").action((idOrSlug, newSlug, opts) => {
|
|
33820
|
+
const globalOpts = program2.opts();
|
|
33821
|
+
const useJson = opts.json || globalOpts.json;
|
|
33822
|
+
try {
|
|
33823
|
+
const { renameProject: renameProject2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
33824
|
+
const db = getDatabase();
|
|
33825
|
+
let resolvedId = resolvePartialId(db, "projects", idOrSlug);
|
|
33826
|
+
if (!resolvedId) {
|
|
33827
|
+
const bySlug = db.query("SELECT id FROM projects WHERE task_list_id = ?").get(idOrSlug);
|
|
33828
|
+
resolvedId = bySlug?.id ?? null;
|
|
33829
|
+
}
|
|
33830
|
+
if (!resolvedId) {
|
|
33831
|
+
console.error(chalk3.red(`Project not found: ${idOrSlug}`));
|
|
33832
|
+
process.exit(1);
|
|
33833
|
+
}
|
|
33834
|
+
const result = renameProject2(resolvedId, { name: opts.name, new_slug: newSlug });
|
|
33835
|
+
if (useJson) {
|
|
33836
|
+
output({ project: result.project, task_lists_updated: result.task_lists_updated }, true);
|
|
33837
|
+
} else {
|
|
33838
|
+
console.log(chalk3.green(`Project renamed: ${result.project.name} (slug: ${result.project.task_list_id})`));
|
|
33839
|
+
if (result.task_lists_updated > 0) {
|
|
33840
|
+
console.log(chalk3.dim(` Updated ${result.task_lists_updated} task list slug(s).`));
|
|
33841
|
+
}
|
|
33842
|
+
}
|
|
33843
|
+
} catch (e) {
|
|
33844
|
+
console.error(chalk3.red(e instanceof Error ? e.message : String(e)));
|
|
33845
|
+
process.exit(1);
|
|
33846
|
+
}
|
|
33847
|
+
});
|
|
33848
|
+
var projectsPathCmd = program2.command("projects-path").description("Manage machine-local path overrides for projects");
|
|
33849
|
+
projectsPathCmd.command("set <project-id> <path>").description("Set the local path for a project on this machine").option("--json", "Output as JSON").action((projectId, projectPath, opts) => {
|
|
33850
|
+
const globalOpts = program2.opts();
|
|
33851
|
+
const useJson = opts.json || globalOpts.json;
|
|
33852
|
+
try {
|
|
33853
|
+
const { setMachineLocalPath: setMachineLocalPath2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
33854
|
+
const db = getDatabase();
|
|
33855
|
+
const resolved = resolvePartialId(db, "projects", projectId);
|
|
33856
|
+
if (!resolved) {
|
|
33857
|
+
console.error(chalk3.red(`Project not found: ${projectId}`));
|
|
33858
|
+
process.exit(1);
|
|
33859
|
+
}
|
|
33860
|
+
const entry = setMachineLocalPath2(resolved, resolve4(projectPath));
|
|
33861
|
+
if (useJson) {
|
|
33862
|
+
output(entry, true);
|
|
33863
|
+
} else {
|
|
33864
|
+
console.log(chalk3.green(`Local path set: ${entry.path} (machine: ${entry.machine_id.slice(0, 8)})`));
|
|
33865
|
+
}
|
|
33866
|
+
} catch (e) {
|
|
33867
|
+
console.error(chalk3.red(e instanceof Error ? e.message : String(e)));
|
|
33868
|
+
process.exit(1);
|
|
33869
|
+
}
|
|
33870
|
+
});
|
|
33871
|
+
projectsPathCmd.command("list <project-id>").description("List all machine path overrides for a project").option("--json", "Output as JSON").action((projectId, opts) => {
|
|
33872
|
+
const globalOpts = program2.opts();
|
|
33873
|
+
const useJson = opts.json || globalOpts.json;
|
|
33874
|
+
try {
|
|
33875
|
+
const { listMachineLocalPaths: listMachineLocalPaths2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
33876
|
+
const db = getDatabase();
|
|
33877
|
+
const resolved = resolvePartialId(db, "projects", projectId);
|
|
33878
|
+
if (!resolved) {
|
|
33879
|
+
console.error(chalk3.red(`Project not found: ${projectId}`));
|
|
33880
|
+
process.exit(1);
|
|
33881
|
+
}
|
|
33882
|
+
const paths = listMachineLocalPaths2(resolved);
|
|
33883
|
+
if (useJson) {
|
|
33884
|
+
output(paths, true);
|
|
33885
|
+
return;
|
|
33886
|
+
}
|
|
33887
|
+
if (paths.length === 0) {
|
|
33888
|
+
console.log(chalk3.dim("No machine path overrides."));
|
|
33889
|
+
return;
|
|
33890
|
+
}
|
|
33891
|
+
for (const p of paths) {
|
|
33892
|
+
console.log(`${chalk3.dim(p.machine_id.slice(0, 8))} ${p.path} ${chalk3.dim(p.updated_at)}`);
|
|
33893
|
+
}
|
|
33894
|
+
} catch (e) {
|
|
33895
|
+
console.error(chalk3.red(e instanceof Error ? e.message : String(e)));
|
|
33896
|
+
process.exit(1);
|
|
33897
|
+
}
|
|
33898
|
+
});
|
|
33899
|
+
projectsPathCmd.command("remove <project-id>").description("Remove the local path override for a project on this machine").option("--machine <id>", "Machine ID to remove override for (default: this machine)").action((projectId, opts) => {
|
|
33900
|
+
try {
|
|
33901
|
+
const { removeMachineLocalPath: removeMachineLocalPath2 } = (init_projects(), __toCommonJS(exports_projects));
|
|
33902
|
+
const db = getDatabase();
|
|
33903
|
+
const resolved = resolvePartialId(db, "projects", projectId);
|
|
33904
|
+
if (!resolved) {
|
|
33905
|
+
console.error(chalk3.red(`Project not found: ${projectId}`));
|
|
33906
|
+
process.exit(1);
|
|
33907
|
+
}
|
|
33908
|
+
const removed = removeMachineLocalPath2(resolved, opts.machine);
|
|
33909
|
+
if (removed) {
|
|
33910
|
+
console.log(chalk3.green("Machine path override removed."));
|
|
33911
|
+
} else {
|
|
33912
|
+
console.log(chalk3.dim("No override found to remove."));
|
|
33913
|
+
}
|
|
33914
|
+
} catch (e) {
|
|
33915
|
+
console.error(chalk3.red(e instanceof Error ? e.message : String(e)));
|
|
33916
|
+
process.exit(1);
|
|
33917
|
+
}
|
|
33918
|
+
});
|
|
33541
33919
|
program2.command("extract <path>").description("Extract TODO/FIXME/HACK/BUG/XXX/NOTE comments from source files and create tasks").option("--dry-run", "Show extracted comments without creating tasks").option("--pattern <tags>", "Comma-separated tags to look for (default: TODO,FIXME,HACK,XXX,BUG,NOTE)").option("-t, --tags <tags>", "Extra comma-separated tags to add to created tasks").option("--assign <agent>", "Assign extracted tasks to an agent").option("--list <id>", "Task list ID").option("--ext <extensions>", "Comma-separated file extensions to scan (e.g. ts,py,go)").action((scanPath, opts) => {
|
|
33542
33920
|
try {
|
|
33543
33921
|
const globalOpts = program2.opts();
|