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.
Files changed (38) hide show
  1. package/dist/cli.js +1015 -407
  2. package/dist/extension.js +897 -347
  3. package/dist/mcp-server.js +550 -189
  4. package/dist/sdk/index.cjs +1223 -0
  5. package/dist/sdk/index.mjs +1168 -0
  6. package/dist/sdk/sdk/KanbanSDK.d.ts +39 -21
  7. package/dist/sdk/sdk/index.d.ts +4 -3
  8. package/dist/sdk/sdk/migration.d.ts +6 -0
  9. package/dist/sdk/sdk/types.d.ts +3 -5
  10. package/dist/sdk/shared/config.d.ts +23 -10
  11. package/dist/sdk/shared/types.d.ts +18 -4
  12. package/dist/standalone-webview/index.js +27 -27
  13. package/dist/standalone-webview/index.js.map +1 -1
  14. package/dist/standalone-webview/style.css +1 -1
  15. package/dist/standalone.js +831 -328
  16. package/dist/webview/index.js +45 -45
  17. package/dist/webview/index.js.map +1 -1
  18. package/dist/webview/style.css +1 -1
  19. package/package.json +4 -3
  20. package/src/cli/index.ts +217 -95
  21. package/src/extension/KanbanPanel.ts +49 -22
  22. package/src/mcp-server/index.ts +138 -62
  23. package/src/sdk/KanbanSDK.ts +283 -77
  24. package/src/sdk/__tests__/KanbanSDK.test.ts +5 -5
  25. package/src/sdk/__tests__/migration.test.ts +269 -0
  26. package/src/sdk/__tests__/multi-board.test.ts +449 -0
  27. package/src/sdk/index.ts +4 -3
  28. package/src/sdk/migration.ts +52 -0
  29. package/src/sdk/types.ts +3 -6
  30. package/src/shared/config.ts +144 -22
  31. package/src/shared/types.ts +14 -5
  32. package/src/standalone/__tests__/server.integration.test.ts +38 -37
  33. package/src/standalone/server.ts +239 -21
  34. package/src/webview/App.tsx +17 -7
  35. package/src/webview/components/Toolbar.tsx +99 -3
  36. package/src/webview/store/index.ts +11 -3
  37. package/.kanban/backlog/1-test1.md +0 -30
  38. package/.kanban.json +0 -42
package/dist/cli.js CHANGED
@@ -267,9 +267,17 @@ function extractNumericId(filenameOrId) {
267
267
  const match = filenameOrId.match(/^(\d+)(?:-|$)/);
268
268
  return match ? parseInt(match[1], 10) : null;
269
269
  }
270
+ var DEFAULT_COLUMNS;
270
271
  var init_types = __esm({
271
272
  "src/shared/types.ts"() {
272
273
  "use strict";
274
+ DEFAULT_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
+ ];
273
281
  }
274
282
  });
275
283
 
@@ -277,12 +285,65 @@ var init_types = __esm({
277
285
  function configPath(workspaceRoot) {
278
286
  return path.join(workspaceRoot, CONFIG_FILENAME);
279
287
  }
288
+ function migrateConfigV1ToV2(raw) {
289
+ const v1Defaults = {
290
+ featuresDirectory: ".kanban",
291
+ defaultPriority: "medium",
292
+ defaultStatus: "backlog",
293
+ columns: [...DEFAULT_COLUMNS],
294
+ aiAgent: "claude",
295
+ nextCardId: 1,
296
+ showPriorityBadges: true,
297
+ showAssignee: true,
298
+ showDueDate: true,
299
+ showLabels: true,
300
+ showBuildWithAI: true,
301
+ showFileName: false,
302
+ compactMode: false,
303
+ markdownEditorMode: false
304
+ };
305
+ const v1 = { ...v1Defaults, ...raw };
306
+ return {
307
+ version: 2,
308
+ boards: {
309
+ default: {
310
+ name: "Default",
311
+ columns: v1.columns,
312
+ nextCardId: v1.nextCardId,
313
+ defaultStatus: v1.defaultStatus,
314
+ defaultPriority: v1.defaultPriority
315
+ }
316
+ },
317
+ defaultBoard: "default",
318
+ featuresDirectory: v1.featuresDirectory,
319
+ aiAgent: v1.aiAgent,
320
+ defaultPriority: v1.defaultPriority,
321
+ defaultStatus: v1.defaultStatus,
322
+ showPriorityBadges: v1.showPriorityBadges,
323
+ showAssignee: v1.showAssignee,
324
+ showDueDate: v1.showDueDate,
325
+ showLabels: v1.showLabels,
326
+ showBuildWithAI: v1.showBuildWithAI,
327
+ showFileName: v1.showFileName,
328
+ compactMode: v1.compactMode,
329
+ markdownEditorMode: v1.markdownEditorMode
330
+ };
331
+ }
280
332
  function readConfig(workspaceRoot) {
281
333
  const filePath = configPath(workspaceRoot);
282
- const defaults = { ...DEFAULT_CONFIG, columns: [...DEFAULT_CONFIG.columns] };
334
+ const defaults = { ...DEFAULT_CONFIG, boards: { default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] } } };
283
335
  try {
284
336
  const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
285
- return { ...defaults, ...raw };
337
+ if (!raw.version || raw.version === 1) {
338
+ const v2 = migrateConfigV1ToV2(raw);
339
+ writeConfig(workspaceRoot, v2);
340
+ return v2;
341
+ }
342
+ const config = { ...defaults, ...raw };
343
+ if (!config.boards || Object.keys(config.boards).length === 0) {
344
+ config.boards = defaults.boards;
345
+ }
346
+ return config;
286
347
  } catch {
287
348
  return defaults;
288
349
  }
@@ -291,19 +352,39 @@ function writeConfig(workspaceRoot, config) {
291
352
  const filePath = configPath(workspaceRoot);
292
353
  fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
293
354
  }
294
- function allocateCardId(workspaceRoot) {
355
+ function getBoardConfig(workspaceRoot, boardId) {
295
356
  const config = readConfig(workspaceRoot);
296
- const id = config.nextCardId;
297
- writeConfig(workspaceRoot, { ...config, nextCardId: id + 1 });
357
+ const resolvedId = boardId || config.defaultBoard;
358
+ const board = config.boards[resolvedId];
359
+ if (!board) {
360
+ throw new Error(`Board '${resolvedId}' not found`);
361
+ }
362
+ return board;
363
+ }
364
+ function allocateCardId(workspaceRoot, boardId) {
365
+ const config = readConfig(workspaceRoot);
366
+ const resolvedId = boardId || config.defaultBoard;
367
+ const board = config.boards[resolvedId];
368
+ if (!board) {
369
+ throw new Error(`Board '${resolvedId}' not found`);
370
+ }
371
+ const id = board.nextCardId;
372
+ board.nextCardId = id + 1;
373
+ writeConfig(workspaceRoot, config);
298
374
  return id;
299
375
  }
300
- function syncCardIdCounter(workspaceRoot, existingIds) {
376
+ function syncCardIdCounter(workspaceRoot, boardId, existingIds) {
301
377
  if (existingIds.length === 0)
302
378
  return;
303
379
  const maxId = Math.max(...existingIds);
304
380
  const config = readConfig(workspaceRoot);
305
- if (config.nextCardId <= maxId) {
306
- writeConfig(workspaceRoot, { ...config, nextCardId: maxId + 1 });
381
+ const resolvedId = boardId || config.defaultBoard;
382
+ const board = config.boards[resolvedId];
383
+ if (!board)
384
+ return;
385
+ if (board.nextCardId <= maxId) {
386
+ board.nextCardId = maxId + 1;
387
+ writeConfig(workspaceRoot, config);
307
388
  }
308
389
  }
309
390
  function configToSettings(config) {
@@ -333,25 +414,30 @@ function settingsToConfig(config, settings) {
333
414
  defaultStatus: settings.defaultStatus
334
415
  };
335
416
  }
336
- var fs, path, DEFAULT_CONFIG, CONFIG_FILENAME;
417
+ var fs, path, DEFAULT_BOARD_CONFIG, DEFAULT_CONFIG, CONFIG_FILENAME;
337
418
  var init_config = __esm({
338
419
  "src/shared/config.ts"() {
339
420
  "use strict";
340
421
  fs = __toESM(require("fs"));
341
422
  path = __toESM(require("path"));
423
+ init_types();
424
+ DEFAULT_BOARD_CONFIG = {
425
+ name: "Default",
426
+ columns: [...DEFAULT_COLUMNS],
427
+ nextCardId: 1,
428
+ defaultStatus: "backlog",
429
+ defaultPriority: "medium"
430
+ };
342
431
  DEFAULT_CONFIG = {
432
+ version: 2,
433
+ boards: {
434
+ default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] }
435
+ },
436
+ defaultBoard: "default",
343
437
  featuresDirectory: ".kanban",
438
+ aiAgent: "claude",
344
439
  defaultPriority: "medium",
345
440
  defaultStatus: "backlog",
346
- columns: [
347
- { id: "backlog", name: "Backlog", color: "#6b7280" },
348
- { id: "todo", name: "To Do", color: "#3b82f6" },
349
- { id: "in-progress", name: "In Progress", color: "#f59e0b" },
350
- { id: "review", name: "Review", color: "#8b5cf6" },
351
- { id: "done", name: "Done", color: "#22c55e" }
352
- ],
353
- aiAgent: "claude",
354
- nextCardId: 1,
355
441
  showPriorityBadges: true,
356
442
  showAssignee: true,
357
443
  showDueDate: true,
@@ -564,41 +650,221 @@ var init_fileUtils = __esm({
564
650
  }
565
651
  });
566
652
 
653
+ // src/sdk/migration.ts
654
+ async function migrateFileSystemToMultiBoard(featuresDir) {
655
+ const boardsDir = path4.join(featuresDir, "boards");
656
+ const defaultBoardDir = path4.join(boardsDir, "default");
657
+ try {
658
+ await fs3.access(boardsDir);
659
+ return;
660
+ } catch {
661
+ }
662
+ await fs3.mkdir(defaultBoardDir, { recursive: true });
663
+ let entries;
664
+ try {
665
+ entries = await fs3.readdir(featuresDir, { withFileTypes: true });
666
+ } catch {
667
+ return;
668
+ }
669
+ for (const entry of entries) {
670
+ if (!entry.isDirectory())
671
+ continue;
672
+ if (entry.name === "boards" || entry.name.startsWith("."))
673
+ continue;
674
+ const src = path4.join(featuresDir, entry.name);
675
+ const dest = path4.join(defaultBoardDir, entry.name);
676
+ await fs3.rename(src, dest);
677
+ }
678
+ const rootMdFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".md"));
679
+ if (rootMdFiles.length > 0) {
680
+ const backlogDir = path4.join(defaultBoardDir, "backlog");
681
+ await fs3.mkdir(backlogDir, { recursive: true });
682
+ for (const file of rootMdFiles) {
683
+ await fs3.rename(
684
+ path4.join(featuresDir, file.name),
685
+ path4.join(backlogDir, file.name)
686
+ );
687
+ }
688
+ }
689
+ }
690
+ var fs3, path4;
691
+ var init_migration = __esm({
692
+ "src/sdk/migration.ts"() {
693
+ "use strict";
694
+ fs3 = __toESM(require("fs/promises"));
695
+ path4 = __toESM(require("path"));
696
+ }
697
+ });
698
+
567
699
  // src/sdk/KanbanSDK.ts
568
- var fs3, path4, KanbanSDK;
700
+ var fs4, path5, KanbanSDK;
569
701
  var init_KanbanSDK = __esm({
570
702
  "src/sdk/KanbanSDK.ts"() {
571
703
  "use strict";
572
- fs3 = __toESM(require("fs/promises"));
573
- path4 = __toESM(require("path"));
704
+ fs4 = __toESM(require("fs/promises"));
705
+ path5 = __toESM(require("path"));
574
706
  init_src();
575
707
  init_types();
576
708
  init_config();
577
709
  init_parser();
578
710
  init_fileUtils();
711
+ init_migration();
579
712
  KanbanSDK = class {
580
713
  constructor(featuresDir) {
581
714
  this.featuresDir = featuresDir;
715
+ this._migrated = false;
582
716
  }
583
717
  get workspaceRoot() {
584
- return path4.dirname(this.featuresDir);
718
+ return path5.dirname(this.featuresDir);
719
+ }
720
+ // --- Board resolution helpers ---
721
+ _resolveBoardId(boardId) {
722
+ const config = readConfig(this.workspaceRoot);
723
+ return boardId || config.defaultBoard;
724
+ }
725
+ _boardDir(boardId) {
726
+ const resolvedId = this._resolveBoardId(boardId);
727
+ return path5.join(this.featuresDir, "boards", resolvedId);
728
+ }
729
+ _isCompletedStatus(status, boardId) {
730
+ const config = readConfig(this.workspaceRoot);
731
+ const resolvedId = boardId || config.defaultBoard;
732
+ const board = config.boards[resolvedId];
733
+ if (!board || board.columns.length === 0)
734
+ return status === "done";
735
+ return board.columns[board.columns.length - 1].id === status;
736
+ }
737
+ async _ensureMigrated() {
738
+ if (this._migrated)
739
+ return;
740
+ await migrateFileSystemToMultiBoard(this.featuresDir);
741
+ this._migrated = true;
585
742
  }
586
743
  async init() {
587
- await ensureDirectories(this.featuresDir);
744
+ await this._ensureMigrated();
745
+ const boardDir = this._boardDir();
746
+ await ensureDirectories(boardDir);
747
+ }
748
+ // --- Board management ---
749
+ listBoards() {
750
+ const config = readConfig(this.workspaceRoot);
751
+ return Object.entries(config.boards).map(([id, board]) => ({
752
+ id,
753
+ name: board.name,
754
+ description: board.description
755
+ }));
756
+ }
757
+ createBoard(id, name, options) {
758
+ const config = readConfig(this.workspaceRoot);
759
+ if (config.boards[id]) {
760
+ throw new Error(`Board already exists: ${id}`);
761
+ }
762
+ const columns = options?.columns || [...config.boards[config.defaultBoard]?.columns || [
763
+ { id: "backlog", name: "Backlog", color: "#6b7280" },
764
+ { id: "todo", name: "To Do", color: "#3b82f6" },
765
+ { id: "in-progress", name: "In Progress", color: "#f59e0b" },
766
+ { id: "review", name: "Review", color: "#8b5cf6" },
767
+ { id: "done", name: "Done", color: "#22c55e" }
768
+ ]];
769
+ config.boards[id] = {
770
+ name,
771
+ description: options?.description,
772
+ columns,
773
+ nextCardId: 1,
774
+ defaultStatus: options?.defaultStatus || columns[0]?.id || "backlog",
775
+ defaultPriority: options?.defaultPriority || config.defaultPriority
776
+ };
777
+ writeConfig(this.workspaceRoot, config);
778
+ return { id, name, description: options?.description };
779
+ }
780
+ async deleteBoard(boardId) {
781
+ const config = readConfig(this.workspaceRoot);
782
+ if (!config.boards[boardId]) {
783
+ throw new Error(`Board not found: ${boardId}`);
784
+ }
785
+ if (config.defaultBoard === boardId) {
786
+ throw new Error(`Cannot delete the default board: ${boardId}`);
787
+ }
788
+ const cards = await this.listCards(void 0, boardId);
789
+ if (cards.length > 0) {
790
+ throw new Error(`Cannot delete board "${boardId}": ${cards.length} card(s) still exist`);
791
+ }
792
+ const boardDir = this._boardDir(boardId);
793
+ try {
794
+ await fs4.rm(boardDir, { recursive: true });
795
+ } catch {
796
+ }
797
+ delete config.boards[boardId];
798
+ writeConfig(this.workspaceRoot, config);
799
+ }
800
+ getBoard(boardId) {
801
+ return getBoardConfig(this.workspaceRoot, boardId);
802
+ }
803
+ updateBoard(boardId, updates) {
804
+ const config = readConfig(this.workspaceRoot);
805
+ const board = config.boards[boardId];
806
+ if (!board) {
807
+ throw new Error(`Board not found: ${boardId}`);
808
+ }
809
+ if (updates.name !== void 0)
810
+ board.name = updates.name;
811
+ if (updates.description !== void 0)
812
+ board.description = updates.description;
813
+ if (updates.columns !== void 0)
814
+ board.columns = updates.columns;
815
+ if (updates.defaultStatus !== void 0)
816
+ board.defaultStatus = updates.defaultStatus;
817
+ if (updates.defaultPriority !== void 0)
818
+ board.defaultPriority = updates.defaultPriority;
819
+ writeConfig(this.workspaceRoot, config);
820
+ return board;
821
+ }
822
+ async transferCard(cardId, fromBoardId, toBoardId, targetStatus) {
823
+ const toBoardDir = this._boardDir(toBoardId);
824
+ const config = readConfig(this.workspaceRoot);
825
+ if (!config.boards[fromBoardId])
826
+ throw new Error(`Board not found: ${fromBoardId}`);
827
+ if (!config.boards[toBoardId])
828
+ throw new Error(`Board not found: ${toBoardId}`);
829
+ const card = await this.getCard(cardId, fromBoardId);
830
+ if (!card)
831
+ throw new Error(`Card not found: ${cardId} in board ${fromBoardId}`);
832
+ const toBoard = config.boards[toBoardId];
833
+ const newStatus = targetStatus || toBoard.defaultStatus || toBoard.columns[0]?.id || "backlog";
834
+ const targetDir = path5.join(toBoardDir, newStatus);
835
+ await fs4.mkdir(targetDir, { recursive: true });
836
+ const oldPath = card.filePath;
837
+ const filename = path5.basename(oldPath);
838
+ const newPath = path5.join(targetDir, filename);
839
+ await fs4.rename(oldPath, newPath);
840
+ card.status = newStatus;
841
+ card.boardId = toBoardId;
842
+ card.filePath = newPath;
843
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
844
+ card.completedAt = this._isCompletedStatus(newStatus, toBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
845
+ const targetCards = await this.listCards(void 0, toBoardId);
846
+ 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);
847
+ const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
848
+ card.order = generateKeyBetween(lastOrder, null);
849
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
850
+ return card;
588
851
  }
589
852
  // --- Card CRUD ---
590
- async listCards(columns) {
591
- await ensureDirectories(this.featuresDir);
853
+ async listCards(columns, boardId) {
854
+ await this._ensureMigrated();
855
+ const boardDir = this._boardDir(boardId);
856
+ const resolvedBoardId = this._resolveBoardId(boardId);
857
+ await ensureDirectories(boardDir);
592
858
  if (columns) {
593
- await ensureStatusSubfolders(this.featuresDir, columns);
859
+ await ensureStatusSubfolders(boardDir, columns);
594
860
  }
595
861
  try {
596
- const rootFiles = await this._readMdFiles(this.featuresDir);
862
+ const rootFiles = await this._readMdFiles(boardDir);
597
863
  for (const filePath of rootFiles) {
598
864
  try {
599
865
  const card = await this._loadCard(filePath);
600
866
  if (card) {
601
- await moveFeatureFile(filePath, this.featuresDir, card.status, card.attachments);
867
+ await moveFeatureFile(filePath, boardDir, card.status, card.attachments);
602
868
  }
603
869
  } catch {
604
870
  }
@@ -606,26 +872,33 @@ var init_KanbanSDK = __esm({
606
872
  } catch {
607
873
  }
608
874
  const cards = [];
609
- const entries = await fs3.readdir(this.featuresDir, { withFileTypes: true });
875
+ let entries;
876
+ try {
877
+ entries = await fs4.readdir(boardDir, { withFileTypes: true });
878
+ } catch {
879
+ return [];
880
+ }
610
881
  for (const entry of entries) {
611
882
  if (!entry.isDirectory() || entry.name.startsWith("."))
612
883
  continue;
613
- const subdir = path4.join(this.featuresDir, entry.name);
884
+ const subdir = path5.join(boardDir, entry.name);
614
885
  try {
615
886
  const mdFiles = await this._readMdFiles(subdir);
616
887
  for (const filePath of mdFiles) {
617
888
  const card = await this._loadCard(filePath);
618
- if (card)
889
+ if (card) {
890
+ card.boardId = resolvedBoardId;
619
891
  cards.push(card);
892
+ }
620
893
  }
621
894
  } catch {
622
895
  }
623
896
  }
624
897
  for (const card of cards) {
625
- const pathStatus = getStatusFromPath(card.filePath, this.featuresDir);
898
+ const pathStatus = getStatusFromPath(card.filePath, boardDir);
626
899
  if (pathStatus !== null && pathStatus !== card.status) {
627
900
  try {
628
- card.filePath = await moveFeatureFile(card.filePath, this.featuresDir, card.status, card.attachments);
901
+ card.filePath = await moveFeatureFile(card.filePath, boardDir, card.status, card.attachments);
629
902
  } catch {
630
903
  }
631
904
  }
@@ -643,65 +916,72 @@ var init_KanbanSDK = __esm({
643
916
  const keys = generateNKeysBetween(null, null, columnCards.length);
644
917
  for (let i = 0; i < columnCards.length; i++) {
645
918
  columnCards[i].order = keys[i];
646
- await fs3.writeFile(columnCards[i].filePath, serializeFeature(columnCards[i]), "utf-8");
919
+ await fs4.writeFile(columnCards[i].filePath, serializeFeature(columnCards[i]), "utf-8");
647
920
  }
648
921
  }
649
922
  }
650
923
  const numericIds = cards.map((c) => parseInt(c.id, 10)).filter((n) => !Number.isNaN(n));
651
924
  if (numericIds.length > 0) {
652
- syncCardIdCounter(path4.dirname(this.featuresDir), numericIds);
925
+ syncCardIdCounter(this.workspaceRoot, resolvedBoardId, numericIds);
653
926
  }
654
927
  return cards.sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
655
928
  }
656
- async getCard(cardId) {
657
- const cards = await this.listCards();
929
+ async getCard(cardId, boardId) {
930
+ const cards = await this.listCards(void 0, boardId);
658
931
  return cards.find((c) => c.id === cardId) || null;
659
932
  }
660
933
  async createCard(data) {
661
- await ensureDirectories(this.featuresDir);
662
- const status = data.status || "backlog";
663
- const priority = data.priority || "medium";
934
+ await this._ensureMigrated();
935
+ const resolvedBoardId = this._resolveBoardId(data.boardId);
936
+ const boardDir = this._boardDir(resolvedBoardId);
937
+ await ensureDirectories(boardDir);
938
+ const config = readConfig(this.workspaceRoot);
939
+ const board = config.boards[resolvedBoardId];
940
+ const status = data.status || board?.defaultStatus || config.defaultStatus || "backlog";
941
+ const priority = data.priority || board?.defaultPriority || config.defaultPriority || "medium";
664
942
  const title = getTitleFromContent(data.content);
665
- const workspaceRoot = path4.dirname(this.featuresDir);
666
- const numericId = allocateCardId(workspaceRoot);
943
+ const numericId = allocateCardId(this.workspaceRoot, resolvedBoardId);
667
944
  const filename = generateFeatureFilename(numericId, title);
668
945
  const now = (/* @__PURE__ */ new Date()).toISOString();
669
- const cards = await this.listCards();
946
+ const cards = await this.listCards(void 0, resolvedBoardId);
670
947
  const cardsInStatus = cards.filter((c) => c.status === status).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
671
948
  const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
672
949
  const card = {
673
950
  id: String(numericId),
951
+ boardId: resolvedBoardId,
674
952
  status,
675
953
  priority,
676
954
  assignee: data.assignee ?? null,
677
955
  dueDate: data.dueDate ?? null,
678
956
  created: now,
679
957
  modified: now,
680
- completedAt: status === "done" ? now : null,
958
+ completedAt: this._isCompletedStatus(status, resolvedBoardId) ? now : null,
681
959
  labels: data.labels || [],
682
960
  attachments: data.attachments || [],
683
961
  comments: [],
684
962
  order: generateKeyBetween(lastOrder, null),
685
963
  content: data.content,
686
- filePath: getFeatureFilePath(this.featuresDir, status, filename)
964
+ filePath: getFeatureFilePath(boardDir, status, filename)
687
965
  };
688
- await fs3.mkdir(path4.dirname(card.filePath), { recursive: true });
689
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
966
+ await fs4.mkdir(path5.dirname(card.filePath), { recursive: true });
967
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
690
968
  return card;
691
969
  }
692
- async updateCard(cardId, updates) {
693
- const card = await this.getCard(cardId);
970
+ async updateCard(cardId, updates, boardId) {
971
+ const card = await this.getCard(cardId, boardId);
694
972
  if (!card)
695
973
  throw new Error(`Card not found: ${cardId}`);
974
+ const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
975
+ const boardDir = this._boardDir(resolvedBoardId);
696
976
  const oldStatus = card.status;
697
977
  const oldTitle = getTitleFromContent(card.content);
698
- const { filePath: _fp, id: _id, ...safeUpdates } = updates;
978
+ const { filePath: _fp, id: _id, boardId: _bid, ...safeUpdates } = updates;
699
979
  Object.assign(card, safeUpdates);
700
980
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
701
981
  if (oldStatus !== card.status) {
702
- card.completedAt = card.status === "done" ? (/* @__PURE__ */ new Date()).toISOString() : null;
982
+ card.completedAt = this._isCompletedStatus(card.status, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
703
983
  }
704
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
984
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
705
985
  const newTitle = getTitleFromContent(card.content);
706
986
  const numericId = extractNumericId(card.id);
707
987
  if (numericId !== null && newTitle !== oldTitle) {
@@ -709,46 +989,48 @@ var init_KanbanSDK = __esm({
709
989
  card.filePath = await renameFeatureFile(card.filePath, newFilename);
710
990
  }
711
991
  if (oldStatus !== card.status) {
712
- const newPath = await moveFeatureFile(card.filePath, this.featuresDir, card.status, card.attachments);
992
+ const newPath = await moveFeatureFile(card.filePath, boardDir, card.status, card.attachments);
713
993
  card.filePath = newPath;
714
994
  }
715
995
  return card;
716
996
  }
717
- async moveCard(cardId, newStatus, position) {
718
- const cards = await this.listCards();
997
+ async moveCard(cardId, newStatus, position, boardId) {
998
+ const cards = await this.listCards(void 0, boardId);
719
999
  const card = cards.find((c) => c.id === cardId);
720
1000
  if (!card)
721
1001
  throw new Error(`Card not found: ${cardId}`);
1002
+ const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
1003
+ const boardDir = this._boardDir(resolvedBoardId);
722
1004
  const oldStatus = card.status;
723
1005
  card.status = newStatus;
724
1006
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
725
1007
  if (oldStatus !== newStatus) {
726
- card.completedAt = newStatus === "done" ? (/* @__PURE__ */ new Date()).toISOString() : null;
1008
+ card.completedAt = this._isCompletedStatus(newStatus, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
727
1009
  }
728
1010
  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);
729
1011
  const pos = position !== void 0 ? Math.max(0, Math.min(position, targetColumnCards.length)) : targetColumnCards.length;
730
1012
  const before = pos > 0 ? targetColumnCards[pos - 1].order : null;
731
1013
  const after = pos < targetColumnCards.length ? targetColumnCards[pos].order : null;
732
1014
  card.order = generateKeyBetween(before, after);
733
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
1015
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
734
1016
  if (oldStatus !== newStatus) {
735
- const newPath = await moveFeatureFile(card.filePath, this.featuresDir, newStatus, card.attachments);
1017
+ const newPath = await moveFeatureFile(card.filePath, boardDir, newStatus, card.attachments);
736
1018
  card.filePath = newPath;
737
1019
  }
738
1020
  return card;
739
1021
  }
740
- async deleteCard(cardId) {
741
- const card = await this.getCard(cardId);
1022
+ async deleteCard(cardId, boardId) {
1023
+ const card = await this.getCard(cardId, boardId);
742
1024
  if (!card)
743
1025
  throw new Error(`Card not found: ${cardId}`);
744
- await fs3.unlink(card.filePath);
1026
+ await fs4.unlink(card.filePath);
745
1027
  }
746
- async getCardsByStatus(status) {
747
- const cards = await this.listCards();
1028
+ async getCardsByStatus(status, boardId) {
1029
+ const cards = await this.listCards(void 0, boardId);
748
1030
  return cards.filter((c) => c.status === status);
749
1031
  }
750
- async getUniqueAssignees() {
751
- const cards = await this.listCards();
1032
+ async getUniqueAssignees(boardId) {
1033
+ const cards = await this.listCards(void 0, boardId);
752
1034
  const assignees = /* @__PURE__ */ new Set();
753
1035
  for (const c of cards) {
754
1036
  if (c.assignee)
@@ -756,8 +1038,8 @@ var init_KanbanSDK = __esm({
756
1038
  }
757
1039
  return [...assignees].sort();
758
1040
  }
759
- async getUniqueLabels() {
760
- const cards = await this.listCards();
1041
+ async getUniqueLabels(boardId) {
1042
+ const cards = await this.listCards(void 0, boardId);
761
1043
  const labels = /* @__PURE__ */ new Set();
762
1044
  for (const c of cards) {
763
1045
  for (const l of c.labels)
@@ -766,48 +1048,48 @@ var init_KanbanSDK = __esm({
766
1048
  return [...labels].sort();
767
1049
  }
768
1050
  // --- Attachment management ---
769
- async addAttachment(cardId, sourcePath) {
770
- const card = await this.getCard(cardId);
1051
+ async addAttachment(cardId, sourcePath, boardId) {
1052
+ const card = await this.getCard(cardId, boardId);
771
1053
  if (!card)
772
1054
  throw new Error(`Card not found: ${cardId}`);
773
- const fileName = path4.basename(sourcePath);
774
- const cardDir = path4.dirname(card.filePath);
775
- const destPath = path4.join(cardDir, fileName);
776
- const sourceDir = path4.dirname(path4.resolve(sourcePath));
1055
+ const fileName = path5.basename(sourcePath);
1056
+ const cardDir = path5.dirname(card.filePath);
1057
+ const destPath = path5.join(cardDir, fileName);
1058
+ const sourceDir = path5.dirname(path5.resolve(sourcePath));
777
1059
  if (sourceDir !== cardDir) {
778
- await fs3.copyFile(path4.resolve(sourcePath), destPath);
1060
+ await fs4.copyFile(path5.resolve(sourcePath), destPath);
779
1061
  }
780
1062
  if (!card.attachments.includes(fileName)) {
781
1063
  card.attachments.push(fileName);
782
1064
  }
783
1065
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
784
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
1066
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
785
1067
  return card;
786
1068
  }
787
- async removeAttachment(cardId, attachment) {
788
- const card = await this.getCard(cardId);
1069
+ async removeAttachment(cardId, attachment, boardId) {
1070
+ const card = await this.getCard(cardId, boardId);
789
1071
  if (!card)
790
1072
  throw new Error(`Card not found: ${cardId}`);
791
1073
  card.attachments = card.attachments.filter((a) => a !== attachment);
792
1074
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
793
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
1075
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
794
1076
  return card;
795
1077
  }
796
- async listAttachments(cardId) {
797
- const card = await this.getCard(cardId);
1078
+ async listAttachments(cardId, boardId) {
1079
+ const card = await this.getCard(cardId, boardId);
798
1080
  if (!card)
799
1081
  throw new Error(`Card not found: ${cardId}`);
800
1082
  return card.attachments;
801
1083
  }
802
1084
  // --- Comment management ---
803
- async listComments(cardId) {
804
- const card = await this.getCard(cardId);
1085
+ async listComments(cardId, boardId) {
1086
+ const card = await this.getCard(cardId, boardId);
805
1087
  if (!card)
806
1088
  throw new Error(`Card not found: ${cardId}`);
807
1089
  return card.comments || [];
808
1090
  }
809
- async addComment(cardId, author, content) {
810
- const card = await this.getCard(cardId);
1091
+ async addComment(cardId, author, content, boardId) {
1092
+ const card = await this.getCard(cardId, boardId);
811
1093
  if (!card)
812
1094
  throw new Error(`Card not found: ${cardId}`);
813
1095
  if (!card.comments)
@@ -824,11 +1106,11 @@ var init_KanbanSDK = __esm({
824
1106
  };
825
1107
  card.comments.push(comment);
826
1108
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
827
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
1109
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
828
1110
  return card;
829
1111
  }
830
- async updateComment(cardId, commentId, content) {
831
- const card = await this.getCard(cardId);
1112
+ async updateComment(cardId, commentId, content, boardId) {
1113
+ const card = await this.getCard(cardId, boardId);
832
1114
  if (!card)
833
1115
  throw new Error(`Card not found: ${cardId}`);
834
1116
  const comment = (card.comments || []).find((c) => c.id === commentId);
@@ -836,34 +1118,45 @@ var init_KanbanSDK = __esm({
836
1118
  throw new Error(`Comment not found: ${commentId}`);
837
1119
  comment.content = content;
838
1120
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
839
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
1121
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
840
1122
  return card;
841
1123
  }
842
- async deleteComment(cardId, commentId) {
843
- const card = await this.getCard(cardId);
1124
+ async deleteComment(cardId, commentId, boardId) {
1125
+ const card = await this.getCard(cardId, boardId);
844
1126
  if (!card)
845
1127
  throw new Error(`Card not found: ${cardId}`);
846
1128
  card.comments = (card.comments || []).filter((c) => c.id !== commentId);
847
1129
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
848
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
1130
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
849
1131
  return card;
850
1132
  }
851
- // --- Column management ---
852
- listColumns() {
853
- return readConfig(this.workspaceRoot).columns;
1133
+ // --- Column management (board-scoped) ---
1134
+ listColumns(boardId) {
1135
+ const config = readConfig(this.workspaceRoot);
1136
+ const resolvedId = boardId || config.defaultBoard;
1137
+ const board = config.boards[resolvedId];
1138
+ return board?.columns || [];
854
1139
  }
855
- addColumn(column) {
1140
+ addColumn(column, boardId) {
856
1141
  const config = readConfig(this.workspaceRoot);
857
- if (config.columns.some((c) => c.id === column.id)) {
1142
+ const resolvedId = boardId || config.defaultBoard;
1143
+ const board = config.boards[resolvedId];
1144
+ if (!board)
1145
+ throw new Error(`Board not found: ${resolvedId}`);
1146
+ if (board.columns.some((c) => c.id === column.id)) {
858
1147
  throw new Error(`Column already exists: ${column.id}`);
859
1148
  }
860
- config.columns.push(column);
1149
+ board.columns.push(column);
861
1150
  writeConfig(this.workspaceRoot, config);
862
- return config.columns;
1151
+ return board.columns;
863
1152
  }
864
- updateColumn(columnId, updates) {
1153
+ updateColumn(columnId, updates, boardId) {
865
1154
  const config = readConfig(this.workspaceRoot);
866
- const col = config.columns.find((c) => c.id === columnId);
1155
+ const resolvedId = boardId || config.defaultBoard;
1156
+ const board = config.boards[resolvedId];
1157
+ if (!board)
1158
+ throw new Error(`Board not found: ${resolvedId}`);
1159
+ const col = board.columns.find((c) => c.id === columnId);
867
1160
  if (!col)
868
1161
  throw new Error(`Column not found: ${columnId}`);
869
1162
  if (updates.name !== void 0)
@@ -871,37 +1164,45 @@ var init_KanbanSDK = __esm({
871
1164
  if (updates.color !== void 0)
872
1165
  col.color = updates.color;
873
1166
  writeConfig(this.workspaceRoot, config);
874
- return config.columns;
1167
+ return board.columns;
875
1168
  }
876
- async removeColumn(columnId) {
1169
+ async removeColumn(columnId, boardId) {
877
1170
  const config = readConfig(this.workspaceRoot);
878
- const idx = config.columns.findIndex((c) => c.id === columnId);
1171
+ const resolvedId = boardId || config.defaultBoard;
1172
+ const board = config.boards[resolvedId];
1173
+ if (!board)
1174
+ throw new Error(`Board not found: ${resolvedId}`);
1175
+ const idx = board.columns.findIndex((c) => c.id === columnId);
879
1176
  if (idx === -1)
880
1177
  throw new Error(`Column not found: ${columnId}`);
881
- const cards = await this.listCards();
1178
+ const cards = await this.listCards(void 0, resolvedId);
882
1179
  const cardsInColumn = cards.filter((c) => c.status === columnId);
883
1180
  if (cardsInColumn.length > 0) {
884
1181
  throw new Error(`Cannot remove column "${columnId}": ${cardsInColumn.length} card(s) still in this column`);
885
1182
  }
886
- config.columns.splice(idx, 1);
1183
+ board.columns.splice(idx, 1);
887
1184
  writeConfig(this.workspaceRoot, config);
888
- return config.columns;
1185
+ return board.columns;
889
1186
  }
890
- reorderColumns(columnIds) {
1187
+ reorderColumns(columnIds, boardId) {
891
1188
  const config = readConfig(this.workspaceRoot);
892
- const colMap = new Map(config.columns.map((c) => [c.id, c]));
1189
+ const resolvedId = boardId || config.defaultBoard;
1190
+ const board = config.boards[resolvedId];
1191
+ if (!board)
1192
+ throw new Error(`Board not found: ${resolvedId}`);
1193
+ const colMap = new Map(board.columns.map((c) => [c.id, c]));
893
1194
  for (const id of columnIds) {
894
1195
  if (!colMap.has(id))
895
1196
  throw new Error(`Column not found: ${id}`);
896
1197
  }
897
- if (columnIds.length !== config.columns.length) {
1198
+ if (columnIds.length !== board.columns.length) {
898
1199
  throw new Error("Must include all column IDs when reordering");
899
1200
  }
900
- config.columns = columnIds.map((id) => colMap.get(id));
1201
+ board.columns = columnIds.map((id) => colMap.get(id));
901
1202
  writeConfig(this.workspaceRoot, config);
902
- return config.columns;
1203
+ return board.columns;
903
1204
  }
904
- // --- Settings management ---
1205
+ // --- Settings management (global) ---
905
1206
  getSettings() {
906
1207
  return configToSettings(readConfig(this.workspaceRoot));
907
1208
  }
@@ -911,11 +1212,11 @@ var init_KanbanSDK = __esm({
911
1212
  }
912
1213
  // --- Private helpers ---
913
1214
  async _readMdFiles(dir) {
914
- const entries = await fs3.readdir(dir, { withFileTypes: true });
915
- return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => path4.join(dir, e.name));
1215
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
1216
+ return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => path5.join(dir, e.name));
916
1217
  }
917
1218
  async _loadCard(filePath) {
918
- const content = await fs3.readFile(filePath, "utf-8");
1219
+ const content = await fs4.readFile(filePath, "utf-8");
919
1220
  return parseFeatureFile(content, filePath);
920
1221
  }
921
1222
  };
@@ -924,11 +1225,11 @@ var init_KanbanSDK = __esm({
924
1225
 
925
1226
  // src/standalone/webhooks.ts
926
1227
  function webhooksPath(workspaceRoot) {
927
- return path5.join(workspaceRoot, WEBHOOKS_FILENAME);
1228
+ return path6.join(workspaceRoot, WEBHOOKS_FILENAME);
928
1229
  }
929
1230
  function loadWebhooks(workspaceRoot) {
930
1231
  try {
931
- const raw = fs4.readFileSync(webhooksPath(workspaceRoot), "utf-8");
1232
+ const raw = fs5.readFileSync(webhooksPath(workspaceRoot), "utf-8");
932
1233
  const data = JSON.parse(raw);
933
1234
  return Array.isArray(data) ? data : [];
934
1235
  } catch {
@@ -936,7 +1237,7 @@ function loadWebhooks(workspaceRoot) {
936
1237
  }
937
1238
  }
938
1239
  function saveWebhooks(workspaceRoot, webhooks) {
939
- fs4.writeFileSync(webhooksPath(workspaceRoot), JSON.stringify(webhooks, null, 2) + "\n", "utf-8");
1240
+ fs5.writeFileSync(webhooksPath(workspaceRoot), JSON.stringify(webhooks, null, 2) + "\n", "utf-8");
940
1241
  }
941
1242
  function createWebhook(workspaceRoot, config) {
942
1243
  const webhooks = loadWebhooks(workspaceRoot);
@@ -1018,12 +1319,12 @@ async function deliverWebhook(webhook, event, payload) {
1018
1319
  req.end();
1019
1320
  });
1020
1321
  }
1021
- var fs4, path5, http, https, crypto, WEBHOOKS_FILENAME;
1322
+ var fs5, path6, http, https, crypto, WEBHOOKS_FILENAME;
1022
1323
  var init_webhooks = __esm({
1023
1324
  "src/standalone/webhooks.ts"() {
1024
1325
  "use strict";
1025
- fs4 = __toESM(require("fs"));
1026
- path5 = __toESM(require("path"));
1326
+ fs5 = __toESM(require("fs"));
1327
+ path6 = __toESM(require("path"));
1027
1328
  http = __toESM(require("http"));
1028
1329
  https = __toESM(require("https"));
1029
1330
  crypto = __toESM(require("crypto"));
@@ -4841,7 +5142,7 @@ var init_esm = __esm({
4841
5142
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
4842
5143
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
4843
5144
  if (wantBigintFsStats) {
4844
- this._stat = (path9) => statMethod(path9, { bigint: true });
5145
+ this._stat = (path10) => statMethod(path10, { bigint: true });
4845
5146
  } else {
4846
5147
  this._stat = statMethod;
4847
5148
  }
@@ -4866,8 +5167,8 @@ var init_esm = __esm({
4866
5167
  const par = this.parent;
4867
5168
  const fil = par && par.files;
4868
5169
  if (fil && fil.length > 0) {
4869
- const { path: path9, depth } = par;
4870
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path9));
5170
+ const { path: path10, depth } = par;
5171
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path10));
4871
5172
  const awaited = await Promise.all(slice);
4872
5173
  for (const entry of awaited) {
4873
5174
  if (!entry)
@@ -4907,20 +5208,20 @@ var init_esm = __esm({
4907
5208
  this.reading = false;
4908
5209
  }
4909
5210
  }
4910
- async _exploreDir(path9, depth) {
5211
+ async _exploreDir(path10, depth) {
4911
5212
  let files;
4912
5213
  try {
4913
- files = await (0, import_promises.readdir)(path9, this._rdOptions);
5214
+ files = await (0, import_promises.readdir)(path10, this._rdOptions);
4914
5215
  } catch (error) {
4915
5216
  this._onError(error);
4916
5217
  }
4917
- return { files, depth, path: path9 };
5218
+ return { files, depth, path: path10 };
4918
5219
  }
4919
- async _formatEntry(dirent, path9) {
5220
+ async _formatEntry(dirent, path10) {
4920
5221
  let entry;
4921
5222
  const basename7 = this._isDirent ? dirent.name : dirent;
4922
5223
  try {
4923
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path9, basename7));
5224
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path10, basename7));
4924
5225
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename7 };
4925
5226
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
4926
5227
  } catch (err) {
@@ -4977,16 +5278,16 @@ var init_esm = __esm({
4977
5278
  });
4978
5279
 
4979
5280
  // node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/handler.js
4980
- function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
5281
+ function createFsWatchInstance(path10, options, listener, errHandler, emitRaw) {
4981
5282
  const handleEvent = (rawEvent, evPath) => {
4982
- listener(path9);
4983
- emitRaw(rawEvent, evPath, { watchedPath: path9 });
4984
- if (evPath && path9 !== evPath) {
4985
- fsWatchBroadcast(sysPath.resolve(path9, evPath), KEY_LISTENERS, sysPath.join(path9, evPath));
5283
+ listener(path10);
5284
+ emitRaw(rawEvent, evPath, { watchedPath: path10 });
5285
+ if (evPath && path10 !== evPath) {
5286
+ fsWatchBroadcast(sysPath.resolve(path10, evPath), KEY_LISTENERS, sysPath.join(path10, evPath));
4986
5287
  }
4987
5288
  };
4988
5289
  try {
4989
- return (0, import_fs.watch)(path9, {
5290
+ return (0, import_fs.watch)(path10, {
4990
5291
  persistent: options.persistent
4991
5292
  }, handleEvent);
4992
5293
  } catch (error) {
@@ -5334,12 +5635,12 @@ var init_handler = __esm({
5334
5635
  listener(val1, val2, val3);
5335
5636
  });
5336
5637
  };
5337
- setFsWatchListener = (path9, fullPath, options, handlers) => {
5638
+ setFsWatchListener = (path10, fullPath, options, handlers) => {
5338
5639
  const { listener, errHandler, rawEmitter } = handlers;
5339
5640
  let cont = FsWatchInstances.get(fullPath);
5340
5641
  let watcher;
5341
5642
  if (!options.persistent) {
5342
- watcher = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
5643
+ watcher = createFsWatchInstance(path10, options, listener, errHandler, rawEmitter);
5343
5644
  if (!watcher)
5344
5645
  return;
5345
5646
  return watcher.close.bind(watcher);
@@ -5350,7 +5651,7 @@ var init_handler = __esm({
5350
5651
  addAndConvert(cont, KEY_RAW, rawEmitter);
5351
5652
  } else {
5352
5653
  watcher = createFsWatchInstance(
5353
- path9,
5654
+ path10,
5354
5655
  options,
5355
5656
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
5356
5657
  errHandler,
@@ -5365,7 +5666,7 @@ var init_handler = __esm({
5365
5666
  cont.watcherUnusable = true;
5366
5667
  if (isWindows && error.code === "EPERM") {
5367
5668
  try {
5368
- const fd = await (0, import_promises2.open)(path9, "r");
5669
+ const fd = await (0, import_promises2.open)(path10, "r");
5369
5670
  await fd.close();
5370
5671
  broadcastErr(error);
5371
5672
  } catch (err) {
@@ -5396,7 +5697,7 @@ var init_handler = __esm({
5396
5697
  };
5397
5698
  };
5398
5699
  FsWatchFileInstances = /* @__PURE__ */ new Map();
5399
- setFsWatchFileListener = (path9, fullPath, options, handlers) => {
5700
+ setFsWatchFileListener = (path10, fullPath, options, handlers) => {
5400
5701
  const { listener, rawEmitter } = handlers;
5401
5702
  let cont = FsWatchFileInstances.get(fullPath);
5402
5703
  const copts = cont && cont.options;
@@ -5418,7 +5719,7 @@ var init_handler = __esm({
5418
5719
  });
5419
5720
  const currmtime = curr.mtimeMs;
5420
5721
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
5421
- foreach(cont.listeners, (listener2) => listener2(path9, curr));
5722
+ foreach(cont.listeners, (listener2) => listener2(path10, curr));
5422
5723
  }
5423
5724
  })
5424
5725
  };
@@ -5446,13 +5747,13 @@ var init_handler = __esm({
5446
5747
  * @param listener on fs change
5447
5748
  * @returns closer for the watcher instance
5448
5749
  */
5449
- _watchWithNodeFs(path9, listener) {
5750
+ _watchWithNodeFs(path10, listener) {
5450
5751
  const opts = this.fsw.options;
5451
- const directory = sysPath.dirname(path9);
5452
- const basename7 = sysPath.basename(path9);
5752
+ const directory = sysPath.dirname(path10);
5753
+ const basename7 = sysPath.basename(path10);
5453
5754
  const parent = this.fsw._getWatchedDir(directory);
5454
5755
  parent.add(basename7);
5455
- const absolutePath = sysPath.resolve(path9);
5756
+ const absolutePath = sysPath.resolve(path10);
5456
5757
  const options = {
5457
5758
  persistent: opts.persistent
5458
5759
  };
@@ -5462,12 +5763,12 @@ var init_handler = __esm({
5462
5763
  if (opts.usePolling) {
5463
5764
  const enableBin = opts.interval !== opts.binaryInterval;
5464
5765
  options.interval = enableBin && isBinaryPath(basename7) ? opts.binaryInterval : opts.interval;
5465
- closer = setFsWatchFileListener(path9, absolutePath, options, {
5766
+ closer = setFsWatchFileListener(path10, absolutePath, options, {
5466
5767
  listener,
5467
5768
  rawEmitter: this.fsw._emitRaw
5468
5769
  });
5469
5770
  } else {
5470
- closer = setFsWatchListener(path9, absolutePath, options, {
5771
+ closer = setFsWatchListener(path10, absolutePath, options, {
5471
5772
  listener,
5472
5773
  errHandler: this._boundHandleError,
5473
5774
  rawEmitter: this.fsw._emitRaw
@@ -5489,7 +5790,7 @@ var init_handler = __esm({
5489
5790
  let prevStats = stats;
5490
5791
  if (parent.has(basename7))
5491
5792
  return;
5492
- const listener = async (path9, newStats) => {
5793
+ const listener = async (path10, newStats) => {
5493
5794
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
5494
5795
  return;
5495
5796
  if (!newStats || newStats.mtimeMs === 0) {
@@ -5503,11 +5804,11 @@ var init_handler = __esm({
5503
5804
  this.fsw._emit(EV.CHANGE, file, newStats2);
5504
5805
  }
5505
5806
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
5506
- this.fsw._closeFile(path9);
5807
+ this.fsw._closeFile(path10);
5507
5808
  prevStats = newStats2;
5508
5809
  const closer2 = this._watchWithNodeFs(file, listener);
5509
5810
  if (closer2)
5510
- this.fsw._addPathCloser(path9, closer2);
5811
+ this.fsw._addPathCloser(path10, closer2);
5511
5812
  } else {
5512
5813
  prevStats = newStats2;
5513
5814
  }
@@ -5539,7 +5840,7 @@ var init_handler = __esm({
5539
5840
  * @param item basename of this item
5540
5841
  * @returns true if no more processing is needed for this entry.
5541
5842
  */
5542
- async _handleSymlink(entry, directory, path9, item) {
5843
+ async _handleSymlink(entry, directory, path10, item) {
5543
5844
  if (this.fsw.closed) {
5544
5845
  return;
5545
5846
  }
@@ -5549,7 +5850,7 @@ var init_handler = __esm({
5549
5850
  this.fsw._incrReadyCount();
5550
5851
  let linkPath;
5551
5852
  try {
5552
- linkPath = await (0, import_promises2.realpath)(path9);
5853
+ linkPath = await (0, import_promises2.realpath)(path10);
5553
5854
  } catch (e) {
5554
5855
  this.fsw._emitReady();
5555
5856
  return true;
@@ -5559,12 +5860,12 @@ var init_handler = __esm({
5559
5860
  if (dir.has(item)) {
5560
5861
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
5561
5862
  this.fsw._symlinkPaths.set(full, linkPath);
5562
- this.fsw._emit(EV.CHANGE, path9, entry.stats);
5863
+ this.fsw._emit(EV.CHANGE, path10, entry.stats);
5563
5864
  }
5564
5865
  } else {
5565
5866
  dir.add(item);
5566
5867
  this.fsw._symlinkPaths.set(full, linkPath);
5567
- this.fsw._emit(EV.ADD, path9, entry.stats);
5868
+ this.fsw._emit(EV.ADD, path10, entry.stats);
5568
5869
  }
5569
5870
  this.fsw._emitReady();
5570
5871
  return true;
@@ -5593,9 +5894,9 @@ var init_handler = __esm({
5593
5894
  return;
5594
5895
  }
5595
5896
  const item = entry.path;
5596
- let path9 = sysPath.join(directory, item);
5897
+ let path10 = sysPath.join(directory, item);
5597
5898
  current.add(item);
5598
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
5899
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path10, item)) {
5599
5900
  return;
5600
5901
  }
5601
5902
  if (this.fsw.closed) {
@@ -5604,8 +5905,8 @@ var init_handler = __esm({
5604
5905
  }
5605
5906
  if (item === target || !target && !previous.has(item)) {
5606
5907
  this.fsw._incrReadyCount();
5607
- path9 = sysPath.join(dir, sysPath.relative(dir, path9));
5608
- this._addToNodeFs(path9, initialAdd, wh, depth + 1);
5908
+ path10 = sysPath.join(dir, sysPath.relative(dir, path10));
5909
+ this._addToNodeFs(path10, initialAdd, wh, depth + 1);
5609
5910
  }
5610
5911
  }).on(EV.ERROR, this._boundHandleError);
5611
5912
  return new Promise((resolve6, reject) => {
@@ -5674,13 +5975,13 @@ var init_handler = __esm({
5674
5975
  * @param depth Child path actually targeted for watch
5675
5976
  * @param target Child path actually targeted for watch
5676
5977
  */
5677
- async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
5978
+ async _addToNodeFs(path10, initialAdd, priorWh, depth, target) {
5678
5979
  const ready = this.fsw._emitReady;
5679
- if (this.fsw._isIgnored(path9) || this.fsw.closed) {
5980
+ if (this.fsw._isIgnored(path10) || this.fsw.closed) {
5680
5981
  ready();
5681
5982
  return false;
5682
5983
  }
5683
- const wh = this.fsw._getWatchHelpers(path9);
5984
+ const wh = this.fsw._getWatchHelpers(path10);
5684
5985
  if (priorWh) {
5685
5986
  wh.filterPath = (entry) => priorWh.filterPath(entry);
5686
5987
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -5696,8 +5997,8 @@ var init_handler = __esm({
5696
5997
  const follow = this.fsw.options.followSymlinks;
5697
5998
  let closer;
5698
5999
  if (stats.isDirectory()) {
5699
- const absPath = sysPath.resolve(path9);
5700
- const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
6000
+ const absPath = sysPath.resolve(path10);
6001
+ const targetPath = follow ? await (0, import_promises2.realpath)(path10) : path10;
5701
6002
  if (this.fsw.closed)
5702
6003
  return;
5703
6004
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -5707,29 +6008,29 @@ var init_handler = __esm({
5707
6008
  this.fsw._symlinkPaths.set(absPath, targetPath);
5708
6009
  }
5709
6010
  } else if (stats.isSymbolicLink()) {
5710
- const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
6011
+ const targetPath = follow ? await (0, import_promises2.realpath)(path10) : path10;
5711
6012
  if (this.fsw.closed)
5712
6013
  return;
5713
6014
  const parent = sysPath.dirname(wh.watchPath);
5714
6015
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
5715
6016
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
5716
- closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
6017
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path10, wh, targetPath);
5717
6018
  if (this.fsw.closed)
5718
6019
  return;
5719
6020
  if (targetPath !== void 0) {
5720
- this.fsw._symlinkPaths.set(sysPath.resolve(path9), targetPath);
6021
+ this.fsw._symlinkPaths.set(sysPath.resolve(path10), targetPath);
5721
6022
  }
5722
6023
  } else {
5723
6024
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
5724
6025
  }
5725
6026
  ready();
5726
6027
  if (closer)
5727
- this.fsw._addPathCloser(path9, closer);
6028
+ this.fsw._addPathCloser(path10, closer);
5728
6029
  return false;
5729
6030
  } catch (error) {
5730
6031
  if (this.fsw._handleError(error)) {
5731
6032
  ready();
5732
- return path9;
6033
+ return path10;
5733
6034
  }
5734
6035
  }
5735
6036
  }
@@ -5764,26 +6065,26 @@ function createPattern(matcher) {
5764
6065
  }
5765
6066
  return () => false;
5766
6067
  }
5767
- function normalizePath(path9) {
5768
- if (typeof path9 !== "string")
6068
+ function normalizePath(path10) {
6069
+ if (typeof path10 !== "string")
5769
6070
  throw new Error("string expected");
5770
- path9 = sysPath2.normalize(path9);
5771
- path9 = path9.replace(/\\/g, "/");
6071
+ path10 = sysPath2.normalize(path10);
6072
+ path10 = path10.replace(/\\/g, "/");
5772
6073
  let prepend = false;
5773
- if (path9.startsWith("//"))
6074
+ if (path10.startsWith("//"))
5774
6075
  prepend = true;
5775
6076
  const DOUBLE_SLASH_RE2 = /\/\//;
5776
- while (path9.match(DOUBLE_SLASH_RE2))
5777
- path9 = path9.replace(DOUBLE_SLASH_RE2, "/");
6077
+ while (path10.match(DOUBLE_SLASH_RE2))
6078
+ path10 = path10.replace(DOUBLE_SLASH_RE2, "/");
5778
6079
  if (prepend)
5779
- path9 = "/" + path9;
5780
- return path9;
6080
+ path10 = "/" + path10;
6081
+ return path10;
5781
6082
  }
5782
6083
  function matchPatterns(patterns, testString, stats) {
5783
- const path9 = normalizePath(testString);
6084
+ const path10 = normalizePath(testString);
5784
6085
  for (let index = 0; index < patterns.length; index++) {
5785
6086
  const pattern = patterns[index];
5786
- if (pattern(path9, stats)) {
6087
+ if (pattern(path10, stats)) {
5787
6088
  return true;
5788
6089
  }
5789
6090
  }
@@ -5847,19 +6148,19 @@ var init_esm2 = __esm({
5847
6148
  }
5848
6149
  return str;
5849
6150
  };
5850
- normalizePathToUnix = (path9) => toUnix(sysPath2.normalize(toUnix(path9)));
5851
- normalizeIgnored = (cwd = "") => (path9) => {
5852
- if (typeof path9 === "string") {
5853
- return normalizePathToUnix(sysPath2.isAbsolute(path9) ? path9 : sysPath2.join(cwd, path9));
6151
+ normalizePathToUnix = (path10) => toUnix(sysPath2.normalize(toUnix(path10)));
6152
+ normalizeIgnored = (cwd = "") => (path10) => {
6153
+ if (typeof path10 === "string") {
6154
+ return normalizePathToUnix(sysPath2.isAbsolute(path10) ? path10 : sysPath2.join(cwd, path10));
5854
6155
  } else {
5855
- return path9;
6156
+ return path10;
5856
6157
  }
5857
6158
  };
5858
- getAbsolutePath = (path9, cwd) => {
5859
- if (sysPath2.isAbsolute(path9)) {
5860
- return path9;
6159
+ getAbsolutePath = (path10, cwd) => {
6160
+ if (sysPath2.isAbsolute(path10)) {
6161
+ return path10;
5861
6162
  }
5862
- return sysPath2.join(cwd, path9);
6163
+ return sysPath2.join(cwd, path10);
5863
6164
  };
5864
6165
  EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
5865
6166
  DirEntry = class {
@@ -5914,10 +6215,10 @@ var init_esm2 = __esm({
5914
6215
  STAT_METHOD_F = "stat";
5915
6216
  STAT_METHOD_L = "lstat";
5916
6217
  WatchHelper = class {
5917
- constructor(path9, follow, fsw) {
6218
+ constructor(path10, follow, fsw) {
5918
6219
  this.fsw = fsw;
5919
- const watchPath = path9;
5920
- this.path = path9 = path9.replace(REPLACER_RE, "");
6220
+ const watchPath = path10;
6221
+ this.path = path10 = path10.replace(REPLACER_RE, "");
5921
6222
  this.watchPath = watchPath;
5922
6223
  this.fullWatchPath = sysPath2.resolve(watchPath);
5923
6224
  this.dirParts = [];
@@ -6039,20 +6340,20 @@ var init_esm2 = __esm({
6039
6340
  this._closePromise = void 0;
6040
6341
  let paths = unifyPaths(paths_);
6041
6342
  if (cwd) {
6042
- paths = paths.map((path9) => {
6043
- const absPath = getAbsolutePath(path9, cwd);
6343
+ paths = paths.map((path10) => {
6344
+ const absPath = getAbsolutePath(path10, cwd);
6044
6345
  return absPath;
6045
6346
  });
6046
6347
  }
6047
- paths.forEach((path9) => {
6048
- this._removeIgnoredPath(path9);
6348
+ paths.forEach((path10) => {
6349
+ this._removeIgnoredPath(path10);
6049
6350
  });
6050
6351
  this._userIgnored = void 0;
6051
6352
  if (!this._readyCount)
6052
6353
  this._readyCount = 0;
6053
6354
  this._readyCount += paths.length;
6054
- Promise.all(paths.map(async (path9) => {
6055
- const res = await this._nodeFsHandler._addToNodeFs(path9, !_internal, void 0, 0, _origAdd);
6355
+ Promise.all(paths.map(async (path10) => {
6356
+ const res = await this._nodeFsHandler._addToNodeFs(path10, !_internal, void 0, 0, _origAdd);
6056
6357
  if (res)
6057
6358
  this._emitReady();
6058
6359
  return res;
@@ -6074,17 +6375,17 @@ var init_esm2 = __esm({
6074
6375
  return this;
6075
6376
  const paths = unifyPaths(paths_);
6076
6377
  const { cwd } = this.options;
6077
- paths.forEach((path9) => {
6078
- if (!sysPath2.isAbsolute(path9) && !this._closers.has(path9)) {
6378
+ paths.forEach((path10) => {
6379
+ if (!sysPath2.isAbsolute(path10) && !this._closers.has(path10)) {
6079
6380
  if (cwd)
6080
- path9 = sysPath2.join(cwd, path9);
6081
- path9 = sysPath2.resolve(path9);
6381
+ path10 = sysPath2.join(cwd, path10);
6382
+ path10 = sysPath2.resolve(path10);
6082
6383
  }
6083
- this._closePath(path9);
6084
- this._addIgnoredPath(path9);
6085
- if (this._watched.has(path9)) {
6384
+ this._closePath(path10);
6385
+ this._addIgnoredPath(path10);
6386
+ if (this._watched.has(path10)) {
6086
6387
  this._addIgnoredPath({
6087
- path: path9,
6388
+ path: path10,
6088
6389
  recursive: true
6089
6390
  });
6090
6391
  }
@@ -6148,38 +6449,38 @@ var init_esm2 = __esm({
6148
6449
  * @param stats arguments to be passed with event
6149
6450
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
6150
6451
  */
6151
- async _emit(event, path9, stats) {
6452
+ async _emit(event, path10, stats) {
6152
6453
  if (this.closed)
6153
6454
  return;
6154
6455
  const opts = this.options;
6155
6456
  if (isWindows)
6156
- path9 = sysPath2.normalize(path9);
6457
+ path10 = sysPath2.normalize(path10);
6157
6458
  if (opts.cwd)
6158
- path9 = sysPath2.relative(opts.cwd, path9);
6159
- const args = [path9];
6459
+ path10 = sysPath2.relative(opts.cwd, path10);
6460
+ const args = [path10];
6160
6461
  if (stats != null)
6161
6462
  args.push(stats);
6162
6463
  const awf = opts.awaitWriteFinish;
6163
6464
  let pw;
6164
- if (awf && (pw = this._pendingWrites.get(path9))) {
6465
+ if (awf && (pw = this._pendingWrites.get(path10))) {
6165
6466
  pw.lastChange = /* @__PURE__ */ new Date();
6166
6467
  return this;
6167
6468
  }
6168
6469
  if (opts.atomic) {
6169
6470
  if (event === EVENTS.UNLINK) {
6170
- this._pendingUnlinks.set(path9, [event, ...args]);
6471
+ this._pendingUnlinks.set(path10, [event, ...args]);
6171
6472
  setTimeout(() => {
6172
- this._pendingUnlinks.forEach((entry, path10) => {
6473
+ this._pendingUnlinks.forEach((entry, path11) => {
6173
6474
  this.emit(...entry);
6174
6475
  this.emit(EVENTS.ALL, ...entry);
6175
- this._pendingUnlinks.delete(path10);
6476
+ this._pendingUnlinks.delete(path11);
6176
6477
  });
6177
6478
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
6178
6479
  return this;
6179
6480
  }
6180
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
6481
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path10)) {
6181
6482
  event = EVENTS.CHANGE;
6182
- this._pendingUnlinks.delete(path9);
6483
+ this._pendingUnlinks.delete(path10);
6183
6484
  }
6184
6485
  }
6185
6486
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -6197,16 +6498,16 @@ var init_esm2 = __esm({
6197
6498
  this.emitWithAll(event, args);
6198
6499
  }
6199
6500
  };
6200
- this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
6501
+ this._awaitWriteFinish(path10, awf.stabilityThreshold, event, awfEmit);
6201
6502
  return this;
6202
6503
  }
6203
6504
  if (event === EVENTS.CHANGE) {
6204
- const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
6505
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path10, 50);
6205
6506
  if (isThrottled)
6206
6507
  return this;
6207
6508
  }
6208
6509
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
6209
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path9) : path9;
6510
+ const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path10) : path10;
6210
6511
  let stats2;
6211
6512
  try {
6212
6513
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -6237,23 +6538,23 @@ var init_esm2 = __esm({
6237
6538
  * @param timeout duration of time to suppress duplicate actions
6238
6539
  * @returns tracking object or false if action should be suppressed
6239
6540
  */
6240
- _throttle(actionType, path9, timeout) {
6541
+ _throttle(actionType, path10, timeout) {
6241
6542
  if (!this._throttled.has(actionType)) {
6242
6543
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
6243
6544
  }
6244
6545
  const action = this._throttled.get(actionType);
6245
6546
  if (!action)
6246
6547
  throw new Error("invalid throttle");
6247
- const actionPath = action.get(path9);
6548
+ const actionPath = action.get(path10);
6248
6549
  if (actionPath) {
6249
6550
  actionPath.count++;
6250
6551
  return false;
6251
6552
  }
6252
6553
  let timeoutObject;
6253
6554
  const clear = () => {
6254
- const item = action.get(path9);
6555
+ const item = action.get(path10);
6255
6556
  const count = item ? item.count : 0;
6256
- action.delete(path9);
6557
+ action.delete(path10);
6257
6558
  clearTimeout(timeoutObject);
6258
6559
  if (item)
6259
6560
  clearTimeout(item.timeoutObject);
@@ -6261,7 +6562,7 @@ var init_esm2 = __esm({
6261
6562
  };
6262
6563
  timeoutObject = setTimeout(clear, timeout);
6263
6564
  const thr = { timeoutObject, clear, count: 0 };
6264
- action.set(path9, thr);
6565
+ action.set(path10, thr);
6265
6566
  return thr;
6266
6567
  }
6267
6568
  _incrReadyCount() {
@@ -6275,44 +6576,44 @@ var init_esm2 = __esm({
6275
6576
  * @param event
6276
6577
  * @param awfEmit Callback to be called when ready for event to be emitted.
6277
6578
  */
6278
- _awaitWriteFinish(path9, threshold, event, awfEmit) {
6579
+ _awaitWriteFinish(path10, threshold, event, awfEmit) {
6279
6580
  const awf = this.options.awaitWriteFinish;
6280
6581
  if (typeof awf !== "object")
6281
6582
  return;
6282
6583
  const pollInterval = awf.pollInterval;
6283
6584
  let timeoutHandler;
6284
- let fullPath = path9;
6285
- if (this.options.cwd && !sysPath2.isAbsolute(path9)) {
6286
- fullPath = sysPath2.join(this.options.cwd, path9);
6585
+ let fullPath = path10;
6586
+ if (this.options.cwd && !sysPath2.isAbsolute(path10)) {
6587
+ fullPath = sysPath2.join(this.options.cwd, path10);
6287
6588
  }
6288
6589
  const now = /* @__PURE__ */ new Date();
6289
6590
  const writes = this._pendingWrites;
6290
6591
  function awaitWriteFinishFn(prevStat) {
6291
6592
  (0, import_fs2.stat)(fullPath, (err, curStat) => {
6292
- if (err || !writes.has(path9)) {
6593
+ if (err || !writes.has(path10)) {
6293
6594
  if (err && err.code !== "ENOENT")
6294
6595
  awfEmit(err);
6295
6596
  return;
6296
6597
  }
6297
6598
  const now2 = Number(/* @__PURE__ */ new Date());
6298
6599
  if (prevStat && curStat.size !== prevStat.size) {
6299
- writes.get(path9).lastChange = now2;
6600
+ writes.get(path10).lastChange = now2;
6300
6601
  }
6301
- const pw = writes.get(path9);
6602
+ const pw = writes.get(path10);
6302
6603
  const df = now2 - pw.lastChange;
6303
6604
  if (df >= threshold) {
6304
- writes.delete(path9);
6605
+ writes.delete(path10);
6305
6606
  awfEmit(void 0, curStat);
6306
6607
  } else {
6307
6608
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
6308
6609
  }
6309
6610
  });
6310
6611
  }
6311
- if (!writes.has(path9)) {
6312
- writes.set(path9, {
6612
+ if (!writes.has(path10)) {
6613
+ writes.set(path10, {
6313
6614
  lastChange: now,
6314
6615
  cancelWait: () => {
6315
- writes.delete(path9);
6616
+ writes.delete(path10);
6316
6617
  clearTimeout(timeoutHandler);
6317
6618
  return event;
6318
6619
  }
@@ -6323,8 +6624,8 @@ var init_esm2 = __esm({
6323
6624
  /**
6324
6625
  * Determines whether user has asked to ignore this path.
6325
6626
  */
6326
- _isIgnored(path9, stats) {
6327
- if (this.options.atomic && DOT_RE.test(path9))
6627
+ _isIgnored(path10, stats) {
6628
+ if (this.options.atomic && DOT_RE.test(path10))
6328
6629
  return true;
6329
6630
  if (!this._userIgnored) {
6330
6631
  const { cwd } = this.options;
@@ -6334,17 +6635,17 @@ var init_esm2 = __esm({
6334
6635
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
6335
6636
  this._userIgnored = anymatch(list, void 0);
6336
6637
  }
6337
- return this._userIgnored(path9, stats);
6638
+ return this._userIgnored(path10, stats);
6338
6639
  }
6339
- _isntIgnored(path9, stat4) {
6340
- return !this._isIgnored(path9, stat4);
6640
+ _isntIgnored(path10, stat4) {
6641
+ return !this._isIgnored(path10, stat4);
6341
6642
  }
6342
6643
  /**
6343
6644
  * Provides a set of common helpers and properties relating to symlink handling.
6344
6645
  * @param path file or directory pattern being watched
6345
6646
  */
6346
- _getWatchHelpers(path9) {
6347
- return new WatchHelper(path9, this.options.followSymlinks, this);
6647
+ _getWatchHelpers(path10) {
6648
+ return new WatchHelper(path10, this.options.followSymlinks, this);
6348
6649
  }
6349
6650
  // Directory helpers
6350
6651
  // -----------------
@@ -6376,63 +6677,63 @@ var init_esm2 = __esm({
6376
6677
  * @param item base path of item/directory
6377
6678
  */
6378
6679
  _remove(directory, item, isDirectory) {
6379
- const path9 = sysPath2.join(directory, item);
6380
- const fullPath = sysPath2.resolve(path9);
6381
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path9) || this._watched.has(fullPath);
6382
- if (!this._throttle("remove", path9, 100))
6680
+ const path10 = sysPath2.join(directory, item);
6681
+ const fullPath = sysPath2.resolve(path10);
6682
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path10) || this._watched.has(fullPath);
6683
+ if (!this._throttle("remove", path10, 100))
6383
6684
  return;
6384
6685
  if (!isDirectory && this._watched.size === 1) {
6385
6686
  this.add(directory, item, true);
6386
6687
  }
6387
- const wp = this._getWatchedDir(path9);
6688
+ const wp = this._getWatchedDir(path10);
6388
6689
  const nestedDirectoryChildren = wp.getChildren();
6389
- nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
6690
+ nestedDirectoryChildren.forEach((nested) => this._remove(path10, nested));
6390
6691
  const parent = this._getWatchedDir(directory);
6391
6692
  const wasTracked = parent.has(item);
6392
6693
  parent.remove(item);
6393
6694
  if (this._symlinkPaths.has(fullPath)) {
6394
6695
  this._symlinkPaths.delete(fullPath);
6395
6696
  }
6396
- let relPath = path9;
6697
+ let relPath = path10;
6397
6698
  if (this.options.cwd)
6398
- relPath = sysPath2.relative(this.options.cwd, path9);
6699
+ relPath = sysPath2.relative(this.options.cwd, path10);
6399
6700
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
6400
6701
  const event = this._pendingWrites.get(relPath).cancelWait();
6401
6702
  if (event === EVENTS.ADD)
6402
6703
  return;
6403
6704
  }
6404
- this._watched.delete(path9);
6705
+ this._watched.delete(path10);
6405
6706
  this._watched.delete(fullPath);
6406
6707
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
6407
- if (wasTracked && !this._isIgnored(path9))
6408
- this._emit(eventName, path9);
6409
- this._closePath(path9);
6708
+ if (wasTracked && !this._isIgnored(path10))
6709
+ this._emit(eventName, path10);
6710
+ this._closePath(path10);
6410
6711
  }
6411
6712
  /**
6412
6713
  * Closes all watchers for a path
6413
6714
  */
6414
- _closePath(path9) {
6415
- this._closeFile(path9);
6416
- const dir = sysPath2.dirname(path9);
6417
- this._getWatchedDir(dir).remove(sysPath2.basename(path9));
6715
+ _closePath(path10) {
6716
+ this._closeFile(path10);
6717
+ const dir = sysPath2.dirname(path10);
6718
+ this._getWatchedDir(dir).remove(sysPath2.basename(path10));
6418
6719
  }
6419
6720
  /**
6420
6721
  * Closes only file-specific watchers
6421
6722
  */
6422
- _closeFile(path9) {
6423
- const closers = this._closers.get(path9);
6723
+ _closeFile(path10) {
6724
+ const closers = this._closers.get(path10);
6424
6725
  if (!closers)
6425
6726
  return;
6426
6727
  closers.forEach((closer) => closer());
6427
- this._closers.delete(path9);
6728
+ this._closers.delete(path10);
6428
6729
  }
6429
- _addPathCloser(path9, closer) {
6730
+ _addPathCloser(path10, closer) {
6430
6731
  if (!closer)
6431
6732
  return;
6432
- let list = this._closers.get(path9);
6733
+ let list = this._closers.get(path10);
6433
6734
  if (!list) {
6434
6735
  list = [];
6435
- this._closers.set(path9, list);
6736
+ this._closers.set(path10, list);
6436
6737
  }
6437
6738
  list.push(closer);
6438
6739
  }
@@ -6464,13 +6765,14 @@ __export(server_exports, {
6464
6765
  startServer: () => startServer
6465
6766
  });
6466
6767
  function startServer(featuresDir, port, webviewDir) {
6467
- const absoluteFeaturesDir = path6.resolve(featuresDir);
6768
+ const absoluteFeaturesDir = path7.resolve(featuresDir);
6468
6769
  let features = [];
6469
6770
  let migrating = false;
6470
6771
  let currentEditingFeatureId = null;
6471
6772
  let lastWrittenContent = "";
6472
- const resolvedWebviewDir = webviewDir || path6.join(__dirname, "standalone-webview");
6473
- const workspaceRoot = path6.dirname(absoluteFeaturesDir);
6773
+ let currentBoardId;
6774
+ const resolvedWebviewDir = webviewDir || path7.join(__dirname, "standalone-webview");
6775
+ const workspaceRoot = path7.dirname(absoluteFeaturesDir);
6474
6776
  const sdk = new KanbanSDK(absoluteFeaturesDir);
6475
6777
  function sanitizeFeature(feature) {
6476
6778
  const { filePath: _, ...rest } = feature;
@@ -6536,17 +6838,20 @@ function startServer(featuresDir, port, webviewDir) {
6536
6838
  </body>
6537
6839
  </html>`;
6538
6840
  async function loadFeatures() {
6539
- features = await sdk.listCards(sdk.listColumns().map((c) => c.id));
6841
+ features = await sdk.listCards(sdk.listColumns(currentBoardId).map((c) => c.id), currentBoardId);
6540
6842
  }
6541
6843
  function buildInitMessage() {
6844
+ const config = readConfig(workspaceRoot);
6542
6845
  const settings = sdk.getSettings();
6543
6846
  settings.showBuildWithAI = false;
6544
6847
  settings.markdownEditorMode = false;
6545
6848
  return {
6546
6849
  type: "init",
6547
6850
  features,
6548
- columns: sdk.listColumns(),
6549
- settings
6851
+ columns: sdk.listColumns(currentBoardId),
6852
+ settings,
6853
+ boards: sdk.listBoards(),
6854
+ currentBoard: currentBoardId || config.defaultBoard
6550
6855
  };
6551
6856
  }
6552
6857
  function broadcast(message) {
@@ -6566,7 +6871,8 @@ function startServer(featuresDir, port, webviewDir) {
6566
6871
  priority: data.priority,
6567
6872
  assignee: data.assignee,
6568
6873
  dueDate: data.dueDate,
6569
- labels: data.labels
6874
+ labels: data.labels,
6875
+ boardId: currentBoardId
6570
6876
  });
6571
6877
  await loadFeatures();
6572
6878
  broadcast(buildInitMessage());
@@ -6583,7 +6889,7 @@ function startServer(featuresDir, port, webviewDir) {
6583
6889
  const oldStatus = feature.status;
6584
6890
  migrating = true;
6585
6891
  try {
6586
- const updated = await sdk.moveCard(featureId, newStatus, newOrder);
6892
+ const updated = await sdk.moveCard(featureId, newStatus, newOrder, currentBoardId);
6587
6893
  await loadFeatures();
6588
6894
  broadcast(buildInitMessage());
6589
6895
  fireWebhooks(workspaceRoot, "task.moved", {
@@ -6601,7 +6907,7 @@ function startServer(featuresDir, port, webviewDir) {
6601
6907
  return null;
6602
6908
  migrating = true;
6603
6909
  try {
6604
- const updated = await sdk.updateCard(featureId, updates);
6910
+ const updated = await sdk.updateCard(featureId, updates, currentBoardId);
6605
6911
  lastWrittenContent = serializeFeature(updated);
6606
6912
  await loadFeatures();
6607
6913
  broadcast(buildInitMessage());
@@ -6617,7 +6923,7 @@ function startServer(featuresDir, port, webviewDir) {
6617
6923
  return false;
6618
6924
  try {
6619
6925
  const deleted = sanitizeFeature(feature);
6620
- await sdk.deleteCard(featureId);
6926
+ await sdk.deleteCard(featureId, currentBoardId);
6621
6927
  await loadFeatures();
6622
6928
  broadcast(buildInitMessage());
6623
6929
  fireWebhooks(workspaceRoot, "task.deleted", deleted);
@@ -6628,7 +6934,7 @@ function startServer(featuresDir, port, webviewDir) {
6628
6934
  }
6629
6935
  }
6630
6936
  function doAddColumn(name, color) {
6631
- const existingColumns = sdk.listColumns();
6937
+ const existingColumns = sdk.listColumns(currentBoardId);
6632
6938
  const id = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
6633
6939
  let uniqueId = id;
6634
6940
  let counter = 1;
@@ -6636,14 +6942,14 @@ function startServer(featuresDir, port, webviewDir) {
6636
6942
  uniqueId = `${id}-${counter++}`;
6637
6943
  }
6638
6944
  const column = { id: uniqueId, name, color };
6639
- sdk.addColumn(column);
6945
+ sdk.addColumn(column, currentBoardId);
6640
6946
  broadcast(buildInitMessage());
6641
6947
  fireWebhooks(workspaceRoot, "column.created", column);
6642
6948
  return column;
6643
6949
  }
6644
6950
  function doEditColumn(columnId, updates) {
6645
6951
  try {
6646
- const columns = sdk.updateColumn(columnId, { name: updates.name, color: updates.color });
6952
+ const columns = sdk.updateColumn(columnId, { name: updates.name, color: updates.color }, currentBoardId);
6647
6953
  const updated = columns.find((c) => c.id === columnId) ?? null;
6648
6954
  broadcast(buildInitMessage());
6649
6955
  if (updated)
@@ -6655,13 +6961,13 @@ function startServer(featuresDir, port, webviewDir) {
6655
6961
  }
6656
6962
  async function doRemoveColumn(columnId) {
6657
6963
  try {
6658
- const columns = sdk.listColumns();
6964
+ const columns = sdk.listColumns(currentBoardId);
6659
6965
  if (columns.length <= 1)
6660
6966
  return { removed: false, error: "Cannot remove last column" };
6661
6967
  const col = columns.find((c) => c.id === columnId);
6662
6968
  if (!col)
6663
6969
  return { removed: false, error: "Column not found" };
6664
- await sdk.removeColumn(columnId);
6970
+ await sdk.removeColumn(columnId, currentBoardId);
6665
6971
  broadcast(buildInitMessage());
6666
6972
  fireWebhooks(workspaceRoot, "column.deleted", col);
6667
6973
  return { removed: true };
@@ -6677,11 +6983,11 @@ function startServer(featuresDir, port, webviewDir) {
6677
6983
  const feature = features.find((f) => f.id === featureId);
6678
6984
  if (!feature)
6679
6985
  return false;
6680
- const featureDir = path6.dirname(feature.filePath);
6681
- fs5.writeFileSync(path6.join(featureDir, filename), fileData);
6986
+ const featureDir = path7.dirname(feature.filePath);
6987
+ fs6.writeFileSync(path7.join(featureDir, filename), fileData);
6682
6988
  migrating = true;
6683
6989
  try {
6684
- const updated = await sdk.addAttachment(featureId, path6.join(featureDir, filename));
6990
+ const updated = await sdk.addAttachment(featureId, path7.join(featureDir, filename), currentBoardId);
6685
6991
  lastWrittenContent = serializeFeature(updated);
6686
6992
  await loadFeatures();
6687
6993
  } finally {
@@ -6695,7 +7001,7 @@ function startServer(featuresDir, port, webviewDir) {
6695
7001
  return null;
6696
7002
  migrating = true;
6697
7003
  try {
6698
- const updated = await sdk.removeAttachment(featureId, attachment);
7004
+ const updated = await sdk.removeAttachment(featureId, attachment, currentBoardId);
6699
7005
  lastWrittenContent = serializeFeature(updated);
6700
7006
  await loadFeatures();
6701
7007
  broadcast(buildInitMessage());
@@ -6707,7 +7013,7 @@ function startServer(featuresDir, port, webviewDir) {
6707
7013
  async function doAddComment(featureId, author, content) {
6708
7014
  migrating = true;
6709
7015
  try {
6710
- const updated = await sdk.addComment(featureId, author, content);
7016
+ const updated = await sdk.addComment(featureId, author, content, currentBoardId);
6711
7017
  lastWrittenContent = serializeFeature(updated);
6712
7018
  const comment = updated.comments[updated.comments.length - 1];
6713
7019
  await loadFeatures();
@@ -6723,7 +7029,7 @@ function startServer(featuresDir, port, webviewDir) {
6723
7029
  async function doUpdateComment(featureId, commentId, content) {
6724
7030
  migrating = true;
6725
7031
  try {
6726
- const updated = await sdk.updateComment(featureId, commentId, content);
7032
+ const updated = await sdk.updateComment(featureId, commentId, content, currentBoardId);
6727
7033
  lastWrittenContent = serializeFeature(updated);
6728
7034
  const comment = (updated.comments || []).find((c) => c.id === commentId);
6729
7035
  await loadFeatures();
@@ -6746,7 +7052,7 @@ function startServer(featuresDir, port, webviewDir) {
6746
7052
  return false;
6747
7053
  migrating = true;
6748
7054
  try {
6749
- const updated = await sdk.deleteComment(featureId, commentId);
7055
+ const updated = await sdk.deleteComment(featureId, commentId, currentBoardId);
6750
7056
  lastWrittenContent = serializeFeature(updated);
6751
7057
  await loadFeatures();
6752
7058
  broadcast(buildInitMessage());
@@ -6931,6 +7237,34 @@ function startServer(featuresDir, port, webviewDir) {
6931
7237
  }
6932
7238
  break;
6933
7239
  }
7240
+ case "switchBoard":
7241
+ currentBoardId = msg.boardId;
7242
+ migrating = true;
7243
+ try {
7244
+ await loadFeatures();
7245
+ broadcast(buildInitMessage());
7246
+ } finally {
7247
+ migrating = false;
7248
+ }
7249
+ break;
7250
+ case "createBoard": {
7251
+ const boardName = msg.name;
7252
+ const boardId = generateSlug(boardName) || "board";
7253
+ try {
7254
+ sdk.createBoard(boardId, boardName);
7255
+ currentBoardId = boardId;
7256
+ migrating = true;
7257
+ try {
7258
+ await loadFeatures();
7259
+ broadcast(buildInitMessage());
7260
+ } finally {
7261
+ migrating = false;
7262
+ }
7263
+ } catch (err) {
7264
+ console.error("Failed to create board:", err);
7265
+ }
7266
+ break;
7267
+ }
6934
7268
  case "openFile":
6935
7269
  case "focusMenuBar":
6936
7270
  case "startWithAI":
@@ -6954,7 +7288,178 @@ function startServer(featuresDir, port, webviewDir) {
6954
7288
  return;
6955
7289
  }
6956
7290
  const route = (expectedMethod, pattern) => matchRoute(expectedMethod, method, pathname, pattern);
6957
- let params = route("GET", "/api/tasks");
7291
+ let params = route("GET", "/api/boards");
7292
+ if (params) {
7293
+ return jsonOk(res, sdk.listBoards());
7294
+ }
7295
+ params = route("POST", "/api/boards");
7296
+ if (params) {
7297
+ try {
7298
+ const body = await readBody(req);
7299
+ const id = body.id;
7300
+ const name = body.name;
7301
+ if (!id)
7302
+ return jsonError(res, 400, "id is required");
7303
+ if (!name)
7304
+ return jsonError(res, 400, "name is required");
7305
+ const board = sdk.createBoard(id, name, {
7306
+ description: body.description,
7307
+ columns: body.columns
7308
+ });
7309
+ return jsonOk(res, board, 201);
7310
+ } catch (err) {
7311
+ return jsonError(res, 400, String(err));
7312
+ }
7313
+ }
7314
+ params = route("GET", "/api/boards/:boardId");
7315
+ if (params) {
7316
+ try {
7317
+ const { boardId } = params;
7318
+ const board = sdk.getBoard(boardId);
7319
+ return jsonOk(res, board);
7320
+ } catch (err) {
7321
+ return jsonError(res, 404, String(err));
7322
+ }
7323
+ }
7324
+ params = route("PUT", "/api/boards/:boardId");
7325
+ if (params) {
7326
+ try {
7327
+ const { boardId } = params;
7328
+ const body = await readBody(req);
7329
+ const board = sdk.updateBoard(boardId, body);
7330
+ return jsonOk(res, board);
7331
+ } catch (err) {
7332
+ return jsonError(res, 400, String(err));
7333
+ }
7334
+ }
7335
+ params = route("DELETE", "/api/boards/:boardId");
7336
+ if (params) {
7337
+ try {
7338
+ const { boardId } = params;
7339
+ await sdk.deleteBoard(boardId);
7340
+ return jsonOk(res, { deleted: true });
7341
+ } catch (err) {
7342
+ return jsonError(res, 400, String(err));
7343
+ }
7344
+ }
7345
+ params = route("POST", "/api/boards/:boardId/tasks/:id/transfer");
7346
+ if (params) {
7347
+ try {
7348
+ const { boardId, id } = params;
7349
+ const body = await readBody(req);
7350
+ const config = readConfig(workspaceRoot);
7351
+ const fromBoard = currentBoardId || config.defaultBoard;
7352
+ const targetStatus = body.targetStatus;
7353
+ const card = await sdk.transferCard(id, fromBoard, boardId, targetStatus);
7354
+ return jsonOk(res, sanitizeFeature(card));
7355
+ } catch (err) {
7356
+ return jsonError(res, 400, String(err));
7357
+ }
7358
+ }
7359
+ params = route("GET", "/api/boards/:boardId/tasks");
7360
+ if (params) {
7361
+ try {
7362
+ const { boardId } = params;
7363
+ const boardColumns = sdk.listColumns(boardId);
7364
+ const boardTasks = await sdk.listCards(boardColumns.map((c) => c.id), boardId);
7365
+ let result = boardTasks.map(sanitizeFeature);
7366
+ const status = url.searchParams.get("status");
7367
+ if (status)
7368
+ result = result.filter((f) => f.status === status);
7369
+ const priority = url.searchParams.get("priority");
7370
+ if (priority)
7371
+ result = result.filter((f) => f.priority === priority);
7372
+ const assignee = url.searchParams.get("assignee");
7373
+ if (assignee)
7374
+ result = result.filter((f) => f.assignee === assignee);
7375
+ const label = url.searchParams.get("label");
7376
+ if (label)
7377
+ result = result.filter((f) => f.labels.includes(label));
7378
+ return jsonOk(res, result);
7379
+ } catch (err) {
7380
+ return jsonError(res, 400, String(err));
7381
+ }
7382
+ }
7383
+ params = route("POST", "/api/boards/:boardId/tasks");
7384
+ if (params) {
7385
+ try {
7386
+ const { boardId } = params;
7387
+ const body = await readBody(req);
7388
+ const content = body.content || "";
7389
+ if (!content)
7390
+ return jsonError(res, 400, "content is required");
7391
+ const feature = await sdk.createCard({
7392
+ content,
7393
+ status: body.status || "backlog",
7394
+ priority: body.priority || "medium",
7395
+ assignee: body.assignee || null,
7396
+ dueDate: body.dueDate || null,
7397
+ labels: body.labels || [],
7398
+ boardId
7399
+ });
7400
+ return jsonOk(res, sanitizeFeature(feature), 201);
7401
+ } catch (err) {
7402
+ return jsonError(res, 400, String(err));
7403
+ }
7404
+ }
7405
+ params = route("GET", "/api/boards/:boardId/tasks/:id");
7406
+ if (params) {
7407
+ try {
7408
+ const { boardId, id } = params;
7409
+ const card = await sdk.getCard(id, boardId);
7410
+ if (!card)
7411
+ return jsonError(res, 404, "Task not found");
7412
+ return jsonOk(res, sanitizeFeature(card));
7413
+ } catch (err) {
7414
+ return jsonError(res, 400, String(err));
7415
+ }
7416
+ }
7417
+ params = route("PUT", "/api/boards/:boardId/tasks/:id");
7418
+ if (params) {
7419
+ try {
7420
+ const { boardId, id } = params;
7421
+ const body = await readBody(req);
7422
+ const feature = await sdk.updateCard(id, body, boardId);
7423
+ return jsonOk(res, sanitizeFeature(feature));
7424
+ } catch (err) {
7425
+ return jsonError(res, 400, String(err));
7426
+ }
7427
+ }
7428
+ params = route("PATCH", "/api/boards/:boardId/tasks/:id/move");
7429
+ if (params) {
7430
+ try {
7431
+ const { boardId, id } = params;
7432
+ const body = await readBody(req);
7433
+ const newStatus = body.status;
7434
+ const position = body.position ?? 0;
7435
+ if (!newStatus)
7436
+ return jsonError(res, 400, "status is required");
7437
+ const feature = await sdk.moveCard(id, newStatus, position, boardId);
7438
+ return jsonOk(res, sanitizeFeature(feature));
7439
+ } catch (err) {
7440
+ return jsonError(res, 400, String(err));
7441
+ }
7442
+ }
7443
+ params = route("DELETE", "/api/boards/:boardId/tasks/:id");
7444
+ if (params) {
7445
+ try {
7446
+ const { boardId, id } = params;
7447
+ await sdk.deleteCard(id, boardId);
7448
+ return jsonOk(res, { deleted: true });
7449
+ } catch (err) {
7450
+ return jsonError(res, 400, String(err));
7451
+ }
7452
+ }
7453
+ params = route("GET", "/api/boards/:boardId/columns");
7454
+ if (params) {
7455
+ try {
7456
+ const { boardId } = params;
7457
+ return jsonOk(res, sdk.listColumns(boardId));
7458
+ } catch (err) {
7459
+ return jsonError(res, 400, String(err));
7460
+ }
7461
+ }
7462
+ params = route("GET", "/api/tasks");
6958
7463
  if (params) {
6959
7464
  await loadFeatures();
6960
7465
  let result = features.map(sanitizeFeature);
@@ -7065,16 +7570,16 @@ function startServer(featuresDir, port, webviewDir) {
7065
7570
  const feature = features.find((f) => f.id === id);
7066
7571
  if (!feature)
7067
7572
  return jsonError(res, 404, "Task not found");
7068
- const featureDir = path6.dirname(feature.filePath);
7069
- const attachmentPath = path6.resolve(featureDir, attachName);
7573
+ const featureDir = path7.dirname(feature.filePath);
7574
+ const attachmentPath = path7.resolve(featureDir, attachName);
7070
7575
  if (!attachmentPath.startsWith(absoluteFeaturesDir)) {
7071
7576
  res.writeHead(403, { "Content-Type": "text/plain" });
7072
7577
  res.end("Forbidden");
7073
7578
  return;
7074
7579
  }
7075
- const ext2 = path6.extname(attachName);
7580
+ const ext2 = path7.extname(attachName);
7076
7581
  const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
7077
- fs5.readFile(attachmentPath, (err, data) => {
7582
+ fs6.readFile(attachmentPath, (err, data) => {
7078
7583
  if (err) {
7079
7584
  res.writeHead(404);
7080
7585
  res.end("File not found");
@@ -7150,7 +7655,7 @@ function startServer(featuresDir, port, webviewDir) {
7150
7655
  }
7151
7656
  params = route("GET", "/api/columns");
7152
7657
  if (params) {
7153
- return jsonOk(res, sdk.listColumns());
7658
+ return jsonOk(res, sdk.listColumns(currentBoardId));
7154
7659
  }
7155
7660
  params = route("POST", "/api/columns");
7156
7661
  if (params) {
@@ -7289,16 +7794,16 @@ function startServer(featuresDir, port, webviewDir) {
7289
7794
  res.end("Feature not found");
7290
7795
  return;
7291
7796
  }
7292
- const featureDir = path6.dirname(feature.filePath);
7293
- const attachmentPath = path6.resolve(featureDir, filename);
7797
+ const featureDir = path7.dirname(feature.filePath);
7798
+ const attachmentPath = path7.resolve(featureDir, filename);
7294
7799
  if (!attachmentPath.startsWith(absoluteFeaturesDir)) {
7295
7800
  res.writeHead(403, { "Content-Type": "text/plain" });
7296
7801
  res.end("Forbidden");
7297
7802
  return;
7298
7803
  }
7299
- const ext2 = path6.extname(filename);
7804
+ const ext2 = path7.extname(filename);
7300
7805
  const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
7301
- fs5.readFile(attachmentPath, (err, data) => {
7806
+ fs6.readFile(attachmentPath, (err, data) => {
7302
7807
  if (err) {
7303
7808
  res.writeHead(404, { "Content-Type": "text/plain" });
7304
7809
  res.end("File not found");
@@ -7312,15 +7817,15 @@ function startServer(featuresDir, port, webviewDir) {
7312
7817
  if (pathname.startsWith("/api/")) {
7313
7818
  return jsonError(res, 404, "Not found");
7314
7819
  }
7315
- const filePath = path6.join(resolvedWebviewDir, pathname === "/" ? "index.html" : pathname);
7316
- if (!path6.extname(filePath)) {
7820
+ const filePath = path7.join(resolvedWebviewDir, pathname === "/" ? "index.html" : pathname);
7821
+ if (!path7.extname(filePath)) {
7317
7822
  res.writeHead(200, { "Content-Type": "text/html" });
7318
7823
  res.end(indexHtml);
7319
7824
  return;
7320
7825
  }
7321
- const ext = path6.extname(filePath);
7826
+ const ext = path7.extname(filePath);
7322
7827
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
7323
- fs5.readFile(filePath, (err, data) => {
7828
+ fs6.readFile(filePath, (err, data) => {
7324
7829
  if (err) {
7325
7830
  res.writeHead(200, { "Content-Type": "text/html" });
7326
7831
  res.end(indexHtml);
@@ -7342,7 +7847,7 @@ function startServer(featuresDir, port, webviewDir) {
7342
7847
  });
7343
7848
  });
7344
7849
  let debounceTimer;
7345
- fs5.mkdirSync(absoluteFeaturesDir, { recursive: true });
7850
+ fs6.mkdirSync(absoluteFeaturesDir, { recursive: true });
7346
7851
  const watcher = esm_default.watch(absoluteFeaturesDir, {
7347
7852
  ignoreInitial: true,
7348
7853
  awaitWriteFinish: { stabilityThreshold: 100 }
@@ -7402,17 +7907,19 @@ function startServer(featuresDir, port, webviewDir) {
7402
7907
  });
7403
7908
  return server;
7404
7909
  }
7405
- var http2, fs5, path6, MIME_TYPES;
7910
+ var http2, fs6, path7, MIME_TYPES;
7406
7911
  var init_server = __esm({
7407
7912
  "src/standalone/server.ts"() {
7408
7913
  "use strict";
7409
7914
  http2 = __toESM(require("http"));
7410
- fs5 = __toESM(require("fs"));
7411
- path6 = __toESM(require("path"));
7915
+ fs6 = __toESM(require("fs"));
7916
+ path7 = __toESM(require("path"));
7412
7917
  init_wrapper();
7413
7918
  init_esm2();
7919
+ init_types();
7414
7920
  init_KanbanSDK();
7415
7921
  init_parser();
7922
+ init_config();
7416
7923
  init_webhooks();
7417
7924
  MIME_TYPES = {
7418
7925
  ".html": "text/html",
@@ -8018,12 +8525,11 @@ var init_open = __esm({
8018
8525
  });
8019
8526
 
8020
8527
  // src/cli/index.ts
8021
- var path8 = __toESM(require("path"));
8022
- var fs11 = __toESM(require("fs/promises"));
8528
+ var path9 = __toESM(require("path"));
8529
+ var fs12 = __toESM(require("fs/promises"));
8023
8530
  init_KanbanSDK();
8024
8531
  init_webhooks();
8025
8532
  init_config();
8026
- var VALID_STATUSES = ["backlog", "todo", "in-progress", "review", "done"];
8027
8533
  var VALID_PRIORITIES = ["critical", "high", "medium", "low"];
8028
8534
  function parseArgs(argv) {
8029
8535
  const args = argv.slice(2);
@@ -8047,20 +8553,27 @@ function parseArgs(argv) {
8047
8553
  }
8048
8554
  return { command, positional, flags };
8049
8555
  }
8556
+ function getBoardId(flags) {
8557
+ return typeof flags.board === "string" ? flags.board : void 0;
8558
+ }
8559
+ async function getValidStatuses(sdk, boardId) {
8560
+ const columns = await sdk.listColumns(boardId);
8561
+ return columns.map((c) => c.id);
8562
+ }
8050
8563
  async function findWorkspaceRoot(startDir) {
8051
8564
  let dir = startDir;
8052
8565
  while (true) {
8053
8566
  try {
8054
- await fs11.access(path8.join(dir, ".git"));
8567
+ await fs12.access(path9.join(dir, ".git"));
8055
8568
  return dir;
8056
8569
  } catch {
8057
8570
  }
8058
8571
  try {
8059
- await fs11.access(path8.join(dir, "package.json"));
8572
+ await fs12.access(path9.join(dir, "package.json"));
8060
8573
  return dir;
8061
8574
  } catch {
8062
8575
  }
8063
- const parent = path8.dirname(dir);
8576
+ const parent = path9.dirname(dir);
8064
8577
  if (parent === dir)
8065
8578
  return startDir;
8066
8579
  dir = parent;
@@ -8068,10 +8581,10 @@ async function findWorkspaceRoot(startDir) {
8068
8581
  }
8069
8582
  async function resolveFeaturesDir(flags) {
8070
8583
  if (typeof flags.dir === "string") {
8071
- return path8.resolve(flags.dir);
8584
+ return path9.resolve(flags.dir);
8072
8585
  }
8073
8586
  const root = await findWorkspaceRoot(process.cwd());
8074
- return path8.join(root, ".kanban");
8587
+ return path9.join(root, ".kanban");
8075
8588
  }
8076
8589
  var bold = (s) => `\x1B[1m${s}\x1B[0m`;
8077
8590
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
@@ -8138,6 +8651,9 @@ function formatCardDetail(c) {
8138
8651
  ` Modified: ${c.modified}`,
8139
8652
  ` File: ${c.filePath}`
8140
8653
  ];
8654
+ if (c.boardId) {
8655
+ lines.push(` Board: ${c.boardId}`);
8656
+ }
8141
8657
  if (c.completedAt) {
8142
8658
  lines.push(` Completed: ${c.completedAt}`);
8143
8659
  }
@@ -8148,7 +8664,8 @@ function formatCardDetail(c) {
8148
8664
  return lines.join("\n");
8149
8665
  }
8150
8666
  async function cmdList(sdk, flags) {
8151
- let cards = await sdk.listCards();
8667
+ const boardId = getBoardId(flags);
8668
+ let cards = await sdk.listCards(void 0, boardId);
8152
8669
  if (typeof flags.status === "string") {
8153
8670
  cards = cards.filter((c) => c.status === flags.status);
8154
8671
  }
@@ -8183,9 +8700,10 @@ async function cmdShow(sdk, positional, flags) {
8183
8700
  console.error(red("Error: card ID required. Usage: kl show <id>"));
8184
8701
  process.exit(1);
8185
8702
  }
8186
- const card = await sdk.getCard(cardId);
8703
+ const boardId = getBoardId(flags);
8704
+ const card = await sdk.getCard(cardId, boardId);
8187
8705
  if (!card) {
8188
- const all = await sdk.listCards();
8706
+ const all = await sdk.listCards(void 0, boardId);
8189
8707
  const matches = all.filter((c) => c.id.includes(cardId));
8190
8708
  if (matches.length === 1) {
8191
8709
  if (flags.json) {
@@ -8215,10 +8733,14 @@ async function cmdAdd(sdk, flags) {
8215
8733
  console.error(red('Error: --title is required. Usage: kl add --title "My card"'));
8216
8734
  process.exit(1);
8217
8735
  }
8218
- const status = typeof flags.status === "string" ? flags.status : "backlog";
8219
- if (!VALID_STATUSES.includes(status)) {
8220
- console.error(red(`Invalid status: ${status}. Must be one of: ${VALID_STATUSES.join(", ")}`));
8221
- process.exit(1);
8736
+ const boardId = getBoardId(flags);
8737
+ const status = typeof flags.status === "string" ? flags.status : void 0;
8738
+ if (status) {
8739
+ const validStatuses = await getValidStatuses(sdk, boardId);
8740
+ if (!validStatuses.includes(status)) {
8741
+ console.error(red(`Invalid status: ${status}. Must be one of: ${validStatuses.join(", ")}`));
8742
+ process.exit(1);
8743
+ }
8222
8744
  }
8223
8745
  const priority = typeof flags.priority === "string" ? flags.priority : "medium";
8224
8746
  if (!VALID_PRIORITIES.includes(priority)) {
@@ -8230,7 +8752,7 @@ async function cmdAdd(sdk, flags) {
8230
8752
  const labels = typeof flags.label === "string" ? flags.label.split(",").map((l) => l.trim()) : [];
8231
8753
  const body = typeof flags.body === "string" ? flags.body : "";
8232
8754
  const content = `# ${title}${body ? "\n\n" + body : ""}`;
8233
- const card = await sdk.createCard({ content, status, priority, assignee, dueDate, labels });
8755
+ const card = await sdk.createCard({ content, status, priority, assignee, dueDate, labels, boardId });
8234
8756
  if (flags.json) {
8235
8757
  console.log(JSON.stringify(card, null, 2));
8236
8758
  } else {
@@ -8246,29 +8768,15 @@ async function cmdMove(sdk, positional, flags) {
8246
8768
  console.error(red("Usage: kl move <id> <status> [--position <n>]"));
8247
8769
  process.exit(1);
8248
8770
  }
8249
- if (!VALID_STATUSES.includes(newStatus)) {
8250
- console.error(red(`Invalid status: ${newStatus}. Must be one of: ${VALID_STATUSES.join(", ")}`));
8771
+ const boardId = getBoardId(flags);
8772
+ const validStatuses = await getValidStatuses(sdk, boardId);
8773
+ if (!validStatuses.includes(newStatus)) {
8774
+ console.error(red(`Invalid status: ${newStatus}. Must be one of: ${validStatuses.join(", ")}`));
8251
8775
  process.exit(1);
8252
8776
  }
8253
- let resolvedId = cardId;
8254
- const card = await sdk.getCard(cardId);
8255
- if (!card) {
8256
- const all = await sdk.listCards();
8257
- const matches = all.filter((c) => c.id.includes(cardId));
8258
- if (matches.length === 1) {
8259
- resolvedId = matches[0].id;
8260
- } else if (matches.length > 1) {
8261
- console.error(red(`Multiple cards match "${cardId}":`));
8262
- for (const m of matches)
8263
- console.error(` ${m.id}`);
8264
- process.exit(1);
8265
- } else {
8266
- console.error(red(`Card not found: ${cardId}`));
8267
- process.exit(1);
8268
- }
8269
- }
8777
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
8270
8778
  const position = typeof flags.position === "string" ? parseInt(flags.position, 10) : void 0;
8271
- const updated = await sdk.moveCard(resolvedId, newStatus, position);
8779
+ const updated = await sdk.moveCard(resolvedId, newStatus, position, boardId);
8272
8780
  console.log(green(`Moved ${updated.id} \u2192 ${colorStatus(newStatus)}`));
8273
8781
  }
8274
8782
  async function cmdEdit(sdk, positional, flags) {
@@ -8277,27 +8785,13 @@ async function cmdEdit(sdk, positional, flags) {
8277
8785
  console.error(red("Usage: kl edit <id> [--status ...] [--priority ...] [--assignee ...] [--due ...] [--label ...]"));
8278
8786
  process.exit(1);
8279
8787
  }
8280
- let resolvedId = cardId;
8281
- const card = await sdk.getCard(cardId);
8282
- if (!card) {
8283
- const all = await sdk.listCards();
8284
- const matches = all.filter((c) => c.id.includes(cardId));
8285
- if (matches.length === 1) {
8286
- resolvedId = matches[0].id;
8287
- } else if (matches.length > 1) {
8288
- console.error(red(`Multiple cards match "${cardId}":`));
8289
- for (const m of matches)
8290
- console.error(` ${m.id}`);
8291
- process.exit(1);
8292
- } else {
8293
- console.error(red(`Card not found: ${cardId}`));
8294
- process.exit(1);
8295
- }
8296
- }
8788
+ const boardId = getBoardId(flags);
8789
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
8297
8790
  const updates = {};
8298
8791
  if (typeof flags.status === "string") {
8299
- if (!VALID_STATUSES.includes(flags.status)) {
8300
- console.error(red(`Invalid status: ${flags.status}`));
8792
+ const validStatuses = await getValidStatuses(sdk, boardId);
8793
+ if (!validStatuses.includes(flags.status)) {
8794
+ console.error(red(`Invalid status: ${flags.status}. Must be one of: ${validStatuses.join(", ")}`));
8301
8795
  process.exit(1);
8302
8796
  }
8303
8797
  updates.status = flags.status;
@@ -8319,45 +8813,136 @@ async function cmdEdit(sdk, positional, flags) {
8319
8813
  console.error(red("No updates specified. Use --status, --priority, --assignee, --due, or --label"));
8320
8814
  process.exit(1);
8321
8815
  }
8322
- const updated = await sdk.updateCard(resolvedId, updates);
8816
+ const updated = await sdk.updateCard(resolvedId, updates, boardId);
8323
8817
  console.log(green(`Updated: ${updated.id}`));
8324
8818
  }
8325
- async function cmdDelete(sdk, positional) {
8819
+ async function cmdDelete(sdk, positional, flags) {
8326
8820
  const cardId = positional[0];
8327
8821
  if (!cardId) {
8328
8822
  console.error(red("Usage: kl delete <id>"));
8329
8823
  process.exit(1);
8330
8824
  }
8331
- let resolvedId = cardId;
8332
- const card = await sdk.getCard(cardId);
8333
- if (!card) {
8334
- const all = await sdk.listCards();
8335
- const matches = all.filter((c) => c.id.includes(cardId));
8336
- if (matches.length === 1) {
8337
- resolvedId = matches[0].id;
8338
- } else if (matches.length > 1) {
8339
- console.error(red(`Multiple cards match "${cardId}":`));
8340
- for (const m of matches)
8341
- console.error(` ${m.id}`);
8342
- process.exit(1);
8343
- } else {
8344
- console.error(red(`Card not found: ${cardId}`));
8345
- process.exit(1);
8346
- }
8347
- }
8348
- await sdk.deleteCard(resolvedId);
8825
+ const boardId = getBoardId(flags);
8826
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
8827
+ await sdk.deleteCard(resolvedId, boardId);
8349
8828
  console.log(green(`Deleted: ${resolvedId}`));
8350
8829
  }
8351
8830
  async function cmdInit(sdk) {
8352
8831
  await sdk.init();
8353
8832
  console.log(green(`Initialized: ${sdk.featuresDir}`));
8354
8833
  }
8834
+ async function cmdBoards(sdk, positional, flags, workspaceRoot) {
8835
+ const subcommand = positional[0] || "list";
8836
+ switch (subcommand) {
8837
+ case "list": {
8838
+ const boards = sdk.listBoards();
8839
+ if (flags.json) {
8840
+ console.log(JSON.stringify(boards, null, 2));
8841
+ } else if (boards.length === 0) {
8842
+ console.log(dim(" No boards found."));
8843
+ } else {
8844
+ console.log(` ${dim("ID".padEnd(20))} ${dim("NAME".padEnd(20))} ${dim("DESCRIPTION")}`);
8845
+ console.log(dim(" " + "-".repeat(60)));
8846
+ for (const b of boards) {
8847
+ console.log(` ${bold(b.id.padEnd(20))} ${b.name.padEnd(20)} ${b.description || "-"}`);
8848
+ }
8849
+ }
8850
+ break;
8851
+ }
8852
+ case "add": {
8853
+ const id = typeof flags.id === "string" ? flags.id : "";
8854
+ const name = typeof flags.name === "string" ? flags.name : "";
8855
+ if (!id || !name) {
8856
+ console.error(red("Usage: kl boards add --id <id> --name <name> [--description <desc>]"));
8857
+ process.exit(1);
8858
+ }
8859
+ const description = typeof flags.description === "string" ? flags.description : void 0;
8860
+ const board = await sdk.createBoard(id, name, { description });
8861
+ if (flags.json) {
8862
+ console.log(JSON.stringify(board, null, 2));
8863
+ } else {
8864
+ console.log(green(`Created board: ${board.id} (${board.name})`));
8865
+ }
8866
+ break;
8867
+ }
8868
+ case "show": {
8869
+ const boardId = positional[1];
8870
+ if (!boardId) {
8871
+ console.error(red("Usage: kl boards show <id>"));
8872
+ process.exit(1);
8873
+ }
8874
+ const board = sdk.getBoard(boardId);
8875
+ if (flags.json) {
8876
+ console.log(JSON.stringify(board, null, 2));
8877
+ } else {
8878
+ console.log(`${bold(board.name)}`);
8879
+ console.log(` ID: ${boardId}`);
8880
+ if (board.description)
8881
+ console.log(` Description: ${board.description}`);
8882
+ console.log(` Columns: ${board.columns.map((c) => c.name).join(", ")}`);
8883
+ console.log(` Next Card: ${board.nextCardId}`);
8884
+ console.log(` Default: status=${board.defaultStatus}, priority=${board.defaultPriority}`);
8885
+ }
8886
+ break;
8887
+ }
8888
+ case "remove":
8889
+ case "rm": {
8890
+ const boardId = positional[1];
8891
+ if (!boardId) {
8892
+ console.error(red("Usage: kl boards remove <id>"));
8893
+ process.exit(1);
8894
+ }
8895
+ await sdk.deleteBoard(boardId);
8896
+ console.log(green(`Removed board: ${boardId}`));
8897
+ break;
8898
+ }
8899
+ case "default": {
8900
+ const boardId = positional[1];
8901
+ if (!boardId) {
8902
+ const config2 = readConfig(workspaceRoot);
8903
+ console.log(config2.defaultBoard);
8904
+ break;
8905
+ }
8906
+ const config = readConfig(workspaceRoot);
8907
+ if (!config.boards[boardId]) {
8908
+ console.error(red(`Board not found: ${boardId}`));
8909
+ process.exit(1);
8910
+ }
8911
+ config.defaultBoard = boardId;
8912
+ writeConfig(workspaceRoot, config);
8913
+ console.log(green(`Default board set to: ${boardId}`));
8914
+ break;
8915
+ }
8916
+ default:
8917
+ console.error(red(`Unknown boards subcommand: ${subcommand}`));
8918
+ console.error("Available: list, add, show, remove, default");
8919
+ process.exit(1);
8920
+ }
8921
+ }
8922
+ async function cmdTransfer(sdk, positional, flags) {
8923
+ const cardId = positional[0];
8924
+ if (!cardId) {
8925
+ console.error(red("Usage: kl transfer <card-id> --from <board> --to <board> [--status <status>]"));
8926
+ process.exit(1);
8927
+ }
8928
+ const fromBoard = typeof flags.from === "string" ? flags.from : void 0;
8929
+ const toBoard = typeof flags.to === "string" ? flags.to : void 0;
8930
+ if (!fromBoard || !toBoard) {
8931
+ console.error(red("Both --from and --to are required"));
8932
+ process.exit(1);
8933
+ }
8934
+ const targetStatus = typeof flags.status === "string" ? flags.status : void 0;
8935
+ const resolvedId = await resolveCardId(sdk, cardId, fromBoard);
8936
+ const card = await sdk.transferCard(resolvedId, fromBoard, toBoard, targetStatus);
8937
+ console.log(green(`Transferred ${card.id} from ${fromBoard} \u2192 ${toBoard} (${colorStatus(card.status)})`));
8938
+ }
8355
8939
  async function cmdAttach(sdk, positional, flags) {
8356
8940
  const subcommand = positional[0] || "list";
8357
8941
  const cardId = positional[1];
8942
+ const boardId = getBoardId(flags);
8358
8943
  if (subcommand !== "list" && subcommand !== "add" && subcommand !== "rm" && subcommand !== "remove") {
8359
- const resolvedId = await resolveCardId(sdk, subcommand);
8360
- const attachments = await sdk.listAttachments(resolvedId);
8944
+ const resolvedId = await resolveCardId(sdk, subcommand, boardId);
8945
+ const attachments = await sdk.listAttachments(resolvedId, boardId);
8361
8946
  if (flags.json) {
8362
8947
  console.log(JSON.stringify(attachments, null, 2));
8363
8948
  } else if (attachments.length === 0) {
@@ -8374,8 +8959,8 @@ async function cmdAttach(sdk, positional, flags) {
8374
8959
  console.error(red("Usage: kl attach list <card-id>"));
8375
8960
  process.exit(1);
8376
8961
  }
8377
- const resolvedId = await resolveCardId(sdk, cardId);
8378
- const attachments = await sdk.listAttachments(resolvedId);
8962
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
8963
+ const attachments = await sdk.listAttachments(resolvedId, boardId);
8379
8964
  if (flags.json) {
8380
8965
  console.log(JSON.stringify(attachments, null, 2));
8381
8966
  } else if (attachments.length === 0) {
@@ -8396,9 +8981,9 @@ async function cmdAttach(sdk, positional, flags) {
8396
8981
  console.error(red("Usage: kl attach add <card-id> <file-path>"));
8397
8982
  process.exit(1);
8398
8983
  }
8399
- const resolvedId = await resolveCardId(sdk, cardId);
8400
- const updated = await sdk.addAttachment(resolvedId, filePath);
8401
- console.log(green(`Attached to ${updated.id}: ${path8.basename(filePath)}`));
8984
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
8985
+ const updated = await sdk.addAttachment(resolvedId, filePath, boardId);
8986
+ console.log(green(`Attached to ${updated.id}: ${path9.basename(filePath)}`));
8402
8987
  break;
8403
8988
  }
8404
8989
  case "remove":
@@ -8412,18 +8997,18 @@ async function cmdAttach(sdk, positional, flags) {
8412
8997
  console.error(red("Usage: kl attach remove <card-id> <filename>"));
8413
8998
  process.exit(1);
8414
8999
  }
8415
- const resolvedId = await resolveCardId(sdk, cardId);
8416
- const updated = await sdk.removeAttachment(resolvedId, filename);
9000
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
9001
+ const updated = await sdk.removeAttachment(resolvedId, filename, boardId);
8417
9002
  console.log(green(`Removed from ${updated.id}: ${filename}`));
8418
9003
  break;
8419
9004
  }
8420
9005
  }
8421
9006
  }
8422
- async function resolveCardId(sdk, cardId) {
8423
- const card = await sdk.getCard(cardId);
9007
+ async function resolveCardId(sdk, cardId, boardId) {
9008
+ const card = await sdk.getCard(cardId, boardId);
8424
9009
  if (card)
8425
9010
  return cardId;
8426
- const all = await sdk.listCards();
9011
+ const all = await sdk.listCards(void 0, boardId);
8427
9012
  const matches = all.filter((c) => c.id.includes(cardId));
8428
9013
  if (matches.length === 1)
8429
9014
  return matches[0].id;
@@ -8438,9 +9023,10 @@ async function resolveCardId(sdk, cardId) {
8438
9023
  }
8439
9024
  async function cmdComment(sdk, positional, flags) {
8440
9025
  const subcommand = positional[0] || "list";
9026
+ const boardId = getBoardId(flags);
8441
9027
  if (subcommand !== "list" && subcommand !== "add" && subcommand !== "edit" && subcommand !== "remove" && subcommand !== "rm") {
8442
- const resolvedId = await resolveCardId(sdk, subcommand);
8443
- const comments = await sdk.listComments(resolvedId);
9028
+ const resolvedId = await resolveCardId(sdk, subcommand, boardId);
9029
+ const comments = await sdk.listComments(resolvedId, boardId);
8444
9030
  if (flags.json) {
8445
9031
  console.log(JSON.stringify(comments, null, 2));
8446
9032
  } else if (comments.length === 0) {
@@ -8461,8 +9047,8 @@ async function cmdComment(sdk, positional, flags) {
8461
9047
  console.error(red("Usage: kl comment list <card-id>"));
8462
9048
  process.exit(1);
8463
9049
  }
8464
- const resolvedId = await resolveCardId(sdk, cardId);
8465
- const comments = await sdk.listComments(resolvedId);
9050
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
9051
+ const comments = await sdk.listComments(resolvedId, boardId);
8466
9052
  if (flags.json) {
8467
9053
  console.log(JSON.stringify(comments, null, 2));
8468
9054
  } else if (comments.length === 0) {
@@ -8491,8 +9077,8 @@ async function cmdComment(sdk, positional, flags) {
8491
9077
  console.error(red("Error: --body is required"));
8492
9078
  process.exit(1);
8493
9079
  }
8494
- const resolvedId = await resolveCardId(sdk, cardId);
8495
- const card = await sdk.addComment(resolvedId, author, body);
9080
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
9081
+ const card = await sdk.addComment(resolvedId, author, body, boardId);
8496
9082
  const added = card.comments[card.comments.length - 1];
8497
9083
  if (flags.json) {
8498
9084
  console.log(JSON.stringify(added, null, 2));
@@ -8516,10 +9102,10 @@ async function cmdComment(sdk, positional, flags) {
8516
9102
  console.error(red("Error: --body is required"));
8517
9103
  process.exit(1);
8518
9104
  }
8519
- const resolvedId = await resolveCardId(sdk, cardId);
8520
- await sdk.updateComment(resolvedId, commentId, body);
9105
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
9106
+ await sdk.updateComment(resolvedId, commentId, body, boardId);
8521
9107
  if (flags.json) {
8522
- const comments = await sdk.listComments(resolvedId);
9108
+ const comments = await sdk.listComments(resolvedId, boardId);
8523
9109
  const updated = comments.find((c) => c.id === commentId);
8524
9110
  console.log(JSON.stringify(updated, null, 2));
8525
9111
  } else {
@@ -8538,8 +9124,8 @@ async function cmdComment(sdk, positional, flags) {
8538
9124
  console.error(red("Usage: kl comment remove <card-id> <comment-id>"));
8539
9125
  process.exit(1);
8540
9126
  }
8541
- const resolvedId = await resolveCardId(sdk, cardId);
8542
- await sdk.deleteComment(resolvedId, commentId);
9127
+ const resolvedId = await resolveCardId(sdk, cardId, boardId);
9128
+ await sdk.deleteComment(resolvedId, commentId, boardId);
8543
9129
  console.log(green(`Deleted comment ${commentId}`));
8544
9130
  break;
8545
9131
  }
@@ -8547,9 +9133,10 @@ async function cmdComment(sdk, positional, flags) {
8547
9133
  }
8548
9134
  async function cmdColumns(sdk, positional, flags) {
8549
9135
  const subcommand = positional[0] || "list";
9136
+ const boardId = getBoardId(flags);
8550
9137
  switch (subcommand) {
8551
9138
  case "list": {
8552
- const columns = await sdk.listColumns();
9139
+ const columns = await sdk.listColumns(boardId);
8553
9140
  if (flags.json) {
8554
9141
  console.log(JSON.stringify(columns, null, 2));
8555
9142
  } else {
@@ -8569,7 +9156,7 @@ async function cmdColumns(sdk, positional, flags) {
8569
9156
  console.error(red("Usage: kl columns add --id <id> --name <name> [--color <hex>]"));
8570
9157
  process.exit(1);
8571
9158
  }
8572
- const columns = await sdk.addColumn({ id, name, color });
9159
+ const columns = await sdk.addColumn({ id, name, color }, boardId);
8573
9160
  console.log(green(`Added column: ${id} (${name})`));
8574
9161
  if (flags.json)
8575
9162
  console.log(JSON.stringify(columns, null, 2));
@@ -8590,7 +9177,7 @@ async function cmdColumns(sdk, positional, flags) {
8590
9177
  console.error(red("No updates specified. Use --name or --color"));
8591
9178
  process.exit(1);
8592
9179
  }
8593
- const columns = await sdk.updateColumn(columnId, updates);
9180
+ const columns = await sdk.updateColumn(columnId, updates, boardId);
8594
9181
  console.log(green(`Updated column: ${columnId}`));
8595
9182
  if (flags.json)
8596
9183
  console.log(JSON.stringify(columns, null, 2));
@@ -8603,7 +9190,7 @@ async function cmdColumns(sdk, positional, flags) {
8603
9190
  console.error(red("Usage: kl columns remove <id>"));
8604
9191
  process.exit(1);
8605
9192
  }
8606
- const columns = await sdk.removeColumn(columnId);
9193
+ const columns = await sdk.removeColumn(columnId, boardId);
8607
9194
  console.log(green(`Removed column: ${columnId}`));
8608
9195
  if (flags.json)
8609
9196
  console.log(JSON.stringify(columns, null, 2));
@@ -8783,6 +9370,14 @@ ${bold("Card Commands:")}
8783
9370
  edit <id> [--field value] Update card fields
8784
9371
  delete <id> Delete a card
8785
9372
 
9373
+ ${bold("Board Commands:")}
9374
+ boards List boards
9375
+ boards add Create a board (--id, --name, --description)
9376
+ boards show <id> Show board details
9377
+ boards remove <id> Remove a board
9378
+ boards default [id] Get or set the default board
9379
+ transfer <id> Transfer a card (--from, --to, --status)
9380
+
8786
9381
  ${bold("Attachment Commands:")}
8787
9382
  attach <id> List attachments on a card
8788
9383
  attach add <id> <path> Attach a file to a card
@@ -8818,6 +9413,7 @@ ${bold("Other:")}
8818
9413
 
8819
9414
  ${bold("Global Options:")}
8820
9415
  --dir <path> Features directory (default: .kanban)
9416
+ --board <id> Target board (default: default board)
8821
9417
  --json Output as JSON
8822
9418
 
8823
9419
  ${bold("List Filters:")}
@@ -8835,6 +9431,11 @@ ${bold("Add/Edit Options:")}
8835
9431
  --due <date> Due date
8836
9432
  --label <l1,l2> Labels (comma-separated)
8837
9433
 
9434
+ ${bold("Transfer Options:")}
9435
+ --from <board> Source board (required)
9436
+ --to <board> Destination board (required)
9437
+ --status <status> Target status in destination board
9438
+
8838
9439
  ${bold("Webhook Options:")}
8839
9440
  --url <url> Webhook target URL (required for add)
8840
9441
  --events <e1,e2> Events to subscribe to (default: *)
@@ -8856,7 +9457,7 @@ async function main() {
8856
9457
  return;
8857
9458
  }
8858
9459
  const featuresDir = await resolveFeaturesDir(flags);
8859
- const workspaceRoot = path8.dirname(featuresDir);
9460
+ const workspaceRoot = path9.dirname(featuresDir);
8860
9461
  const sdk = new KanbanSDK(featuresDir);
8861
9462
  switch (command) {
8862
9463
  case "list":
@@ -8882,7 +9483,14 @@ async function main() {
8882
9483
  break;
8883
9484
  case "delete":
8884
9485
  case "rm":
8885
- await cmdDelete(sdk, positional);
9486
+ await cmdDelete(sdk, positional, flags);
9487
+ break;
9488
+ case "boards":
9489
+ case "board":
9490
+ await cmdBoards(sdk, positional, flags, workspaceRoot);
9491
+ break;
9492
+ case "transfer":
9493
+ await cmdTransfer(sdk, positional, flags);
8886
9494
  break;
8887
9495
  case "attach":
8888
9496
  await cmdAttach(sdk, positional, flags);