@kaban-board/cli 0.2.0 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +68 -22
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import { Command } from "commander";
12
12
  import { existsSync, readFileSync } from "node:fs";
13
13
  import { join } from "node:path";
14
14
  import { BoardService, createDb, TaskService } from "@kaban-board/core";
15
- function getContext() {
15
+ async function getContext() {
16
16
  const kabanDir = join(process.cwd(), ".kaban");
17
17
  const dbPath = join(kabanDir, "board.db");
18
18
  const configPath = join(kabanDir, "config.json");
@@ -20,7 +20,7 @@ function getContext() {
20
20
  console.error("Error: No board found. Run 'kaban init' first");
21
21
  process.exit(1);
22
22
  }
23
- const db = createDb(dbPath);
23
+ const db = await createDb(dbPath);
24
24
  const config = JSON.parse(readFileSync(configPath, "utf-8"));
25
25
  const boardService = new BoardService(db);
26
26
  const taskService = new TaskService(db, boardService);
@@ -60,7 +60,7 @@ function outputError(code, message) {
60
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
61
  const json = options.json;
62
62
  try {
63
- const { taskService, config } = getContext();
63
+ const { taskService, config } = await getContext();
64
64
  const agent = options.agent ?? getAgent();
65
65
  const columnId = options.column ?? config.defaults.column;
66
66
  const dependsOn = options.dependsOn ? options.dependsOn.split(",").map((s) => s.trim()) : [];
@@ -95,7 +95,7 @@ import { Command as Command2 } from "commander";
95
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) => {
96
96
  const json = options.json;
97
97
  try {
98
- const { taskService, boardService } = getContext();
98
+ const { taskService, boardService } = await getContext();
99
99
  const tasks = await taskService.listTasks();
100
100
  const task = tasks.find((t) => t.id.startsWith(id));
101
101
  if (!task) {
@@ -686,7 +686,12 @@ var hookCommand = new Command3("hook").description("Manage TodoWrite sync hook f
686
686
 
687
687
  // src/commands/init.ts
688
688
  import { existsSync as existsSync4, mkdirSync, writeFileSync } from "node:fs";
689
- import { BoardService as BoardService2, createDb as createDb2, DEFAULT_CONFIG as DEFAULT_CONFIG2, initializeSchema } from "@kaban-board/core";
689
+ import {
690
+ BoardService as BoardService2,
691
+ createDb as createDb2,
692
+ DEFAULT_CONFIG as DEFAULT_CONFIG2,
693
+ initializeSchema
694
+ } from "@kaban-board/core";
690
695
  import { Command as Command4 } from "commander";
691
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) => {
692
697
  const { kabanDir, dbPath, configPath } = getKabanPaths();
@@ -700,7 +705,7 @@ var initCommand = new Command4("init").description("Initialize a new Kaban board
700
705
  board: { name: options.name }
701
706
  };
702
707
  writeFileSync(configPath, JSON.stringify(config, null, 2));
703
- const db = createDb2(dbPath);
708
+ const db = await createDb2(dbPath);
704
709
  await initializeSchema(db);
705
710
  const boardService = new BoardService2(db);
706
711
  await boardService.initializeBoard(config);
@@ -730,7 +735,7 @@ function sortTasks(tasks, sortBy, reverse) {
730
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) => {
731
736
  const json = options.json;
732
737
  try {
733
- const { taskService, boardService } = getContext();
738
+ const { taskService, boardService } = await getContext();
734
739
  let tasks = await taskService.listTasks({
735
740
  columnId: options.column,
736
741
  agent: options.agent,
@@ -806,12 +811,12 @@ function getKabanPaths2(basePath) {
806
811
  configPath: join4(kabanDir, "config.json")
807
812
  };
808
813
  }
809
- function createContext(basePath) {
814
+ async function createContext(basePath) {
810
815
  const { dbPath, configPath } = getKabanPaths2(basePath);
811
816
  if (!existsSync5(dbPath)) {
812
817
  throw new Error("No board found. Run 'kaban init' first");
813
818
  }
814
- const db = createDb3(dbPath);
819
+ const db = await createDb3(dbPath);
815
820
  const config = JSON.parse(readFileSync2(configPath, "utf-8"));
816
821
  const boardService = new BoardService3(db);
817
822
  const taskService = new TaskService2(db, boardService);
@@ -964,7 +969,7 @@ async function startMcpServer(workingDirectory) {
964
969
  board: { name: boardName }
965
970
  };
966
971
  writeFileSync2(configPath, JSON.stringify(config, null, 2));
967
- const db = createDb3(dbPath);
972
+ const db = await createDb3(dbPath);
968
973
  await initializeSchema2(db);
969
974
  const boardService2 = new BoardService3(db);
970
975
  await boardService2.initializeBoard(config);
@@ -981,7 +986,7 @@ async function startMcpServer(workingDirectory) {
981
986
  ]
982
987
  };
983
988
  }
984
- const { taskService, boardService } = createContext(workingDirectory);
989
+ const { taskService, boardService } = await createContext(workingDirectory);
985
990
  switch (name) {
986
991
  case "kaban_add_task": {
987
992
  const task = await taskService.addTask(args);
@@ -1091,7 +1096,7 @@ async function startMcpServer(workingDirectory) {
1091
1096
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
1092
1097
  const { uri } = request.params;
1093
1098
  try {
1094
- const { taskService, boardService } = createContext(workingDirectory);
1099
+ const { taskService, boardService } = await createContext(workingDirectory);
1095
1100
  if (uri === "kaban://board/status") {
1096
1101
  const board = await boardService.getBoard();
1097
1102
  const columns = await boardService.getColumns();
@@ -1185,7 +1190,7 @@ import { Command as Command7 } from "commander";
1185
1190
  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) => {
1186
1191
  const json = options.json;
1187
1192
  try {
1188
- const { taskService, boardService } = getContext();
1193
+ const { taskService, boardService } = await getContext();
1189
1194
  const tasks = await taskService.listTasks();
1190
1195
  const task = tasks.find((t) => t.id.startsWith(id));
1191
1196
  if (!task) {
@@ -1262,7 +1267,7 @@ import { Command as Command9 } from "commander";
1262
1267
  var statusCommand2 = new Command9("status").description("Show board status summary").option("-j, --json", "Output as JSON").action(async (options) => {
1263
1268
  const json = options.json;
1264
1269
  try {
1265
- const { taskService, boardService } = getContext();
1270
+ const { taskService, boardService } = await getContext();
1266
1271
  const board = await boardService.getBoard();
1267
1272
  const columns = await boardService.getColumns();
1268
1273
  const tasks = await taskService.listTasks();
@@ -1696,20 +1701,61 @@ var syncCommand = new Command10("sync").description("Sync TodoWrite input to Kab
1696
1701
  });
1697
1702
 
1698
1703
  // src/commands/tui.ts
1699
- import { spawn as spawn3 } from "node:child_process";
1704
+ import { spawn as spawn3, spawnSync } from "node:child_process";
1705
+ import { existsSync as existsSync7 } from "node:fs";
1700
1706
  import { dirname as dirname3, join as join5 } from "node:path";
1701
- import { fileURLToPath } from "node:url";
1702
1707
  import { Command as Command11 } from "commander";
1703
- var __dirname2 = dirname3(fileURLToPath(import.meta.url));
1704
- var tuiCommand = new Command11("tui").description("Start interactive Terminal UI").action(async () => {
1705
- const tuiEntry = join5(__dirname2, "../../../tui/src/index.ts");
1706
- const child = spawn3("bun", ["run", tuiEntry], {
1708
+ function findInPath(name) {
1709
+ const result = spawnSync("which", [name], { encoding: "utf-8" });
1710
+ return result.status === 0 ? result.stdout.trim() : null;
1711
+ }
1712
+ function runBinary(path, args) {
1713
+ const child = spawn3(path, args, { stdio: "inherit", cwd: process.cwd() });
1714
+ child.on("exit", (code) => process.exit(code ?? 0));
1715
+ }
1716
+ function runBunx(bunPath, args) {
1717
+ let started = false;
1718
+ const child = spawn3(bunPath, ["x", "@kaban-board/tui", ...args], {
1707
1719
  stdio: "inherit",
1708
1720
  cwd: process.cwd()
1709
1721
  });
1710
- child.on("exit", (code) => {
1711
- process.exit(code ?? 0);
1722
+ child.on("spawn", () => {
1723
+ started = true;
1724
+ });
1725
+ child.on("error", () => {
1726
+ if (!started)
1727
+ showInstallError();
1712
1728
  });
1729
+ child.on("exit", (code) => process.exit(code ?? 0));
1730
+ return true;
1731
+ }
1732
+ function showInstallError() {
1733
+ console.error(`
1734
+ Error: kaban-tui not found
1735
+
1736
+ The TUI requires Bun runtime. Install with one of:
1737
+
1738
+ # Homebrew (recommended)
1739
+ brew install beshkenadze/tap/kaban-tui
1740
+
1741
+ # Or install Bun, then run via bunx
1742
+ curl -fsSL https://bun.sh/install | bash
1743
+ bunx @kaban-board/tui
1744
+ `);
1745
+ process.exit(1);
1746
+ }
1747
+ var tuiCommand = new Command11("tui").description("Start interactive Terminal UI (requires Bun)").action(async () => {
1748
+ const args = process.argv.slice(3);
1749
+ const siblingBinary = join5(dirname3(process.execPath), "kaban-tui");
1750
+ if (existsSync7(siblingBinary))
1751
+ return runBinary(siblingBinary, args);
1752
+ const pathBinary = findInPath("kaban-tui");
1753
+ if (pathBinary)
1754
+ return runBinary(pathBinary, args);
1755
+ const bunPath = findInPath("bun");
1756
+ if (bunPath)
1757
+ return runBunx(bunPath, args);
1758
+ showInstallError();
1713
1759
  });
1714
1760
 
1715
1761
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaban-board/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.5",
4
4
  "description": "Terminal Kanban for AI Code Agents - CLI and MCP server",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@clack/prompts": "^0.11.0",
21
- "@kaban-board/core": "0.2.0",
21
+ "@kaban-board/core": "0.1.3",
22
22
  "@modelcontextprotocol/sdk": "^1.25.2",
23
23
  "chalk": "^5.6.2",
24
24
  "commander": "^12.0.0",