kanban-lite 1.0.9 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1015 -407
- package/dist/extension.js +897 -347
- package/dist/mcp-server.js +550 -189
- package/dist/sdk/index.cjs +1223 -0
- package/dist/sdk/index.mjs +1168 -0
- package/dist/sdk/sdk/KanbanSDK.d.ts +39 -21
- package/dist/sdk/sdk/index.d.ts +4 -3
- package/dist/sdk/sdk/migration.d.ts +6 -0
- package/dist/sdk/sdk/types.d.ts +3 -5
- package/dist/sdk/shared/config.d.ts +23 -10
- package/dist/sdk/shared/types.d.ts +18 -4
- package/dist/standalone-webview/index.js +27 -27
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone-webview/style.css +1 -1
- package/dist/standalone.js +831 -328
- package/dist/webview/index.js +45 -45
- package/dist/webview/index.js.map +1 -1
- package/dist/webview/style.css +1 -1
- package/package.json +4 -3
- package/src/cli/index.ts +217 -95
- package/src/extension/KanbanPanel.ts +49 -22
- package/src/mcp-server/index.ts +138 -62
- package/src/sdk/KanbanSDK.ts +283 -77
- package/src/sdk/__tests__/KanbanSDK.test.ts +5 -5
- package/src/sdk/__tests__/migration.test.ts +269 -0
- package/src/sdk/__tests__/multi-board.test.ts +449 -0
- package/src/sdk/index.ts +4 -3
- package/src/sdk/migration.ts +52 -0
- package/src/sdk/types.ts +3 -6
- package/src/shared/config.ts +144 -22
- package/src/shared/types.ts +14 -5
- package/src/standalone/__tests__/server.integration.test.ts +38 -37
- package/src/standalone/server.ts +239 -21
- package/src/webview/App.tsx +17 -7
- package/src/webview/components/Toolbar.tsx +99 -3
- package/src/webview/store/index.ts +11 -3
- package/.kanban/backlog/1-test1.md +0 -30
- package/.kanban.json +0 -42
package/dist/mcp-server.js
CHANGED
|
@@ -24,15 +24,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/mcp-server/index.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
27
|
+
var path7 = __toESM(require("path"));
|
|
28
|
+
var fs6 = __toESM(require("fs/promises"));
|
|
29
29
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
30
30
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
31
31
|
var import_zod = require("zod");
|
|
32
32
|
|
|
33
33
|
// src/sdk/KanbanSDK.ts
|
|
34
|
-
var
|
|
35
|
-
var
|
|
34
|
+
var fs4 = __toESM(require("fs/promises"));
|
|
35
|
+
var path5 = __toESM(require("path"));
|
|
36
36
|
|
|
37
37
|
// node_modules/.pnpm/fractional-indexing@3.2.0/node_modules/fractional-indexing/src/index.js
|
|
38
38
|
var BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
@@ -263,23 +263,34 @@ function extractNumericId(filenameOrId) {
|
|
|
263
263
|
const match = filenameOrId.match(/^(\d+)(?:-|$)/);
|
|
264
264
|
return match ? parseInt(match[1], 10) : null;
|
|
265
265
|
}
|
|
266
|
+
var DEFAULT_COLUMNS = [
|
|
267
|
+
{ id: "backlog", name: "Backlog", color: "#6b7280" },
|
|
268
|
+
{ id: "todo", name: "To Do", color: "#3b82f6" },
|
|
269
|
+
{ id: "in-progress", name: "In Progress", color: "#f59e0b" },
|
|
270
|
+
{ id: "review", name: "Review", color: "#8b5cf6" },
|
|
271
|
+
{ id: "done", name: "Done", color: "#22c55e" }
|
|
272
|
+
];
|
|
266
273
|
|
|
267
274
|
// src/shared/config.ts
|
|
268
275
|
var fs = __toESM(require("fs"));
|
|
269
276
|
var path = __toESM(require("path"));
|
|
277
|
+
var DEFAULT_BOARD_CONFIG = {
|
|
278
|
+
name: "Default",
|
|
279
|
+
columns: [...DEFAULT_COLUMNS],
|
|
280
|
+
nextCardId: 1,
|
|
281
|
+
defaultStatus: "backlog",
|
|
282
|
+
defaultPriority: "medium"
|
|
283
|
+
};
|
|
270
284
|
var DEFAULT_CONFIG = {
|
|
285
|
+
version: 2,
|
|
286
|
+
boards: {
|
|
287
|
+
default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] }
|
|
288
|
+
},
|
|
289
|
+
defaultBoard: "default",
|
|
271
290
|
featuresDirectory: ".kanban",
|
|
291
|
+
aiAgent: "claude",
|
|
272
292
|
defaultPriority: "medium",
|
|
273
293
|
defaultStatus: "backlog",
|
|
274
|
-
columns: [
|
|
275
|
-
{ id: "backlog", name: "Backlog", color: "#6b7280" },
|
|
276
|
-
{ id: "todo", name: "To Do", color: "#3b82f6" },
|
|
277
|
-
{ id: "in-progress", name: "In Progress", color: "#f59e0b" },
|
|
278
|
-
{ id: "review", name: "Review", color: "#8b5cf6" },
|
|
279
|
-
{ id: "done", name: "Done", color: "#22c55e" }
|
|
280
|
-
],
|
|
281
|
-
aiAgent: "claude",
|
|
282
|
-
nextCardId: 1,
|
|
283
294
|
showPriorityBadges: true,
|
|
284
295
|
showAssignee: true,
|
|
285
296
|
showDueDate: true,
|
|
@@ -293,12 +304,65 @@ var CONFIG_FILENAME = ".kanban.json";
|
|
|
293
304
|
function configPath(workspaceRoot) {
|
|
294
305
|
return path.join(workspaceRoot, CONFIG_FILENAME);
|
|
295
306
|
}
|
|
307
|
+
function migrateConfigV1ToV2(raw) {
|
|
308
|
+
const v1Defaults = {
|
|
309
|
+
featuresDirectory: ".kanban",
|
|
310
|
+
defaultPriority: "medium",
|
|
311
|
+
defaultStatus: "backlog",
|
|
312
|
+
columns: [...DEFAULT_COLUMNS],
|
|
313
|
+
aiAgent: "claude",
|
|
314
|
+
nextCardId: 1,
|
|
315
|
+
showPriorityBadges: true,
|
|
316
|
+
showAssignee: true,
|
|
317
|
+
showDueDate: true,
|
|
318
|
+
showLabels: true,
|
|
319
|
+
showBuildWithAI: true,
|
|
320
|
+
showFileName: false,
|
|
321
|
+
compactMode: false,
|
|
322
|
+
markdownEditorMode: false
|
|
323
|
+
};
|
|
324
|
+
const v1 = { ...v1Defaults, ...raw };
|
|
325
|
+
return {
|
|
326
|
+
version: 2,
|
|
327
|
+
boards: {
|
|
328
|
+
default: {
|
|
329
|
+
name: "Default",
|
|
330
|
+
columns: v1.columns,
|
|
331
|
+
nextCardId: v1.nextCardId,
|
|
332
|
+
defaultStatus: v1.defaultStatus,
|
|
333
|
+
defaultPriority: v1.defaultPriority
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
defaultBoard: "default",
|
|
337
|
+
featuresDirectory: v1.featuresDirectory,
|
|
338
|
+
aiAgent: v1.aiAgent,
|
|
339
|
+
defaultPriority: v1.defaultPriority,
|
|
340
|
+
defaultStatus: v1.defaultStatus,
|
|
341
|
+
showPriorityBadges: v1.showPriorityBadges,
|
|
342
|
+
showAssignee: v1.showAssignee,
|
|
343
|
+
showDueDate: v1.showDueDate,
|
|
344
|
+
showLabels: v1.showLabels,
|
|
345
|
+
showBuildWithAI: v1.showBuildWithAI,
|
|
346
|
+
showFileName: v1.showFileName,
|
|
347
|
+
compactMode: v1.compactMode,
|
|
348
|
+
markdownEditorMode: v1.markdownEditorMode
|
|
349
|
+
};
|
|
350
|
+
}
|
|
296
351
|
function readConfig(workspaceRoot) {
|
|
297
352
|
const filePath = configPath(workspaceRoot);
|
|
298
|
-
const defaults = { ...DEFAULT_CONFIG, columns: [...
|
|
353
|
+
const defaults = { ...DEFAULT_CONFIG, boards: { default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] } } };
|
|
299
354
|
try {
|
|
300
355
|
const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
301
|
-
|
|
356
|
+
if (!raw.version || raw.version === 1) {
|
|
357
|
+
const v2 = migrateConfigV1ToV2(raw);
|
|
358
|
+
writeConfig(workspaceRoot, v2);
|
|
359
|
+
return v2;
|
|
360
|
+
}
|
|
361
|
+
const config = { ...defaults, ...raw };
|
|
362
|
+
if (!config.boards || Object.keys(config.boards).length === 0) {
|
|
363
|
+
config.boards = defaults.boards;
|
|
364
|
+
}
|
|
365
|
+
return config;
|
|
302
366
|
} catch {
|
|
303
367
|
return defaults;
|
|
304
368
|
}
|
|
@@ -307,19 +371,39 @@ function writeConfig(workspaceRoot, config) {
|
|
|
307
371
|
const filePath = configPath(workspaceRoot);
|
|
308
372
|
fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
309
373
|
}
|
|
310
|
-
function
|
|
374
|
+
function getBoardConfig(workspaceRoot, boardId) {
|
|
375
|
+
const config = readConfig(workspaceRoot);
|
|
376
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
377
|
+
const board = config.boards[resolvedId];
|
|
378
|
+
if (!board) {
|
|
379
|
+
throw new Error(`Board '${resolvedId}' not found`);
|
|
380
|
+
}
|
|
381
|
+
return board;
|
|
382
|
+
}
|
|
383
|
+
function allocateCardId(workspaceRoot, boardId) {
|
|
311
384
|
const config = readConfig(workspaceRoot);
|
|
312
|
-
const
|
|
313
|
-
|
|
385
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
386
|
+
const board = config.boards[resolvedId];
|
|
387
|
+
if (!board) {
|
|
388
|
+
throw new Error(`Board '${resolvedId}' not found`);
|
|
389
|
+
}
|
|
390
|
+
const id = board.nextCardId;
|
|
391
|
+
board.nextCardId = id + 1;
|
|
392
|
+
writeConfig(workspaceRoot, config);
|
|
314
393
|
return id;
|
|
315
394
|
}
|
|
316
|
-
function syncCardIdCounter(workspaceRoot, existingIds) {
|
|
395
|
+
function syncCardIdCounter(workspaceRoot, boardId, existingIds) {
|
|
317
396
|
if (existingIds.length === 0)
|
|
318
397
|
return;
|
|
319
398
|
const maxId = Math.max(...existingIds);
|
|
320
399
|
const config = readConfig(workspaceRoot);
|
|
321
|
-
|
|
322
|
-
|
|
400
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
401
|
+
const board = config.boards[resolvedId];
|
|
402
|
+
if (!board)
|
|
403
|
+
return;
|
|
404
|
+
if (board.nextCardId <= maxId) {
|
|
405
|
+
board.nextCardId = maxId + 1;
|
|
406
|
+
writeConfig(workspaceRoot, config);
|
|
323
407
|
}
|
|
324
408
|
}
|
|
325
409
|
function configToSettings(config) {
|
|
@@ -537,30 +621,203 @@ async function fileExists(filePath) {
|
|
|
537
621
|
}
|
|
538
622
|
}
|
|
539
623
|
|
|
624
|
+
// src/sdk/migration.ts
|
|
625
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
626
|
+
var path4 = __toESM(require("path"));
|
|
627
|
+
async function migrateFileSystemToMultiBoard(featuresDir) {
|
|
628
|
+
const boardsDir = path4.join(featuresDir, "boards");
|
|
629
|
+
const defaultBoardDir = path4.join(boardsDir, "default");
|
|
630
|
+
try {
|
|
631
|
+
await fs3.access(boardsDir);
|
|
632
|
+
return;
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
await fs3.mkdir(defaultBoardDir, { recursive: true });
|
|
636
|
+
let entries;
|
|
637
|
+
try {
|
|
638
|
+
entries = await fs3.readdir(featuresDir, { withFileTypes: true });
|
|
639
|
+
} catch {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
for (const entry of entries) {
|
|
643
|
+
if (!entry.isDirectory())
|
|
644
|
+
continue;
|
|
645
|
+
if (entry.name === "boards" || entry.name.startsWith("."))
|
|
646
|
+
continue;
|
|
647
|
+
const src = path4.join(featuresDir, entry.name);
|
|
648
|
+
const dest = path4.join(defaultBoardDir, entry.name);
|
|
649
|
+
await fs3.rename(src, dest);
|
|
650
|
+
}
|
|
651
|
+
const rootMdFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".md"));
|
|
652
|
+
if (rootMdFiles.length > 0) {
|
|
653
|
+
const backlogDir = path4.join(defaultBoardDir, "backlog");
|
|
654
|
+
await fs3.mkdir(backlogDir, { recursive: true });
|
|
655
|
+
for (const file of rootMdFiles) {
|
|
656
|
+
await fs3.rename(
|
|
657
|
+
path4.join(featuresDir, file.name),
|
|
658
|
+
path4.join(backlogDir, file.name)
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
540
664
|
// src/sdk/KanbanSDK.ts
|
|
541
665
|
var KanbanSDK = class {
|
|
542
666
|
constructor(featuresDir) {
|
|
543
667
|
this.featuresDir = featuresDir;
|
|
668
|
+
this._migrated = false;
|
|
544
669
|
}
|
|
545
670
|
get workspaceRoot() {
|
|
546
|
-
return
|
|
671
|
+
return path5.dirname(this.featuresDir);
|
|
672
|
+
}
|
|
673
|
+
// --- Board resolution helpers ---
|
|
674
|
+
_resolveBoardId(boardId) {
|
|
675
|
+
const config = readConfig(this.workspaceRoot);
|
|
676
|
+
return boardId || config.defaultBoard;
|
|
677
|
+
}
|
|
678
|
+
_boardDir(boardId) {
|
|
679
|
+
const resolvedId = this._resolveBoardId(boardId);
|
|
680
|
+
return path5.join(this.featuresDir, "boards", resolvedId);
|
|
681
|
+
}
|
|
682
|
+
_isCompletedStatus(status, boardId) {
|
|
683
|
+
const config = readConfig(this.workspaceRoot);
|
|
684
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
685
|
+
const board = config.boards[resolvedId];
|
|
686
|
+
if (!board || board.columns.length === 0)
|
|
687
|
+
return status === "done";
|
|
688
|
+
return board.columns[board.columns.length - 1].id === status;
|
|
689
|
+
}
|
|
690
|
+
async _ensureMigrated() {
|
|
691
|
+
if (this._migrated)
|
|
692
|
+
return;
|
|
693
|
+
await migrateFileSystemToMultiBoard(this.featuresDir);
|
|
694
|
+
this._migrated = true;
|
|
547
695
|
}
|
|
548
696
|
async init() {
|
|
549
|
-
await
|
|
697
|
+
await this._ensureMigrated();
|
|
698
|
+
const boardDir = this._boardDir();
|
|
699
|
+
await ensureDirectories(boardDir);
|
|
700
|
+
}
|
|
701
|
+
// --- Board management ---
|
|
702
|
+
listBoards() {
|
|
703
|
+
const config = readConfig(this.workspaceRoot);
|
|
704
|
+
return Object.entries(config.boards).map(([id, board]) => ({
|
|
705
|
+
id,
|
|
706
|
+
name: board.name,
|
|
707
|
+
description: board.description
|
|
708
|
+
}));
|
|
709
|
+
}
|
|
710
|
+
createBoard(id, name, options) {
|
|
711
|
+
const config = readConfig(this.workspaceRoot);
|
|
712
|
+
if (config.boards[id]) {
|
|
713
|
+
throw new Error(`Board already exists: ${id}`);
|
|
714
|
+
}
|
|
715
|
+
const columns = options?.columns || [...config.boards[config.defaultBoard]?.columns || [
|
|
716
|
+
{ id: "backlog", name: "Backlog", color: "#6b7280" },
|
|
717
|
+
{ id: "todo", name: "To Do", color: "#3b82f6" },
|
|
718
|
+
{ id: "in-progress", name: "In Progress", color: "#f59e0b" },
|
|
719
|
+
{ id: "review", name: "Review", color: "#8b5cf6" },
|
|
720
|
+
{ id: "done", name: "Done", color: "#22c55e" }
|
|
721
|
+
]];
|
|
722
|
+
config.boards[id] = {
|
|
723
|
+
name,
|
|
724
|
+
description: options?.description,
|
|
725
|
+
columns,
|
|
726
|
+
nextCardId: 1,
|
|
727
|
+
defaultStatus: options?.defaultStatus || columns[0]?.id || "backlog",
|
|
728
|
+
defaultPriority: options?.defaultPriority || config.defaultPriority
|
|
729
|
+
};
|
|
730
|
+
writeConfig(this.workspaceRoot, config);
|
|
731
|
+
return { id, name, description: options?.description };
|
|
732
|
+
}
|
|
733
|
+
async deleteBoard(boardId) {
|
|
734
|
+
const config = readConfig(this.workspaceRoot);
|
|
735
|
+
if (!config.boards[boardId]) {
|
|
736
|
+
throw new Error(`Board not found: ${boardId}`);
|
|
737
|
+
}
|
|
738
|
+
if (config.defaultBoard === boardId) {
|
|
739
|
+
throw new Error(`Cannot delete the default board: ${boardId}`);
|
|
740
|
+
}
|
|
741
|
+
const cards = await this.listCards(void 0, boardId);
|
|
742
|
+
if (cards.length > 0) {
|
|
743
|
+
throw new Error(`Cannot delete board "${boardId}": ${cards.length} card(s) still exist`);
|
|
744
|
+
}
|
|
745
|
+
const boardDir = this._boardDir(boardId);
|
|
746
|
+
try {
|
|
747
|
+
await fs4.rm(boardDir, { recursive: true });
|
|
748
|
+
} catch {
|
|
749
|
+
}
|
|
750
|
+
delete config.boards[boardId];
|
|
751
|
+
writeConfig(this.workspaceRoot, config);
|
|
752
|
+
}
|
|
753
|
+
getBoard(boardId) {
|
|
754
|
+
return getBoardConfig(this.workspaceRoot, boardId);
|
|
755
|
+
}
|
|
756
|
+
updateBoard(boardId, updates) {
|
|
757
|
+
const config = readConfig(this.workspaceRoot);
|
|
758
|
+
const board = config.boards[boardId];
|
|
759
|
+
if (!board) {
|
|
760
|
+
throw new Error(`Board not found: ${boardId}`);
|
|
761
|
+
}
|
|
762
|
+
if (updates.name !== void 0)
|
|
763
|
+
board.name = updates.name;
|
|
764
|
+
if (updates.description !== void 0)
|
|
765
|
+
board.description = updates.description;
|
|
766
|
+
if (updates.columns !== void 0)
|
|
767
|
+
board.columns = updates.columns;
|
|
768
|
+
if (updates.defaultStatus !== void 0)
|
|
769
|
+
board.defaultStatus = updates.defaultStatus;
|
|
770
|
+
if (updates.defaultPriority !== void 0)
|
|
771
|
+
board.defaultPriority = updates.defaultPriority;
|
|
772
|
+
writeConfig(this.workspaceRoot, config);
|
|
773
|
+
return board;
|
|
774
|
+
}
|
|
775
|
+
async transferCard(cardId, fromBoardId, toBoardId, targetStatus) {
|
|
776
|
+
const toBoardDir = this._boardDir(toBoardId);
|
|
777
|
+
const config = readConfig(this.workspaceRoot);
|
|
778
|
+
if (!config.boards[fromBoardId])
|
|
779
|
+
throw new Error(`Board not found: ${fromBoardId}`);
|
|
780
|
+
if (!config.boards[toBoardId])
|
|
781
|
+
throw new Error(`Board not found: ${toBoardId}`);
|
|
782
|
+
const card = await this.getCard(cardId, fromBoardId);
|
|
783
|
+
if (!card)
|
|
784
|
+
throw new Error(`Card not found: ${cardId} in board ${fromBoardId}`);
|
|
785
|
+
const toBoard = config.boards[toBoardId];
|
|
786
|
+
const newStatus = targetStatus || toBoard.defaultStatus || toBoard.columns[0]?.id || "backlog";
|
|
787
|
+
const targetDir = path5.join(toBoardDir, newStatus);
|
|
788
|
+
await fs4.mkdir(targetDir, { recursive: true });
|
|
789
|
+
const oldPath = card.filePath;
|
|
790
|
+
const filename = path5.basename(oldPath);
|
|
791
|
+
const newPath = path5.join(targetDir, filename);
|
|
792
|
+
await fs4.rename(oldPath, newPath);
|
|
793
|
+
card.status = newStatus;
|
|
794
|
+
card.boardId = toBoardId;
|
|
795
|
+
card.filePath = newPath;
|
|
796
|
+
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
797
|
+
card.completedAt = this._isCompletedStatus(newStatus, toBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
798
|
+
const targetCards = await this.listCards(void 0, toBoardId);
|
|
799
|
+
const cardsInStatus = targetCards.filter((c) => c.status === newStatus && c.id !== cardId).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
|
|
800
|
+
const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
|
|
801
|
+
card.order = generateKeyBetween(lastOrder, null);
|
|
802
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
803
|
+
return card;
|
|
550
804
|
}
|
|
551
805
|
// --- Card CRUD ---
|
|
552
|
-
async listCards(columns) {
|
|
553
|
-
await
|
|
806
|
+
async listCards(columns, boardId) {
|
|
807
|
+
await this._ensureMigrated();
|
|
808
|
+
const boardDir = this._boardDir(boardId);
|
|
809
|
+
const resolvedBoardId = this._resolveBoardId(boardId);
|
|
810
|
+
await ensureDirectories(boardDir);
|
|
554
811
|
if (columns) {
|
|
555
|
-
await ensureStatusSubfolders(
|
|
812
|
+
await ensureStatusSubfolders(boardDir, columns);
|
|
556
813
|
}
|
|
557
814
|
try {
|
|
558
|
-
const rootFiles = await this._readMdFiles(
|
|
815
|
+
const rootFiles = await this._readMdFiles(boardDir);
|
|
559
816
|
for (const filePath of rootFiles) {
|
|
560
817
|
try {
|
|
561
818
|
const card = await this._loadCard(filePath);
|
|
562
819
|
if (card) {
|
|
563
|
-
await moveFeatureFile(filePath,
|
|
820
|
+
await moveFeatureFile(filePath, boardDir, card.status, card.attachments);
|
|
564
821
|
}
|
|
565
822
|
} catch {
|
|
566
823
|
}
|
|
@@ -568,26 +825,33 @@ var KanbanSDK = class {
|
|
|
568
825
|
} catch {
|
|
569
826
|
}
|
|
570
827
|
const cards = [];
|
|
571
|
-
|
|
828
|
+
let entries;
|
|
829
|
+
try {
|
|
830
|
+
entries = await fs4.readdir(boardDir, { withFileTypes: true });
|
|
831
|
+
} catch {
|
|
832
|
+
return [];
|
|
833
|
+
}
|
|
572
834
|
for (const entry of entries) {
|
|
573
835
|
if (!entry.isDirectory() || entry.name.startsWith("."))
|
|
574
836
|
continue;
|
|
575
|
-
const subdir =
|
|
837
|
+
const subdir = path5.join(boardDir, entry.name);
|
|
576
838
|
try {
|
|
577
839
|
const mdFiles = await this._readMdFiles(subdir);
|
|
578
840
|
for (const filePath of mdFiles) {
|
|
579
841
|
const card = await this._loadCard(filePath);
|
|
580
|
-
if (card)
|
|
842
|
+
if (card) {
|
|
843
|
+
card.boardId = resolvedBoardId;
|
|
581
844
|
cards.push(card);
|
|
845
|
+
}
|
|
582
846
|
}
|
|
583
847
|
} catch {
|
|
584
848
|
}
|
|
585
849
|
}
|
|
586
850
|
for (const card of cards) {
|
|
587
|
-
const pathStatus = getStatusFromPath(card.filePath,
|
|
851
|
+
const pathStatus = getStatusFromPath(card.filePath, boardDir);
|
|
588
852
|
if (pathStatus !== null && pathStatus !== card.status) {
|
|
589
853
|
try {
|
|
590
|
-
card.filePath = await moveFeatureFile(card.filePath,
|
|
854
|
+
card.filePath = await moveFeatureFile(card.filePath, boardDir, card.status, card.attachments);
|
|
591
855
|
} catch {
|
|
592
856
|
}
|
|
593
857
|
}
|
|
@@ -605,65 +869,72 @@ var KanbanSDK = class {
|
|
|
605
869
|
const keys = generateNKeysBetween(null, null, columnCards.length);
|
|
606
870
|
for (let i = 0; i < columnCards.length; i++) {
|
|
607
871
|
columnCards[i].order = keys[i];
|
|
608
|
-
await
|
|
872
|
+
await fs4.writeFile(columnCards[i].filePath, serializeFeature(columnCards[i]), "utf-8");
|
|
609
873
|
}
|
|
610
874
|
}
|
|
611
875
|
}
|
|
612
876
|
const numericIds = cards.map((c) => parseInt(c.id, 10)).filter((n) => !Number.isNaN(n));
|
|
613
877
|
if (numericIds.length > 0) {
|
|
614
|
-
syncCardIdCounter(
|
|
878
|
+
syncCardIdCounter(this.workspaceRoot, resolvedBoardId, numericIds);
|
|
615
879
|
}
|
|
616
880
|
return cards.sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
|
|
617
881
|
}
|
|
618
|
-
async getCard(cardId) {
|
|
619
|
-
const cards = await this.listCards();
|
|
882
|
+
async getCard(cardId, boardId) {
|
|
883
|
+
const cards = await this.listCards(void 0, boardId);
|
|
620
884
|
return cards.find((c) => c.id === cardId) || null;
|
|
621
885
|
}
|
|
622
886
|
async createCard(data) {
|
|
623
|
-
await
|
|
624
|
-
const
|
|
625
|
-
const
|
|
887
|
+
await this._ensureMigrated();
|
|
888
|
+
const resolvedBoardId = this._resolveBoardId(data.boardId);
|
|
889
|
+
const boardDir = this._boardDir(resolvedBoardId);
|
|
890
|
+
await ensureDirectories(boardDir);
|
|
891
|
+
const config = readConfig(this.workspaceRoot);
|
|
892
|
+
const board = config.boards[resolvedBoardId];
|
|
893
|
+
const status = data.status || board?.defaultStatus || config.defaultStatus || "backlog";
|
|
894
|
+
const priority = data.priority || board?.defaultPriority || config.defaultPriority || "medium";
|
|
626
895
|
const title = getTitleFromContent(data.content);
|
|
627
|
-
const
|
|
628
|
-
const numericId = allocateCardId(workspaceRoot);
|
|
896
|
+
const numericId = allocateCardId(this.workspaceRoot, resolvedBoardId);
|
|
629
897
|
const filename = generateFeatureFilename(numericId, title);
|
|
630
898
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
631
|
-
const cards = await this.listCards();
|
|
899
|
+
const cards = await this.listCards(void 0, resolvedBoardId);
|
|
632
900
|
const cardsInStatus = cards.filter((c) => c.status === status).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
|
|
633
901
|
const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
|
|
634
902
|
const card = {
|
|
635
903
|
id: String(numericId),
|
|
904
|
+
boardId: resolvedBoardId,
|
|
636
905
|
status,
|
|
637
906
|
priority,
|
|
638
907
|
assignee: data.assignee ?? null,
|
|
639
908
|
dueDate: data.dueDate ?? null,
|
|
640
909
|
created: now,
|
|
641
910
|
modified: now,
|
|
642
|
-
completedAt: status
|
|
911
|
+
completedAt: this._isCompletedStatus(status, resolvedBoardId) ? now : null,
|
|
643
912
|
labels: data.labels || [],
|
|
644
913
|
attachments: data.attachments || [],
|
|
645
914
|
comments: [],
|
|
646
915
|
order: generateKeyBetween(lastOrder, null),
|
|
647
916
|
content: data.content,
|
|
648
|
-
filePath: getFeatureFilePath(
|
|
917
|
+
filePath: getFeatureFilePath(boardDir, status, filename)
|
|
649
918
|
};
|
|
650
|
-
await
|
|
651
|
-
await
|
|
919
|
+
await fs4.mkdir(path5.dirname(card.filePath), { recursive: true });
|
|
920
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
652
921
|
return card;
|
|
653
922
|
}
|
|
654
|
-
async updateCard(cardId, updates) {
|
|
655
|
-
const card = await this.getCard(cardId);
|
|
923
|
+
async updateCard(cardId, updates, boardId) {
|
|
924
|
+
const card = await this.getCard(cardId, boardId);
|
|
656
925
|
if (!card)
|
|
657
926
|
throw new Error(`Card not found: ${cardId}`);
|
|
927
|
+
const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
|
|
928
|
+
const boardDir = this._boardDir(resolvedBoardId);
|
|
658
929
|
const oldStatus = card.status;
|
|
659
930
|
const oldTitle = getTitleFromContent(card.content);
|
|
660
|
-
const { filePath: _fp, id: _id, ...safeUpdates } = updates;
|
|
931
|
+
const { filePath: _fp, id: _id, boardId: _bid, ...safeUpdates } = updates;
|
|
661
932
|
Object.assign(card, safeUpdates);
|
|
662
933
|
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
663
934
|
if (oldStatus !== card.status) {
|
|
664
|
-
card.completedAt = card.status
|
|
935
|
+
card.completedAt = this._isCompletedStatus(card.status, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
665
936
|
}
|
|
666
|
-
await
|
|
937
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
667
938
|
const newTitle = getTitleFromContent(card.content);
|
|
668
939
|
const numericId = extractNumericId(card.id);
|
|
669
940
|
if (numericId !== null && newTitle !== oldTitle) {
|
|
@@ -671,46 +942,48 @@ var KanbanSDK = class {
|
|
|
671
942
|
card.filePath = await renameFeatureFile(card.filePath, newFilename);
|
|
672
943
|
}
|
|
673
944
|
if (oldStatus !== card.status) {
|
|
674
|
-
const newPath = await moveFeatureFile(card.filePath,
|
|
945
|
+
const newPath = await moveFeatureFile(card.filePath, boardDir, card.status, card.attachments);
|
|
675
946
|
card.filePath = newPath;
|
|
676
947
|
}
|
|
677
948
|
return card;
|
|
678
949
|
}
|
|
679
|
-
async moveCard(cardId, newStatus, position) {
|
|
680
|
-
const cards = await this.listCards();
|
|
950
|
+
async moveCard(cardId, newStatus, position, boardId) {
|
|
951
|
+
const cards = await this.listCards(void 0, boardId);
|
|
681
952
|
const card = cards.find((c) => c.id === cardId);
|
|
682
953
|
if (!card)
|
|
683
954
|
throw new Error(`Card not found: ${cardId}`);
|
|
955
|
+
const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
|
|
956
|
+
const boardDir = this._boardDir(resolvedBoardId);
|
|
684
957
|
const oldStatus = card.status;
|
|
685
958
|
card.status = newStatus;
|
|
686
959
|
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
687
960
|
if (oldStatus !== newStatus) {
|
|
688
|
-
card.completedAt = newStatus
|
|
961
|
+
card.completedAt = this._isCompletedStatus(newStatus, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
689
962
|
}
|
|
690
963
|
const targetColumnCards = cards.filter((c) => c.status === newStatus && c.id !== cardId).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
|
|
691
964
|
const pos = position !== void 0 ? Math.max(0, Math.min(position, targetColumnCards.length)) : targetColumnCards.length;
|
|
692
965
|
const before = pos > 0 ? targetColumnCards[pos - 1].order : null;
|
|
693
966
|
const after = pos < targetColumnCards.length ? targetColumnCards[pos].order : null;
|
|
694
967
|
card.order = generateKeyBetween(before, after);
|
|
695
|
-
await
|
|
968
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
696
969
|
if (oldStatus !== newStatus) {
|
|
697
|
-
const newPath = await moveFeatureFile(card.filePath,
|
|
970
|
+
const newPath = await moveFeatureFile(card.filePath, boardDir, newStatus, card.attachments);
|
|
698
971
|
card.filePath = newPath;
|
|
699
972
|
}
|
|
700
973
|
return card;
|
|
701
974
|
}
|
|
702
|
-
async deleteCard(cardId) {
|
|
703
|
-
const card = await this.getCard(cardId);
|
|
975
|
+
async deleteCard(cardId, boardId) {
|
|
976
|
+
const card = await this.getCard(cardId, boardId);
|
|
704
977
|
if (!card)
|
|
705
978
|
throw new Error(`Card not found: ${cardId}`);
|
|
706
|
-
await
|
|
979
|
+
await fs4.unlink(card.filePath);
|
|
707
980
|
}
|
|
708
|
-
async getCardsByStatus(status) {
|
|
709
|
-
const cards = await this.listCards();
|
|
981
|
+
async getCardsByStatus(status, boardId) {
|
|
982
|
+
const cards = await this.listCards(void 0, boardId);
|
|
710
983
|
return cards.filter((c) => c.status === status);
|
|
711
984
|
}
|
|
712
|
-
async getUniqueAssignees() {
|
|
713
|
-
const cards = await this.listCards();
|
|
985
|
+
async getUniqueAssignees(boardId) {
|
|
986
|
+
const cards = await this.listCards(void 0, boardId);
|
|
714
987
|
const assignees = /* @__PURE__ */ new Set();
|
|
715
988
|
for (const c of cards) {
|
|
716
989
|
if (c.assignee)
|
|
@@ -718,8 +991,8 @@ var KanbanSDK = class {
|
|
|
718
991
|
}
|
|
719
992
|
return [...assignees].sort();
|
|
720
993
|
}
|
|
721
|
-
async getUniqueLabels() {
|
|
722
|
-
const cards = await this.listCards();
|
|
994
|
+
async getUniqueLabels(boardId) {
|
|
995
|
+
const cards = await this.listCards(void 0, boardId);
|
|
723
996
|
const labels = /* @__PURE__ */ new Set();
|
|
724
997
|
for (const c of cards) {
|
|
725
998
|
for (const l of c.labels)
|
|
@@ -728,48 +1001,48 @@ var KanbanSDK = class {
|
|
|
728
1001
|
return [...labels].sort();
|
|
729
1002
|
}
|
|
730
1003
|
// --- Attachment management ---
|
|
731
|
-
async addAttachment(cardId, sourcePath) {
|
|
732
|
-
const card = await this.getCard(cardId);
|
|
1004
|
+
async addAttachment(cardId, sourcePath, boardId) {
|
|
1005
|
+
const card = await this.getCard(cardId, boardId);
|
|
733
1006
|
if (!card)
|
|
734
1007
|
throw new Error(`Card not found: ${cardId}`);
|
|
735
|
-
const fileName =
|
|
736
|
-
const cardDir =
|
|
737
|
-
const destPath =
|
|
738
|
-
const sourceDir =
|
|
1008
|
+
const fileName = path5.basename(sourcePath);
|
|
1009
|
+
const cardDir = path5.dirname(card.filePath);
|
|
1010
|
+
const destPath = path5.join(cardDir, fileName);
|
|
1011
|
+
const sourceDir = path5.dirname(path5.resolve(sourcePath));
|
|
739
1012
|
if (sourceDir !== cardDir) {
|
|
740
|
-
await
|
|
1013
|
+
await fs4.copyFile(path5.resolve(sourcePath), destPath);
|
|
741
1014
|
}
|
|
742
1015
|
if (!card.attachments.includes(fileName)) {
|
|
743
1016
|
card.attachments.push(fileName);
|
|
744
1017
|
}
|
|
745
1018
|
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
746
|
-
await
|
|
1019
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
747
1020
|
return card;
|
|
748
1021
|
}
|
|
749
|
-
async removeAttachment(cardId, attachment) {
|
|
750
|
-
const card = await this.getCard(cardId);
|
|
1022
|
+
async removeAttachment(cardId, attachment, boardId) {
|
|
1023
|
+
const card = await this.getCard(cardId, boardId);
|
|
751
1024
|
if (!card)
|
|
752
1025
|
throw new Error(`Card not found: ${cardId}`);
|
|
753
1026
|
card.attachments = card.attachments.filter((a) => a !== attachment);
|
|
754
1027
|
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
755
|
-
await
|
|
1028
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
756
1029
|
return card;
|
|
757
1030
|
}
|
|
758
|
-
async listAttachments(cardId) {
|
|
759
|
-
const card = await this.getCard(cardId);
|
|
1031
|
+
async listAttachments(cardId, boardId) {
|
|
1032
|
+
const card = await this.getCard(cardId, boardId);
|
|
760
1033
|
if (!card)
|
|
761
1034
|
throw new Error(`Card not found: ${cardId}`);
|
|
762
1035
|
return card.attachments;
|
|
763
1036
|
}
|
|
764
1037
|
// --- Comment management ---
|
|
765
|
-
async listComments(cardId) {
|
|
766
|
-
const card = await this.getCard(cardId);
|
|
1038
|
+
async listComments(cardId, boardId) {
|
|
1039
|
+
const card = await this.getCard(cardId, boardId);
|
|
767
1040
|
if (!card)
|
|
768
1041
|
throw new Error(`Card not found: ${cardId}`);
|
|
769
1042
|
return card.comments || [];
|
|
770
1043
|
}
|
|
771
|
-
async addComment(cardId, author, content) {
|
|
772
|
-
const card = await this.getCard(cardId);
|
|
1044
|
+
async addComment(cardId, author, content, boardId) {
|
|
1045
|
+
const card = await this.getCard(cardId, boardId);
|
|
773
1046
|
if (!card)
|
|
774
1047
|
throw new Error(`Card not found: ${cardId}`);
|
|
775
1048
|
if (!card.comments)
|
|
@@ -786,11 +1059,11 @@ var KanbanSDK = class {
|
|
|
786
1059
|
};
|
|
787
1060
|
card.comments.push(comment);
|
|
788
1061
|
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
789
|
-
await
|
|
1062
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
790
1063
|
return card;
|
|
791
1064
|
}
|
|
792
|
-
async updateComment(cardId, commentId, content) {
|
|
793
|
-
const card = await this.getCard(cardId);
|
|
1065
|
+
async updateComment(cardId, commentId, content, boardId) {
|
|
1066
|
+
const card = await this.getCard(cardId, boardId);
|
|
794
1067
|
if (!card)
|
|
795
1068
|
throw new Error(`Card not found: ${cardId}`);
|
|
796
1069
|
const comment = (card.comments || []).find((c) => c.id === commentId);
|
|
@@ -798,34 +1071,45 @@ var KanbanSDK = class {
|
|
|
798
1071
|
throw new Error(`Comment not found: ${commentId}`);
|
|
799
1072
|
comment.content = content;
|
|
800
1073
|
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
801
|
-
await
|
|
1074
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
802
1075
|
return card;
|
|
803
1076
|
}
|
|
804
|
-
async deleteComment(cardId, commentId) {
|
|
805
|
-
const card = await this.getCard(cardId);
|
|
1077
|
+
async deleteComment(cardId, commentId, boardId) {
|
|
1078
|
+
const card = await this.getCard(cardId, boardId);
|
|
806
1079
|
if (!card)
|
|
807
1080
|
throw new Error(`Card not found: ${cardId}`);
|
|
808
1081
|
card.comments = (card.comments || []).filter((c) => c.id !== commentId);
|
|
809
1082
|
card.modified = (/* @__PURE__ */ new Date()).toISOString();
|
|
810
|
-
await
|
|
1083
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
811
1084
|
return card;
|
|
812
1085
|
}
|
|
813
|
-
// --- Column management ---
|
|
814
|
-
listColumns() {
|
|
815
|
-
|
|
1086
|
+
// --- Column management (board-scoped) ---
|
|
1087
|
+
listColumns(boardId) {
|
|
1088
|
+
const config = readConfig(this.workspaceRoot);
|
|
1089
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
1090
|
+
const board = config.boards[resolvedId];
|
|
1091
|
+
return board?.columns || [];
|
|
816
1092
|
}
|
|
817
|
-
addColumn(column) {
|
|
1093
|
+
addColumn(column, boardId) {
|
|
818
1094
|
const config = readConfig(this.workspaceRoot);
|
|
819
|
-
|
|
1095
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
1096
|
+
const board = config.boards[resolvedId];
|
|
1097
|
+
if (!board)
|
|
1098
|
+
throw new Error(`Board not found: ${resolvedId}`);
|
|
1099
|
+
if (board.columns.some((c) => c.id === column.id)) {
|
|
820
1100
|
throw new Error(`Column already exists: ${column.id}`);
|
|
821
1101
|
}
|
|
822
|
-
|
|
1102
|
+
board.columns.push(column);
|
|
823
1103
|
writeConfig(this.workspaceRoot, config);
|
|
824
|
-
return
|
|
1104
|
+
return board.columns;
|
|
825
1105
|
}
|
|
826
|
-
updateColumn(columnId, updates) {
|
|
1106
|
+
updateColumn(columnId, updates, boardId) {
|
|
827
1107
|
const config = readConfig(this.workspaceRoot);
|
|
828
|
-
const
|
|
1108
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
1109
|
+
const board = config.boards[resolvedId];
|
|
1110
|
+
if (!board)
|
|
1111
|
+
throw new Error(`Board not found: ${resolvedId}`);
|
|
1112
|
+
const col = board.columns.find((c) => c.id === columnId);
|
|
829
1113
|
if (!col)
|
|
830
1114
|
throw new Error(`Column not found: ${columnId}`);
|
|
831
1115
|
if (updates.name !== void 0)
|
|
@@ -833,37 +1117,45 @@ var KanbanSDK = class {
|
|
|
833
1117
|
if (updates.color !== void 0)
|
|
834
1118
|
col.color = updates.color;
|
|
835
1119
|
writeConfig(this.workspaceRoot, config);
|
|
836
|
-
return
|
|
1120
|
+
return board.columns;
|
|
837
1121
|
}
|
|
838
|
-
async removeColumn(columnId) {
|
|
1122
|
+
async removeColumn(columnId, boardId) {
|
|
839
1123
|
const config = readConfig(this.workspaceRoot);
|
|
840
|
-
const
|
|
1124
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
1125
|
+
const board = config.boards[resolvedId];
|
|
1126
|
+
if (!board)
|
|
1127
|
+
throw new Error(`Board not found: ${resolvedId}`);
|
|
1128
|
+
const idx = board.columns.findIndex((c) => c.id === columnId);
|
|
841
1129
|
if (idx === -1)
|
|
842
1130
|
throw new Error(`Column not found: ${columnId}`);
|
|
843
|
-
const cards = await this.listCards();
|
|
1131
|
+
const cards = await this.listCards(void 0, resolvedId);
|
|
844
1132
|
const cardsInColumn = cards.filter((c) => c.status === columnId);
|
|
845
1133
|
if (cardsInColumn.length > 0) {
|
|
846
1134
|
throw new Error(`Cannot remove column "${columnId}": ${cardsInColumn.length} card(s) still in this column`);
|
|
847
1135
|
}
|
|
848
|
-
|
|
1136
|
+
board.columns.splice(idx, 1);
|
|
849
1137
|
writeConfig(this.workspaceRoot, config);
|
|
850
|
-
return
|
|
1138
|
+
return board.columns;
|
|
851
1139
|
}
|
|
852
|
-
reorderColumns(columnIds) {
|
|
1140
|
+
reorderColumns(columnIds, boardId) {
|
|
853
1141
|
const config = readConfig(this.workspaceRoot);
|
|
854
|
-
const
|
|
1142
|
+
const resolvedId = boardId || config.defaultBoard;
|
|
1143
|
+
const board = config.boards[resolvedId];
|
|
1144
|
+
if (!board)
|
|
1145
|
+
throw new Error(`Board not found: ${resolvedId}`);
|
|
1146
|
+
const colMap = new Map(board.columns.map((c) => [c.id, c]));
|
|
855
1147
|
for (const id of columnIds) {
|
|
856
1148
|
if (!colMap.has(id))
|
|
857
1149
|
throw new Error(`Column not found: ${id}`);
|
|
858
1150
|
}
|
|
859
|
-
if (columnIds.length !==
|
|
1151
|
+
if (columnIds.length !== board.columns.length) {
|
|
860
1152
|
throw new Error("Must include all column IDs when reordering");
|
|
861
1153
|
}
|
|
862
|
-
|
|
1154
|
+
board.columns = columnIds.map((id) => colMap.get(id));
|
|
863
1155
|
writeConfig(this.workspaceRoot, config);
|
|
864
|
-
return
|
|
1156
|
+
return board.columns;
|
|
865
1157
|
}
|
|
866
|
-
// --- Settings management ---
|
|
1158
|
+
// --- Settings management (global) ---
|
|
867
1159
|
getSettings() {
|
|
868
1160
|
return configToSettings(readConfig(this.workspaceRoot));
|
|
869
1161
|
}
|
|
@@ -873,26 +1165,26 @@ var KanbanSDK = class {
|
|
|
873
1165
|
}
|
|
874
1166
|
// --- Private helpers ---
|
|
875
1167
|
async _readMdFiles(dir) {
|
|
876
|
-
const entries = await
|
|
877
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) =>
|
|
1168
|
+
const entries = await fs4.readdir(dir, { withFileTypes: true });
|
|
1169
|
+
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => path5.join(dir, e.name));
|
|
878
1170
|
}
|
|
879
1171
|
async _loadCard(filePath) {
|
|
880
|
-
const content = await
|
|
1172
|
+
const content = await fs4.readFile(filePath, "utf-8");
|
|
881
1173
|
return parseFeatureFile(content, filePath);
|
|
882
1174
|
}
|
|
883
1175
|
};
|
|
884
1176
|
|
|
885
1177
|
// src/standalone/webhooks.ts
|
|
886
|
-
var
|
|
887
|
-
var
|
|
1178
|
+
var fs5 = __toESM(require("fs"));
|
|
1179
|
+
var path6 = __toESM(require("path"));
|
|
888
1180
|
var crypto = __toESM(require("crypto"));
|
|
889
1181
|
var WEBHOOKS_FILENAME = ".kanban-webhooks.json";
|
|
890
1182
|
function webhooksPath(workspaceRoot) {
|
|
891
|
-
return
|
|
1183
|
+
return path6.join(workspaceRoot, WEBHOOKS_FILENAME);
|
|
892
1184
|
}
|
|
893
1185
|
function loadWebhooks(workspaceRoot) {
|
|
894
1186
|
try {
|
|
895
|
-
const raw =
|
|
1187
|
+
const raw = fs5.readFileSync(webhooksPath(workspaceRoot), "utf-8");
|
|
896
1188
|
const data = JSON.parse(raw);
|
|
897
1189
|
return Array.isArray(data) ? data : [];
|
|
898
1190
|
} catch {
|
|
@@ -900,7 +1192,7 @@ function loadWebhooks(workspaceRoot) {
|
|
|
900
1192
|
}
|
|
901
1193
|
}
|
|
902
1194
|
function saveWebhooks(workspaceRoot, webhooks) {
|
|
903
|
-
|
|
1195
|
+
fs5.writeFileSync(webhooksPath(workspaceRoot), JSON.stringify(webhooks, null, 2) + "\n", "utf-8");
|
|
904
1196
|
}
|
|
905
1197
|
function createWebhook(workspaceRoot, config) {
|
|
906
1198
|
const webhooks = loadWebhooks(workspaceRoot);
|
|
@@ -929,16 +1221,16 @@ async function findWorkspaceRoot(startDir) {
|
|
|
929
1221
|
let dir = startDir;
|
|
930
1222
|
while (true) {
|
|
931
1223
|
try {
|
|
932
|
-
await
|
|
1224
|
+
await fs6.access(path7.join(dir, ".git"));
|
|
933
1225
|
return dir;
|
|
934
1226
|
} catch {
|
|
935
1227
|
}
|
|
936
1228
|
try {
|
|
937
|
-
await
|
|
1229
|
+
await fs6.access(path7.join(dir, "package.json"));
|
|
938
1230
|
return dir;
|
|
939
1231
|
} catch {
|
|
940
1232
|
}
|
|
941
|
-
const parent =
|
|
1233
|
+
const parent = path7.dirname(dir);
|
|
942
1234
|
if (parent === dir)
|
|
943
1235
|
return startDir;
|
|
944
1236
|
dir = parent;
|
|
@@ -947,13 +1239,13 @@ async function findWorkspaceRoot(startDir) {
|
|
|
947
1239
|
async function resolveFeaturesDir() {
|
|
948
1240
|
const dirIndex = process.argv.indexOf("--dir");
|
|
949
1241
|
if (dirIndex !== -1 && process.argv[dirIndex + 1]) {
|
|
950
|
-
return
|
|
1242
|
+
return path7.resolve(process.argv[dirIndex + 1]);
|
|
951
1243
|
}
|
|
952
1244
|
if (process.env.KANBAN_FEATURES_DIR) {
|
|
953
|
-
return
|
|
1245
|
+
return path7.resolve(process.env.KANBAN_FEATURES_DIR);
|
|
954
1246
|
}
|
|
955
1247
|
const root = await findWorkspaceRoot(process.cwd());
|
|
956
|
-
return
|
|
1248
|
+
return path7.join(root, ".devtool", "features");
|
|
957
1249
|
}
|
|
958
1250
|
function getTitleFromContent2(content) {
|
|
959
1251
|
const match = content.match(/^#\s+(.+)$/m);
|
|
@@ -969,17 +1261,68 @@ async function main() {
|
|
|
969
1261
|
name: "kanban-lite",
|
|
970
1262
|
version: "1.0.0"
|
|
971
1263
|
});
|
|
1264
|
+
server.tool("list_boards", "List all kanban boards.", {}, async () => {
|
|
1265
|
+
const boards = sdk.listBoards();
|
|
1266
|
+
return { content: [{ type: "text", text: JSON.stringify(boards, null, 2) }] };
|
|
1267
|
+
});
|
|
1268
|
+
server.tool("create_board", "Create a new kanban board.", {
|
|
1269
|
+
id: import_zod.z.string().describe("Board ID (used in directory name)"),
|
|
1270
|
+
name: import_zod.z.string().describe("Display name"),
|
|
1271
|
+
description: import_zod.z.string().optional().describe("Board description"),
|
|
1272
|
+
columns: import_zod.z.array(import_zod.z.object({ id: import_zod.z.string(), name: import_zod.z.string(), color: import_zod.z.string() })).optional().describe("Board columns (defaults to standard columns)")
|
|
1273
|
+
}, async ({ id, name, description, columns }) => {
|
|
1274
|
+
try {
|
|
1275
|
+
const board = sdk.createBoard(id, name, { description, columns });
|
|
1276
|
+
return { content: [{ type: "text", text: JSON.stringify(board, null, 2) }] };
|
|
1277
|
+
} catch (err) {
|
|
1278
|
+
return { content: [{ type: "text", text: String(err) }], isError: true };
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1281
|
+
server.tool("get_board", "Get details of a specific board.", {
|
|
1282
|
+
boardId: import_zod.z.string().describe("Board ID")
|
|
1283
|
+
}, async ({ boardId }) => {
|
|
1284
|
+
try {
|
|
1285
|
+
const board = sdk.getBoard(boardId);
|
|
1286
|
+
return { content: [{ type: "text", text: JSON.stringify({ id: boardId, ...board }, null, 2) }] };
|
|
1287
|
+
} catch (err) {
|
|
1288
|
+
return { content: [{ type: "text", text: String(err) }], isError: true };
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
server.tool("delete_board", "Delete an empty kanban board.", {
|
|
1292
|
+
boardId: import_zod.z.string().describe("Board ID to delete")
|
|
1293
|
+
}, async ({ boardId }) => {
|
|
1294
|
+
try {
|
|
1295
|
+
await sdk.deleteBoard(boardId);
|
|
1296
|
+
return { content: [{ type: "text", text: `Deleted board: ${boardId}` }] };
|
|
1297
|
+
} catch (err) {
|
|
1298
|
+
return { content: [{ type: "text", text: String(err) }], isError: true };
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
server.tool("transfer_card", "Transfer a card from one board to another.", {
|
|
1302
|
+
cardId: import_zod.z.string().describe("Card ID"),
|
|
1303
|
+
fromBoard: import_zod.z.string().describe("Source board ID"),
|
|
1304
|
+
toBoard: import_zod.z.string().describe("Target board ID"),
|
|
1305
|
+
targetStatus: import_zod.z.string().optional().describe("Status in the target board (defaults to board default)")
|
|
1306
|
+
}, async ({ cardId, fromBoard, toBoard, targetStatus }) => {
|
|
1307
|
+
try {
|
|
1308
|
+
const card = await sdk.transferCard(cardId, fromBoard, toBoard, targetStatus);
|
|
1309
|
+
return { content: [{ type: "text", text: JSON.stringify(card, null, 2) }] };
|
|
1310
|
+
} catch (err) {
|
|
1311
|
+
return { content: [{ type: "text", text: String(err) }], isError: true };
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
972
1314
|
server.tool(
|
|
973
1315
|
"list_cards",
|
|
974
1316
|
"List all kanban cards. Optionally filter by status, priority, assignee, or label.",
|
|
975
1317
|
{
|
|
976
|
-
|
|
1318
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1319
|
+
status: import_zod.z.string().optional().describe("Filter by status"),
|
|
977
1320
|
priority: import_zod.z.enum(["critical", "high", "medium", "low"]).optional().describe("Filter by priority"),
|
|
978
1321
|
assignee: import_zod.z.string().optional().describe("Filter by assignee name"),
|
|
979
1322
|
label: import_zod.z.string().optional().describe("Filter by label")
|
|
980
1323
|
},
|
|
981
|
-
async ({ status, priority, assignee, label }) => {
|
|
982
|
-
let cards = await sdk.listCards();
|
|
1324
|
+
async ({ boardId, status, priority, assignee, label }) => {
|
|
1325
|
+
let cards = await sdk.listCards(void 0, boardId);
|
|
983
1326
|
if (status)
|
|
984
1327
|
cards = cards.filter((c) => c.status === status);
|
|
985
1328
|
if (priority)
|
|
@@ -1009,12 +1352,13 @@ async function main() {
|
|
|
1009
1352
|
"get_card",
|
|
1010
1353
|
"Get full details of a specific kanban card by ID. Supports partial ID matching.",
|
|
1011
1354
|
{
|
|
1355
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1012
1356
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)")
|
|
1013
1357
|
},
|
|
1014
|
-
async ({ cardId }) => {
|
|
1015
|
-
let card = await sdk.getCard(cardId);
|
|
1358
|
+
async ({ boardId, cardId }) => {
|
|
1359
|
+
let card = await sdk.getCard(cardId, boardId);
|
|
1016
1360
|
if (!card) {
|
|
1017
|
-
const all = await sdk.listCards();
|
|
1361
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1018
1362
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1019
1363
|
if (matches.length === 1) {
|
|
1020
1364
|
card = matches[0];
|
|
@@ -1042,23 +1386,25 @@ async function main() {
|
|
|
1042
1386
|
"create_card",
|
|
1043
1387
|
"Create a new kanban card. Returns the created card.",
|
|
1044
1388
|
{
|
|
1389
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1045
1390
|
title: import_zod.z.string().describe("Card title"),
|
|
1046
1391
|
body: import_zod.z.string().optional().describe("Card body/description (markdown)"),
|
|
1047
|
-
status: import_zod.z.
|
|
1392
|
+
status: import_zod.z.string().optional().describe("Initial status (default: backlog)"),
|
|
1048
1393
|
priority: import_zod.z.enum(["critical", "high", "medium", "low"]).optional().describe("Priority level (default: medium)"),
|
|
1049
1394
|
assignee: import_zod.z.string().optional().describe("Assignee name"),
|
|
1050
1395
|
dueDate: import_zod.z.string().optional().describe("Due date (ISO format or YYYY-MM-DD)"),
|
|
1051
1396
|
labels: import_zod.z.array(import_zod.z.string()).optional().describe("Labels/tags")
|
|
1052
1397
|
},
|
|
1053
|
-
async ({ title, body, status, priority, assignee, dueDate, labels }) => {
|
|
1398
|
+
async ({ boardId, title, body, status, priority, assignee, dueDate, labels }) => {
|
|
1054
1399
|
const content = `# ${title}${body ? "\n\n" + body : ""}`;
|
|
1055
1400
|
const card = await sdk.createCard({
|
|
1056
1401
|
content,
|
|
1057
|
-
status,
|
|
1402
|
+
status: status || void 0,
|
|
1058
1403
|
priority,
|
|
1059
1404
|
assignee: assignee || null,
|
|
1060
1405
|
dueDate: dueDate || null,
|
|
1061
|
-
labels: labels || []
|
|
1406
|
+
labels: labels || [],
|
|
1407
|
+
boardId
|
|
1062
1408
|
});
|
|
1063
1409
|
return {
|
|
1064
1410
|
content: [{
|
|
@@ -1072,19 +1418,20 @@ async function main() {
|
|
|
1072
1418
|
"update_card",
|
|
1073
1419
|
"Update fields of an existing kanban card. Only specified fields are changed.",
|
|
1074
1420
|
{
|
|
1421
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1075
1422
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)"),
|
|
1076
|
-
status: import_zod.z.
|
|
1423
|
+
status: import_zod.z.string().optional().describe("New status"),
|
|
1077
1424
|
priority: import_zod.z.enum(["critical", "high", "medium", "low"]).optional().describe("New priority"),
|
|
1078
1425
|
assignee: import_zod.z.string().optional().describe("New assignee"),
|
|
1079
1426
|
dueDate: import_zod.z.string().optional().describe("New due date"),
|
|
1080
1427
|
labels: import_zod.z.array(import_zod.z.string()).optional().describe("New labels (replaces existing)"),
|
|
1081
1428
|
content: import_zod.z.string().optional().describe("New markdown content (replaces existing body)")
|
|
1082
1429
|
},
|
|
1083
|
-
async ({ cardId, status, priority, assignee, dueDate, labels, content }) => {
|
|
1430
|
+
async ({ boardId, cardId, status, priority, assignee, dueDate, labels, content }) => {
|
|
1084
1431
|
let resolvedId = cardId;
|
|
1085
|
-
const card = await sdk.getCard(cardId);
|
|
1432
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1086
1433
|
if (!card) {
|
|
1087
|
-
const all = await sdk.listCards();
|
|
1434
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1088
1435
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1089
1436
|
if (matches.length === 1) {
|
|
1090
1437
|
resolvedId = matches[0].id;
|
|
@@ -1113,7 +1460,7 @@ async function main() {
|
|
|
1113
1460
|
updates.labels = labels;
|
|
1114
1461
|
if (content !== void 0)
|
|
1115
1462
|
updates.content = content;
|
|
1116
|
-
const updated = await sdk.updateCard(resolvedId, updates);
|
|
1463
|
+
const updated = await sdk.updateCard(resolvedId, updates, boardId);
|
|
1117
1464
|
return {
|
|
1118
1465
|
content: [{
|
|
1119
1466
|
type: "text",
|
|
@@ -1126,14 +1473,15 @@ async function main() {
|
|
|
1126
1473
|
"move_card",
|
|
1127
1474
|
"Move a kanban card to a different status column.",
|
|
1128
1475
|
{
|
|
1476
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1129
1477
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)"),
|
|
1130
|
-
status: import_zod.z.
|
|
1478
|
+
status: import_zod.z.string().describe("Target status column")
|
|
1131
1479
|
},
|
|
1132
|
-
async ({ cardId, status }) => {
|
|
1480
|
+
async ({ boardId, cardId, status }) => {
|
|
1133
1481
|
let resolvedId = cardId;
|
|
1134
|
-
const card = await sdk.getCard(cardId);
|
|
1482
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1135
1483
|
if (!card) {
|
|
1136
|
-
const all = await sdk.listCards();
|
|
1484
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1137
1485
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1138
1486
|
if (matches.length === 1) {
|
|
1139
1487
|
resolvedId = matches[0].id;
|
|
@@ -1149,7 +1497,7 @@ async function main() {
|
|
|
1149
1497
|
};
|
|
1150
1498
|
}
|
|
1151
1499
|
}
|
|
1152
|
-
const updated = await sdk.moveCard(resolvedId, status);
|
|
1500
|
+
const updated = await sdk.moveCard(resolvedId, status, void 0, boardId);
|
|
1153
1501
|
return {
|
|
1154
1502
|
content: [{
|
|
1155
1503
|
type: "text",
|
|
@@ -1162,13 +1510,14 @@ async function main() {
|
|
|
1162
1510
|
"delete_card",
|
|
1163
1511
|
"Permanently delete a kanban card.",
|
|
1164
1512
|
{
|
|
1513
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1165
1514
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)")
|
|
1166
1515
|
},
|
|
1167
|
-
async ({ cardId }) => {
|
|
1516
|
+
async ({ boardId, cardId }) => {
|
|
1168
1517
|
let resolvedId = cardId;
|
|
1169
|
-
const card = await sdk.getCard(cardId);
|
|
1518
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1170
1519
|
if (!card) {
|
|
1171
|
-
const all = await sdk.listCards();
|
|
1520
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1172
1521
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1173
1522
|
if (matches.length === 1) {
|
|
1174
1523
|
resolvedId = matches[0].id;
|
|
@@ -1184,7 +1533,7 @@ async function main() {
|
|
|
1184
1533
|
};
|
|
1185
1534
|
}
|
|
1186
1535
|
}
|
|
1187
|
-
await sdk.deleteCard(resolvedId);
|
|
1536
|
+
await sdk.deleteCard(resolvedId, boardId);
|
|
1188
1537
|
return {
|
|
1189
1538
|
content: [{
|
|
1190
1539
|
type: "text",
|
|
@@ -1197,13 +1546,14 @@ async function main() {
|
|
|
1197
1546
|
"list_attachments",
|
|
1198
1547
|
"List all attachments on a kanban card.",
|
|
1199
1548
|
{
|
|
1549
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1200
1550
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)")
|
|
1201
1551
|
},
|
|
1202
|
-
async ({ cardId }) => {
|
|
1552
|
+
async ({ boardId, cardId }) => {
|
|
1203
1553
|
let resolvedId = cardId;
|
|
1204
|
-
const card = await sdk.getCard(cardId);
|
|
1554
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1205
1555
|
if (!card) {
|
|
1206
|
-
const all = await sdk.listCards();
|
|
1556
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1207
1557
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1208
1558
|
if (matches.length === 1) {
|
|
1209
1559
|
resolvedId = matches[0].id;
|
|
@@ -1219,7 +1569,7 @@ async function main() {
|
|
|
1219
1569
|
};
|
|
1220
1570
|
}
|
|
1221
1571
|
}
|
|
1222
|
-
const attachments = await sdk.listAttachments(resolvedId);
|
|
1572
|
+
const attachments = await sdk.listAttachments(resolvedId, boardId);
|
|
1223
1573
|
return {
|
|
1224
1574
|
content: [{
|
|
1225
1575
|
type: "text",
|
|
@@ -1232,14 +1582,15 @@ async function main() {
|
|
|
1232
1582
|
"add_attachment",
|
|
1233
1583
|
"Add a file attachment to a kanban card. Copies the file to the card directory.",
|
|
1234
1584
|
{
|
|
1585
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1235
1586
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)"),
|
|
1236
1587
|
filePath: import_zod.z.string().describe("Absolute path to the file to attach")
|
|
1237
1588
|
},
|
|
1238
|
-
async ({ cardId, filePath }) => {
|
|
1589
|
+
async ({ boardId, cardId, filePath }) => {
|
|
1239
1590
|
let resolvedId = cardId;
|
|
1240
|
-
const card = await sdk.getCard(cardId);
|
|
1591
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1241
1592
|
if (!card) {
|
|
1242
|
-
const all = await sdk.listCards();
|
|
1593
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1243
1594
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1244
1595
|
if (matches.length === 1) {
|
|
1245
1596
|
resolvedId = matches[0].id;
|
|
@@ -1255,7 +1606,7 @@ async function main() {
|
|
|
1255
1606
|
};
|
|
1256
1607
|
}
|
|
1257
1608
|
}
|
|
1258
|
-
const updated = await sdk.addAttachment(resolvedId, filePath);
|
|
1609
|
+
const updated = await sdk.addAttachment(resolvedId, filePath, boardId);
|
|
1259
1610
|
return {
|
|
1260
1611
|
content: [{
|
|
1261
1612
|
type: "text",
|
|
@@ -1268,14 +1619,15 @@ async function main() {
|
|
|
1268
1619
|
"remove_attachment",
|
|
1269
1620
|
"Remove an attachment from a kanban card. Only removes the reference, not the file.",
|
|
1270
1621
|
{
|
|
1622
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1271
1623
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)"),
|
|
1272
1624
|
attachment: import_zod.z.string().describe("Attachment filename to remove")
|
|
1273
1625
|
},
|
|
1274
|
-
async ({ cardId, attachment }) => {
|
|
1626
|
+
async ({ boardId, cardId, attachment }) => {
|
|
1275
1627
|
let resolvedId = cardId;
|
|
1276
|
-
const card = await sdk.getCard(cardId);
|
|
1628
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1277
1629
|
if (!card) {
|
|
1278
|
-
const all = await sdk.listCards();
|
|
1630
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1279
1631
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1280
1632
|
if (matches.length === 1) {
|
|
1281
1633
|
resolvedId = matches[0].id;
|
|
@@ -1291,7 +1643,7 @@ async function main() {
|
|
|
1291
1643
|
};
|
|
1292
1644
|
}
|
|
1293
1645
|
}
|
|
1294
|
-
const updated = await sdk.removeAttachment(resolvedId, attachment);
|
|
1646
|
+
const updated = await sdk.removeAttachment(resolvedId, attachment, boardId);
|
|
1295
1647
|
return {
|
|
1296
1648
|
content: [{
|
|
1297
1649
|
type: "text",
|
|
@@ -1304,13 +1656,14 @@ async function main() {
|
|
|
1304
1656
|
"list_comments",
|
|
1305
1657
|
"List all comments on a kanban card.",
|
|
1306
1658
|
{
|
|
1659
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1307
1660
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)")
|
|
1308
1661
|
},
|
|
1309
|
-
async ({ cardId }) => {
|
|
1662
|
+
async ({ boardId, cardId }) => {
|
|
1310
1663
|
let resolvedId = cardId;
|
|
1311
|
-
const card = await sdk.getCard(cardId);
|
|
1664
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1312
1665
|
if (!card) {
|
|
1313
|
-
const all = await sdk.listCards();
|
|
1666
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1314
1667
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1315
1668
|
if (matches.length === 1) {
|
|
1316
1669
|
resolvedId = matches[0].id;
|
|
@@ -1326,7 +1679,7 @@ async function main() {
|
|
|
1326
1679
|
};
|
|
1327
1680
|
}
|
|
1328
1681
|
}
|
|
1329
|
-
const comments = await sdk.listComments(resolvedId);
|
|
1682
|
+
const comments = await sdk.listComments(resolvedId, boardId);
|
|
1330
1683
|
return {
|
|
1331
1684
|
content: [{
|
|
1332
1685
|
type: "text",
|
|
@@ -1339,15 +1692,16 @@ async function main() {
|
|
|
1339
1692
|
"add_comment",
|
|
1340
1693
|
"Add a comment to a kanban card.",
|
|
1341
1694
|
{
|
|
1695
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1342
1696
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)"),
|
|
1343
1697
|
author: import_zod.z.string().describe("Comment author name"),
|
|
1344
1698
|
content: import_zod.z.string().describe("Comment text (supports markdown)")
|
|
1345
1699
|
},
|
|
1346
|
-
async ({ cardId, author, content }) => {
|
|
1700
|
+
async ({ boardId, cardId, author, content }) => {
|
|
1347
1701
|
let resolvedId = cardId;
|
|
1348
|
-
const card = await sdk.getCard(cardId);
|
|
1702
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1349
1703
|
if (!card) {
|
|
1350
|
-
const all = await sdk.listCards();
|
|
1704
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1351
1705
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1352
1706
|
if (matches.length === 1) {
|
|
1353
1707
|
resolvedId = matches[0].id;
|
|
@@ -1363,7 +1717,7 @@ async function main() {
|
|
|
1363
1717
|
};
|
|
1364
1718
|
}
|
|
1365
1719
|
}
|
|
1366
|
-
const updated = await sdk.addComment(resolvedId, author, content);
|
|
1720
|
+
const updated = await sdk.addComment(resolvedId, author, content, boardId);
|
|
1367
1721
|
const added = updated.comments[updated.comments.length - 1];
|
|
1368
1722
|
return {
|
|
1369
1723
|
content: [{
|
|
@@ -1377,15 +1731,16 @@ async function main() {
|
|
|
1377
1731
|
"update_comment",
|
|
1378
1732
|
"Update the content of a comment on a kanban card.",
|
|
1379
1733
|
{
|
|
1734
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1380
1735
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)"),
|
|
1381
1736
|
commentId: import_zod.z.string().describe('Comment ID (e.g. "c1")'),
|
|
1382
1737
|
content: import_zod.z.string().describe("New comment text")
|
|
1383
1738
|
},
|
|
1384
|
-
async ({ cardId, commentId, content }) => {
|
|
1739
|
+
async ({ boardId, cardId, commentId, content }) => {
|
|
1385
1740
|
let resolvedId = cardId;
|
|
1386
|
-
const card = await sdk.getCard(cardId);
|
|
1741
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1387
1742
|
if (!card) {
|
|
1388
|
-
const all = await sdk.listCards();
|
|
1743
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1389
1744
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1390
1745
|
if (matches.length === 1) {
|
|
1391
1746
|
resolvedId = matches[0].id;
|
|
@@ -1402,7 +1757,7 @@ async function main() {
|
|
|
1402
1757
|
}
|
|
1403
1758
|
}
|
|
1404
1759
|
try {
|
|
1405
|
-
const updated = await sdk.updateComment(resolvedId, commentId, content);
|
|
1760
|
+
const updated = await sdk.updateComment(resolvedId, commentId, content, boardId);
|
|
1406
1761
|
const comment = updated.comments.find((c) => c.id === commentId);
|
|
1407
1762
|
return {
|
|
1408
1763
|
content: [{
|
|
@@ -1422,14 +1777,15 @@ async function main() {
|
|
|
1422
1777
|
"delete_comment",
|
|
1423
1778
|
"Delete a comment from a kanban card.",
|
|
1424
1779
|
{
|
|
1780
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1425
1781
|
cardId: import_zod.z.string().describe("Card ID (or partial ID)"),
|
|
1426
1782
|
commentId: import_zod.z.string().describe('Comment ID (e.g. "c1")')
|
|
1427
1783
|
},
|
|
1428
|
-
async ({ cardId, commentId }) => {
|
|
1784
|
+
async ({ boardId, cardId, commentId }) => {
|
|
1429
1785
|
let resolvedId = cardId;
|
|
1430
|
-
const card = await sdk.getCard(cardId);
|
|
1786
|
+
const card = await sdk.getCard(cardId, boardId);
|
|
1431
1787
|
if (!card) {
|
|
1432
|
-
const all = await sdk.listCards();
|
|
1788
|
+
const all = await sdk.listCards(void 0, boardId);
|
|
1433
1789
|
const matches = all.filter((c) => c.id.includes(cardId));
|
|
1434
1790
|
if (matches.length === 1) {
|
|
1435
1791
|
resolvedId = matches[0].id;
|
|
@@ -1446,7 +1802,7 @@ async function main() {
|
|
|
1446
1802
|
}
|
|
1447
1803
|
}
|
|
1448
1804
|
try {
|
|
1449
|
-
await sdk.deleteComment(resolvedId, commentId);
|
|
1805
|
+
await sdk.deleteComment(resolvedId, commentId, boardId);
|
|
1450
1806
|
return {
|
|
1451
1807
|
content: [{
|
|
1452
1808
|
type: "text",
|
|
@@ -1464,9 +1820,11 @@ async function main() {
|
|
|
1464
1820
|
server.tool(
|
|
1465
1821
|
"list_columns",
|
|
1466
1822
|
"List all kanban board columns.",
|
|
1467
|
-
{
|
|
1468
|
-
|
|
1469
|
-
|
|
1823
|
+
{
|
|
1824
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)")
|
|
1825
|
+
},
|
|
1826
|
+
async ({ boardId }) => {
|
|
1827
|
+
const columns = await sdk.listColumns(boardId);
|
|
1470
1828
|
return {
|
|
1471
1829
|
content: [{
|
|
1472
1830
|
type: "text",
|
|
@@ -1479,12 +1837,13 @@ async function main() {
|
|
|
1479
1837
|
"add_column",
|
|
1480
1838
|
"Add a new column to the kanban board.",
|
|
1481
1839
|
{
|
|
1840
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1482
1841
|
id: import_zod.z.string().describe("Unique column ID (used in card status field)"),
|
|
1483
1842
|
name: import_zod.z.string().describe("Display name for the column"),
|
|
1484
1843
|
color: import_zod.z.string().describe('Column color (hex format, e.g. "#3b82f6")')
|
|
1485
1844
|
},
|
|
1486
|
-
async ({ id, name, color }) => {
|
|
1487
|
-
const columns = await sdk.addColumn({ id, name, color });
|
|
1845
|
+
async ({ boardId, id, name, color }) => {
|
|
1846
|
+
const columns = await sdk.addColumn({ id, name, color }, boardId);
|
|
1488
1847
|
return {
|
|
1489
1848
|
content: [{
|
|
1490
1849
|
type: "text",
|
|
@@ -1497,17 +1856,18 @@ async function main() {
|
|
|
1497
1856
|
"update_column",
|
|
1498
1857
|
"Update an existing kanban board column.",
|
|
1499
1858
|
{
|
|
1859
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1500
1860
|
columnId: import_zod.z.string().describe("Column ID to update"),
|
|
1501
1861
|
name: import_zod.z.string().optional().describe("New display name"),
|
|
1502
1862
|
color: import_zod.z.string().optional().describe("New color (hex format)")
|
|
1503
1863
|
},
|
|
1504
|
-
async ({ columnId, name, color }) => {
|
|
1864
|
+
async ({ boardId, columnId, name, color }) => {
|
|
1505
1865
|
const updates = {};
|
|
1506
1866
|
if (name)
|
|
1507
1867
|
updates.name = name;
|
|
1508
1868
|
if (color)
|
|
1509
1869
|
updates.color = color;
|
|
1510
|
-
const columns = await sdk.updateColumn(columnId, updates);
|
|
1870
|
+
const columns = await sdk.updateColumn(columnId, updates, boardId);
|
|
1511
1871
|
return {
|
|
1512
1872
|
content: [{
|
|
1513
1873
|
type: "text",
|
|
@@ -1520,10 +1880,11 @@ async function main() {
|
|
|
1520
1880
|
"remove_column",
|
|
1521
1881
|
"Remove a column from the kanban board. Fails if any cards are in the column.",
|
|
1522
1882
|
{
|
|
1883
|
+
boardId: import_zod.z.string().optional().describe("Board ID (uses default board if omitted)"),
|
|
1523
1884
|
columnId: import_zod.z.string().describe("Column ID to remove")
|
|
1524
1885
|
},
|
|
1525
|
-
async ({ columnId }) => {
|
|
1526
|
-
const columns = await sdk.removeColumn(columnId);
|
|
1886
|
+
async ({ boardId, columnId }) => {
|
|
1887
|
+
const columns = await sdk.removeColumn(columnId, boardId);
|
|
1527
1888
|
return {
|
|
1528
1889
|
content: [{
|
|
1529
1890
|
type: "text",
|
|
@@ -1532,7 +1893,7 @@ async function main() {
|
|
|
1532
1893
|
};
|
|
1533
1894
|
}
|
|
1534
1895
|
);
|
|
1535
|
-
const workspaceRoot =
|
|
1896
|
+
const workspaceRoot = path7.dirname(featuresDir);
|
|
1536
1897
|
server.tool(
|
|
1537
1898
|
"get_settings",
|
|
1538
1899
|
"Get the current kanban board display settings.",
|
|
@@ -1559,7 +1920,7 @@ async function main() {
|
|
|
1559
1920
|
showFileName: import_zod.z.boolean().optional().describe("Show file name on cards"),
|
|
1560
1921
|
compactMode: import_zod.z.boolean().optional().describe("Enable compact card display"),
|
|
1561
1922
|
defaultPriority: import_zod.z.enum(["critical", "high", "medium", "low"]).optional().describe("Default priority for new cards"),
|
|
1562
|
-
defaultStatus: import_zod.z.
|
|
1923
|
+
defaultStatus: import_zod.z.string().optional().describe("Default status for new cards")
|
|
1563
1924
|
},
|
|
1564
1925
|
async (updates) => {
|
|
1565
1926
|
const config = readConfig(workspaceRoot);
|