@kaban-board/cli 0.2.5 → 0.2.6
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 +138 -115
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { createRequire } from "node:module";
|
|
4
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
5
5
|
import { Command as Command12 } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/add.ts
|
|
@@ -802,6 +802,25 @@ import {
|
|
|
802
802
|
ReadResourceRequestSchema
|
|
803
803
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
804
804
|
import { Command as Command6 } from "commander";
|
|
805
|
+
var mcpHelpers = {
|
|
806
|
+
getParam(args, primary, alias) {
|
|
807
|
+
if (!args)
|
|
808
|
+
return;
|
|
809
|
+
const primaryVal = args[primary];
|
|
810
|
+
if (typeof primaryVal === "string" && primaryVal.trim())
|
|
811
|
+
return primaryVal;
|
|
812
|
+
const aliasVal = args[alias];
|
|
813
|
+
if (typeof aliasVal === "string" && aliasVal.trim())
|
|
814
|
+
return aliasVal;
|
|
815
|
+
return;
|
|
816
|
+
},
|
|
817
|
+
errorResponse(message) {
|
|
818
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: message }) }], isError: true };
|
|
819
|
+
},
|
|
820
|
+
jsonResponse(data) {
|
|
821
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
822
|
+
}
|
|
823
|
+
};
|
|
805
824
|
function getKabanPaths2(basePath) {
|
|
806
825
|
const base = basePath ?? process.cwd();
|
|
807
826
|
const kabanDir = join4(base, ".kaban");
|
|
@@ -867,8 +886,10 @@ async function startMcpServer(workingDirectory) {
|
|
|
867
886
|
description: "Get a task by its ID",
|
|
868
887
|
inputSchema: {
|
|
869
888
|
type: "object",
|
|
870
|
-
properties: {
|
|
871
|
-
|
|
889
|
+
properties: {
|
|
890
|
+
id: { type: "string", description: "Task ID (ULID)" },
|
|
891
|
+
taskId: { type: "string", description: "Task ID (ULID) - alias for 'id'" }
|
|
892
|
+
}
|
|
872
893
|
}
|
|
873
894
|
},
|
|
874
895
|
{
|
|
@@ -890,10 +911,11 @@ async function startMcpServer(workingDirectory) {
|
|
|
890
911
|
type: "object",
|
|
891
912
|
properties: {
|
|
892
913
|
id: { type: "string", description: "Task ID (ULID)" },
|
|
914
|
+
taskId: { type: "string", description: "Task ID (ULID) - alias for 'id'" },
|
|
893
915
|
columnId: { type: "string", description: "Target column ID" },
|
|
916
|
+
column: { type: "string", description: "Target column ID - alias for 'columnId'" },
|
|
894
917
|
force: { type: "boolean", description: "Force move even if WIP limit exceeded" }
|
|
895
|
-
}
|
|
896
|
-
required: ["id", "columnId"]
|
|
918
|
+
}
|
|
897
919
|
}
|
|
898
920
|
},
|
|
899
921
|
{
|
|
@@ -903,6 +925,7 @@ async function startMcpServer(workingDirectory) {
|
|
|
903
925
|
type: "object",
|
|
904
926
|
properties: {
|
|
905
927
|
id: { type: "string", description: "Task ID (ULID)" },
|
|
928
|
+
taskId: { type: "string", description: "Task ID (ULID) - alias for 'id'" },
|
|
906
929
|
title: { type: "string", description: "New task title" },
|
|
907
930
|
description: { type: ["string", "null"], description: "New task description" },
|
|
908
931
|
assignedTo: { type: ["string", "null"], description: "Assigned agent name" },
|
|
@@ -916,8 +939,7 @@ async function startMcpServer(workingDirectory) {
|
|
|
916
939
|
type: "number",
|
|
917
940
|
description: "Expected version for optimistic locking"
|
|
918
941
|
}
|
|
919
|
-
}
|
|
920
|
-
required: ["id"]
|
|
942
|
+
}
|
|
921
943
|
}
|
|
922
944
|
},
|
|
923
945
|
{
|
|
@@ -925,8 +947,10 @@ async function startMcpServer(workingDirectory) {
|
|
|
925
947
|
description: "Delete a task",
|
|
926
948
|
inputSchema: {
|
|
927
949
|
type: "object",
|
|
928
|
-
properties: {
|
|
929
|
-
|
|
950
|
+
properties: {
|
|
951
|
+
id: { type: "string", description: "Task ID (ULID)" },
|
|
952
|
+
taskId: { type: "string", description: "Task ID (ULID) - alias for 'id'" }
|
|
953
|
+
}
|
|
930
954
|
}
|
|
931
955
|
},
|
|
932
956
|
{
|
|
@@ -934,8 +958,13 @@ async function startMcpServer(workingDirectory) {
|
|
|
934
958
|
description: "Mark a task as completed (move to terminal column)",
|
|
935
959
|
inputSchema: {
|
|
936
960
|
type: "object",
|
|
937
|
-
properties: {
|
|
938
|
-
|
|
961
|
+
properties: {
|
|
962
|
+
id: { type: "string", description: "Task ID (ULID) or partial ID" },
|
|
963
|
+
taskId: {
|
|
964
|
+
type: "string",
|
|
965
|
+
description: "Task ID (ULID) or partial ID - alias for 'id'"
|
|
966
|
+
}
|
|
967
|
+
}
|
|
939
968
|
}
|
|
940
969
|
},
|
|
941
970
|
{
|
|
@@ -945,6 +974,7 @@ async function startMcpServer(workingDirectory) {
|
|
|
945
974
|
}
|
|
946
975
|
]
|
|
947
976
|
}));
|
|
977
|
+
const { getParam, errorResponse, jsonResponse } = mcpHelpers;
|
|
948
978
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
949
979
|
const { name, arguments: args } = request.params;
|
|
950
980
|
try {
|
|
@@ -953,15 +983,7 @@ async function startMcpServer(workingDirectory) {
|
|
|
953
983
|
const targetPath = basePath ?? workingDirectory;
|
|
954
984
|
const { kabanDir, dbPath, configPath } = getKabanPaths2(targetPath);
|
|
955
985
|
if (existsSync5(dbPath)) {
|
|
956
|
-
return
|
|
957
|
-
content: [
|
|
958
|
-
{
|
|
959
|
-
type: "text",
|
|
960
|
-
text: JSON.stringify({ error: "Board already exists in this directory" })
|
|
961
|
-
}
|
|
962
|
-
],
|
|
963
|
-
isError: true
|
|
964
|
-
};
|
|
986
|
+
return errorResponse("Board already exists in this directory");
|
|
965
987
|
}
|
|
966
988
|
mkdirSync2(kabanDir, { recursive: true });
|
|
967
989
|
const config = {
|
|
@@ -973,70 +995,77 @@ async function startMcpServer(workingDirectory) {
|
|
|
973
995
|
await initializeSchema2(db);
|
|
974
996
|
const boardService2 = new BoardService3(db);
|
|
975
997
|
await boardService2.initializeBoard(config);
|
|
976
|
-
return {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
success: true,
|
|
982
|
-
board: boardName,
|
|
983
|
-
paths: { database: dbPath, config: configPath }
|
|
984
|
-
}, null, 2)
|
|
985
|
-
}
|
|
986
|
-
]
|
|
987
|
-
};
|
|
998
|
+
return jsonResponse({
|
|
999
|
+
success: true,
|
|
1000
|
+
board: boardName,
|
|
1001
|
+
paths: { database: dbPath, config: configPath }
|
|
1002
|
+
});
|
|
988
1003
|
}
|
|
989
1004
|
const { taskService, boardService } = await createContext(workingDirectory);
|
|
1005
|
+
const taskArgs = args;
|
|
1006
|
+
const taskId = getParam(taskArgs, "id", "taskId");
|
|
990
1007
|
switch (name) {
|
|
991
1008
|
case "kaban_add_task": {
|
|
1009
|
+
const addArgs = args;
|
|
1010
|
+
const title = addArgs?.title;
|
|
1011
|
+
if (typeof title !== "string" || !title.trim()) {
|
|
1012
|
+
return errorResponse("Title required (non-empty string)");
|
|
1013
|
+
}
|
|
992
1014
|
const task = await taskService.addTask(args);
|
|
993
|
-
return
|
|
1015
|
+
return jsonResponse(task);
|
|
994
1016
|
}
|
|
995
1017
|
case "kaban_get_task": {
|
|
996
|
-
|
|
1018
|
+
if (!taskId)
|
|
1019
|
+
return errorResponse("Task ID required (use 'id' or 'taskId')");
|
|
1020
|
+
const task = await taskService.getTask(taskId);
|
|
997
1021
|
if (!task)
|
|
998
|
-
return
|
|
999
|
-
|
|
1000
|
-
};
|
|
1001
|
-
return { content: [{ type: "text", text: JSON.stringify(task, null, 2) }] };
|
|
1022
|
+
return errorResponse("Task not found");
|
|
1023
|
+
return jsonResponse(task);
|
|
1002
1024
|
}
|
|
1003
1025
|
case "kaban_list_tasks": {
|
|
1004
1026
|
const tasks = await taskService.listTasks(args);
|
|
1005
|
-
return
|
|
1027
|
+
return jsonResponse(tasks);
|
|
1006
1028
|
}
|
|
1007
1029
|
case "kaban_move_task": {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1030
|
+
if (!taskId)
|
|
1031
|
+
return errorResponse("Task ID required (use 'id' or 'taskId')");
|
|
1032
|
+
const targetColumn = getParam(taskArgs, "columnId", "column");
|
|
1033
|
+
if (!targetColumn)
|
|
1034
|
+
return errorResponse("Column ID required (use 'columnId' or 'column')");
|
|
1035
|
+
const { force } = args ?? {};
|
|
1036
|
+
const task = await taskService.moveTask(taskId, targetColumn, { force });
|
|
1037
|
+
return jsonResponse(task);
|
|
1011
1038
|
}
|
|
1012
1039
|
case "kaban_update_task": {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1040
|
+
if (!taskId)
|
|
1041
|
+
return errorResponse("Task ID required (use 'id' or 'taskId')");
|
|
1042
|
+
const {
|
|
1043
|
+
taskId: _t,
|
|
1044
|
+
id: _i,
|
|
1045
|
+
expectedVersion,
|
|
1046
|
+
...updates
|
|
1047
|
+
} = args ?? {};
|
|
1048
|
+
const task = await taskService.updateTask(taskId, updates, expectedVersion);
|
|
1049
|
+
return jsonResponse(task);
|
|
1016
1050
|
}
|
|
1017
1051
|
case "kaban_delete_task": {
|
|
1018
|
-
|
|
1019
|
-
|
|
1052
|
+
if (!taskId)
|
|
1053
|
+
return errorResponse("Task ID required (use 'id' or 'taskId')");
|
|
1054
|
+
await taskService.deleteTask(taskId);
|
|
1055
|
+
return jsonResponse({ success: true });
|
|
1020
1056
|
}
|
|
1021
1057
|
case "kaban_complete_task": {
|
|
1022
|
-
|
|
1058
|
+
if (!taskId)
|
|
1059
|
+
return errorResponse("Task ID required (use 'id' or 'taskId')");
|
|
1023
1060
|
const tasks = await taskService.listTasks();
|
|
1024
|
-
const task = tasks.find((t) => t.id.startsWith(
|
|
1061
|
+
const task = tasks.find((t) => t.id.startsWith(taskId));
|
|
1025
1062
|
if (!task)
|
|
1026
|
-
return {
|
|
1027
|
-
content: [
|
|
1028
|
-
{ type: "text", text: JSON.stringify({ error: `Task '${id}' not found` }) }
|
|
1029
|
-
]
|
|
1030
|
-
};
|
|
1063
|
+
return errorResponse(`Task '${taskId}' not found`);
|
|
1031
1064
|
const terminal = await boardService.getTerminalColumn();
|
|
1032
1065
|
if (!terminal)
|
|
1033
|
-
return
|
|
1034
|
-
content: [
|
|
1035
|
-
{ type: "text", text: JSON.stringify({ error: "No terminal column configured" }) }
|
|
1036
|
-
]
|
|
1037
|
-
};
|
|
1066
|
+
return errorResponse("No terminal column configured");
|
|
1038
1067
|
const completed = await taskService.moveTask(task.id, terminal.id);
|
|
1039
|
-
return
|
|
1068
|
+
return jsonResponse(completed);
|
|
1040
1069
|
}
|
|
1041
1070
|
case "kaban_status": {
|
|
1042
1071
|
const board = await boardService.getBoard();
|
|
@@ -1049,25 +1078,15 @@ async function startMcpServer(workingDirectory) {
|
|
|
1049
1078
|
wipLimit: column.wipLimit,
|
|
1050
1079
|
isTerminal: column.isTerminal
|
|
1051
1080
|
}));
|
|
1052
|
-
return {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
columns: columnStats,
|
|
1059
|
-
blockedCount: tasks.filter((t) => t.blockedReason).length,
|
|
1060
|
-
totalTasks: tasks.length
|
|
1061
|
-
}, null, 2)
|
|
1062
|
-
}
|
|
1063
|
-
]
|
|
1064
|
-
};
|
|
1081
|
+
return jsonResponse({
|
|
1082
|
+
board: { name: board?.name ?? "Kaban Board" },
|
|
1083
|
+
columns: columnStats,
|
|
1084
|
+
blockedCount: tasks.filter((t) => t.blockedReason).length,
|
|
1085
|
+
totalTasks: tasks.length
|
|
1086
|
+
});
|
|
1065
1087
|
}
|
|
1066
1088
|
default:
|
|
1067
|
-
return {
|
|
1068
|
-
content: [{ type: "text", text: JSON.stringify({ error: `Unknown tool: ${name}` }) }],
|
|
1069
|
-
isError: true
|
|
1070
|
-
};
|
|
1089
|
+
return errorResponse(`Unknown tool: ${name}`);
|
|
1071
1090
|
}
|
|
1072
1091
|
} catch (error2) {
|
|
1073
1092
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
@@ -1704,63 +1723,67 @@ var syncCommand = new Command10("sync").description("Sync TodoWrite input to Kab
|
|
|
1704
1723
|
import { spawn as spawn3, spawnSync } from "node:child_process";
|
|
1705
1724
|
import { existsSync as existsSync7 } from "node:fs";
|
|
1706
1725
|
import { dirname as dirname3, join as join5 } from "node:path";
|
|
1726
|
+
import { fileURLToPath } from "node:url";
|
|
1727
|
+
import { createRequire } from "node:module";
|
|
1707
1728
|
import { Command as Command11 } from "commander";
|
|
1729
|
+
var __dirname2 = dirname3(fileURLToPath(import.meta.url));
|
|
1730
|
+
var require2 = createRequire(import.meta.url);
|
|
1731
|
+
function hasBun() {
|
|
1732
|
+
try {
|
|
1733
|
+
const result = spawnSync("bun", ["--version"], { stdio: "ignore" });
|
|
1734
|
+
return result.status === 0;
|
|
1735
|
+
} catch {
|
|
1736
|
+
return false;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1708
1739
|
function findInPath(name) {
|
|
1709
1740
|
const result = spawnSync("which", [name], { encoding: "utf-8" });
|
|
1710
1741
|
return result.status === 0 ? result.stdout.trim() : null;
|
|
1711
1742
|
}
|
|
1743
|
+
function resolveTuiPackage() {
|
|
1744
|
+
try {
|
|
1745
|
+
return require2.resolve("@kaban-board/tui");
|
|
1746
|
+
} catch {
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1712
1750
|
function runBinary(path, args) {
|
|
1713
1751
|
const child = spawn3(path, args, { stdio: "inherit", cwd: process.cwd() });
|
|
1714
1752
|
child.on("exit", (code) => process.exit(code ?? 0));
|
|
1715
1753
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
const child = spawn3(bunPath, ["x", "@kaban-board/tui", ...args], {
|
|
1719
|
-
stdio: "inherit",
|
|
1720
|
-
cwd: process.cwd()
|
|
1721
|
-
});
|
|
1722
|
-
child.on("spawn", () => {
|
|
1723
|
-
started = true;
|
|
1724
|
-
});
|
|
1725
|
-
child.on("error", () => {
|
|
1726
|
-
if (!started)
|
|
1727
|
-
showInstallError();
|
|
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 () => {
|
|
1754
|
+
var tuiCommand = new Command11("tui").description("Start interactive Terminal UI").action(async () => {
|
|
1755
|
+
const cwd = process.cwd();
|
|
1748
1756
|
const args = process.argv.slice(3);
|
|
1757
|
+
const useBun = hasBun();
|
|
1749
1758
|
const siblingBinary = join5(dirname3(process.execPath), "kaban-tui");
|
|
1750
1759
|
if (existsSync7(siblingBinary))
|
|
1751
1760
|
return runBinary(siblingBinary, args);
|
|
1752
1761
|
const pathBinary = findInPath("kaban-tui");
|
|
1753
1762
|
if (pathBinary)
|
|
1754
1763
|
return runBinary(pathBinary, args);
|
|
1755
|
-
const
|
|
1756
|
-
if (
|
|
1757
|
-
|
|
1758
|
-
|
|
1764
|
+
const tuiDevEntry = join5(__dirname2, "../../../tui/src/index.ts");
|
|
1765
|
+
if (existsSync7(tuiDevEntry) && useBun) {
|
|
1766
|
+
const child2 = spawn3("bun", ["run", tuiDevEntry], { stdio: "inherit", cwd });
|
|
1767
|
+
child2.on("exit", (code) => process.exit(code ?? 0));
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
if (!useBun) {
|
|
1771
|
+
console.error("TUI requires Bun. Install: curl -fsSL https://bun.sh/install | bash");
|
|
1772
|
+
process.exit(1);
|
|
1773
|
+
}
|
|
1774
|
+
const tuiEntry = resolveTuiPackage();
|
|
1775
|
+
if (tuiEntry) {
|
|
1776
|
+
const child2 = spawn3("bun", [tuiEntry, ...args], { stdio: "inherit", cwd });
|
|
1777
|
+
child2.on("exit", (code) => process.exit(code ?? 0));
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
const child = spawn3("bun", ["x", "@kaban-board/tui", ...args], { stdio: "inherit", cwd });
|
|
1781
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
1759
1782
|
});
|
|
1760
1783
|
|
|
1761
1784
|
// src/index.ts
|
|
1762
|
-
var
|
|
1763
|
-
var pkg =
|
|
1785
|
+
var require3 = createRequire2(import.meta.url);
|
|
1786
|
+
var pkg = require3("../package.json");
|
|
1764
1787
|
var program = new Command12;
|
|
1765
1788
|
program.name("kaban").description("Terminal Kanban for AI Code Agents").version(pkg.version);
|
|
1766
1789
|
program.addCommand(initCommand);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaban-board/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Terminal Kanban for AI Code Agents - CLI and MCP server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@clack/prompts": "^0.11.0",
|
|
21
|
-
"@kaban-board/core": "0.
|
|
21
|
+
"@kaban-board/core": "0.2.4",
|
|
22
|
+
"@kaban-board/tui": "0.2.4",
|
|
22
23
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
23
24
|
"chalk": "^5.6.2",
|
|
24
25
|
"commander": "^12.0.0",
|