@kaban-board/cli 0.2.6 → 0.2.9

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 CHANGED
@@ -1,16 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ // @bun
2
3
 
3
4
  // src/index.ts
4
- import { createRequire as createRequire2 } from "node:module";
5
- import { Command as Command12 } from "commander";
5
+ import { createRequire as createRequire2 } from "module";
6
+ import { Command as Command15 } from "commander";
6
7
 
7
8
  // src/commands/add.ts
8
9
  import { KabanError } from "@kaban-board/core";
9
10
  import { Command } from "commander";
10
11
 
11
12
  // src/lib/context.ts
12
- import { existsSync, readFileSync } from "node:fs";
13
- import { join } from "node:path";
13
+ import { existsSync, readFileSync } from "fs";
14
+ import { join } from "path";
14
15
  import { BoardService, createDb, TaskService } from "@kaban-board/core";
15
16
  async function getContext() {
16
17
  const kabanDir = join(process.cwd(), ".kaban");
@@ -57,20 +58,30 @@ function outputError(code, message) {
57
58
  }
58
59
 
59
60
  // src/commands/add.ts
60
- var addCommand = new Command("add").description("Add a new task").argument("<title>", "Task title").option("-c, --column <column>", "Column to add task to").option("-a, --agent <agent>", "Agent creating the task").option("-D, --description <text>", "Task description").option("-d, --depends-on <ids>", "Comma-separated task IDs this depends on").option("-j, --json", "Output as JSON").action(async (title, options) => {
61
+ var addCommand = new Command("add").description("Add a new task").argument("<title>", "Task title").option("-c, --column <column>", "Column to add task to").option("-a, --agent <agent>", "Agent creating the task").option("-D, --description <text>", "Task description").option("-d, --depends-on <ids>", "Comma-separated task IDs this depends on").option("-f, --force", "Force creation even if similar tasks exist").option("-j, --json", "Output as JSON").action(async (title, options) => {
61
62
  const json = options.json;
62
63
  try {
63
64
  const { taskService, config } = await getContext();
64
65
  const agent = options.agent ?? getAgent();
65
66
  const columnId = options.column ?? config.defaults.column;
66
67
  const dependsOn = options.dependsOn ? options.dependsOn.split(",").map((s) => s.trim()) : [];
67
- const task = await taskService.addTask({
68
+ const result = await taskService.addTaskChecked({
68
69
  title,
69
70
  description: options.description,
70
71
  columnId,
71
72
  agent,
72
73
  dependsOn
73
- });
74
+ }, { force: options.force });
75
+ if (result.rejected) {
76
+ if (json) {
77
+ outputError(409, result.rejectionReason ?? "Similar task exists");
78
+ } else {
79
+ console.error(`Error: ${result.rejectionReason}`);
80
+ console.error("Use --force to create anyway.");
81
+ }
82
+ process.exit(1);
83
+ }
84
+ const task = result.task;
74
85
  if (json) {
75
86
  outputSuccess(task);
76
87
  return;
@@ -78,6 +89,9 @@ var addCommand = new Command("add").description("Add a new task").argument("<tit
78
89
  console.log(`Created task [${task.id.slice(0, 8)}] "${task.title}"`);
79
90
  console.log(` Column: ${task.columnId}`);
80
91
  console.log(` Agent: ${task.createdBy}`);
92
+ if (result.similarTasks.length > 0) {
93
+ console.log(` Note: Found ${result.similarTasks.length} similar task(s)`);
94
+ }
81
95
  } catch (error2) {
82
96
  if (error2 instanceof KabanError) {
83
97
  if (json)
@@ -89,10 +103,237 @@ var addCommand = new Command("add").description("Add a new task").argument("<tit
89
103
  }
90
104
  });
91
105
 
92
- // src/commands/done.ts
106
+ // src/commands/archive.ts
93
107
  import { KabanError as KabanError2 } from "@kaban-board/core";
94
108
  import { Command as Command2 } from "commander";
95
- var doneCommand = new Command2("done").description("Mark a task as done").argument("<id>", "Task ID (can be partial)").option("-j, --json", "Output as JSON").action(async (id, options) => {
109
+ var archiveCommand = new Command2("archive").description("Archive completed tasks").option("-c, --column <column>", "Archive tasks from specific column").option("-o, --older-than <days>", "Archive tasks older than N days", parseInt).option("--all-columns", "Archive from all columns (requires --older-than)").option("--dry-run", "Show what would be archived without archiving").option("-j, --json", "Output as JSON").action(async (options) => {
110
+ const json = options.json;
111
+ try {
112
+ const { taskService, boardService } = await getContext();
113
+ if (!options.column && !options.allColumns) {
114
+ const terminal = await boardService.getTerminalColumn();
115
+ if (!terminal) {
116
+ if (json)
117
+ outputError(1, "No terminal column configured and no criteria specified");
118
+ console.error("Error: No terminal column configured and no criteria specified");
119
+ process.exit(1);
120
+ }
121
+ options.column = terminal.id;
122
+ }
123
+ if (options.allColumns && !options.olderThan) {
124
+ if (json)
125
+ outputError(1, "--all-columns requires --older-than");
126
+ console.error("Error: --all-columns requires --older-than");
127
+ process.exit(1);
128
+ }
129
+ const criteria = {};
130
+ if (options.column && !options.allColumns) {
131
+ criteria.status = options.column;
132
+ }
133
+ if (options.olderThan) {
134
+ const date = new Date;
135
+ date.setDate(date.getDate() - options.olderThan);
136
+ criteria.olderThan = date;
137
+ }
138
+ if (options.dryRun) {
139
+ const tasks = await taskService.listTasks({
140
+ columnId: criteria.status,
141
+ includeArchived: false
142
+ });
143
+ const olderThan = criteria.olderThan;
144
+ const filtered = olderThan ? tasks.filter((t) => t.createdAt < olderThan) : tasks;
145
+ if (json) {
146
+ outputSuccess({ dryRun: true, wouldArchive: filtered.length, tasks: filtered });
147
+ return;
148
+ }
149
+ console.log(`Would archive ${filtered.length} task(s):`);
150
+ for (const task of filtered) {
151
+ console.log(` [${task.id.slice(0, 8)}] ${task.title}`);
152
+ }
153
+ return;
154
+ }
155
+ const result = await taskService.archiveTasks(criteria);
156
+ if (json) {
157
+ outputSuccess(result);
158
+ return;
159
+ }
160
+ console.log(`Archived ${result.archivedCount} task(s)`);
161
+ } catch (error2) {
162
+ if (error2 instanceof KabanError2) {
163
+ if (json)
164
+ outputError(error2.code, error2.message);
165
+ console.error(`Error: ${error2.message}`);
166
+ process.exit(error2.code);
167
+ }
168
+ throw error2;
169
+ }
170
+ });
171
+ var restoreCommand = new Command2("restore").description("Restore a task from archive").argument("<id>", "Task ID to restore (can be partial)").option("-t, --to <column>", "Restore to specific column").option("-j, --json", "Output as JSON").action(async (id, options) => {
172
+ const json = options.json;
173
+ try {
174
+ const { taskService } = await getContext();
175
+ const archivedTasks = await taskService.listTasks({ includeArchived: true });
176
+ const task = archivedTasks.find((t) => t.id.startsWith(id) && t.archived);
177
+ if (!task) {
178
+ if (json)
179
+ outputError(2, `Archived task '${id}' not found`);
180
+ console.error(`Error: Archived task '${id}' not found`);
181
+ process.exit(2);
182
+ }
183
+ const restored = await taskService.restoreTask(task.id, options.to);
184
+ if (json) {
185
+ outputSuccess(restored);
186
+ return;
187
+ }
188
+ console.log(`Restored [${restored.id.slice(0, 8)}] "${restored.title}" to ${restored.columnId}`);
189
+ } catch (error2) {
190
+ if (error2 instanceof KabanError2) {
191
+ if (json)
192
+ outputError(error2.code, error2.message);
193
+ console.error(`Error: ${error2.message}`);
194
+ process.exit(error2.code);
195
+ }
196
+ throw error2;
197
+ }
198
+ });
199
+ var purgeCommand = new Command2("purge").description("Permanently delete archived tasks").option("--confirm", "Confirm deletion (required)").option("--dry-run", "Show what would be deleted without deleting").option("-j, --json", "Output as JSON").action(async (options) => {
200
+ const json = options.json;
201
+ try {
202
+ const { taskService } = await getContext();
203
+ if (options.dryRun) {
204
+ const archived = await taskService.listTasks({ includeArchived: true });
205
+ const archivedOnly = archived.filter((t) => t.archived);
206
+ if (json) {
207
+ outputSuccess({ dryRun: true, wouldDelete: archivedOnly.length, tasks: archivedOnly });
208
+ return;
209
+ }
210
+ console.log(`Would permanently delete ${archivedOnly.length} archived task(s):`);
211
+ for (const task of archivedOnly) {
212
+ console.log(` [${task.id.slice(0, 8)}] ${task.title}`);
213
+ }
214
+ return;
215
+ }
216
+ if (!options.confirm) {
217
+ if (json)
218
+ outputError(1, "Use --confirm to permanently delete archived tasks");
219
+ console.error("Error: Use --confirm to permanently delete archived tasks");
220
+ process.exit(1);
221
+ }
222
+ const result = await taskService.purgeArchive();
223
+ if (json) {
224
+ outputSuccess(result);
225
+ return;
226
+ }
227
+ console.log(`Permanently deleted ${result.deletedCount} archived task(s)`);
228
+ } catch (error2) {
229
+ if (error2 instanceof KabanError2) {
230
+ if (json)
231
+ outputError(error2.code, error2.message);
232
+ console.error(`Error: ${error2.message}`);
233
+ process.exit(error2.code);
234
+ }
235
+ throw error2;
236
+ }
237
+ });
238
+ var resetCommand = new Command2("reset").description("Delete ALL tasks (destructive)").option("--confirm", "Confirm deletion (required)").option("--dry-run", "Show what would be deleted without deleting").option("-j, --json", "Output as JSON").action(async (options) => {
239
+ const json = options.json;
240
+ try {
241
+ const { taskService } = await getContext();
242
+ if (options.dryRun) {
243
+ const allTasks = await taskService.listTasks({ includeArchived: true });
244
+ if (json) {
245
+ outputSuccess({ dryRun: true, wouldDelete: allTasks.length, tasks: allTasks });
246
+ return;
247
+ }
248
+ console.log(`Would permanently delete ALL ${allTasks.length} task(s):`);
249
+ for (const task of allTasks) {
250
+ const archived = task.archived ? " [archived]" : "";
251
+ console.log(` [${task.id.slice(0, 8)}] ${task.title}${archived}`);
252
+ }
253
+ return;
254
+ }
255
+ if (!options.confirm) {
256
+ if (json)
257
+ outputError(1, "Use --confirm to delete ALL tasks");
258
+ console.error("Error: Use --confirm to delete ALL tasks");
259
+ process.exit(1);
260
+ }
261
+ const result = await taskService.resetBoard();
262
+ if (json) {
263
+ outputSuccess(result);
264
+ return;
265
+ }
266
+ console.log(`Deleted ALL ${result.deletedCount} task(s)`);
267
+ } catch (error2) {
268
+ if (error2 instanceof KabanError2) {
269
+ if (json)
270
+ outputError(error2.code, error2.message);
271
+ console.error(`Error: ${error2.message}`);
272
+ process.exit(error2.code);
273
+ }
274
+ throw error2;
275
+ }
276
+ });
277
+
278
+ // src/commands/assign.ts
279
+ import { KabanError as KabanError3 } from "@kaban-board/core";
280
+ import { Command as Command3 } from "commander";
281
+ var assignCommand = new Command3("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
+ const json = options.json;
283
+ try {
284
+ const { taskService } = await getContext();
285
+ if (options.clear && agent) {
286
+ if (json)
287
+ outputError(4, "Cannot use --clear with agent argument");
288
+ console.error("Error: Cannot use --clear with agent argument");
289
+ process.exit(4);
290
+ }
291
+ const tasks = await taskService.listTasks();
292
+ const task = tasks.find((t) => t.id.startsWith(id));
293
+ if (!task) {
294
+ if (json)
295
+ outputError(2, `Task '${id}' not found`);
296
+ console.error(`Error: Task '${id}' not found`);
297
+ process.exit(2);
298
+ }
299
+ const previousAssignee = task.assignedTo;
300
+ if (options.clear) {
301
+ const updated2 = await taskService.updateTask(task.id, { assignedTo: null });
302
+ if (json) {
303
+ outputSuccess(updated2);
304
+ return;
305
+ }
306
+ console.log(`Unassigned [${updated2.id.slice(0, 8)}] "${updated2.title}"`);
307
+ return;
308
+ }
309
+ if (!agent) {
310
+ if (json)
311
+ outputError(4, "Specify an agent or use --clear");
312
+ console.error("Error: Specify an agent or use --clear to unassign");
313
+ process.exit(4);
314
+ }
315
+ const updated = await taskService.updateTask(task.id, { assignedTo: agent });
316
+ if (json) {
317
+ outputSuccess(updated);
318
+ return;
319
+ }
320
+ const prevMsg = previousAssignee ? ` (was: ${previousAssignee})` : "";
321
+ console.log(`Assigned [${updated.id.slice(0, 8)}] "${updated.title}" to ${updated.assignedTo}${prevMsg}`);
322
+ } catch (error2) {
323
+ if (error2 instanceof KabanError3) {
324
+ if (json)
325
+ outputError(error2.code, error2.message);
326
+ console.error(`Error: ${error2.message}`);
327
+ process.exit(error2.code);
328
+ }
329
+ throw error2;
330
+ }
331
+ });
332
+
333
+ // src/commands/done.ts
334
+ import { KabanError as KabanError4 } from "@kaban-board/core";
335
+ import { Command as Command4 } from "commander";
336
+ var doneCommand = new Command4("done").description("Mark a task as done").argument("<id>", "Task ID (can be partial)").option("-j, --json", "Output as JSON").action(async (id, options) => {
96
337
  const json = options.json;
97
338
  try {
98
339
  const { taskService, boardService } = await getContext();
@@ -118,7 +359,7 @@ var doneCommand = new Command2("done").description("Mark a task as done").argume
118
359
  }
119
360
  console.log(`Completed [${moved.id.slice(0, 8)}] "${moved.title}"`);
120
361
  } catch (error2) {
121
- if (error2 instanceof KabanError2) {
362
+ if (error2 instanceof KabanError4) {
122
363
  if (json)
123
364
  outputError(error2.code, error2.message);
124
365
  console.error(`Error: ${error2.message}`);
@@ -129,20 +370,20 @@ var doneCommand = new Command2("done").description("Mark a task as done").argume
129
370
  });
130
371
 
131
372
  // src/commands/hook.ts
132
- import { spawn } from "node:child_process";
133
- import { existsSync as existsSync3, realpathSync } from "node:fs";
134
- import { chmod, copyFile as copyFile2, mkdir, readFile as readFile2, stat, unlink } from "node:fs/promises";
135
- import { homedir as homedir2 } from "node:os";
136
- import { dirname, join as join3 } from "node:path";
373
+ import { spawn } from "child_process";
374
+ import { existsSync as existsSync3, realpathSync } from "fs";
375
+ import { chmod, copyFile as copyFile2, mkdir, readFile as readFile2, stat, unlink } from "fs/promises";
376
+ import { homedir as homedir2 } from "os";
377
+ import { dirname, join as join3 } from "path";
137
378
  import * as p from "@clack/prompts";
138
379
  import chalk from "chalk";
139
- import { Command as Command3 } from "commander";
380
+ import { Command as Command5 } from "commander";
140
381
 
141
382
  // src/hook/settings-manager.ts
142
- import { existsSync as existsSync2 } from "node:fs";
143
- import { copyFile, readFile, writeFile } from "node:fs/promises";
144
- import { homedir } from "node:os";
145
- import { join as join2 } from "node:path";
383
+ import { existsSync as existsSync2 } from "fs";
384
+ import { copyFile, readFile, writeFile } from "fs/promises";
385
+ import { homedir } from "os";
386
+ import { join as join2 } from "path";
146
387
 
147
388
  // src/hook/schemas.ts
148
389
  import { z } from "zod";
@@ -381,7 +622,7 @@ async function checkDependencies() {
381
622
  function formatCheckResults(results) {
382
623
  const maxNameLen = Math.max(...results.map((r) => r.name.length));
383
624
  return results.map((r) => {
384
- const icon = r.ok ? chalk.green("") : chalk.red("");
625
+ const icon = r.ok ? chalk.green("\u2713") : chalk.red("\u2717");
385
626
  const name = r.name.padEnd(maxNameLen);
386
627
  const msg = r.ok ? chalk.dim(r.message) : chalk.red(r.message);
387
628
  return ` ${icon} ${name} ${msg}`;
@@ -485,7 +726,7 @@ function formatSize(bytes) {
485
726
  return `${bytes} B`;
486
727
  return `${Math.round(bytes / 1024)} KB`;
487
728
  }
488
- var installCommand = new Command3("install").description("Install TodoWrite sync hook for Claude Code").option("-y, --yes", "Skip confirmation").action(async (options) => {
729
+ var installCommand = new Command5("install").description("Install TodoWrite sync hook for Claude Code").option("-y, --yes", "Skip confirmation").action(async (options) => {
489
730
  p.intro(chalk.bgCyan.black(" kaban hook install "));
490
731
  const s = p.spinner();
491
732
  s.start("Checking dependencies...");
@@ -527,7 +768,7 @@ var installCommand = new Command3("install").description("Install TodoWrite sync
527
768
  summaryLines.push("");
528
769
  }
529
770
  if (!installResult.hookConfigured) {
530
- summaryLines.push(` ${chalk.yellow("")} Hook was already configured`);
771
+ summaryLines.push(` ${chalk.yellow("\u26A0")} Hook was already configured`);
531
772
  summaryLines.push("");
532
773
  }
533
774
  p.note(summaryLines.join(`
@@ -546,7 +787,7 @@ var installCommand = new Command3("install").description("Install TodoWrite sync
546
787
  process.exit(1);
547
788
  }
548
789
  });
549
- var uninstallCommand = new Command3("uninstall").description("Remove TodoWrite sync hook").option("-y, --yes", "Skip confirmation").option("--clean", "Also remove sync logs").action(async (options) => {
790
+ var uninstallCommand = new Command5("uninstall").description("Remove TodoWrite sync hook").option("-y, --yes", "Skip confirmation").option("--clean", "Also remove sync logs").action(async (options) => {
550
791
  p.intro(chalk.bgRed.white(" kaban hook uninstall "));
551
792
  const binaryExists = existsSync3(join3(HOOKS_DIR, HOOK_BINARY_NAME));
552
793
  const logExists = existsSync3(join3(HOOKS_DIR, LOG_FILE));
@@ -564,7 +805,7 @@ var uninstallCommand = new Command3("uninstall").description("Remove TodoWrite s
564
805
  process.exit(0);
565
806
  }
566
807
  const formatStatusLine = (exists, label) => {
567
- const icon = exists ? chalk.green("") : chalk.dim("");
808
+ const icon = exists ? chalk.green("\u2713") : chalk.dim("\u25CB");
568
809
  const text = exists ? "" : chalk.dim(" (not found)");
569
810
  return ` ${icon} ${label}${text}`;
570
811
  };
@@ -572,7 +813,7 @@ var uninstallCommand = new Command3("uninstall").description("Remove TodoWrite s
572
813
  if (!logExists)
573
814
  return formatStatusLine(false, "Sync log");
574
815
  const suffix = options.clean ? chalk.yellow(" (will be removed)") : chalk.dim(" (will be preserved)");
575
- return ` ${chalk.yellow("")} Sync log${suffix}`;
816
+ return ` ${chalk.yellow("\u25CB")} Sync log${suffix}`;
576
817
  };
577
818
  const statusLines = [
578
819
  formatStatusLine(binaryExists, "Hook binary"),
@@ -598,13 +839,13 @@ var uninstallCommand = new Command3("uninstall").description("Remove TodoWrite s
598
839
  s.stop("Removal complete");
599
840
  const summaryLines = [];
600
841
  if (result.hookRemoved)
601
- summaryLines.push(` ${chalk.green("")} Removed hook from settings.json`);
842
+ summaryLines.push(` ${chalk.green("\u2713")} Removed hook from settings.json`);
602
843
  if (result.binaryRemoved)
603
- summaryLines.push(` ${chalk.green("")} Removed ${HOOK_BINARY_NAME}`);
844
+ summaryLines.push(` ${chalk.green("\u2713")} Removed ${HOOK_BINARY_NAME}`);
604
845
  if (result.logRemoved)
605
- summaryLines.push(` ${chalk.green("")} Removed ${LOG_FILE}`);
846
+ summaryLines.push(` ${chalk.green("\u2713")} Removed ${LOG_FILE}`);
606
847
  if (logExists && !options.clean)
607
- summaryLines.push(` ${chalk.yellow("")} Preserved ${LOG_FILE} (use --clean to remove)`);
848
+ summaryLines.push(` ${chalk.yellow("\u25CB")} Preserved ${LOG_FILE} (use --clean to remove)`);
608
849
  if (summaryLines.length > 0) {
609
850
  p.note(summaryLines.join(`
610
851
  `), "Removed");
@@ -616,7 +857,7 @@ var uninstallCommand = new Command3("uninstall").description("Remove TodoWrite s
616
857
  process.exit(1);
617
858
  }
618
859
  });
619
- var statusCommand = new Command3("status").description("Check hook installation status").action(async () => {
860
+ var statusCommand = new Command5("status").description("Check hook installation status").action(async () => {
620
861
  p.intro(chalk.bgBlue.white(" kaban hook status "));
621
862
  const s = p.spinner();
622
863
  s.start("Checking status...");
@@ -652,7 +893,7 @@ var statusCommand = new Command3("status").description("Check hook installation
652
893
  ];
653
894
  const maxNameLen = Math.max(...results.map((r) => r.name.length));
654
895
  const statusLines = results.map((r) => {
655
- const icon = r.ok ? chalk.green("") : chalk.red("");
896
+ const icon = r.ok ? chalk.green("\u2713") : chalk.red("\u2717");
656
897
  const name = r.name.padEnd(maxNameLen);
657
898
  const detail = r.ok ? chalk.dim(r.detail) : chalk.red(r.detail);
658
899
  return ` ${icon} ${name} ${detail}`;
@@ -682,18 +923,18 @@ var statusCommand = new Command3("status").description("Check hook installation
682
923
  process.exit(1);
683
924
  }
684
925
  });
685
- var hookCommand = new Command3("hook").description("Manage TodoWrite sync hook for Claude Code").addCommand(installCommand).addCommand(uninstallCommand).addCommand(statusCommand);
926
+ var hookCommand = new Command5("hook").description("Manage TodoWrite sync hook for Claude Code").addCommand(installCommand).addCommand(uninstallCommand).addCommand(statusCommand);
686
927
 
687
928
  // src/commands/init.ts
688
- import { existsSync as existsSync4, mkdirSync, writeFileSync } from "node:fs";
929
+ import { existsSync as existsSync4, mkdirSync, writeFileSync } from "fs";
689
930
  import {
690
931
  BoardService as BoardService2,
691
932
  createDb as createDb2,
692
933
  DEFAULT_CONFIG as DEFAULT_CONFIG2,
693
934
  initializeSchema
694
935
  } from "@kaban-board/core";
695
- import { Command as Command4 } from "commander";
696
- var initCommand = new Command4("init").description("Initialize a new Kaban board in the current directory").option("-n, --name <name>", "Board name", "Kaban Board").action(async (options) => {
936
+ import { Command as Command6 } from "commander";
937
+ var initCommand = new Command6("init").description("Initialize a new Kaban board in the current directory").option("-n, --name <name>", "Board name", "Kaban Board").action(async (options) => {
697
938
  const { kabanDir, dbPath, configPath } = getKabanPaths();
698
939
  if (existsSync4(dbPath)) {
699
940
  console.error("Error: Board already exists in this directory");
@@ -715,8 +956,8 @@ var initCommand = new Command4("init").description("Initialize a new Kaban board
715
956
  });
716
957
 
717
958
  // src/commands/list.ts
718
- import { KabanError as KabanError3 } from "@kaban-board/core";
719
- import { Command as Command5 } from "commander";
959
+ import { KabanError as KabanError5 } from "@kaban-board/core";
960
+ import { Command as Command7 } from "commander";
720
961
  function sortTasks(tasks, sortBy, reverse) {
721
962
  const sorted = [...tasks].sort((a, b) => {
722
963
  switch (sortBy) {
@@ -732,7 +973,7 @@ function sortTasks(tasks, sortBy, reverse) {
732
973
  });
733
974
  return reverse ? sorted.reverse() : sorted;
734
975
  }
735
- var listCommand = new Command5("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) => {
976
+ var listCommand = new Command7("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) => {
736
977
  const json = options.json;
737
978
  try {
738
979
  const { taskService, boardService } = await getContext();
@@ -764,7 +1005,7 @@ var listCommand = new Command5("list").description("List tasks").option("-c, --c
764
1005
  const column = columnMap.get(task.columnId);
765
1006
  const blocked = task.blockedReason ? " [blocked]" : "";
766
1007
  const agent = task.createdBy !== "user" ? ` @${task.createdBy}` : "";
767
- const assignee = task.assignedTo ? ` ${task.assignedTo}` : "";
1008
+ const assignee = task.assignedTo ? ` \u2192 ${task.assignedTo}` : "";
768
1009
  console.log(`[${task.id.slice(0, 8)}] ${task.title}${agent}${assignee}${blocked}`);
769
1010
  console.log(` ${column?.name ?? task.columnId}`);
770
1011
  if (task.description) {
@@ -773,7 +1014,7 @@ var listCommand = new Command5("list").description("List tasks").option("-c, --c
773
1014
  }
774
1015
  }
775
1016
  } catch (error2) {
776
- if (error2 instanceof KabanError3) {
1017
+ if (error2 instanceof KabanError5) {
777
1018
  if (json)
778
1019
  outputError(error2.code, error2.message);
779
1020
  console.error(`Error: ${error2.message}`);
@@ -784,8 +1025,8 @@ var listCommand = new Command5("list").description("List tasks").option("-c, --c
784
1025
  });
785
1026
 
786
1027
  // src/commands/mcp.ts
787
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
788
- import { join as join4 } from "node:path";
1028
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1029
+ import { join as join4 } from "path";
789
1030
  import {
790
1031
  BoardService as BoardService3,
791
1032
  createDb as createDb3,
@@ -801,7 +1042,7 @@ import {
801
1042
  ListToolsRequestSchema,
802
1043
  ReadResourceRequestSchema
803
1044
  } from "@modelcontextprotocol/sdk/types.js";
804
- import { Command as Command6 } from "commander";
1045
+ import { Command as Command8 } from "commander";
805
1046
  var mcpHelpers = {
806
1047
  getParam(args, primary, alias) {
807
1048
  if (!args)
@@ -899,7 +1140,8 @@ async function startMcpServer(workingDirectory) {
899
1140
  type: "object",
900
1141
  properties: {
901
1142
  columnId: { type: "string", description: "Filter by column ID" },
902
- agent: { type: "string", description: "Filter by agent name" },
1143
+ agent: { type: "string", description: "Filter by creator agent name" },
1144
+ assignee: { type: "string", description: "Filter by assigned agent name" },
903
1145
  blocked: { type: "boolean", description: "Show only blocked tasks" }
904
1146
  }
905
1147
  }
@@ -971,6 +1213,127 @@ async function startMcpServer(workingDirectory) {
971
1213
  name: "kaban_status",
972
1214
  description: "Get board status summary",
973
1215
  inputSchema: { type: "object", properties: {} }
1216
+ },
1217
+ {
1218
+ name: "kaban_archive_tasks",
1219
+ description: "Archive completed or stale tasks. By default archives from terminal columns only.",
1220
+ inputSchema: {
1221
+ type: "object",
1222
+ properties: {
1223
+ columnId: { type: "string", description: "Archive from this column only" },
1224
+ olderThanDays: { type: "number", description: "Archive tasks older than N days" },
1225
+ allColumns: { type: "boolean", description: "Archive from ALL columns" },
1226
+ dryRun: { type: "boolean", description: "Preview without archiving" }
1227
+ }
1228
+ }
1229
+ },
1230
+ {
1231
+ name: "kaban_search_archive",
1232
+ description: "Search archived tasks using full-text search",
1233
+ inputSchema: {
1234
+ type: "object",
1235
+ properties: {
1236
+ query: { type: "string", description: "Search query" },
1237
+ limit: { type: "number", description: "Max results (default: 50)" }
1238
+ },
1239
+ required: ["query"]
1240
+ }
1241
+ },
1242
+ {
1243
+ name: "kaban_restore_task",
1244
+ description: "Restore a task from archive",
1245
+ inputSchema: {
1246
+ type: "object",
1247
+ properties: {
1248
+ id: { type: "string", description: "Task ID to restore" },
1249
+ taskId: { type: "string", description: "Task ID - alias for id" },
1250
+ columnId: { type: "string", description: "Target column (default: todo)" }
1251
+ }
1252
+ }
1253
+ },
1254
+ {
1255
+ name: "kaban_purge_archive",
1256
+ description: "Permanently delete all archived tasks",
1257
+ inputSchema: {
1258
+ type: "object",
1259
+ properties: {
1260
+ confirm: { type: "boolean", description: "Must be true to confirm" },
1261
+ dryRun: { type: "boolean", description: "Preview without deleting" }
1262
+ },
1263
+ required: ["confirm"]
1264
+ }
1265
+ },
1266
+ {
1267
+ name: "kaban_reset_board",
1268
+ description: "Delete ALL tasks (active and archived)",
1269
+ inputSchema: {
1270
+ type: "object",
1271
+ properties: {
1272
+ confirm: { type: "boolean", description: "Must be true to confirm" },
1273
+ dryRun: { type: "boolean", description: "Preview without deleting" }
1274
+ },
1275
+ required: ["confirm"]
1276
+ }
1277
+ },
1278
+ {
1279
+ name: "kaban_archive_stats",
1280
+ description: "Get archive statistics",
1281
+ inputSchema: { type: "object", properties: {} }
1282
+ },
1283
+ {
1284
+ name: "kaban_add_dependency",
1285
+ description: "Add a dependency to a task (task cannot start until dependency is done)",
1286
+ inputSchema: {
1287
+ type: "object",
1288
+ properties: {
1289
+ taskId: { type: "string", description: "Task that depends on another" },
1290
+ id: { type: "string", description: "Task ID - alias for taskId" },
1291
+ dependsOnId: { type: "string", description: "Task that must be completed first" }
1292
+ },
1293
+ required: ["dependsOnId"]
1294
+ }
1295
+ },
1296
+ {
1297
+ name: "kaban_remove_dependency",
1298
+ description: "Remove a dependency from a task",
1299
+ inputSchema: {
1300
+ type: "object",
1301
+ properties: {
1302
+ taskId: { type: "string", description: "Task ID" },
1303
+ id: { type: "string", description: "Task ID - alias" },
1304
+ dependsOnId: { type: "string", description: "Dependency to remove" }
1305
+ },
1306
+ required: ["dependsOnId"]
1307
+ }
1308
+ },
1309
+ {
1310
+ name: "kaban_check_dependencies",
1311
+ description: "Check if task dependencies are resolved",
1312
+ inputSchema: {
1313
+ type: "object",
1314
+ properties: {
1315
+ taskId: { type: "string", description: "Task ID" },
1316
+ id: { type: "string", description: "Task ID - alias" }
1317
+ }
1318
+ }
1319
+ },
1320
+ {
1321
+ name: "kaban_add_task_checked",
1322
+ description: "Add task with duplicate detection. Rejects if very similar task exists.",
1323
+ inputSchema: {
1324
+ type: "object",
1325
+ properties: {
1326
+ title: { type: "string", description: "Task title" },
1327
+ description: { type: "string", description: "Task description" },
1328
+ columnId: { type: "string", description: "Target column (default: todo)" },
1329
+ createdBy: { type: "string", description: "Creator: user, claude, etc." },
1330
+ agent: { type: "string", description: "Creator (deprecated, use createdBy)" },
1331
+ assignedTo: { type: "string", description: "Assignee" },
1332
+ dependsOn: { type: "array", items: { type: "string" }, description: "Dependency IDs" },
1333
+ force: { type: "boolean", description: "Create even if similar exists" }
1334
+ },
1335
+ required: ["title"]
1336
+ }
974
1337
  }
975
1338
  ]
976
1339
  }));
@@ -1085,6 +1448,153 @@ async function startMcpServer(workingDirectory) {
1085
1448
  totalTasks: tasks.length
1086
1449
  });
1087
1450
  }
1451
+ case "kaban_archive_tasks": {
1452
+ const { columnId, olderThanDays, allColumns, dryRun } = args ?? {};
1453
+ const columns = await boardService.getColumns();
1454
+ const terminalColumns = columns.filter((c) => c.isTerminal);
1455
+ let targetColumnIds = [];
1456
+ if (columnId) {
1457
+ targetColumnIds = [columnId];
1458
+ } else if (allColumns) {
1459
+ targetColumnIds = columns.map((c) => c.id);
1460
+ } else {
1461
+ targetColumnIds = terminalColumns.map((c) => c.id);
1462
+ }
1463
+ if (targetColumnIds.length === 0) {
1464
+ return jsonResponse({
1465
+ archivedCount: 0,
1466
+ taskIds: [],
1467
+ message: "No columns to archive from"
1468
+ });
1469
+ }
1470
+ const allTasks = await taskService.listTasks();
1471
+ let tasksToArchive = allTasks.filter((t) => !t.archived && targetColumnIds.includes(t.columnId));
1472
+ if (olderThanDays !== undefined) {
1473
+ const cutoff = new Date;
1474
+ cutoff.setDate(cutoff.getDate() - olderThanDays);
1475
+ tasksToArchive = tasksToArchive.filter((t) => t.createdAt < cutoff);
1476
+ }
1477
+ if (tasksToArchive.length === 0) {
1478
+ return jsonResponse({
1479
+ archivedCount: 0,
1480
+ taskIds: [],
1481
+ message: "No matching tasks to archive"
1482
+ });
1483
+ }
1484
+ if (dryRun) {
1485
+ return jsonResponse({
1486
+ dryRun: true,
1487
+ wouldArchive: tasksToArchive.length,
1488
+ taskIds: tasksToArchive.map((t) => t.id),
1489
+ tasks: tasksToArchive.map((t) => ({
1490
+ id: t.id,
1491
+ title: t.title,
1492
+ columnId: t.columnId
1493
+ }))
1494
+ });
1495
+ }
1496
+ const result = await taskService.archiveTasks({
1497
+ taskIds: tasksToArchive.map((t) => t.id)
1498
+ });
1499
+ return jsonResponse(result);
1500
+ }
1501
+ case "kaban_search_archive": {
1502
+ const { query, limit } = args ?? {};
1503
+ if (!query)
1504
+ return errorResponse("Query required");
1505
+ const result = await taskService.searchArchive(query, { limit });
1506
+ return jsonResponse(result);
1507
+ }
1508
+ case "kaban_restore_task": {
1509
+ const id = getParam(taskArgs, "id", "taskId");
1510
+ if (!id)
1511
+ return errorResponse("Task ID required");
1512
+ const { columnId } = args ?? {};
1513
+ const task = await taskService.restoreTask(id, columnId);
1514
+ return jsonResponse(task);
1515
+ }
1516
+ case "kaban_purge_archive": {
1517
+ const { confirm: confirm2, dryRun } = args ?? {};
1518
+ if (!confirm2) {
1519
+ return errorResponse("Must set confirm: true to purge archive");
1520
+ }
1521
+ if (dryRun) {
1522
+ const result2 = await taskService.searchArchive("", { limit: 1000 });
1523
+ return jsonResponse({
1524
+ dryRun: true,
1525
+ wouldDelete: result2.total
1526
+ });
1527
+ }
1528
+ const result = await taskService.purgeArchive();
1529
+ return jsonResponse(result);
1530
+ }
1531
+ case "kaban_reset_board": {
1532
+ const { confirm: confirm2, dryRun } = args ?? {};
1533
+ if (!confirm2) {
1534
+ return errorResponse("Must set confirm: true to reset board");
1535
+ }
1536
+ if (dryRun) {
1537
+ const allTasks = await taskService.listTasks();
1538
+ const archivedResult = await taskService.searchArchive("", { limit: 1000 });
1539
+ return jsonResponse({
1540
+ dryRun: true,
1541
+ wouldDelete: allTasks.length + archivedResult.total,
1542
+ activeTasks: allTasks.length,
1543
+ archivedTasks: archivedResult.total
1544
+ });
1545
+ }
1546
+ const result = await taskService.resetBoard();
1547
+ return jsonResponse(result);
1548
+ }
1549
+ case "kaban_archive_stats": {
1550
+ const archivedResult = await taskService.searchArchive("", { limit: 1 });
1551
+ const allTasks = await taskService.listTasks();
1552
+ const columns = await boardService.getColumns();
1553
+ const terminalColumns = columns.filter((c) => c.isTerminal);
1554
+ const completedCount = allTasks.filter((t) => terminalColumns.some((c) => c.id === t.columnId)).length;
1555
+ return jsonResponse({
1556
+ archivedCount: archivedResult.total,
1557
+ activeTasks: allTasks.length,
1558
+ completedNotArchived: completedCount
1559
+ });
1560
+ }
1561
+ case "kaban_add_dependency": {
1562
+ const id = getParam(taskArgs, "taskId", "id");
1563
+ if (!id)
1564
+ return errorResponse("Task ID required");
1565
+ const { dependsOnId } = args ?? {};
1566
+ if (!dependsOnId)
1567
+ return errorResponse("dependsOnId required");
1568
+ const task = await taskService.addDependency(id, dependsOnId);
1569
+ return jsonResponse(task);
1570
+ }
1571
+ case "kaban_remove_dependency": {
1572
+ const id = getParam(taskArgs, "taskId", "id");
1573
+ if (!id)
1574
+ return errorResponse("Task ID required");
1575
+ const { dependsOnId } = args ?? {};
1576
+ if (!dependsOnId)
1577
+ return errorResponse("dependsOnId required");
1578
+ const task = await taskService.removeDependency(id, dependsOnId);
1579
+ return jsonResponse(task);
1580
+ }
1581
+ case "kaban_check_dependencies": {
1582
+ const id = getParam(taskArgs, "taskId", "id");
1583
+ if (!id)
1584
+ return errorResponse("Task ID required");
1585
+ const result = await taskService.validateDependencies(id);
1586
+ return jsonResponse(result);
1587
+ }
1588
+ case "kaban_add_task_checked": {
1589
+ const addCheckedArgs = args;
1590
+ const title = addCheckedArgs?.title;
1591
+ if (typeof title !== "string" || !title.trim()) {
1592
+ return errorResponse("Title required");
1593
+ }
1594
+ const { force, ...taskInput } = addCheckedArgs;
1595
+ const result = await taskService.addTaskChecked(taskInput, { force });
1596
+ return jsonResponse(result);
1597
+ }
1088
1598
  default:
1089
1599
  return errorResponse(`Unknown tool: ${name}`);
1090
1600
  }
@@ -1198,15 +1708,15 @@ async function startMcpServer(workingDirectory) {
1198
1708
  await server.connect(transport);
1199
1709
  console.error("Kaban MCP server running on stdio");
1200
1710
  }
1201
- var mcpCommand = new Command6("mcp").description("Start MCP server for AI agent integration").option("-p, --path <path>", "Working directory for Kaban board").action(async (options) => {
1711
+ var mcpCommand = new Command8("mcp").description("Start MCP server for AI agent integration").option("-p, --path <path>", "Working directory for Kaban board").action(async (options) => {
1202
1712
  const workingDirectory = options.path ?? process.env.KABAN_PATH ?? process.cwd();
1203
1713
  await startMcpServer(workingDirectory);
1204
1714
  });
1205
1715
 
1206
1716
  // src/commands/move.ts
1207
- import { KabanError as KabanError4 } from "@kaban-board/core";
1208
- import { Command as Command7 } from "commander";
1209
- var moveCommand = new Command7("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("-j, --json", "Output as JSON").action(async (id, column, options) => {
1717
+ import { KabanError as KabanError6 } from "@kaban-board/core";
1718
+ import { Command as Command9 } from "commander";
1719
+ var moveCommand = new Command9("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) => {
1210
1720
  const json = options.json;
1211
1721
  try {
1212
1722
  const { taskService, boardService } = await getContext();
@@ -1240,14 +1750,24 @@ var moveCommand = new Command7("move").description("Move a task to a different c
1240
1750
  const moved = await taskService.moveTask(task.id, targetColumn, {
1241
1751
  force: options.force
1242
1752
  });
1753
+ let assignAgent = null;
1754
+ if (options.assign !== undefined) {
1755
+ assignAgent = options.assign === true ? getAgent() : options.assign;
1756
+ await taskService.updateTask(moved.id, { assignedTo: assignAgent });
1757
+ }
1758
+ const finalTask = assignAgent ? await taskService.getTask(moved.id) : moved;
1243
1759
  if (json) {
1244
- outputSuccess(moved);
1760
+ outputSuccess(finalTask);
1245
1761
  return;
1246
1762
  }
1247
1763
  const col = await boardService.getColumn(moved.columnId);
1248
- console.log(`Moved [${moved.id.slice(0, 8)}] to ${col?.name}`);
1764
+ let msg = `Moved [${moved.id.slice(0, 8)}] to ${col?.name}`;
1765
+ if (assignAgent) {
1766
+ msg += ` (assigned to ${assignAgent})`;
1767
+ }
1768
+ console.log(msg);
1249
1769
  } catch (error2) {
1250
- if (error2 instanceof KabanError4) {
1770
+ if (error2 instanceof KabanError6) {
1251
1771
  if (json)
1252
1772
  outputError(error2.code, error2.message);
1253
1773
  console.error(`Error: ${error2.message}`);
@@ -1259,9 +1779,9 @@ var moveCommand = new Command7("move").description("Move a task to a different c
1259
1779
 
1260
1780
  // src/commands/schema.ts
1261
1781
  import { jsonSchemas } from "@kaban-board/core";
1262
- import { Command as Command8 } from "commander";
1782
+ import { Command as Command10 } from "commander";
1263
1783
  var availableSchemas = Object.keys(jsonSchemas);
1264
- var schemaCommand = new Command8("schema").description("Output JSON schemas for AI agents").argument("[name]", "Schema name (omit to list available)").action((name) => {
1784
+ var schemaCommand = new Command10("schema").description("Output JSON schemas for AI agents").argument("[name]", "Schema name (omit to list available)").action((name) => {
1265
1785
  if (!name) {
1266
1786
  console.log("Available schemas:");
1267
1787
  for (const schemaName of availableSchemas) {
@@ -1280,10 +1800,52 @@ Usage: kaban schema <name>`);
1280
1800
  console.log(JSON.stringify(schema, null, 2));
1281
1801
  });
1282
1802
 
1803
+ // src/commands/search.ts
1804
+ import { KabanError as KabanError7 } from "@kaban-board/core";
1805
+ import { Command as Command11 } from "commander";
1806
+ var searchCommand = new Command11("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
+ const json = options.json;
1808
+ try {
1809
+ const { taskService } = await getContext();
1810
+ let tasks;
1811
+ if (options.archive) {
1812
+ const result = await taskService.searchArchive(query, {
1813
+ limit: options.limit
1814
+ });
1815
+ tasks = result.tasks;
1816
+ } else {
1817
+ const allTasks = await taskService.listTasks();
1818
+ const queryLower = query.toLowerCase();
1819
+ tasks = allTasks.filter((t) => t.title.toLowerCase().includes(queryLower) || t.description?.toLowerCase().includes(queryLower));
1820
+ }
1821
+ if (json) {
1822
+ outputSuccess(tasks);
1823
+ return;
1824
+ }
1825
+ if (tasks.length === 0) {
1826
+ console.log("No tasks found");
1827
+ return;
1828
+ }
1829
+ for (const task of tasks) {
1830
+ const archived = task.archived ? " [archived]" : "";
1831
+ console.log(`[${task.id.slice(0, 8)}] ${task.title}${archived}`);
1832
+ console.log(` Column: ${task.columnId} | By: ${task.createdBy}`);
1833
+ }
1834
+ } catch (error2) {
1835
+ if (error2 instanceof KabanError7) {
1836
+ if (json)
1837
+ outputError(error2.code, error2.message);
1838
+ console.error(`Error: ${error2.message}`);
1839
+ process.exit(error2.code);
1840
+ }
1841
+ throw error2;
1842
+ }
1843
+ });
1844
+
1283
1845
  // src/commands/status.ts
1284
- import { KabanError as KabanError5 } from "@kaban-board/core";
1285
- import { Command as Command9 } from "commander";
1286
- var statusCommand2 = new Command9("status").description("Show board status summary").option("-j, --json", "Output as JSON").action(async (options) => {
1846
+ import { KabanError as KabanError8 } from "@kaban-board/core";
1847
+ import { Command as Command12 } from "commander";
1848
+ var statusCommand2 = new Command12("status").description("Show board status summary").option("-j, --json", "Output as JSON").action(async (options) => {
1287
1849
  const json = options.json;
1288
1850
  try {
1289
1851
  const { taskService, boardService } = await getContext();
@@ -1326,7 +1888,7 @@ var statusCommand2 = new Command9("status").description("Show board status summa
1326
1888
  }
1327
1889
  console.log();
1328
1890
  } catch (error2) {
1329
- if (error2 instanceof KabanError5) {
1891
+ if (error2 instanceof KabanError8) {
1330
1892
  if (json)
1331
1893
  outputError(error2.code, error2.message);
1332
1894
  console.error(`Error: ${error2.message}`);
@@ -1337,7 +1899,7 @@ var statusCommand2 = new Command9("status").description("Show board status summa
1337
1899
  });
1338
1900
 
1339
1901
  // src/commands/sync.ts
1340
- import { Command as Command10 } from "commander";
1902
+ import { Command as Command13 } from "commander";
1341
1903
 
1342
1904
  // src/hook/constants.ts
1343
1905
  var STATUS_PRIORITY = {
@@ -1431,23 +1993,26 @@ class ConflictResolver {
1431
1993
  }
1432
1994
 
1433
1995
  // src/hook/kaban-client.ts
1434
- import { spawn as spawn2 } from "node:child_process";
1996
+ import { spawn as spawn2 } from "child_process";
1435
1997
 
1436
1998
  class KabanClient {
1437
1999
  cwd;
2000
+ kabanCmd;
1438
2001
  constructor(cwd) {
1439
2002
  this.cwd = cwd;
2003
+ const cliOverride = process.env.KABAN_CLI;
2004
+ this.kabanCmd = cliOverride ? cliOverride.split(" ") : ["kaban"];
1440
2005
  }
1441
2006
  async boardExists() {
1442
2007
  try {
1443
- const result = await this.exec(["kaban", "status", "--json"]);
2008
+ const result = await this.exec([...this.kabanCmd, "status", "--json"]);
1444
2009
  return result.exitCode === 0;
1445
2010
  } catch {
1446
2011
  return false;
1447
2012
  }
1448
2013
  }
1449
2014
  async listTasks(columnId) {
1450
- const args = ["kaban", "list", "--json"];
2015
+ const args = [...this.kabanCmd, "list", "--json"];
1451
2016
  if (columnId) {
1452
2017
  args.push("--column", columnId);
1453
2018
  }
@@ -1456,7 +2021,8 @@ class KabanClient {
1456
2021
  return [];
1457
2022
  }
1458
2023
  try {
1459
- const tasks = JSON.parse(result.stdout);
2024
+ const parsed = JSON.parse(result.stdout);
2025
+ const tasks = parsed.data ?? parsed;
1460
2026
  return tasks.map((t) => ({
1461
2027
  id: t.id,
1462
2028
  title: t.title,
@@ -1469,12 +2035,13 @@ class KabanClient {
1469
2035
  }
1470
2036
  }
1471
2037
  async getTaskById(id) {
1472
- const result = await this.exec(["kaban", "get", id, "--json"]);
2038
+ const result = await this.exec([...this.kabanCmd, "get", id, "--json"]);
1473
2039
  if (result.exitCode !== 0) {
1474
2040
  return null;
1475
2041
  }
1476
2042
  try {
1477
- const task = JSON.parse(result.stdout);
2043
+ const parsed = JSON.parse(result.stdout);
2044
+ const task = parsed.data ?? parsed;
1478
2045
  return {
1479
2046
  id: task.id,
1480
2047
  title: task.title,
@@ -1491,7 +2058,7 @@ class KabanClient {
1491
2058
  return tasks.find((t) => t.title === title) ?? null;
1492
2059
  }
1493
2060
  async addTask(title, columnId = "todo") {
1494
- const result = await this.exec(["kaban", "add", title, "--column", columnId, "--json"]);
2061
+ const result = await this.exec([...this.kabanCmd, "add", title, "--column", columnId, "--json"]);
1495
2062
  if (result.exitCode !== 0) {
1496
2063
  return null;
1497
2064
  }
@@ -1504,20 +2071,21 @@ class KabanClient {
1504
2071
  }
1505
2072
  }
1506
2073
  async moveTask(id, columnId) {
1507
- const result = await this.exec(["kaban", "move", id, "--column", columnId]);
2074
+ const result = await this.exec([...this.kabanCmd, "move", id, columnId]);
1508
2075
  return result.exitCode === 0;
1509
2076
  }
1510
2077
  async completeTask(id) {
1511
- const result = await this.exec(["kaban", "done", id]);
2078
+ const result = await this.exec([...this.kabanCmd, "done", id]);
1512
2079
  return result.exitCode === 0;
1513
2080
  }
1514
2081
  async getStatus() {
1515
- const result = await this.exec(["kaban", "status", "--json"]);
2082
+ const result = await this.exec([...this.kabanCmd, "status", "--json"]);
1516
2083
  if (result.exitCode !== 0) {
1517
2084
  return null;
1518
2085
  }
1519
2086
  try {
1520
- return JSON.parse(result.stdout);
2087
+ const parsed = JSON.parse(result.stdout);
2088
+ return parsed.data ?? parsed;
1521
2089
  } catch {
1522
2090
  return null;
1523
2091
  }
@@ -1634,10 +2202,10 @@ class SyncEngine {
1634
2202
  }
1635
2203
 
1636
2204
  // src/hook/sync-logger.ts
1637
- import { existsSync as existsSync6 } from "node:fs";
1638
- import { appendFile, mkdir as mkdir2 } from "node:fs/promises";
1639
- import { homedir as homedir3 } from "node:os";
1640
- import { dirname as dirname2 } from "node:path";
2205
+ import { existsSync as existsSync6 } from "fs";
2206
+ import { appendFile, mkdir as mkdir2 } from "fs/promises";
2207
+ import { homedir as homedir3 } from "os";
2208
+ import { dirname as dirname2 } from "path";
1641
2209
 
1642
2210
  class SyncLogger {
1643
2211
  logPath;
@@ -1673,7 +2241,7 @@ class SyncLogger {
1673
2241
  }
1674
2242
 
1675
2243
  // src/commands/sync.ts
1676
- var syncCommand = new Command10("sync").description("Sync TodoWrite input to Kaban board (reads from stdin)").option("--no-log", "Disable sync logging").action(async (options) => {
2244
+ var syncCommand = new Command13("sync").description("Sync TodoWrite input to Kaban board (reads from stdin)").option("--no-log", "Disable sync logging").action(async (options) => {
1677
2245
  const startTime = performance.now();
1678
2246
  let input;
1679
2247
  try {
@@ -1720,12 +2288,12 @@ var syncCommand = new Command10("sync").description("Sync TodoWrite input to Kab
1720
2288
  });
1721
2289
 
1722
2290
  // src/commands/tui.ts
1723
- import { spawn as spawn3, spawnSync } from "node:child_process";
1724
- import { existsSync as existsSync7 } from "node:fs";
1725
- import { dirname as dirname3, join as join5 } from "node:path";
1726
- import { fileURLToPath } from "node:url";
1727
- import { createRequire } from "node:module";
1728
- import { Command as Command11 } from "commander";
2291
+ import { spawn as spawn3, spawnSync } from "child_process";
2292
+ import { existsSync as existsSync7 } from "fs";
2293
+ import { createRequire } from "module";
2294
+ import { dirname as dirname3, join as join5 } from "path";
2295
+ import { fileURLToPath } from "url";
2296
+ import { Command as Command14 } from "commander";
1729
2297
  var __dirname2 = dirname3(fileURLToPath(import.meta.url));
1730
2298
  var require2 = createRequire(import.meta.url);
1731
2299
  function hasBun() {
@@ -1751,7 +2319,7 @@ function runBinary(path, args) {
1751
2319
  const child = spawn3(path, args, { stdio: "inherit", cwd: process.cwd() });
1752
2320
  child.on("exit", (code) => process.exit(code ?? 0));
1753
2321
  }
1754
- var tuiCommand = new Command11("tui").description("Start interactive Terminal UI").action(async () => {
2322
+ var tuiCommand = new Command14("tui").description("Start interactive Terminal UI").action(async () => {
1755
2323
  const cwd = process.cwd();
1756
2324
  const args = process.argv.slice(3);
1757
2325
  const useBun = hasBun();
@@ -1784,17 +2352,23 @@ var tuiCommand = new Command11("tui").description("Start interactive Terminal UI
1784
2352
  // src/index.ts
1785
2353
  var require3 = createRequire2(import.meta.url);
1786
2354
  var pkg = require3("../package.json");
1787
- var program = new Command12;
2355
+ var program = new Command15;
1788
2356
  program.name("kaban").description("Terminal Kanban for AI Code Agents").version(pkg.version);
1789
2357
  program.addCommand(initCommand);
1790
2358
  program.addCommand(addCommand);
1791
2359
  program.addCommand(listCommand);
1792
2360
  program.addCommand(moveCommand);
2361
+ program.addCommand(assignCommand);
1793
2362
  program.addCommand(doneCommand);
1794
2363
  program.addCommand(statusCommand2);
1795
2364
  program.addCommand(schemaCommand);
2365
+ program.addCommand(searchCommand);
1796
2366
  program.addCommand(mcpCommand);
1797
2367
  program.addCommand(tuiCommand);
1798
2368
  program.addCommand(hookCommand);
1799
2369
  program.addCommand(syncCommand);
2370
+ program.addCommand(archiveCommand);
2371
+ program.addCommand(restoreCommand);
2372
+ program.addCommand(purgeCommand);
2373
+ program.addCommand(resetCommand);
1800
2374
  program.parse();
package/dist/kaban-hook CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ // @bun
2
3
 
3
4
  // src/hook-entry.ts
4
- import { spawn } from "node:child_process";
5
+ import { spawn } from "child_process";
5
6
  var kaban = spawn("kaban", ["sync"], {
6
7
  stdio: ["pipe", "inherit", "inherit"]
7
8
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaban-board/cli",
3
- "version": "0.2.6",
3
+ "version": "0.2.9",
4
4
  "description": "Terminal Kanban for AI Code Agents - CLI and MCP server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "dist"
11
11
  ],
12
12
  "scripts": {
13
- "build": "bun build ./src/index.ts --outdir ./dist --target node --packages external && bun build ./src/hook-entry.ts --outfile ./dist/kaban-hook --target node && chmod +x ./dist/index.js ./dist/kaban-hook",
13
+ "build": "bun build ./src/index.ts --outdir ./dist --target bun --packages external && bun build ./src/hook-entry.ts --outfile ./dist/kaban-hook --target bun && chmod +x ./dist/index.js ./dist/kaban-hook",
14
14
  "dev": "bun run ./src/index.ts",
15
15
  "test": "bun test",
16
16
  "typecheck": "tsc --noEmit",
@@ -18,12 +18,12 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@clack/prompts": "^0.11.0",
21
- "@kaban-board/core": "0.2.4",
22
- "@kaban-board/tui": "0.2.4",
23
- "@modelcontextprotocol/sdk": "^1.25.2",
21
+ "@kaban-board/core": "0.2.5",
22
+ "@kaban-board/tui": "0.2.5",
23
+ "@modelcontextprotocol/sdk": "^1.25.3",
24
24
  "chalk": "^5.6.2",
25
- "commander": "^12.0.0",
26
- "zod": "^4.3.5"
25
+ "commander": "^14.0.2",
26
+ "zod": "^4.3.6"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/bun": "latest",