@hasna/todos 0.9.3 → 0.9.5
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 +122 -65
- package/dist/index.js +123 -65
- package/dist/mcp/index.js +76 -26
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2154,6 +2154,13 @@ function ensureTableMigrations(db) {
|
|
|
2154
2154
|
db.exec(MIGRATIONS[4]);
|
|
2155
2155
|
}
|
|
2156
2156
|
} catch {}
|
|
2157
|
+
try {
|
|
2158
|
+
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
2159
|
+
} catch {
|
|
2160
|
+
try {
|
|
2161
|
+
db.exec(MIGRATIONS[5]);
|
|
2162
|
+
} catch {}
|
|
2163
|
+
}
|
|
2157
2164
|
}
|
|
2158
2165
|
function backfillTaskTags(db) {
|
|
2159
2166
|
try {
|
|
@@ -2358,6 +2365,15 @@ var init_database = __esm(() => {
|
|
|
2358
2365
|
CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id);
|
|
2359
2366
|
|
|
2360
2367
|
INSERT OR IGNORE INTO _migrations (id) VALUES (5);
|
|
2368
|
+
`,
|
|
2369
|
+
`
|
|
2370
|
+
ALTER TABLE projects ADD COLUMN task_prefix TEXT;
|
|
2371
|
+
ALTER TABLE projects ADD COLUMN task_counter INTEGER NOT NULL DEFAULT 0;
|
|
2372
|
+
|
|
2373
|
+
ALTER TABLE tasks ADD COLUMN short_id TEXT;
|
|
2374
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL;
|
|
2375
|
+
|
|
2376
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (6);
|
|
2361
2377
|
`
|
|
2362
2378
|
];
|
|
2363
2379
|
});
|
|
@@ -2431,6 +2447,105 @@ var init_types = __esm(() => {
|
|
|
2431
2447
|
};
|
|
2432
2448
|
});
|
|
2433
2449
|
|
|
2450
|
+
// src/db/projects.ts
|
|
2451
|
+
function slugify(name) {
|
|
2452
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
2453
|
+
}
|
|
2454
|
+
function generatePrefix(name, db) {
|
|
2455
|
+
const words = name.replace(/[^a-zA-Z0-9\s]/g, "").trim().split(/\s+/);
|
|
2456
|
+
let prefix;
|
|
2457
|
+
if (words.length >= 3) {
|
|
2458
|
+
prefix = words.slice(0, 3).map((w) => w[0].toUpperCase()).join("");
|
|
2459
|
+
} else if (words.length === 2) {
|
|
2460
|
+
prefix = (words[0].slice(0, 2) + words[1][0]).toUpperCase();
|
|
2461
|
+
} else {
|
|
2462
|
+
prefix = words[0].slice(0, 3).toUpperCase();
|
|
2463
|
+
}
|
|
2464
|
+
let candidate = prefix;
|
|
2465
|
+
let suffix = 1;
|
|
2466
|
+
while (true) {
|
|
2467
|
+
const existing = db.query("SELECT id FROM projects WHERE task_prefix = ?").get(candidate);
|
|
2468
|
+
if (!existing)
|
|
2469
|
+
return candidate;
|
|
2470
|
+
suffix++;
|
|
2471
|
+
candidate = `${prefix}${suffix}`;
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
function createProject(input, db) {
|
|
2475
|
+
const d = db || getDatabase();
|
|
2476
|
+
const id = uuid();
|
|
2477
|
+
const timestamp = now();
|
|
2478
|
+
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
2479
|
+
const taskPrefix = input.task_prefix || generatePrefix(input.name, d);
|
|
2480
|
+
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at)
|
|
2481
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp, timestamp]);
|
|
2482
|
+
return getProject(id, d);
|
|
2483
|
+
}
|
|
2484
|
+
function getProject(id, db) {
|
|
2485
|
+
const d = db || getDatabase();
|
|
2486
|
+
const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
2487
|
+
return row;
|
|
2488
|
+
}
|
|
2489
|
+
function getProjectByPath(path, db) {
|
|
2490
|
+
const d = db || getDatabase();
|
|
2491
|
+
const row = d.query("SELECT * FROM projects WHERE path = ?").get(path);
|
|
2492
|
+
return row;
|
|
2493
|
+
}
|
|
2494
|
+
function listProjects(db) {
|
|
2495
|
+
const d = db || getDatabase();
|
|
2496
|
+
return d.query("SELECT * FROM projects ORDER BY name").all();
|
|
2497
|
+
}
|
|
2498
|
+
function updateProject(id, input, db) {
|
|
2499
|
+
const d = db || getDatabase();
|
|
2500
|
+
const project = getProject(id, d);
|
|
2501
|
+
if (!project)
|
|
2502
|
+
throw new ProjectNotFoundError(id);
|
|
2503
|
+
const sets = ["updated_at = ?"];
|
|
2504
|
+
const params = [now()];
|
|
2505
|
+
if (input.name !== undefined) {
|
|
2506
|
+
sets.push("name = ?");
|
|
2507
|
+
params.push(input.name);
|
|
2508
|
+
}
|
|
2509
|
+
if (input.description !== undefined) {
|
|
2510
|
+
sets.push("description = ?");
|
|
2511
|
+
params.push(input.description);
|
|
2512
|
+
}
|
|
2513
|
+
if (input.task_list_id !== undefined) {
|
|
2514
|
+
sets.push("task_list_id = ?");
|
|
2515
|
+
params.push(input.task_list_id);
|
|
2516
|
+
}
|
|
2517
|
+
params.push(id);
|
|
2518
|
+
d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
2519
|
+
return getProject(id, d);
|
|
2520
|
+
}
|
|
2521
|
+
function nextTaskShortId(projectId, db) {
|
|
2522
|
+
const d = db || getDatabase();
|
|
2523
|
+
const project = getProject(projectId, d);
|
|
2524
|
+
if (!project || !project.task_prefix)
|
|
2525
|
+
return null;
|
|
2526
|
+
d.run("UPDATE projects SET task_counter = task_counter + 1, updated_at = ? WHERE id = ?", [now(), projectId]);
|
|
2527
|
+
const updated = getProject(projectId, d);
|
|
2528
|
+
const padded = String(updated.task_counter).padStart(5, "0");
|
|
2529
|
+
return `${updated.task_prefix}-${padded}`;
|
|
2530
|
+
}
|
|
2531
|
+
function ensureProject(name, path, db) {
|
|
2532
|
+
const d = db || getDatabase();
|
|
2533
|
+
const existing = getProjectByPath(path, d);
|
|
2534
|
+
if (existing) {
|
|
2535
|
+
if (!existing.task_prefix) {
|
|
2536
|
+
const prefix = generatePrefix(existing.name, d);
|
|
2537
|
+
d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
|
|
2538
|
+
return getProject(existing.id, d);
|
|
2539
|
+
}
|
|
2540
|
+
return existing;
|
|
2541
|
+
}
|
|
2542
|
+
return createProject({ name, path }, d);
|
|
2543
|
+
}
|
|
2544
|
+
var init_projects = __esm(() => {
|
|
2545
|
+
init_types();
|
|
2546
|
+
init_database();
|
|
2547
|
+
});
|
|
2548
|
+
|
|
2434
2549
|
// src/db/tasks.ts
|
|
2435
2550
|
function rowToTask(row) {
|
|
2436
2551
|
return {
|
|
@@ -2459,14 +2574,17 @@ function createTask(input, db) {
|
|
|
2459
2574
|
const id = uuid();
|
|
2460
2575
|
const timestamp = now();
|
|
2461
2576
|
const tags = input.tags || [];
|
|
2462
|
-
|
|
2463
|
-
|
|
2577
|
+
const shortId = input.project_id ? nextTaskShortId(input.project_id, d) : null;
|
|
2578
|
+
const title = shortId ? `${shortId}: ${input.title}` : input.title;
|
|
2579
|
+
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)
|
|
2580
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`, [
|
|
2464
2581
|
id,
|
|
2582
|
+
shortId,
|
|
2465
2583
|
input.project_id || null,
|
|
2466
2584
|
input.parent_id || null,
|
|
2467
2585
|
input.plan_id || null,
|
|
2468
2586
|
input.task_list_id || null,
|
|
2469
|
-
|
|
2587
|
+
title,
|
|
2470
2588
|
input.description || null,
|
|
2471
2589
|
input.status || "pending",
|
|
2472
2590
|
input.priority || "medium",
|
|
@@ -2757,68 +2875,7 @@ function wouldCreateCycle(taskId, dependsOn, db) {
|
|
|
2757
2875
|
var init_tasks = __esm(() => {
|
|
2758
2876
|
init_types();
|
|
2759
2877
|
init_database();
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
// src/db/projects.ts
|
|
2763
|
-
function slugify(name) {
|
|
2764
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
2765
|
-
}
|
|
2766
|
-
function createProject(input, db) {
|
|
2767
|
-
const d = db || getDatabase();
|
|
2768
|
-
const id = uuid();
|
|
2769
|
-
const timestamp = now();
|
|
2770
|
-
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
2771
|
-
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, created_at, updated_at)
|
|
2772
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, timestamp, timestamp]);
|
|
2773
|
-
return getProject(id, d);
|
|
2774
|
-
}
|
|
2775
|
-
function getProject(id, db) {
|
|
2776
|
-
const d = db || getDatabase();
|
|
2777
|
-
const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
2778
|
-
return row;
|
|
2779
|
-
}
|
|
2780
|
-
function getProjectByPath(path, db) {
|
|
2781
|
-
const d = db || getDatabase();
|
|
2782
|
-
const row = d.query("SELECT * FROM projects WHERE path = ?").get(path);
|
|
2783
|
-
return row;
|
|
2784
|
-
}
|
|
2785
|
-
function listProjects(db) {
|
|
2786
|
-
const d = db || getDatabase();
|
|
2787
|
-
return d.query("SELECT * FROM projects ORDER BY name").all();
|
|
2788
|
-
}
|
|
2789
|
-
function updateProject(id, input, db) {
|
|
2790
|
-
const d = db || getDatabase();
|
|
2791
|
-
const project = getProject(id, d);
|
|
2792
|
-
if (!project)
|
|
2793
|
-
throw new ProjectNotFoundError(id);
|
|
2794
|
-
const sets = ["updated_at = ?"];
|
|
2795
|
-
const params = [now()];
|
|
2796
|
-
if (input.name !== undefined) {
|
|
2797
|
-
sets.push("name = ?");
|
|
2798
|
-
params.push(input.name);
|
|
2799
|
-
}
|
|
2800
|
-
if (input.description !== undefined) {
|
|
2801
|
-
sets.push("description = ?");
|
|
2802
|
-
params.push(input.description);
|
|
2803
|
-
}
|
|
2804
|
-
if (input.task_list_id !== undefined) {
|
|
2805
|
-
sets.push("task_list_id = ?");
|
|
2806
|
-
params.push(input.task_list_id);
|
|
2807
|
-
}
|
|
2808
|
-
params.push(id);
|
|
2809
|
-
d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
2810
|
-
return getProject(id, d);
|
|
2811
|
-
}
|
|
2812
|
-
function ensureProject(name, path, db) {
|
|
2813
|
-
const d = db || getDatabase();
|
|
2814
|
-
const existing = getProjectByPath(path, d);
|
|
2815
|
-
if (existing)
|
|
2816
|
-
return existing;
|
|
2817
|
-
return createProject({ name, path }, d);
|
|
2818
|
-
}
|
|
2819
|
-
var init_projects = __esm(() => {
|
|
2820
|
-
init_types();
|
|
2821
|
-
init_database();
|
|
2878
|
+
init_projects();
|
|
2822
2879
|
});
|
|
2823
2880
|
|
|
2824
2881
|
// src/db/agents.ts
|
package/dist/index.js
CHANGED
|
@@ -194,6 +194,15 @@ var MIGRATIONS = [
|
|
|
194
194
|
CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id);
|
|
195
195
|
|
|
196
196
|
INSERT OR IGNORE INTO _migrations (id) VALUES (5);
|
|
197
|
+
`,
|
|
198
|
+
`
|
|
199
|
+
ALTER TABLE projects ADD COLUMN task_prefix TEXT;
|
|
200
|
+
ALTER TABLE projects ADD COLUMN task_counter INTEGER NOT NULL DEFAULT 0;
|
|
201
|
+
|
|
202
|
+
ALTER TABLE tasks ADD COLUMN short_id TEXT;
|
|
203
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL;
|
|
204
|
+
|
|
205
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (6);
|
|
197
206
|
`
|
|
198
207
|
];
|
|
199
208
|
var _db = null;
|
|
@@ -231,6 +240,13 @@ function ensureTableMigrations(db) {
|
|
|
231
240
|
db.exec(MIGRATIONS[4]);
|
|
232
241
|
}
|
|
233
242
|
} catch {}
|
|
243
|
+
try {
|
|
244
|
+
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
245
|
+
} catch {
|
|
246
|
+
try {
|
|
247
|
+
db.exec(MIGRATIONS[5]);
|
|
248
|
+
} catch {}
|
|
249
|
+
}
|
|
234
250
|
}
|
|
235
251
|
function backfillTaskTags(db) {
|
|
236
252
|
try {
|
|
@@ -401,6 +417,106 @@ class DependencyCycleError extends Error {
|
|
|
401
417
|
}
|
|
402
418
|
}
|
|
403
419
|
|
|
420
|
+
// src/db/projects.ts
|
|
421
|
+
function slugify(name) {
|
|
422
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
423
|
+
}
|
|
424
|
+
function generatePrefix(name, db) {
|
|
425
|
+
const words = name.replace(/[^a-zA-Z0-9\s]/g, "").trim().split(/\s+/);
|
|
426
|
+
let prefix;
|
|
427
|
+
if (words.length >= 3) {
|
|
428
|
+
prefix = words.slice(0, 3).map((w) => w[0].toUpperCase()).join("");
|
|
429
|
+
} else if (words.length === 2) {
|
|
430
|
+
prefix = (words[0].slice(0, 2) + words[1][0]).toUpperCase();
|
|
431
|
+
} else {
|
|
432
|
+
prefix = words[0].slice(0, 3).toUpperCase();
|
|
433
|
+
}
|
|
434
|
+
let candidate = prefix;
|
|
435
|
+
let suffix = 1;
|
|
436
|
+
while (true) {
|
|
437
|
+
const existing = db.query("SELECT id FROM projects WHERE task_prefix = ?").get(candidate);
|
|
438
|
+
if (!existing)
|
|
439
|
+
return candidate;
|
|
440
|
+
suffix++;
|
|
441
|
+
candidate = `${prefix}${suffix}`;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
function createProject(input, db) {
|
|
445
|
+
const d = db || getDatabase();
|
|
446
|
+
const id = uuid();
|
|
447
|
+
const timestamp = now();
|
|
448
|
+
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
449
|
+
const taskPrefix = input.task_prefix || generatePrefix(input.name, d);
|
|
450
|
+
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at)
|
|
451
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp, timestamp]);
|
|
452
|
+
return getProject(id, d);
|
|
453
|
+
}
|
|
454
|
+
function getProject(id, db) {
|
|
455
|
+
const d = db || getDatabase();
|
|
456
|
+
const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
457
|
+
return row;
|
|
458
|
+
}
|
|
459
|
+
function getProjectByPath(path, db) {
|
|
460
|
+
const d = db || getDatabase();
|
|
461
|
+
const row = d.query("SELECT * FROM projects WHERE path = ?").get(path);
|
|
462
|
+
return row;
|
|
463
|
+
}
|
|
464
|
+
function listProjects(db) {
|
|
465
|
+
const d = db || getDatabase();
|
|
466
|
+
return d.query("SELECT * FROM projects ORDER BY name").all();
|
|
467
|
+
}
|
|
468
|
+
function updateProject(id, input, db) {
|
|
469
|
+
const d = db || getDatabase();
|
|
470
|
+
const project = getProject(id, d);
|
|
471
|
+
if (!project)
|
|
472
|
+
throw new ProjectNotFoundError(id);
|
|
473
|
+
const sets = ["updated_at = ?"];
|
|
474
|
+
const params = [now()];
|
|
475
|
+
if (input.name !== undefined) {
|
|
476
|
+
sets.push("name = ?");
|
|
477
|
+
params.push(input.name);
|
|
478
|
+
}
|
|
479
|
+
if (input.description !== undefined) {
|
|
480
|
+
sets.push("description = ?");
|
|
481
|
+
params.push(input.description);
|
|
482
|
+
}
|
|
483
|
+
if (input.task_list_id !== undefined) {
|
|
484
|
+
sets.push("task_list_id = ?");
|
|
485
|
+
params.push(input.task_list_id);
|
|
486
|
+
}
|
|
487
|
+
params.push(id);
|
|
488
|
+
d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
489
|
+
return getProject(id, d);
|
|
490
|
+
}
|
|
491
|
+
function deleteProject(id, db) {
|
|
492
|
+
const d = db || getDatabase();
|
|
493
|
+
const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
494
|
+
return result.changes > 0;
|
|
495
|
+
}
|
|
496
|
+
function nextTaskShortId(projectId, db) {
|
|
497
|
+
const d = db || getDatabase();
|
|
498
|
+
const project = getProject(projectId, d);
|
|
499
|
+
if (!project || !project.task_prefix)
|
|
500
|
+
return null;
|
|
501
|
+
d.run("UPDATE projects SET task_counter = task_counter + 1, updated_at = ? WHERE id = ?", [now(), projectId]);
|
|
502
|
+
const updated = getProject(projectId, d);
|
|
503
|
+
const padded = String(updated.task_counter).padStart(5, "0");
|
|
504
|
+
return `${updated.task_prefix}-${padded}`;
|
|
505
|
+
}
|
|
506
|
+
function ensureProject(name, path, db) {
|
|
507
|
+
const d = db || getDatabase();
|
|
508
|
+
const existing = getProjectByPath(path, d);
|
|
509
|
+
if (existing) {
|
|
510
|
+
if (!existing.task_prefix) {
|
|
511
|
+
const prefix = generatePrefix(existing.name, d);
|
|
512
|
+
d.run("UPDATE projects SET task_prefix = ?, updated_at = ? WHERE id = ?", [prefix, now(), existing.id]);
|
|
513
|
+
return getProject(existing.id, d);
|
|
514
|
+
}
|
|
515
|
+
return existing;
|
|
516
|
+
}
|
|
517
|
+
return createProject({ name, path }, d);
|
|
518
|
+
}
|
|
519
|
+
|
|
404
520
|
// src/db/tasks.ts
|
|
405
521
|
function rowToTask(row) {
|
|
406
522
|
return {
|
|
@@ -429,14 +545,17 @@ function createTask(input, db) {
|
|
|
429
545
|
const id = uuid();
|
|
430
546
|
const timestamp = now();
|
|
431
547
|
const tags = input.tags || [];
|
|
432
|
-
|
|
433
|
-
|
|
548
|
+
const shortId = input.project_id ? nextTaskShortId(input.project_id, d) : null;
|
|
549
|
+
const title = shortId ? `${shortId}: ${input.title}` : input.title;
|
|
550
|
+
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)
|
|
551
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`, [
|
|
434
552
|
id,
|
|
553
|
+
shortId,
|
|
435
554
|
input.project_id || null,
|
|
436
555
|
input.parent_id || null,
|
|
437
556
|
input.plan_id || null,
|
|
438
557
|
input.task_list_id || null,
|
|
439
|
-
|
|
558
|
+
title,
|
|
440
559
|
input.description || null,
|
|
441
560
|
input.status || "pending",
|
|
442
561
|
input.priority || "medium",
|
|
@@ -732,68 +851,6 @@ function wouldCreateCycle(taskId, dependsOn, db) {
|
|
|
732
851
|
}
|
|
733
852
|
return false;
|
|
734
853
|
}
|
|
735
|
-
// src/db/projects.ts
|
|
736
|
-
function slugify(name) {
|
|
737
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
738
|
-
}
|
|
739
|
-
function createProject(input, db) {
|
|
740
|
-
const d = db || getDatabase();
|
|
741
|
-
const id = uuid();
|
|
742
|
-
const timestamp = now();
|
|
743
|
-
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
744
|
-
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, created_at, updated_at)
|
|
745
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, timestamp, timestamp]);
|
|
746
|
-
return getProject(id, d);
|
|
747
|
-
}
|
|
748
|
-
function getProject(id, db) {
|
|
749
|
-
const d = db || getDatabase();
|
|
750
|
-
const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
751
|
-
return row;
|
|
752
|
-
}
|
|
753
|
-
function getProjectByPath(path, db) {
|
|
754
|
-
const d = db || getDatabase();
|
|
755
|
-
const row = d.query("SELECT * FROM projects WHERE path = ?").get(path);
|
|
756
|
-
return row;
|
|
757
|
-
}
|
|
758
|
-
function listProjects(db) {
|
|
759
|
-
const d = db || getDatabase();
|
|
760
|
-
return d.query("SELECT * FROM projects ORDER BY name").all();
|
|
761
|
-
}
|
|
762
|
-
function updateProject(id, input, db) {
|
|
763
|
-
const d = db || getDatabase();
|
|
764
|
-
const project = getProject(id, d);
|
|
765
|
-
if (!project)
|
|
766
|
-
throw new ProjectNotFoundError(id);
|
|
767
|
-
const sets = ["updated_at = ?"];
|
|
768
|
-
const params = [now()];
|
|
769
|
-
if (input.name !== undefined) {
|
|
770
|
-
sets.push("name = ?");
|
|
771
|
-
params.push(input.name);
|
|
772
|
-
}
|
|
773
|
-
if (input.description !== undefined) {
|
|
774
|
-
sets.push("description = ?");
|
|
775
|
-
params.push(input.description);
|
|
776
|
-
}
|
|
777
|
-
if (input.task_list_id !== undefined) {
|
|
778
|
-
sets.push("task_list_id = ?");
|
|
779
|
-
params.push(input.task_list_id);
|
|
780
|
-
}
|
|
781
|
-
params.push(id);
|
|
782
|
-
d.run(`UPDATE projects SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
783
|
-
return getProject(id, d);
|
|
784
|
-
}
|
|
785
|
-
function deleteProject(id, db) {
|
|
786
|
-
const d = db || getDatabase();
|
|
787
|
-
const result = d.run("DELETE FROM projects WHERE id = ?", [id]);
|
|
788
|
-
return result.changes > 0;
|
|
789
|
-
}
|
|
790
|
-
function ensureProject(name, path, db) {
|
|
791
|
-
const d = db || getDatabase();
|
|
792
|
-
const existing = getProjectByPath(path, d);
|
|
793
|
-
if (existing)
|
|
794
|
-
return existing;
|
|
795
|
-
return createProject({ name, path }, d);
|
|
796
|
-
}
|
|
797
854
|
// src/db/plans.ts
|
|
798
855
|
function createPlan(input, db) {
|
|
799
856
|
const d = db || getDatabase();
|
|
@@ -1711,6 +1768,7 @@ export {
|
|
|
1711
1768
|
removeDependency,
|
|
1712
1769
|
registerAgent,
|
|
1713
1770
|
now,
|
|
1771
|
+
nextTaskShortId,
|
|
1714
1772
|
lockTask,
|
|
1715
1773
|
loadConfig,
|
|
1716
1774
|
listTasks,
|
package/dist/mcp/index.js
CHANGED
|
@@ -4244,6 +4244,15 @@ var MIGRATIONS = [
|
|
|
4244
4244
|
CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id);
|
|
4245
4245
|
|
|
4246
4246
|
INSERT OR IGNORE INTO _migrations (id) VALUES (5);
|
|
4247
|
+
`,
|
|
4248
|
+
`
|
|
4249
|
+
ALTER TABLE projects ADD COLUMN task_prefix TEXT;
|
|
4250
|
+
ALTER TABLE projects ADD COLUMN task_counter INTEGER NOT NULL DEFAULT 0;
|
|
4251
|
+
|
|
4252
|
+
ALTER TABLE tasks ADD COLUMN short_id TEXT;
|
|
4253
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_short_id ON tasks(short_id) WHERE short_id IS NOT NULL;
|
|
4254
|
+
|
|
4255
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (6);
|
|
4247
4256
|
`
|
|
4248
4257
|
];
|
|
4249
4258
|
var _db = null;
|
|
@@ -4281,6 +4290,13 @@ function ensureTableMigrations(db) {
|
|
|
4281
4290
|
db.exec(MIGRATIONS[4]);
|
|
4282
4291
|
}
|
|
4283
4292
|
} catch {}
|
|
4293
|
+
try {
|
|
4294
|
+
db.query("SELECT task_prefix FROM projects LIMIT 0").get();
|
|
4295
|
+
} catch {
|
|
4296
|
+
try {
|
|
4297
|
+
db.exec(MIGRATIONS[5]);
|
|
4298
|
+
} catch {}
|
|
4299
|
+
}
|
|
4284
4300
|
}
|
|
4285
4301
|
function backfillTaskTags(db) {
|
|
4286
4302
|
try {
|
|
@@ -4347,6 +4363,60 @@ function resolvePartialId(db, table, partialId) {
|
|
|
4347
4363
|
return null;
|
|
4348
4364
|
}
|
|
4349
4365
|
|
|
4366
|
+
// src/db/projects.ts
|
|
4367
|
+
function slugify(name) {
|
|
4368
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
4369
|
+
}
|
|
4370
|
+
function generatePrefix(name, db) {
|
|
4371
|
+
const words = name.replace(/[^a-zA-Z0-9\s]/g, "").trim().split(/\s+/);
|
|
4372
|
+
let prefix;
|
|
4373
|
+
if (words.length >= 3) {
|
|
4374
|
+
prefix = words.slice(0, 3).map((w) => w[0].toUpperCase()).join("");
|
|
4375
|
+
} else if (words.length === 2) {
|
|
4376
|
+
prefix = (words[0].slice(0, 2) + words[1][0]).toUpperCase();
|
|
4377
|
+
} else {
|
|
4378
|
+
prefix = words[0].slice(0, 3).toUpperCase();
|
|
4379
|
+
}
|
|
4380
|
+
let candidate = prefix;
|
|
4381
|
+
let suffix = 1;
|
|
4382
|
+
while (true) {
|
|
4383
|
+
const existing = db.query("SELECT id FROM projects WHERE task_prefix = ?").get(candidate);
|
|
4384
|
+
if (!existing)
|
|
4385
|
+
return candidate;
|
|
4386
|
+
suffix++;
|
|
4387
|
+
candidate = `${prefix}${suffix}`;
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
function createProject(input, db) {
|
|
4391
|
+
const d = db || getDatabase();
|
|
4392
|
+
const id = uuid();
|
|
4393
|
+
const timestamp = now();
|
|
4394
|
+
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
4395
|
+
const taskPrefix = input.task_prefix || generatePrefix(input.name, d);
|
|
4396
|
+
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, task_prefix, task_counter, created_at, updated_at)
|
|
4397
|
+
VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, taskPrefix, timestamp, timestamp]);
|
|
4398
|
+
return getProject(id, d);
|
|
4399
|
+
}
|
|
4400
|
+
function getProject(id, db) {
|
|
4401
|
+
const d = db || getDatabase();
|
|
4402
|
+
const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
4403
|
+
return row;
|
|
4404
|
+
}
|
|
4405
|
+
function listProjects(db) {
|
|
4406
|
+
const d = db || getDatabase();
|
|
4407
|
+
return d.query("SELECT * FROM projects ORDER BY name").all();
|
|
4408
|
+
}
|
|
4409
|
+
function nextTaskShortId(projectId, db) {
|
|
4410
|
+
const d = db || getDatabase();
|
|
4411
|
+
const project = getProject(projectId, d);
|
|
4412
|
+
if (!project || !project.task_prefix)
|
|
4413
|
+
return null;
|
|
4414
|
+
d.run("UPDATE projects SET task_counter = task_counter + 1, updated_at = ? WHERE id = ?", [now(), projectId]);
|
|
4415
|
+
const updated = getProject(projectId, d);
|
|
4416
|
+
const padded = String(updated.task_counter).padStart(5, "0");
|
|
4417
|
+
return `${updated.task_prefix}-${padded}`;
|
|
4418
|
+
}
|
|
4419
|
+
|
|
4350
4420
|
// src/db/tasks.ts
|
|
4351
4421
|
function rowToTask(row) {
|
|
4352
4422
|
return {
|
|
@@ -4375,14 +4445,17 @@ function createTask(input, db) {
|
|
|
4375
4445
|
const id = uuid();
|
|
4376
4446
|
const timestamp = now();
|
|
4377
4447
|
const tags = input.tags || [];
|
|
4378
|
-
|
|
4379
|
-
|
|
4448
|
+
const shortId = input.project_id ? nextTaskShortId(input.project_id, d) : null;
|
|
4449
|
+
const title = shortId ? `${shortId}: ${input.title}` : input.title;
|
|
4450
|
+
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)
|
|
4451
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`, [
|
|
4380
4452
|
id,
|
|
4453
|
+
shortId,
|
|
4381
4454
|
input.project_id || null,
|
|
4382
4455
|
input.parent_id || null,
|
|
4383
4456
|
input.plan_id || null,
|
|
4384
4457
|
input.task_list_id || null,
|
|
4385
|
-
|
|
4458
|
+
title,
|
|
4386
4459
|
input.description || null,
|
|
4387
4460
|
input.status || "pending",
|
|
4388
4461
|
input.priority || "medium",
|
|
@@ -4695,29 +4768,6 @@ function getComment(id, db) {
|
|
|
4695
4768
|
return d.query("SELECT * FROM task_comments WHERE id = ?").get(id);
|
|
4696
4769
|
}
|
|
4697
4770
|
|
|
4698
|
-
// src/db/projects.ts
|
|
4699
|
-
function slugify(name) {
|
|
4700
|
-
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
4701
|
-
}
|
|
4702
|
-
function createProject(input, db) {
|
|
4703
|
-
const d = db || getDatabase();
|
|
4704
|
-
const id = uuid();
|
|
4705
|
-
const timestamp = now();
|
|
4706
|
-
const taskListId = input.task_list_id ?? `todos-${slugify(input.name)}`;
|
|
4707
|
-
d.run(`INSERT INTO projects (id, name, path, description, task_list_id, created_at, updated_at)
|
|
4708
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`, [id, input.name, input.path, input.description || null, taskListId, timestamp, timestamp]);
|
|
4709
|
-
return getProject(id, d);
|
|
4710
|
-
}
|
|
4711
|
-
function getProject(id, db) {
|
|
4712
|
-
const d = db || getDatabase();
|
|
4713
|
-
const row = d.query("SELECT * FROM projects WHERE id = ?").get(id);
|
|
4714
|
-
return row;
|
|
4715
|
-
}
|
|
4716
|
-
function listProjects(db) {
|
|
4717
|
-
const d = db || getDatabase();
|
|
4718
|
-
return d.query("SELECT * FROM projects ORDER BY name").all();
|
|
4719
|
-
}
|
|
4720
|
-
|
|
4721
4771
|
// src/db/plans.ts
|
|
4722
4772
|
function createPlan(input, db) {
|
|
4723
4773
|
const d = db || getDatabase();
|