@kaban-board/cli 0.3.1 → 0.3.2
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/index.js +926 -69
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
5
5
|
import { createRequire as createRequire2 } from "module";
|
|
6
|
-
import { Command as
|
|
6
|
+
import { Command as Command23 } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/commands/add.ts
|
|
9
9
|
import { KabanError } from "@kaban-board/core";
|
|
@@ -21,11 +21,11 @@ async function getContext() {
|
|
|
21
21
|
console.error("Error: No board found. Run 'kaban init' first");
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
|
-
const
|
|
24
|
+
const db2 = await createDb(dbPath);
|
|
25
25
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
26
|
-
const boardService = new BoardService(
|
|
27
|
-
const taskService = new TaskService(
|
|
28
|
-
return { db, config, boardService, taskService };
|
|
26
|
+
const boardService = new BoardService(db2);
|
|
27
|
+
const taskService = new TaskService(db2, boardService);
|
|
28
|
+
return { db: db2, config, boardService, taskService };
|
|
29
29
|
}
|
|
30
30
|
function getKabanPaths() {
|
|
31
31
|
const kabanDir = join(process.cwd(), ".kaban");
|
|
@@ -275,10 +275,200 @@ var resetCommand = new Command2("reset").description("Delete ALL tasks (destruct
|
|
|
275
275
|
}
|
|
276
276
|
});
|
|
277
277
|
|
|
278
|
-
// src/commands/
|
|
279
|
-
import { KabanError as KabanError3 } from "@kaban-board/core";
|
|
278
|
+
// src/commands/audit.ts
|
|
279
|
+
import { AuditService, KabanError as KabanError3 } from "@kaban-board/core";
|
|
280
280
|
import { Command as Command3 } from "commander";
|
|
281
|
-
|
|
281
|
+
function parseRelativeDate(input) {
|
|
282
|
+
const match = input.match(/^(\d+)([dwmh])$/);
|
|
283
|
+
if (!match) {
|
|
284
|
+
return new Date(input);
|
|
285
|
+
}
|
|
286
|
+
const [, numStr, unit] = match;
|
|
287
|
+
const num = parseInt(numStr, 10);
|
|
288
|
+
const now = new Date;
|
|
289
|
+
switch (unit) {
|
|
290
|
+
case "h":
|
|
291
|
+
return new Date(now.getTime() - num * 60 * 60 * 1000);
|
|
292
|
+
case "d":
|
|
293
|
+
return new Date(now.getTime() - num * 24 * 60 * 60 * 1000);
|
|
294
|
+
case "w":
|
|
295
|
+
return new Date(now.getTime() - num * 7 * 24 * 60 * 60 * 1000);
|
|
296
|
+
case "m":
|
|
297
|
+
return new Date(now.getTime() - num * 30 * 24 * 60 * 60 * 1000);
|
|
298
|
+
default:
|
|
299
|
+
return new Date(input);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function formatEntry(entry) {
|
|
303
|
+
const time = entry.timestamp.toISOString().slice(0, 19).replace("T", " ");
|
|
304
|
+
const actor = entry.actor ? `@${entry.actor}` : "";
|
|
305
|
+
const field = entry.fieldName ? `.${entry.fieldName}` : "";
|
|
306
|
+
let change = "";
|
|
307
|
+
if (entry.eventType === "UPDATE") {
|
|
308
|
+
change = `${entry.oldValue ?? "null"} -> ${entry.newValue ?? "null"}`;
|
|
309
|
+
} else if (entry.eventType === "CREATE") {
|
|
310
|
+
change = entry.newValue ?? "";
|
|
311
|
+
} else {
|
|
312
|
+
change = entry.oldValue ?? "";
|
|
313
|
+
}
|
|
314
|
+
return ` ${time} ${entry.eventType.padEnd(6)} ${entry.objectType}${field}
|
|
315
|
+
${entry.objectId.slice(0, 8)} ${actor} ${change}`;
|
|
316
|
+
}
|
|
317
|
+
var auditCommand = new Command3("audit").description("View audit log history");
|
|
318
|
+
auditCommand.command("list").description("List recent audit entries").option("-l, --limit <n>", "Max entries", "50").option("-a, --actor <name>", "Filter by actor").option("-t, --type <type>", "Filter by object type (task|column|board)").option("-e, --event <type>", "Filter by event type (CREATE|UPDATE|DELETE)").option("--since <date>", "Filter from date (ISO 8601 or relative: 1d, 1w, 30d)").option("--until <date>", "Filter to date (ISO 8601 or relative: 1d, 1w, 30d)").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
319
|
+
const json = options.json;
|
|
320
|
+
try {
|
|
321
|
+
const { db: db2 } = await getContext();
|
|
322
|
+
const auditService = new AuditService(db2);
|
|
323
|
+
const result = await auditService.getHistory({
|
|
324
|
+
limit: parseInt(options.limit, 10),
|
|
325
|
+
actor: options.actor,
|
|
326
|
+
objectType: options.type,
|
|
327
|
+
eventType: options.event,
|
|
328
|
+
since: options.since ? parseRelativeDate(options.since) : undefined,
|
|
329
|
+
until: options.until ? parseRelativeDate(options.until) : undefined
|
|
330
|
+
});
|
|
331
|
+
if (json) {
|
|
332
|
+
outputSuccess(result);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
console.log(`
|
|
336
|
+
Audit Log (${result.entries.length} of ${result.total})
|
|
337
|
+
`);
|
|
338
|
+
for (const entry of result.entries) {
|
|
339
|
+
console.log(formatEntry(entry));
|
|
340
|
+
}
|
|
341
|
+
if (result.hasMore)
|
|
342
|
+
console.log(`
|
|
343
|
+
... more entries available`);
|
|
344
|
+
console.log();
|
|
345
|
+
} catch (error2) {
|
|
346
|
+
if (error2 instanceof KabanError3) {
|
|
347
|
+
if (json)
|
|
348
|
+
outputError(error2.code, error2.message);
|
|
349
|
+
console.error(`Error: ${error2.message}`);
|
|
350
|
+
process.exit(error2.code);
|
|
351
|
+
}
|
|
352
|
+
throw error2;
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
auditCommand.command("task <id>").description("View history for a specific task").option("-l, --limit <n>", "Max entries", "50").option("-j, --json", "Output as JSON").action(async (id, options) => {
|
|
356
|
+
const json = options.json;
|
|
357
|
+
try {
|
|
358
|
+
const { db: db2, taskService } = await getContext();
|
|
359
|
+
const task = await taskService.resolveTask(id);
|
|
360
|
+
if (!task) {
|
|
361
|
+
if (json)
|
|
362
|
+
outputError(2, `Task '${id}' not found`);
|
|
363
|
+
console.error(`Error: Task '${id}' not found`);
|
|
364
|
+
process.exit(2);
|
|
365
|
+
}
|
|
366
|
+
const auditService = new AuditService(db2);
|
|
367
|
+
const entries = await auditService.getTaskHistory(task.id, parseInt(options.limit, 10));
|
|
368
|
+
if (json) {
|
|
369
|
+
outputSuccess({ task: { id: task.id, title: task.title }, entries });
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
console.log(`
|
|
373
|
+
History for [${task.id.slice(0, 8)}] "${task.title}"
|
|
374
|
+
`);
|
|
375
|
+
for (const entry of entries) {
|
|
376
|
+
const time = entry.timestamp.toISOString().slice(0, 19).replace("T", " ");
|
|
377
|
+
const actor = entry.actor ? `@${entry.actor}` : "";
|
|
378
|
+
if (entry.eventType === "CREATE") {
|
|
379
|
+
console.log(` ${time} CREATED ${actor}`);
|
|
380
|
+
} else if (entry.eventType === "DELETE") {
|
|
381
|
+
console.log(` ${time} DELETED ${actor}`);
|
|
382
|
+
} else {
|
|
383
|
+
const field = entry.fieldName ?? "?";
|
|
384
|
+
console.log(` ${time} ${field}: ${entry.oldValue ?? "null"} -> ${entry.newValue ?? "null"} ${actor}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
console.log();
|
|
388
|
+
} catch (error2) {
|
|
389
|
+
if (error2 instanceof KabanError3) {
|
|
390
|
+
if (json)
|
|
391
|
+
outputError(error2.code, error2.message);
|
|
392
|
+
console.error(`Error: ${error2.message}`);
|
|
393
|
+
process.exit(error2.code);
|
|
394
|
+
}
|
|
395
|
+
throw error2;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
auditCommand.command("stats").description("Show audit statistics").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
399
|
+
const json = options.json;
|
|
400
|
+
try {
|
|
401
|
+
const { db: db2 } = await getContext();
|
|
402
|
+
const auditService = new AuditService(db2);
|
|
403
|
+
const stats = await auditService.getStats();
|
|
404
|
+
if (json) {
|
|
405
|
+
outputSuccess(stats);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
console.log(`
|
|
409
|
+
Audit Statistics
|
|
410
|
+
`);
|
|
411
|
+
console.log(` Total entries: ${stats.totalEntries}`);
|
|
412
|
+
console.log(`
|
|
413
|
+
By Event Type:`);
|
|
414
|
+
for (const [type, count] of Object.entries(stats.byEventType)) {
|
|
415
|
+
console.log(` ${type}: ${count}`);
|
|
416
|
+
}
|
|
417
|
+
console.log(`
|
|
418
|
+
By Object Type:`);
|
|
419
|
+
for (const [type, count] of Object.entries(stats.byObjectType)) {
|
|
420
|
+
console.log(` ${type}: ${count}`);
|
|
421
|
+
}
|
|
422
|
+
if (stats.recentActors.length > 0) {
|
|
423
|
+
console.log(`
|
|
424
|
+
Recent Actors:`);
|
|
425
|
+
for (const actor of stats.recentActors) {
|
|
426
|
+
console.log(` ${actor}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
console.log();
|
|
430
|
+
} catch (error2) {
|
|
431
|
+
if (error2 instanceof KabanError3) {
|
|
432
|
+
if (json)
|
|
433
|
+
outputError(error2.code, error2.message);
|
|
434
|
+
console.error(`Error: ${error2.message}`);
|
|
435
|
+
process.exit(error2.code);
|
|
436
|
+
}
|
|
437
|
+
throw error2;
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
auditCommand.command("actor <name>").description("View changes by a specific actor").option("-l, --limit <n>", "Max entries", "50").option("-j, --json", "Output as JSON").action(async (name, options) => {
|
|
441
|
+
const json = options.json;
|
|
442
|
+
try {
|
|
443
|
+
const { db: db2 } = await getContext();
|
|
444
|
+
const auditService = new AuditService(db2);
|
|
445
|
+
const entries = await auditService.getChangesByActor(name, parseInt(options.limit, 10));
|
|
446
|
+
if (json) {
|
|
447
|
+
outputSuccess({ actor: name, entries, count: entries.length });
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
console.log(`
|
|
451
|
+
Changes by @${name} (${entries.length} entries)
|
|
452
|
+
`);
|
|
453
|
+
for (const entry of entries) {
|
|
454
|
+
console.log(formatEntry(entry));
|
|
455
|
+
}
|
|
456
|
+
console.log();
|
|
457
|
+
} catch (error2) {
|
|
458
|
+
if (error2 instanceof KabanError3) {
|
|
459
|
+
if (json)
|
|
460
|
+
outputError(error2.code, error2.message);
|
|
461
|
+
console.error(`Error: ${error2.message}`);
|
|
462
|
+
process.exit(error2.code);
|
|
463
|
+
}
|
|
464
|
+
throw error2;
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// src/commands/assign.ts
|
|
469
|
+
import { KabanError as KabanError4 } from "@kaban-board/core";
|
|
470
|
+
import { Command as Command4 } from "commander";
|
|
471
|
+
var assignCommand = new Command4("assign").description("Assign a task to an agent").argument("<id>", "Task ID (can be partial)").argument("[agent]", "Agent to assign (omit with --clear to unassign)").option("-c, --clear", "Unassign the task").option("-j, --json", "Output as JSON").action(async (id, agent, options) => {
|
|
282
472
|
const json = options.json;
|
|
283
473
|
try {
|
|
284
474
|
const { taskService } = await getContext();
|
|
@@ -288,8 +478,7 @@ var assignCommand = new Command3("assign").description("Assign a task to an agen
|
|
|
288
478
|
console.error("Error: Cannot use --clear with agent argument");
|
|
289
479
|
process.exit(4);
|
|
290
480
|
}
|
|
291
|
-
const
|
|
292
|
-
const task = tasks.find((t) => t.id.startsWith(id));
|
|
481
|
+
const task = await taskService.resolveTask(id);
|
|
293
482
|
if (!task) {
|
|
294
483
|
if (json)
|
|
295
484
|
outputError(2, `Task '${id}' not found`);
|
|
@@ -320,7 +509,50 @@ var assignCommand = new Command3("assign").description("Assign a task to an agen
|
|
|
320
509
|
const prevMsg = previousAssignee ? ` (was: ${previousAssignee})` : "";
|
|
321
510
|
console.log(`Assigned [${updated.id.slice(0, 8)}] "${updated.title}" to ${updated.assignedTo}${prevMsg}`);
|
|
322
511
|
} catch (error2) {
|
|
323
|
-
if (error2 instanceof
|
|
512
|
+
if (error2 instanceof KabanError4) {
|
|
513
|
+
if (json)
|
|
514
|
+
outputError(error2.code, error2.message);
|
|
515
|
+
console.error(`Error: ${error2.message}`);
|
|
516
|
+
process.exit(error2.code);
|
|
517
|
+
}
|
|
518
|
+
throw error2;
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// src/commands/delete.ts
|
|
523
|
+
import { KabanError as KabanError5 } from "@kaban-board/core";
|
|
524
|
+
import { Command as Command5 } from "commander";
|
|
525
|
+
import { createInterface } from "readline";
|
|
526
|
+
var deleteCommand = new Command5("delete").description("Delete a task").argument("<id>", "Task ID").option("-f, --force", "Skip confirmation").option("-j, --json", "Output as JSON").action(async (id, options) => {
|
|
527
|
+
const json = options.json;
|
|
528
|
+
try {
|
|
529
|
+
const { taskService } = await getContext();
|
|
530
|
+
const task = await taskService.resolveTask(id);
|
|
531
|
+
if (!task) {
|
|
532
|
+
if (json)
|
|
533
|
+
outputError(2, `Task '${id}' not found`);
|
|
534
|
+
console.error(`Error: Task '${id}' not found`);
|
|
535
|
+
process.exit(2);
|
|
536
|
+
}
|
|
537
|
+
if (!options.force && !json) {
|
|
538
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
539
|
+
const answer = await new Promise((resolve) => {
|
|
540
|
+
rl.question(`Delete "${task.title}"? [y/N] `, resolve);
|
|
541
|
+
});
|
|
542
|
+
rl.close();
|
|
543
|
+
if (answer.toLowerCase() !== "y") {
|
|
544
|
+
console.log("Cancelled");
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
await taskService.deleteTask(task.id);
|
|
549
|
+
if (json) {
|
|
550
|
+
outputSuccess({ deleted: true, id: task.id });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
console.log(`Deleted [${task.id.slice(0, 8)}] "${task.title}"`);
|
|
554
|
+
} catch (error2) {
|
|
555
|
+
if (error2 instanceof KabanError5) {
|
|
324
556
|
if (json)
|
|
325
557
|
outputError(error2.code, error2.message);
|
|
326
558
|
console.error(`Error: ${error2.message}`);
|
|
@@ -331,14 +563,13 @@ var assignCommand = new Command3("assign").description("Assign a task to an agen
|
|
|
331
563
|
});
|
|
332
564
|
|
|
333
565
|
// src/commands/done.ts
|
|
334
|
-
import { KabanError as
|
|
335
|
-
import { Command as
|
|
336
|
-
var doneCommand = new
|
|
566
|
+
import { KabanError as KabanError6 } from "@kaban-board/core";
|
|
567
|
+
import { Command as Command6 } from "commander";
|
|
568
|
+
var doneCommand = new Command6("done").description("Mark a task as done").argument("<id>", "Task ID (can be partial)").option("-j, --json", "Output as JSON").action(async (id, options) => {
|
|
337
569
|
const json = options.json;
|
|
338
570
|
try {
|
|
339
571
|
const { taskService, boardService } = await getContext();
|
|
340
|
-
const
|
|
341
|
-
const task = tasks.find((t) => t.id.startsWith(id));
|
|
572
|
+
const task = await taskService.resolveTask(id);
|
|
342
573
|
if (!task) {
|
|
343
574
|
if (json)
|
|
344
575
|
outputError(2, `Task '${id}' not found`);
|
|
@@ -359,7 +590,145 @@ var doneCommand = new Command4("done").description("Mark a task as done").argume
|
|
|
359
590
|
}
|
|
360
591
|
console.log(`Completed [${moved.id.slice(0, 8)}] "${moved.title}"`);
|
|
361
592
|
} catch (error2) {
|
|
362
|
-
if (error2 instanceof
|
|
593
|
+
if (error2 instanceof KabanError6) {
|
|
594
|
+
if (json)
|
|
595
|
+
outputError(error2.code, error2.message);
|
|
596
|
+
console.error(`Error: ${error2.message}`);
|
|
597
|
+
process.exit(error2.code);
|
|
598
|
+
}
|
|
599
|
+
throw error2;
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// src/commands/edit.ts
|
|
604
|
+
import { KabanError as KabanError7 } from "@kaban-board/core";
|
|
605
|
+
import { Command as Command7 } from "commander";
|
|
606
|
+
var editCommand = new Command7("edit").description("Edit a task").argument("<id>", "Task ID").option("-t, --title <title>", "New title").option("-d, --description <desc>", "New description").option("--clear-description", "Clear description").option("-l, --labels <labels>", "Labels (comma-separated)").option("--due <date>", "Due date (natural language)").option("--clear-due", "Clear due date").option("-j, --json", "Output as JSON").action(async (id, options) => {
|
|
607
|
+
const json = options.json;
|
|
608
|
+
try {
|
|
609
|
+
const { taskService } = await getContext();
|
|
610
|
+
const task = await taskService.resolveTask(id);
|
|
611
|
+
if (!task) {
|
|
612
|
+
if (json)
|
|
613
|
+
outputError(2, `Task '${id}' not found`);
|
|
614
|
+
console.error(`Error: Task '${id}' not found`);
|
|
615
|
+
process.exit(2);
|
|
616
|
+
}
|
|
617
|
+
const updates = {};
|
|
618
|
+
if (options.title)
|
|
619
|
+
updates.title = options.title;
|
|
620
|
+
if (options.description)
|
|
621
|
+
updates.description = options.description;
|
|
622
|
+
if (options.clearDescription)
|
|
623
|
+
updates.description = null;
|
|
624
|
+
if (options.labels)
|
|
625
|
+
updates.labels = options.labels.split(",").map((l) => l.trim());
|
|
626
|
+
if (options.due)
|
|
627
|
+
updates.dueDate = options.due;
|
|
628
|
+
if (options.clearDue)
|
|
629
|
+
updates.dueDate = null;
|
|
630
|
+
if (Object.keys(updates).length === 0) {
|
|
631
|
+
if (json)
|
|
632
|
+
outputError(4, "No updates specified");
|
|
633
|
+
console.error("Error: No updates specified. Use --title, --description, etc.");
|
|
634
|
+
process.exit(4);
|
|
635
|
+
}
|
|
636
|
+
const updated = await taskService.updateTask(task.id, updates);
|
|
637
|
+
if (json) {
|
|
638
|
+
outputSuccess(updated);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
console.log(`Updated [${updated.id.slice(0, 8)}] "${updated.title}"`);
|
|
642
|
+
} catch (error2) {
|
|
643
|
+
if (error2 instanceof KabanError7) {
|
|
644
|
+
if (json)
|
|
645
|
+
outputError(error2.code, error2.message);
|
|
646
|
+
console.error(`Error: ${error2.message}`);
|
|
647
|
+
process.exit(error2.code);
|
|
648
|
+
}
|
|
649
|
+
throw error2;
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
// src/commands/export.ts
|
|
654
|
+
import { writeFileSync } from "fs";
|
|
655
|
+
import { KabanError as KabanError8, MarkdownService } from "@kaban-board/core";
|
|
656
|
+
import { Command as Command8 } from "commander";
|
|
657
|
+
var exportCommand = new Command8("export").description("Export board to markdown format").option("-o, --output <file>", "Output file path (default: stdout)").option("-a, --archived", "Include archived tasks").option("--no-metadata", "Exclude task metadata (IDs)").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
658
|
+
const json = options.json;
|
|
659
|
+
try {
|
|
660
|
+
const { taskService, boardService } = await getContext();
|
|
661
|
+
const markdownService = new MarkdownService;
|
|
662
|
+
const board = await boardService.getBoard();
|
|
663
|
+
const columns = await boardService.getColumns();
|
|
664
|
+
const allTasks = await taskService.listTasks({ includeArchived: options.archived });
|
|
665
|
+
const tasksByColumn = new Map;
|
|
666
|
+
for (const task of allTasks) {
|
|
667
|
+
const existing = tasksByColumn.get(task.columnId) ?? [];
|
|
668
|
+
existing.push(task);
|
|
669
|
+
tasksByColumn.set(task.columnId, existing);
|
|
670
|
+
}
|
|
671
|
+
const markdown = markdownService.exportBoard({ name: board?.name ?? "Kaban Board" }, columns, tasksByColumn, { includeArchived: options.archived, includeMetadata: options.metadata !== false });
|
|
672
|
+
if (options.output) {
|
|
673
|
+
writeFileSync(options.output, markdown);
|
|
674
|
+
if (json) {
|
|
675
|
+
outputSuccess({ file: options.output, tasks: allTasks.length });
|
|
676
|
+
} else {
|
|
677
|
+
console.log(`Exported ${allTasks.length} tasks to ${options.output}`);
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
if (json) {
|
|
681
|
+
outputSuccess({ markdown, tasks: allTasks.length });
|
|
682
|
+
} else {
|
|
683
|
+
console.log(markdown);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
} catch (error2) {
|
|
687
|
+
if (error2 instanceof KabanError8) {
|
|
688
|
+
if (json)
|
|
689
|
+
outputError(error2.code, error2.message);
|
|
690
|
+
console.error(`Error: ${error2.message}`);
|
|
691
|
+
process.exit(error2.code);
|
|
692
|
+
}
|
|
693
|
+
throw error2;
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
// src/commands/get.ts
|
|
698
|
+
import { KabanError as KabanError9 } from "@kaban-board/core";
|
|
699
|
+
import { Command as Command9 } from "commander";
|
|
700
|
+
var getCommand = new Command9("get").description("View a task by ID").argument("<id>", "Task ID (ULID, partial, or #number)").option("-j, --json", "Output as JSON").action(async (id, options) => {
|
|
701
|
+
const json = options.json;
|
|
702
|
+
try {
|
|
703
|
+
const { taskService } = await getContext();
|
|
704
|
+
const task = await taskService.resolveTask(id);
|
|
705
|
+
if (!task) {
|
|
706
|
+
if (json)
|
|
707
|
+
outputError(2, `Task '${id}' not found`);
|
|
708
|
+
console.error(`Error: Task '${id}' not found`);
|
|
709
|
+
process.exit(2);
|
|
710
|
+
}
|
|
711
|
+
if (json) {
|
|
712
|
+
outputSuccess(task);
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
console.log(`
|
|
716
|
+
[${task.id.slice(0, 8)}] ${task.title}`);
|
|
717
|
+
console.log(` Column: ${task.columnId}`);
|
|
718
|
+
if (task.description)
|
|
719
|
+
console.log(` Description: ${task.description}`);
|
|
720
|
+
if (task.assignedTo)
|
|
721
|
+
console.log(` Assigned: ${task.assignedTo}`);
|
|
722
|
+
if (task.labels.length)
|
|
723
|
+
console.log(` Labels: ${task.labels.join(", ")}`);
|
|
724
|
+
if (task.dueDate)
|
|
725
|
+
console.log(` Due: ${task.dueDate.toISOString().split("T")[0]}`);
|
|
726
|
+
if (task.dependsOn.length)
|
|
727
|
+
console.log(` Depends on: ${task.dependsOn.length} task(s)`);
|
|
728
|
+
console.log(` Created: ${task.createdAt.toISOString()}`);
|
|
729
|
+
console.log();
|
|
730
|
+
} catch (error2) {
|
|
731
|
+
if (error2 instanceof KabanError9) {
|
|
363
732
|
if (json)
|
|
364
733
|
outputError(error2.code, error2.message);
|
|
365
734
|
console.error(`Error: ${error2.message}`);
|
|
@@ -377,7 +746,7 @@ import { homedir as homedir2 } from "os";
|
|
|
377
746
|
import { dirname, join as join3 } from "path";
|
|
378
747
|
import * as p from "@clack/prompts";
|
|
379
748
|
import chalk from "chalk";
|
|
380
|
-
import { Command as
|
|
749
|
+
import { Command as Command10 } from "commander";
|
|
381
750
|
|
|
382
751
|
// src/hook/settings-manager.ts
|
|
383
752
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -726,7 +1095,7 @@ function formatSize(bytes) {
|
|
|
726
1095
|
return `${bytes} B`;
|
|
727
1096
|
return `${Math.round(bytes / 1024)} KB`;
|
|
728
1097
|
}
|
|
729
|
-
var installCommand = new
|
|
1098
|
+
var installCommand = new Command10("install").description("Install TodoWrite sync hook for Claude Code").option("-y, --yes", "Skip confirmation").action(async (options) => {
|
|
730
1099
|
p.intro(chalk.bgCyan.black(" kaban hook install "));
|
|
731
1100
|
const s = p.spinner();
|
|
732
1101
|
s.start("Checking dependencies...");
|
|
@@ -787,7 +1156,7 @@ var installCommand = new Command5("install").description("Install TodoWrite sync
|
|
|
787
1156
|
process.exit(1);
|
|
788
1157
|
}
|
|
789
1158
|
});
|
|
790
|
-
var uninstallCommand = new
|
|
1159
|
+
var uninstallCommand = new Command10("uninstall").description("Remove TodoWrite sync hook").option("-y, --yes", "Skip confirmation").option("--clean", "Also remove sync logs").action(async (options) => {
|
|
791
1160
|
p.intro(chalk.bgRed.white(" kaban hook uninstall "));
|
|
792
1161
|
const binaryExists = existsSync3(join3(HOOKS_DIR, HOOK_BINARY_NAME));
|
|
793
1162
|
const logExists = existsSync3(join3(HOOKS_DIR, LOG_FILE));
|
|
@@ -857,7 +1226,7 @@ var uninstallCommand = new Command5("uninstall").description("Remove TodoWrite s
|
|
|
857
1226
|
process.exit(1);
|
|
858
1227
|
}
|
|
859
1228
|
});
|
|
860
|
-
var statusCommand = new
|
|
1229
|
+
var statusCommand = new Command10("status").description("Check hook installation status").action(async () => {
|
|
861
1230
|
p.intro(chalk.bgBlue.white(" kaban hook status "));
|
|
862
1231
|
const s = p.spinner();
|
|
863
1232
|
s.start("Checking status...");
|
|
@@ -923,18 +1292,95 @@ var statusCommand = new Command5("status").description("Check hook installation
|
|
|
923
1292
|
process.exit(1);
|
|
924
1293
|
}
|
|
925
1294
|
});
|
|
926
|
-
var hookCommand = new
|
|
1295
|
+
var hookCommand = new Command10("hook").description("Manage TodoWrite sync hook for Claude Code").addCommand(installCommand).addCommand(uninstallCommand).addCommand(statusCommand);
|
|
1296
|
+
|
|
1297
|
+
// src/commands/import.ts
|
|
1298
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1299
|
+
import { KabanError as KabanError10, MarkdownService as MarkdownService2 } from "@kaban-board/core";
|
|
1300
|
+
import { Command as Command11 } from "commander";
|
|
1301
|
+
var importCommand = new Command11("import").description("Import tasks from markdown file").argument("<file>", "Markdown file to import").option("-d, --dry-run", "Preview import without creating tasks").option("-j, --json", "Output as JSON").action(async (file, options) => {
|
|
1302
|
+
const json = options.json;
|
|
1303
|
+
try {
|
|
1304
|
+
const { taskService, boardService } = await getContext();
|
|
1305
|
+
const markdownService = new MarkdownService2;
|
|
1306
|
+
const markdown = readFileSync2(file, "utf-8");
|
|
1307
|
+
const parseResult = markdownService.parseMarkdown(markdown);
|
|
1308
|
+
if (parseResult.errors.length > 0) {
|
|
1309
|
+
if (json) {
|
|
1310
|
+
outputError(1, `Parse errors: ${parseResult.errors.join(", ")}`);
|
|
1311
|
+
} else {
|
|
1312
|
+
console.error("Parse errors:");
|
|
1313
|
+
for (const error2 of parseResult.errors) {
|
|
1314
|
+
console.error(` - ${error2}`);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
process.exit(1);
|
|
1318
|
+
}
|
|
1319
|
+
const columns = await boardService.getColumns();
|
|
1320
|
+
const columnMap = new Map(columns.map((c) => [c.name.toLowerCase(), c.id]));
|
|
1321
|
+
if (options.dryRun) {
|
|
1322
|
+
let taskCount = 0;
|
|
1323
|
+
for (const column of parseResult.columns) {
|
|
1324
|
+
taskCount += column.tasks.length;
|
|
1325
|
+
}
|
|
1326
|
+
if (json) {
|
|
1327
|
+
outputSuccess({
|
|
1328
|
+
dryRun: true,
|
|
1329
|
+
wouldCreate: taskCount,
|
|
1330
|
+
columns: parseResult.columns.map((c) => ({
|
|
1331
|
+
name: c.name,
|
|
1332
|
+
tasks: c.tasks.length
|
|
1333
|
+
}))
|
|
1334
|
+
});
|
|
1335
|
+
} else {
|
|
1336
|
+
console.log(`Dry run: would import ${taskCount} tasks`);
|
|
1337
|
+
for (const column of parseResult.columns) {
|
|
1338
|
+
console.log(` ${column.name}: ${column.tasks.length} tasks`);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
const createdTasks = [];
|
|
1344
|
+
for (const column of parseResult.columns) {
|
|
1345
|
+
const columnId = columnMap.get(column.name.toLowerCase()) ?? "todo";
|
|
1346
|
+
for (const task of column.tasks) {
|
|
1347
|
+
const created = await taskService.addTask({
|
|
1348
|
+
title: task.title,
|
|
1349
|
+
description: task.description ?? undefined,
|
|
1350
|
+
columnId,
|
|
1351
|
+
labels: task.labels,
|
|
1352
|
+
assignedTo: task.assignedTo ?? undefined,
|
|
1353
|
+
dueDate: task.dueDate?.toISOString()
|
|
1354
|
+
});
|
|
1355
|
+
createdTasks.push(created);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
if (json) {
|
|
1359
|
+
outputSuccess({ imported: createdTasks.length, tasks: createdTasks });
|
|
1360
|
+
} else {
|
|
1361
|
+
console.log(`Imported ${createdTasks.length} tasks`);
|
|
1362
|
+
}
|
|
1363
|
+
} catch (error2) {
|
|
1364
|
+
if (error2 instanceof KabanError10) {
|
|
1365
|
+
if (json)
|
|
1366
|
+
outputError(error2.code, error2.message);
|
|
1367
|
+
console.error(`Error: ${error2.message}`);
|
|
1368
|
+
process.exit(error2.code);
|
|
1369
|
+
}
|
|
1370
|
+
throw error2;
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
927
1373
|
|
|
928
1374
|
// src/commands/init.ts
|
|
929
|
-
import { existsSync as existsSync4, mkdirSync, writeFileSync } from "fs";
|
|
1375
|
+
import { existsSync as existsSync4, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
930
1376
|
import {
|
|
931
1377
|
BoardService as BoardService2,
|
|
932
1378
|
createDb as createDb2,
|
|
933
1379
|
DEFAULT_CONFIG as DEFAULT_CONFIG2,
|
|
934
1380
|
initializeSchema
|
|
935
1381
|
} from "@kaban-board/core";
|
|
936
|
-
import { Command as
|
|
937
|
-
var initCommand = new
|
|
1382
|
+
import { Command as Command12 } from "commander";
|
|
1383
|
+
var initCommand = new Command12("init").description("Initialize a new Kaban board in the current directory").option("-n, --name <name>", "Board name", "Kaban Board").action(async (options) => {
|
|
938
1384
|
const { kabanDir, dbPath, configPath } = getKabanPaths();
|
|
939
1385
|
if (existsSync4(dbPath)) {
|
|
940
1386
|
console.error("Error: Board already exists in this directory");
|
|
@@ -945,10 +1391,10 @@ var initCommand = new Command6("init").description("Initialize a new Kaban board
|
|
|
945
1391
|
...DEFAULT_CONFIG2,
|
|
946
1392
|
board: { name: options.name }
|
|
947
1393
|
};
|
|
948
|
-
|
|
949
|
-
const
|
|
950
|
-
await initializeSchema(
|
|
951
|
-
const boardService = new BoardService2(
|
|
1394
|
+
writeFileSync2(configPath, JSON.stringify(config, null, 2));
|
|
1395
|
+
const db2 = await createDb2(dbPath);
|
|
1396
|
+
await initializeSchema(db2);
|
|
1397
|
+
const boardService = new BoardService2(db2);
|
|
952
1398
|
await boardService.initializeBoard(config);
|
|
953
1399
|
console.log(`Initialized Kaban board: ${options.name}`);
|
|
954
1400
|
console.log(` Database: ${dbPath}`);
|
|
@@ -956,8 +1402,8 @@ var initCommand = new Command6("init").description("Initialize a new Kaban board
|
|
|
956
1402
|
});
|
|
957
1403
|
|
|
958
1404
|
// src/commands/list.ts
|
|
959
|
-
import { KabanError as
|
|
960
|
-
import { Command as
|
|
1405
|
+
import { KabanError as KabanError11 } from "@kaban-board/core";
|
|
1406
|
+
import { Command as Command13 } from "commander";
|
|
961
1407
|
function sortTasks(tasks, sortBy, reverse) {
|
|
962
1408
|
const sorted = [...tasks].sort((a, b) => {
|
|
963
1409
|
switch (sortBy) {
|
|
@@ -973,7 +1419,7 @@ function sortTasks(tasks, sortBy, reverse) {
|
|
|
973
1419
|
});
|
|
974
1420
|
return reverse ? sorted.reverse() : sorted;
|
|
975
1421
|
}
|
|
976
|
-
var listCommand = new
|
|
1422
|
+
var listCommand = new Command13("list").description("List tasks").option("-c, --column <column>", "Filter by column").option("-a, --agent <agent>", "Filter by creator agent").option("-u, --assignee <assignee>", "Filter by assigned agent").option("-b, --blocked", "Show only blocked tasks").option("-s, --sort <field>", "Sort by: name, date, updated").option("-r, --reverse", "Reverse sort order").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
977
1423
|
const json = options.json;
|
|
978
1424
|
try {
|
|
979
1425
|
const { taskService, boardService } = await getContext();
|
|
@@ -1014,7 +1460,7 @@ var listCommand = new Command7("list").description("List tasks").option("-c, --c
|
|
|
1014
1460
|
}
|
|
1015
1461
|
}
|
|
1016
1462
|
} catch (error2) {
|
|
1017
|
-
if (error2 instanceof
|
|
1463
|
+
if (error2 instanceof KabanError11) {
|
|
1018
1464
|
if (json)
|
|
1019
1465
|
outputError(error2.code, error2.message);
|
|
1020
1466
|
console.error(`Error: ${error2.message}`);
|
|
@@ -1025,14 +1471,22 @@ var listCommand = new Command7("list").description("List tasks").option("-c, --c
|
|
|
1025
1471
|
});
|
|
1026
1472
|
|
|
1027
1473
|
// src/commands/mcp.ts
|
|
1028
|
-
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as
|
|
1474
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1029
1475
|
import { join as join4 } from "path";
|
|
1030
1476
|
import {
|
|
1477
|
+
AuditService as AuditService2,
|
|
1031
1478
|
BoardService as BoardService3,
|
|
1032
1479
|
createDb as createDb3,
|
|
1033
1480
|
DEFAULT_CONFIG as DEFAULT_CONFIG3,
|
|
1034
1481
|
initializeSchema as initializeSchema2,
|
|
1035
|
-
TaskService as TaskService2
|
|
1482
|
+
TaskService as TaskService2,
|
|
1483
|
+
LinkService,
|
|
1484
|
+
MarkdownService as MarkdownService3,
|
|
1485
|
+
ScoringService,
|
|
1486
|
+
fifoScorer,
|
|
1487
|
+
priorityScorer,
|
|
1488
|
+
dueDateScorer,
|
|
1489
|
+
createBlockingScorer
|
|
1036
1490
|
} from "@kaban-board/core";
|
|
1037
1491
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
1038
1492
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -1042,7 +1496,7 @@ import {
|
|
|
1042
1496
|
ListToolsRequestSchema,
|
|
1043
1497
|
ReadResourceRequestSchema
|
|
1044
1498
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
1045
|
-
import { Command as
|
|
1499
|
+
import { Command as Command14 } from "commander";
|
|
1046
1500
|
var mcpHelpers = {
|
|
1047
1501
|
getParam(args, primary, alias) {
|
|
1048
1502
|
if (!args)
|
|
@@ -1076,11 +1530,21 @@ async function createContext(basePath) {
|
|
|
1076
1530
|
if (!existsSync5(dbPath)) {
|
|
1077
1531
|
throw new Error("No board found. Run 'kaban init' first");
|
|
1078
1532
|
}
|
|
1079
|
-
const
|
|
1080
|
-
const config = JSON.parse(
|
|
1081
|
-
const boardService = new BoardService3(
|
|
1082
|
-
const taskService = new TaskService2(
|
|
1083
|
-
|
|
1533
|
+
const db2 = await createDb3(dbPath);
|
|
1534
|
+
const config = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
1535
|
+
const boardService = new BoardService3(db2);
|
|
1536
|
+
const taskService = new TaskService2(db2, boardService);
|
|
1537
|
+
const linkService = new LinkService(db2);
|
|
1538
|
+
const markdownService = new MarkdownService3;
|
|
1539
|
+
const scoringService = new ScoringService;
|
|
1540
|
+
scoringService.addScorer(priorityScorer);
|
|
1541
|
+
scoringService.addScorer(dueDateScorer);
|
|
1542
|
+
scoringService.addScorer(createBlockingScorer(async (taskId) => {
|
|
1543
|
+
const blocking = await linkService.getBlocking(taskId);
|
|
1544
|
+
return blocking.length;
|
|
1545
|
+
}));
|
|
1546
|
+
scoringService.addScorer(fifoScorer);
|
|
1547
|
+
return { db: db2, config, boardService, taskService, linkService, markdownService, scoringService };
|
|
1084
1548
|
}
|
|
1085
1549
|
async function startMcpServer(workingDirectory) {
|
|
1086
1550
|
const server = new Server({ name: "kaban-mcp", version: "0.1.0" }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -1184,6 +1648,18 @@ async function startMcpServer(workingDirectory) {
|
|
|
1184
1648
|
}
|
|
1185
1649
|
}
|
|
1186
1650
|
},
|
|
1651
|
+
{
|
|
1652
|
+
name: "kaban_assign_task",
|
|
1653
|
+
description: "Assign or unassign a task to/from an agent (convenience wrapper for kaban_update_task)",
|
|
1654
|
+
inputSchema: {
|
|
1655
|
+
type: "object",
|
|
1656
|
+
properties: {
|
|
1657
|
+
id: { type: "string", description: "Task ID (ULID)" },
|
|
1658
|
+
taskId: { type: "string", description: "Task ID - alias for 'id'" },
|
|
1659
|
+
assignee: { type: ["string", "null"], description: "Agent name (null to unassign)" }
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
},
|
|
1187
1663
|
{
|
|
1188
1664
|
name: "kaban_delete_task",
|
|
1189
1665
|
description: "Delete a task",
|
|
@@ -1334,6 +1810,140 @@ async function startMcpServer(workingDirectory) {
|
|
|
1334
1810
|
},
|
|
1335
1811
|
required: ["title"]
|
|
1336
1812
|
}
|
|
1813
|
+
},
|
|
1814
|
+
{
|
|
1815
|
+
name: "kaban_add_link",
|
|
1816
|
+
description: "Create a link between two tasks",
|
|
1817
|
+
inputSchema: {
|
|
1818
|
+
type: "object",
|
|
1819
|
+
properties: {
|
|
1820
|
+
sourceId: { type: "string", description: "Source task ID" },
|
|
1821
|
+
targetId: { type: "string", description: "Target task ID" },
|
|
1822
|
+
type: {
|
|
1823
|
+
type: "string",
|
|
1824
|
+
enum: ["relates_to", "blocks", "duplicates", "parent_of"],
|
|
1825
|
+
description: "Link type (default: relates_to)"
|
|
1826
|
+
},
|
|
1827
|
+
metadata: { type: "object", description: "Optional metadata for the link" }
|
|
1828
|
+
},
|
|
1829
|
+
required: ["sourceId", "targetId"]
|
|
1830
|
+
}
|
|
1831
|
+
},
|
|
1832
|
+
{
|
|
1833
|
+
name: "kaban_remove_link",
|
|
1834
|
+
description: "Remove a link between two tasks",
|
|
1835
|
+
inputSchema: {
|
|
1836
|
+
type: "object",
|
|
1837
|
+
properties: {
|
|
1838
|
+
sourceId: { type: "string", description: "Source task ID" },
|
|
1839
|
+
targetId: { type: "string", description: "Target task ID" },
|
|
1840
|
+
type: {
|
|
1841
|
+
type: "string",
|
|
1842
|
+
enum: ["relates_to", "blocks", "duplicates", "parent_of"],
|
|
1843
|
+
description: "Link type to remove (removes all types if not specified)"
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
required: ["sourceId", "targetId"]
|
|
1847
|
+
}
|
|
1848
|
+
},
|
|
1849
|
+
{
|
|
1850
|
+
name: "kaban_get_links",
|
|
1851
|
+
description: "Get links for a task",
|
|
1852
|
+
inputSchema: {
|
|
1853
|
+
type: "object",
|
|
1854
|
+
properties: {
|
|
1855
|
+
taskId: { type: "string", description: "Task ID" },
|
|
1856
|
+
id: { type: "string", description: "Task ID - alias for taskId" },
|
|
1857
|
+
direction: {
|
|
1858
|
+
type: "string",
|
|
1859
|
+
enum: ["outgoing", "incoming", "both"],
|
|
1860
|
+
description: "Link direction to return (default: both)"
|
|
1861
|
+
},
|
|
1862
|
+
type: {
|
|
1863
|
+
type: "string",
|
|
1864
|
+
enum: ["relates_to", "blocks", "duplicates", "parent_of"],
|
|
1865
|
+
description: "Filter by link type"
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
},
|
|
1870
|
+
{
|
|
1871
|
+
name: "kaban_get_next_task",
|
|
1872
|
+
description: "Get the highest-priority task to work on next (considering dependencies, due dates, etc.)",
|
|
1873
|
+
inputSchema: {
|
|
1874
|
+
type: "object",
|
|
1875
|
+
properties: {
|
|
1876
|
+
columnId: { type: "string", description: "Filter by column (default: todo)" }
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
{
|
|
1881
|
+
name: "kaban_score_tasks",
|
|
1882
|
+
description: "Get all tasks with their priority scores",
|
|
1883
|
+
inputSchema: {
|
|
1884
|
+
type: "object",
|
|
1885
|
+
properties: {
|
|
1886
|
+
columnId: { type: "string", description: "Filter by column" },
|
|
1887
|
+
limit: { type: "number", description: "Max tasks to return (default: 10)" }
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
},
|
|
1891
|
+
{
|
|
1892
|
+
name: "kaban_export_markdown",
|
|
1893
|
+
description: "Export the board to markdown format",
|
|
1894
|
+
inputSchema: {
|
|
1895
|
+
type: "object",
|
|
1896
|
+
properties: {
|
|
1897
|
+
includeArchived: { type: "boolean", description: "Include archived tasks (default: false)" },
|
|
1898
|
+
includeMetadata: { type: "boolean", description: "Include task metadata (default: true)" }
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
name: "kaban_import_markdown",
|
|
1904
|
+
description: "Import tasks from markdown format (creates new tasks, does not modify existing)",
|
|
1905
|
+
inputSchema: {
|
|
1906
|
+
type: "object",
|
|
1907
|
+
properties: {
|
|
1908
|
+
markdown: { type: "string", description: "Markdown content to import" },
|
|
1909
|
+
dryRun: { type: "boolean", description: "Preview import without creating tasks" }
|
|
1910
|
+
},
|
|
1911
|
+
required: ["markdown"]
|
|
1912
|
+
}
|
|
1913
|
+
},
|
|
1914
|
+
{
|
|
1915
|
+
name: "kaban_get_audit_history",
|
|
1916
|
+
description: "Get audit log history with optional filters",
|
|
1917
|
+
inputSchema: {
|
|
1918
|
+
type: "object",
|
|
1919
|
+
properties: {
|
|
1920
|
+
objectType: {
|
|
1921
|
+
type: "string",
|
|
1922
|
+
enum: ["task", "column", "board"],
|
|
1923
|
+
description: "Filter by object type"
|
|
1924
|
+
},
|
|
1925
|
+
objectId: { type: "string", description: "Filter by object ID" },
|
|
1926
|
+
eventType: {
|
|
1927
|
+
type: "string",
|
|
1928
|
+
enum: ["CREATE", "UPDATE", "DELETE"],
|
|
1929
|
+
description: "Filter by event type"
|
|
1930
|
+
},
|
|
1931
|
+
actor: { type: "string", description: "Filter by actor name" },
|
|
1932
|
+
limit: { type: "number", description: "Max entries (default: 50, max: 1000)" }
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
},
|
|
1936
|
+
{
|
|
1937
|
+
name: "kaban_get_task_history",
|
|
1938
|
+
description: "Get change history for a specific task",
|
|
1939
|
+
inputSchema: {
|
|
1940
|
+
type: "object",
|
|
1941
|
+
properties: {
|
|
1942
|
+
taskId: { type: "string", description: "Task ID (ULID or partial)" },
|
|
1943
|
+
id: { type: "string", description: "Task ID - alias for taskId" },
|
|
1944
|
+
limit: { type: "number", description: "Max entries (default: 50)" }
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1337
1947
|
}
|
|
1338
1948
|
]
|
|
1339
1949
|
}));
|
|
@@ -1353,10 +1963,10 @@ async function startMcpServer(workingDirectory) {
|
|
|
1353
1963
|
...DEFAULT_CONFIG3,
|
|
1354
1964
|
board: { name: boardName }
|
|
1355
1965
|
};
|
|
1356
|
-
|
|
1357
|
-
const
|
|
1358
|
-
await initializeSchema2(
|
|
1359
|
-
const boardService2 = new BoardService3(
|
|
1966
|
+
writeFileSync3(configPath, JSON.stringify(config, null, 2));
|
|
1967
|
+
const db2 = await createDb3(dbPath);
|
|
1968
|
+
await initializeSchema2(db2);
|
|
1969
|
+
const boardService2 = new BoardService3(db2);
|
|
1360
1970
|
await boardService2.initializeBoard(config);
|
|
1361
1971
|
return jsonResponse({
|
|
1362
1972
|
success: true,
|
|
@@ -1364,7 +1974,7 @@ async function startMcpServer(workingDirectory) {
|
|
|
1364
1974
|
paths: { database: dbPath, config: configPath }
|
|
1365
1975
|
});
|
|
1366
1976
|
}
|
|
1367
|
-
const { taskService, boardService } = await createContext(workingDirectory);
|
|
1977
|
+
const { taskService, boardService, linkService, markdownService, scoringService } = await createContext(workingDirectory);
|
|
1368
1978
|
const taskArgs = args;
|
|
1369
1979
|
const taskId = getParam(taskArgs, "id", "taskId");
|
|
1370
1980
|
switch (name) {
|
|
@@ -1411,6 +2021,14 @@ async function startMcpServer(workingDirectory) {
|
|
|
1411
2021
|
const task = await taskService.updateTask(taskId, updates, expectedVersion);
|
|
1412
2022
|
return jsonResponse(task);
|
|
1413
2023
|
}
|
|
2024
|
+
case "kaban_assign_task": {
|
|
2025
|
+
const id = getParam(taskArgs, "id", "taskId");
|
|
2026
|
+
if (!id)
|
|
2027
|
+
return errorResponse("Task ID required (use 'id' or 'taskId')");
|
|
2028
|
+
const { assignee } = args ?? {};
|
|
2029
|
+
const task = await taskService.updateTask(id, { assignedTo: assignee ?? null });
|
|
2030
|
+
return jsonResponse(task);
|
|
2031
|
+
}
|
|
1414
2032
|
case "kaban_delete_task": {
|
|
1415
2033
|
if (!taskId)
|
|
1416
2034
|
return errorResponse("Task ID required (use 'id' or 'taskId')");
|
|
@@ -1595,6 +2213,126 @@ async function startMcpServer(workingDirectory) {
|
|
|
1595
2213
|
const result = await taskService.addTaskChecked(taskInput, { force });
|
|
1596
2214
|
return jsonResponse(result);
|
|
1597
2215
|
}
|
|
2216
|
+
case "kaban_add_link": {
|
|
2217
|
+
const { sourceId, targetId, type } = args ?? {};
|
|
2218
|
+
if (!sourceId)
|
|
2219
|
+
return errorResponse("sourceId required");
|
|
2220
|
+
if (!targetId)
|
|
2221
|
+
return errorResponse("targetId required");
|
|
2222
|
+
const linkType = type ?? "related";
|
|
2223
|
+
const link = await linkService.addLink(sourceId, targetId, linkType);
|
|
2224
|
+
return jsonResponse(link);
|
|
2225
|
+
}
|
|
2226
|
+
case "kaban_remove_link": {
|
|
2227
|
+
const { sourceId, targetId, type } = args ?? {};
|
|
2228
|
+
if (!sourceId)
|
|
2229
|
+
return errorResponse("sourceId required");
|
|
2230
|
+
if (!targetId)
|
|
2231
|
+
return errorResponse("targetId required");
|
|
2232
|
+
if (!type)
|
|
2233
|
+
return errorResponse("type required");
|
|
2234
|
+
await linkService.removeLink(sourceId, targetId, type);
|
|
2235
|
+
return jsonResponse({ success: true });
|
|
2236
|
+
}
|
|
2237
|
+
case "kaban_get_links": {
|
|
2238
|
+
const id = getParam(taskArgs, "taskId", "id");
|
|
2239
|
+
if (!id)
|
|
2240
|
+
return errorResponse("Task ID required");
|
|
2241
|
+
const { direction } = args ?? {};
|
|
2242
|
+
let links;
|
|
2243
|
+
if (direction === "outgoing") {
|
|
2244
|
+
links = await linkService.getLinksFrom(id);
|
|
2245
|
+
} else if (direction === "incoming") {
|
|
2246
|
+
links = await linkService.getLinksTo(id);
|
|
2247
|
+
} else {
|
|
2248
|
+
links = await linkService.getAllLinks(id);
|
|
2249
|
+
}
|
|
2250
|
+
return jsonResponse(links);
|
|
2251
|
+
}
|
|
2252
|
+
case "kaban_get_next_task": {
|
|
2253
|
+
const { columnId } = args ?? {};
|
|
2254
|
+
const allTasks = await taskService.listTasks({ columnId: columnId ?? "todo" });
|
|
2255
|
+
const unblockedTasks = allTasks.filter((t) => !t.blockedReason && t.dependsOn.length === 0);
|
|
2256
|
+
if (unblockedTasks.length === 0) {
|
|
2257
|
+
return jsonResponse({ message: "No actionable tasks found", task: null });
|
|
2258
|
+
}
|
|
2259
|
+
const ranked = await scoringService.rankTasks(unblockedTasks);
|
|
2260
|
+
return jsonResponse(ranked[0]);
|
|
2261
|
+
}
|
|
2262
|
+
case "kaban_score_tasks": {
|
|
2263
|
+
const { columnId, limit } = args ?? {};
|
|
2264
|
+
const allTasks = await taskService.listTasks(columnId ? { columnId } : undefined);
|
|
2265
|
+
const ranked = await scoringService.rankTasks(allTasks);
|
|
2266
|
+
return jsonResponse(ranked.slice(0, limit ?? 10));
|
|
2267
|
+
}
|
|
2268
|
+
case "kaban_export_markdown": {
|
|
2269
|
+
const { includeArchived, includeMetadata } = args ?? {};
|
|
2270
|
+
const board = await boardService.getBoard();
|
|
2271
|
+
const columns = await boardService.getColumns();
|
|
2272
|
+
const allTasks = await taskService.listTasks({ includeArchived });
|
|
2273
|
+
const tasksByColumn = new Map;
|
|
2274
|
+
for (const task of allTasks) {
|
|
2275
|
+
const existing = tasksByColumn.get(task.columnId) ?? [];
|
|
2276
|
+
existing.push(task);
|
|
2277
|
+
tasksByColumn.set(task.columnId, existing);
|
|
2278
|
+
}
|
|
2279
|
+
const markdown = markdownService.exportBoard({ name: board?.name ?? "Kaban Board" }, columns, tasksByColumn, { includeArchived, includeMetadata: includeMetadata ?? true });
|
|
2280
|
+
return jsonResponse({ markdown });
|
|
2281
|
+
}
|
|
2282
|
+
case "kaban_import_markdown": {
|
|
2283
|
+
const { markdown, dryRun } = args ?? {};
|
|
2284
|
+
if (!markdown)
|
|
2285
|
+
return errorResponse("markdown content required");
|
|
2286
|
+
const parseResult = markdownService.parseMarkdown(markdown);
|
|
2287
|
+
if (parseResult.errors.length > 0) {
|
|
2288
|
+
return jsonResponse({ success: false, errors: parseResult.errors, parsed: parseResult });
|
|
2289
|
+
}
|
|
2290
|
+
if (dryRun) {
|
|
2291
|
+
return jsonResponse({ dryRun: true, parsed: parseResult });
|
|
2292
|
+
}
|
|
2293
|
+
const createdTasks = [];
|
|
2294
|
+
for (const column of parseResult.columns) {
|
|
2295
|
+
for (const task of column.tasks) {
|
|
2296
|
+
const created = await taskService.addTask({
|
|
2297
|
+
title: task.title,
|
|
2298
|
+
description: task.description ?? undefined,
|
|
2299
|
+
columnId: column.name.toLowerCase().replace(/\s+/g, "_"),
|
|
2300
|
+
labels: task.labels,
|
|
2301
|
+
assignedTo: task.assignedTo ?? undefined,
|
|
2302
|
+
dueDate: task.dueDate?.toISOString()
|
|
2303
|
+
});
|
|
2304
|
+
createdTasks.push(created);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return jsonResponse({ success: true, tasksCreated: createdTasks.length, tasks: createdTasks });
|
|
2308
|
+
}
|
|
2309
|
+
case "kaban_get_audit_history": {
|
|
2310
|
+
const { objectType, objectId, eventType, actor, limit } = args ?? {};
|
|
2311
|
+
const auditService = new AuditService2(db);
|
|
2312
|
+
const result = await auditService.getHistory({
|
|
2313
|
+
objectType,
|
|
2314
|
+
objectId,
|
|
2315
|
+
eventType,
|
|
2316
|
+
actor,
|
|
2317
|
+
limit: limit ?? 50
|
|
2318
|
+
});
|
|
2319
|
+
return jsonResponse(result);
|
|
2320
|
+
}
|
|
2321
|
+
case "kaban_get_task_history": {
|
|
2322
|
+
const taskIdArg = getParam(args, "taskId", "id");
|
|
2323
|
+
if (!taskIdArg)
|
|
2324
|
+
return errorResponse("Task ID required (use 'taskId' or 'id')");
|
|
2325
|
+
const task = await taskService.resolveTask(taskIdArg);
|
|
2326
|
+
if (!task)
|
|
2327
|
+
return errorResponse(`Task '${taskIdArg}' not found`);
|
|
2328
|
+
const { limit } = args ?? {};
|
|
2329
|
+
const auditService = new AuditService2(db);
|
|
2330
|
+
const entries = await auditService.getTaskHistory(task.id, limit ?? 50);
|
|
2331
|
+
return jsonResponse({
|
|
2332
|
+
task: { id: task.id, title: task.title },
|
|
2333
|
+
entries
|
|
2334
|
+
});
|
|
2335
|
+
}
|
|
1598
2336
|
default:
|
|
1599
2337
|
return errorResponse(`Unknown tool: ${name}`);
|
|
1600
2338
|
}
|
|
@@ -1708,20 +2446,19 @@ async function startMcpServer(workingDirectory) {
|
|
|
1708
2446
|
await server.connect(transport);
|
|
1709
2447
|
console.error("Kaban MCP server running on stdio");
|
|
1710
2448
|
}
|
|
1711
|
-
var mcpCommand = new
|
|
2449
|
+
var mcpCommand = new Command14("mcp").description("Start MCP server for AI agent integration").option("-p, --path <path>", "Working directory for Kaban board").action(async (options) => {
|
|
1712
2450
|
const workingDirectory = options.path ?? process.env.KABAN_PATH ?? process.cwd();
|
|
1713
2451
|
await startMcpServer(workingDirectory);
|
|
1714
2452
|
});
|
|
1715
2453
|
|
|
1716
2454
|
// src/commands/move.ts
|
|
1717
|
-
import { KabanError as
|
|
1718
|
-
import { Command as
|
|
1719
|
-
var moveCommand = new
|
|
2455
|
+
import { KabanError as KabanError12 } from "@kaban-board/core";
|
|
2456
|
+
import { Command as Command15 } from "commander";
|
|
2457
|
+
var moveCommand = new Command15("move").description("Move a task to a different column").argument("<id>", "Task ID (can be partial)").argument("[column]", "Target column").option("-n, --next", "Move to next column").option("-f, --force", "Force move even if WIP limit exceeded").option("-A, --assign [agent]", "Assign task to agent (defaults to current agent)").option("-j, --json", "Output as JSON").action(async (id, column, options) => {
|
|
1720
2458
|
const json = options.json;
|
|
1721
2459
|
try {
|
|
1722
2460
|
const { taskService, boardService } = await getContext();
|
|
1723
|
-
const
|
|
1724
|
-
const task = tasks.find((t) => t.id.startsWith(id));
|
|
2461
|
+
const task = await taskService.resolveTask(id);
|
|
1725
2462
|
if (!task) {
|
|
1726
2463
|
if (json)
|
|
1727
2464
|
outputError(2, `Task '${id}' not found`);
|
|
@@ -1767,7 +2504,64 @@ var moveCommand = new Command9("move").description("Move a task to a different c
|
|
|
1767
2504
|
}
|
|
1768
2505
|
console.log(msg);
|
|
1769
2506
|
} catch (error2) {
|
|
1770
|
-
if (error2 instanceof
|
|
2507
|
+
if (error2 instanceof KabanError12) {
|
|
2508
|
+
if (json)
|
|
2509
|
+
outputError(error2.code, error2.message);
|
|
2510
|
+
console.error(`Error: ${error2.message}`);
|
|
2511
|
+
process.exit(error2.code);
|
|
2512
|
+
}
|
|
2513
|
+
throw error2;
|
|
2514
|
+
}
|
|
2515
|
+
});
|
|
2516
|
+
|
|
2517
|
+
// src/commands/next.ts
|
|
2518
|
+
import {
|
|
2519
|
+
KabanError as KabanError13,
|
|
2520
|
+
ScoringService as ScoringService2,
|
|
2521
|
+
fifoScorer as fifoScorer2,
|
|
2522
|
+
priorityScorer as priorityScorer2,
|
|
2523
|
+
dueDateScorer as dueDateScorer2,
|
|
2524
|
+
createBlockingScorer as createBlockingScorer2,
|
|
2525
|
+
LinkService as LinkService2
|
|
2526
|
+
} from "@kaban-board/core";
|
|
2527
|
+
import { Command as Command16 } from "commander";
|
|
2528
|
+
var nextCommand = new Command16("next").description("Get the next highest-priority task").option("-c, --column <column>", "Column to pick from", "todo").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
2529
|
+
const json = options.json;
|
|
2530
|
+
try {
|
|
2531
|
+
const { taskService, db: db2 } = await getContext();
|
|
2532
|
+
const linkService = new LinkService2(db2);
|
|
2533
|
+
const scoringService = new ScoringService2;
|
|
2534
|
+
scoringService.addScorer(priorityScorer2);
|
|
2535
|
+
scoringService.addScorer(dueDateScorer2);
|
|
2536
|
+
scoringService.addScorer(createBlockingScorer2(async (taskId) => {
|
|
2537
|
+
const blocking = await linkService.getBlocking(taskId);
|
|
2538
|
+
return blocking.length;
|
|
2539
|
+
}));
|
|
2540
|
+
scoringService.addScorer(fifoScorer2);
|
|
2541
|
+
const tasks = await taskService.listTasks({ columnId: options.column });
|
|
2542
|
+
const unblocked = tasks.filter((t) => !t.blockedReason && t.dependsOn.length === 0);
|
|
2543
|
+
if (unblocked.length === 0) {
|
|
2544
|
+
if (json) {
|
|
2545
|
+
outputSuccess({ task: null, message: "No actionable tasks" });
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
console.log("No actionable tasks in", options.column);
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
const ranked = await scoringService.rankTasks(unblocked);
|
|
2552
|
+
const next = ranked[0];
|
|
2553
|
+
if (json) {
|
|
2554
|
+
outputSuccess(next);
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
console.log(`
|
|
2558
|
+
Next: [${next.task.id.slice(0, 8)}] "${next.task.title}"`);
|
|
2559
|
+
console.log(` Score: ${next.score} (${Object.entries(next.breakdown).map(([k, v]) => `${k}:${v}`).join(", ")})`);
|
|
2560
|
+
if (next.task.dueDate)
|
|
2561
|
+
console.log(` Due: ${next.task.dueDate.toISOString().split("T")[0]}`);
|
|
2562
|
+
console.log();
|
|
2563
|
+
} catch (error2) {
|
|
2564
|
+
if (error2 instanceof KabanError13) {
|
|
1771
2565
|
if (json)
|
|
1772
2566
|
outputError(error2.code, error2.message);
|
|
1773
2567
|
console.error(`Error: ${error2.message}`);
|
|
@@ -1779,9 +2573,9 @@ var moveCommand = new Command9("move").description("Move a task to a different c
|
|
|
1779
2573
|
|
|
1780
2574
|
// src/commands/schema.ts
|
|
1781
2575
|
import { jsonSchemas } from "@kaban-board/core";
|
|
1782
|
-
import { Command as
|
|
2576
|
+
import { Command as Command17 } from "commander";
|
|
1783
2577
|
var availableSchemas = Object.keys(jsonSchemas);
|
|
1784
|
-
var schemaCommand = new
|
|
2578
|
+
var schemaCommand = new Command17("schema").description("Output JSON schemas for AI agents").argument("[name]", "Schema name (omit to list available)").action((name) => {
|
|
1785
2579
|
if (!name) {
|
|
1786
2580
|
console.log("Available schemas:");
|
|
1787
2581
|
for (const schemaName of availableSchemas) {
|
|
@@ -1800,10 +2594,65 @@ Usage: kaban schema <name>`);
|
|
|
1800
2594
|
console.log(JSON.stringify(schema, null, 2));
|
|
1801
2595
|
});
|
|
1802
2596
|
|
|
2597
|
+
// src/commands/stats.ts
|
|
2598
|
+
import { KabanError as KabanError14 } from "@kaban-board/core";
|
|
2599
|
+
import { Command as Command18 } from "commander";
|
|
2600
|
+
var statsCommand = new Command18("stats").description("Show board and archive statistics").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
2601
|
+
const json = options.json;
|
|
2602
|
+
try {
|
|
2603
|
+
const { taskService, boardService } = await getContext();
|
|
2604
|
+
const tasks = await taskService.listTasks();
|
|
2605
|
+
const columns = await boardService.getColumns();
|
|
2606
|
+
const terminalColumns = columns.filter((c) => c.isTerminal);
|
|
2607
|
+
const archivedResult = await taskService.searchArchive("", { limit: 1 });
|
|
2608
|
+
const completedNotArchived = tasks.filter((t) => terminalColumns.some((c) => c.id === t.columnId)).length;
|
|
2609
|
+
const blocked = tasks.filter((t) => t.blockedReason).length;
|
|
2610
|
+
const withDeps = tasks.filter((t) => t.dependsOn.length > 0).length;
|
|
2611
|
+
const stats = {
|
|
2612
|
+
activeTasks: tasks.length,
|
|
2613
|
+
archivedTasks: archivedResult.total,
|
|
2614
|
+
completedNotArchived,
|
|
2615
|
+
blockedTasks: blocked,
|
|
2616
|
+
tasksWithDependencies: withDeps,
|
|
2617
|
+
byColumn: columns.map((c) => ({
|
|
2618
|
+
id: c.id,
|
|
2619
|
+
name: c.name,
|
|
2620
|
+
count: tasks.filter((t) => t.columnId === c.id).length
|
|
2621
|
+
}))
|
|
2622
|
+
};
|
|
2623
|
+
if (json) {
|
|
2624
|
+
outputSuccess(stats);
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2627
|
+
console.log(`
|
|
2628
|
+
Board Statistics
|
|
2629
|
+
`);
|
|
2630
|
+
console.log(` Active tasks: ${stats.activeTasks}`);
|
|
2631
|
+
console.log(` Archived tasks: ${stats.archivedTasks}`);
|
|
2632
|
+
console.log(` Completed (live): ${stats.completedNotArchived}`);
|
|
2633
|
+
console.log(` Blocked: ${stats.blockedTasks}`);
|
|
2634
|
+
console.log(` With dependencies: ${stats.tasksWithDependencies}`);
|
|
2635
|
+
console.log(`
|
|
2636
|
+
By Column:`);
|
|
2637
|
+
for (const col of stats.byColumn) {
|
|
2638
|
+
console.log(` ${col.name}: ${col.count}`);
|
|
2639
|
+
}
|
|
2640
|
+
console.log();
|
|
2641
|
+
} catch (error2) {
|
|
2642
|
+
if (error2 instanceof KabanError14) {
|
|
2643
|
+
if (json)
|
|
2644
|
+
outputError(error2.code, error2.message);
|
|
2645
|
+
console.error(`Error: ${error2.message}`);
|
|
2646
|
+
process.exit(error2.code);
|
|
2647
|
+
}
|
|
2648
|
+
throw error2;
|
|
2649
|
+
}
|
|
2650
|
+
});
|
|
2651
|
+
|
|
1803
2652
|
// src/commands/search.ts
|
|
1804
|
-
import { KabanError as
|
|
1805
|
-
import { Command as
|
|
1806
|
-
var searchCommand = new
|
|
2653
|
+
import { KabanError as KabanError15 } from "@kaban-board/core";
|
|
2654
|
+
import { Command as Command19 } from "commander";
|
|
2655
|
+
var searchCommand = new Command19("search").description("Search tasks").argument("<query>", "Search query").option("--archive", "Search in archive only").option("-l, --limit <n>", "Max results", parseInt, 50).option("-j, --json", "Output as JSON").action(async (query, options) => {
|
|
1807
2656
|
const json = options.json;
|
|
1808
2657
|
try {
|
|
1809
2658
|
const { taskService } = await getContext();
|
|
@@ -1832,7 +2681,7 @@ var searchCommand = new Command11("search").description("Search tasks").argument
|
|
|
1832
2681
|
console.log(` Column: ${task.columnId} | By: ${task.createdBy}`);
|
|
1833
2682
|
}
|
|
1834
2683
|
} catch (error2) {
|
|
1835
|
-
if (error2 instanceof
|
|
2684
|
+
if (error2 instanceof KabanError15) {
|
|
1836
2685
|
if (json)
|
|
1837
2686
|
outputError(error2.code, error2.message);
|
|
1838
2687
|
console.error(`Error: ${error2.message}`);
|
|
@@ -1843,9 +2692,9 @@ var searchCommand = new Command11("search").description("Search tasks").argument
|
|
|
1843
2692
|
});
|
|
1844
2693
|
|
|
1845
2694
|
// src/commands/status.ts
|
|
1846
|
-
import { KabanError as
|
|
1847
|
-
import { Command as
|
|
1848
|
-
var statusCommand2 = new
|
|
2695
|
+
import { KabanError as KabanError16 } from "@kaban-board/core";
|
|
2696
|
+
import { Command as Command20 } from "commander";
|
|
2697
|
+
var statusCommand2 = new Command20("status").description("Show board status summary").option("-j, --json", "Output as JSON").action(async (options) => {
|
|
1849
2698
|
const json = options.json;
|
|
1850
2699
|
try {
|
|
1851
2700
|
const { taskService, boardService } = await getContext();
|
|
@@ -1888,7 +2737,7 @@ var statusCommand2 = new Command12("status").description("Show board status summ
|
|
|
1888
2737
|
}
|
|
1889
2738
|
console.log();
|
|
1890
2739
|
} catch (error2) {
|
|
1891
|
-
if (error2 instanceof
|
|
2740
|
+
if (error2 instanceof KabanError16) {
|
|
1892
2741
|
if (json)
|
|
1893
2742
|
outputError(error2.code, error2.message);
|
|
1894
2743
|
console.error(`Error: ${error2.message}`);
|
|
@@ -1899,7 +2748,7 @@ var statusCommand2 = new Command12("status").description("Show board status summ
|
|
|
1899
2748
|
});
|
|
1900
2749
|
|
|
1901
2750
|
// src/commands/sync.ts
|
|
1902
|
-
import { Command as
|
|
2751
|
+
import { Command as Command21 } from "commander";
|
|
1903
2752
|
|
|
1904
2753
|
// src/hook/constants.ts
|
|
1905
2754
|
var STATUS_PRIORITY = {
|
|
@@ -2241,7 +3090,7 @@ class SyncLogger {
|
|
|
2241
3090
|
}
|
|
2242
3091
|
|
|
2243
3092
|
// src/commands/sync.ts
|
|
2244
|
-
var syncCommand = new
|
|
3093
|
+
var syncCommand = new Command21("sync").description("Sync TodoWrite input to Kaban board (reads from stdin)").option("--no-log", "Disable sync logging").action(async (options) => {
|
|
2245
3094
|
const startTime = performance.now();
|
|
2246
3095
|
let input;
|
|
2247
3096
|
try {
|
|
@@ -2293,11 +3142,11 @@ import { existsSync as existsSync7 } from "fs";
|
|
|
2293
3142
|
import { createRequire } from "module";
|
|
2294
3143
|
import { dirname as dirname3, join as join5 } from "path";
|
|
2295
3144
|
import { fileURLToPath } from "url";
|
|
2296
|
-
import { Command as
|
|
3145
|
+
import { Command as Command22 } from "commander";
|
|
2297
3146
|
// package.json
|
|
2298
3147
|
var package_default = {
|
|
2299
3148
|
name: "@kaban-board/cli",
|
|
2300
|
-
version: "0.3.
|
|
3149
|
+
version: "0.3.2",
|
|
2301
3150
|
description: "Terminal Kanban for AI Code Agents - CLI and MCP server",
|
|
2302
3151
|
type: "module",
|
|
2303
3152
|
bin: {
|
|
@@ -2381,7 +3230,7 @@ function runBinary(path, args) {
|
|
|
2381
3230
|
const child = spawn3(path, args, { stdio: "inherit", cwd: process.cwd() });
|
|
2382
3231
|
child.on("exit", (code) => process.exit(code ?? 0));
|
|
2383
3232
|
}
|
|
2384
|
-
var tuiCommand = new
|
|
3233
|
+
var tuiCommand = new Command22("tui").description("Start interactive Terminal UI").action(async () => {
|
|
2385
3234
|
const cwd = process.cwd();
|
|
2386
3235
|
const args = process.argv.slice(3);
|
|
2387
3236
|
const useBun = hasBun();
|
|
@@ -2415,18 +3264,24 @@ var tuiCommand = new Command14("tui").description("Start interactive Terminal UI
|
|
|
2415
3264
|
// src/index.ts
|
|
2416
3265
|
var require3 = createRequire2(import.meta.url);
|
|
2417
3266
|
var pkg = require3("../package.json");
|
|
2418
|
-
var program = new
|
|
3267
|
+
var program = new Command23;
|
|
2419
3268
|
program.name("kaban").description("Terminal Kanban for AI Code Agents").version(pkg.version);
|
|
2420
3269
|
program.addCommand(initCommand);
|
|
2421
3270
|
program.addCommand(addCommand);
|
|
3271
|
+
program.addCommand(auditCommand);
|
|
2422
3272
|
program.addCommand(listCommand);
|
|
2423
3273
|
program.addCommand(moveCommand);
|
|
2424
3274
|
program.addCommand(assignCommand);
|
|
3275
|
+
program.addCommand(deleteCommand);
|
|
2425
3276
|
program.addCommand(doneCommand);
|
|
3277
|
+
program.addCommand(editCommand);
|
|
3278
|
+
program.addCommand(getCommand);
|
|
2426
3279
|
program.addCommand(statusCommand2);
|
|
2427
3280
|
program.addCommand(schemaCommand);
|
|
2428
3281
|
program.addCommand(searchCommand);
|
|
2429
3282
|
program.addCommand(mcpCommand);
|
|
3283
|
+
program.addCommand(nextCommand);
|
|
3284
|
+
program.addCommand(statsCommand);
|
|
2430
3285
|
program.addCommand(tuiCommand);
|
|
2431
3286
|
program.addCommand(hookCommand);
|
|
2432
3287
|
program.addCommand(syncCommand);
|
|
@@ -2434,4 +3289,6 @@ program.addCommand(archiveCommand);
|
|
|
2434
3289
|
program.addCommand(restoreCommand);
|
|
2435
3290
|
program.addCommand(purgeCommand);
|
|
2436
3291
|
program.addCommand(resetCommand);
|
|
3292
|
+
program.addCommand(exportCommand);
|
|
3293
|
+
program.addCommand(importCommand);
|
|
2437
3294
|
program.parse();
|