kanban-lite 1.0.9 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1015 -407
- package/dist/extension.js +897 -347
- package/dist/mcp-server.js +550 -189
- package/dist/sdk/index.cjs +1223 -0
- package/dist/sdk/index.mjs +1168 -0
- package/dist/sdk/sdk/KanbanSDK.d.ts +39 -21
- package/dist/sdk/sdk/index.d.ts +4 -3
- package/dist/sdk/sdk/migration.d.ts +6 -0
- package/dist/sdk/sdk/types.d.ts +3 -5
- package/dist/sdk/shared/config.d.ts +23 -10
- package/dist/sdk/shared/types.d.ts +18 -4
- package/dist/standalone-webview/index.js +27 -27
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone-webview/style.css +1 -1
- package/dist/standalone.js +831 -328
- package/dist/webview/index.js +45 -45
- package/dist/webview/index.js.map +1 -1
- package/dist/webview/style.css +1 -1
- package/package.json +4 -3
- package/src/cli/index.ts +217 -95
- package/src/extension/KanbanPanel.ts +49 -22
- package/src/mcp-server/index.ts +138 -62
- package/src/sdk/KanbanSDK.ts +283 -77
- package/src/sdk/__tests__/KanbanSDK.test.ts +5 -5
- package/src/sdk/__tests__/migration.test.ts +269 -0
- package/src/sdk/__tests__/multi-board.test.ts +449 -0
- package/src/sdk/index.ts +4 -3
- package/src/sdk/migration.ts +52 -0
- package/src/sdk/types.ts +3 -6
- package/src/shared/config.ts +144 -22
- package/src/shared/types.ts +14 -5
- package/src/standalone/__tests__/server.integration.test.ts +38 -37
- package/src/standalone/server.ts +239 -21
- package/src/webview/App.tsx +17 -7
- package/src/webview/components/Toolbar.tsx +99 -3
- package/src/webview/store/index.ts +11 -3
- package/.kanban/backlog/1-test1.md +0 -30
- package/.kanban.json +0 -42
package/dist/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: [...
|
|
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
|
-
|
|
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
|
|
355
|
+
function getBoardConfig(workspaceRoot, boardId) {
|
|
295
356
|
const config = readConfig(workspaceRoot);
|
|
296
|
-
const
|
|
297
|
-
|
|
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
|
-
|
|
306
|
-
|
|
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
|
|
700
|
+
var fs4, path5, KanbanSDK;
|
|
569
701
|
var init_KanbanSDK = __esm({
|
|
570
702
|
"src/sdk/KanbanSDK.ts"() {
|
|
571
703
|
"use strict";
|
|
572
|
-
|
|
573
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
859
|
+
await ensureStatusSubfolders(boardDir, columns);
|
|
594
860
|
}
|
|
595
861
|
try {
|
|
596
|
-
const rootFiles = await this._readMdFiles(
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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,
|
|
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
|
|
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(
|
|
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
|
|
662
|
-
const
|
|
663
|
-
const
|
|
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
|
|
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
|
|
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(
|
|
964
|
+
filePath: getFeatureFilePath(boardDir, status, filename)
|
|
687
965
|
};
|
|
688
|
-
await
|
|
689
|
-
await
|
|
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
|
|
982
|
+
card.completedAt = this._isCompletedStatus(card.status, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
703
983
|
}
|
|
704
|
-
await
|
|
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,
|
|
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
|
|
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
|
|
1015
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
734
1016
|
if (oldStatus !== newStatus) {
|
|
735
|
-
const newPath = await moveFeatureFile(card.filePath,
|
|
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
|
|
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 =
|
|
774
|
-
const cardDir =
|
|
775
|
-
const destPath =
|
|
776
|
-
const sourceDir =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1130
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
849
1131
|
return card;
|
|
850
1132
|
}
|
|
851
|
-
// --- Column management ---
|
|
852
|
-
listColumns() {
|
|
853
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1149
|
+
board.columns.push(column);
|
|
861
1150
|
writeConfig(this.workspaceRoot, config);
|
|
862
|
-
return
|
|
1151
|
+
return board.columns;
|
|
863
1152
|
}
|
|
864
|
-
updateColumn(columnId, updates) {
|
|
1153
|
+
updateColumn(columnId, updates, boardId) {
|
|
865
1154
|
const config = readConfig(this.workspaceRoot);
|
|
866
|
-
const
|
|
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
|
|
1167
|
+
return board.columns;
|
|
875
1168
|
}
|
|
876
|
-
async removeColumn(columnId) {
|
|
1169
|
+
async removeColumn(columnId, boardId) {
|
|
877
1170
|
const config = readConfig(this.workspaceRoot);
|
|
878
|
-
const
|
|
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
|
-
|
|
1183
|
+
board.columns.splice(idx, 1);
|
|
887
1184
|
writeConfig(this.workspaceRoot, config);
|
|
888
|
-
return
|
|
1185
|
+
return board.columns;
|
|
889
1186
|
}
|
|
890
|
-
reorderColumns(columnIds) {
|
|
1187
|
+
reorderColumns(columnIds, boardId) {
|
|
891
1188
|
const config = readConfig(this.workspaceRoot);
|
|
892
|
-
const
|
|
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 !==
|
|
1198
|
+
if (columnIds.length !== board.columns.length) {
|
|
898
1199
|
throw new Error("Must include all column IDs when reordering");
|
|
899
1200
|
}
|
|
900
|
-
|
|
1201
|
+
board.columns = columnIds.map((id) => colMap.get(id));
|
|
901
1202
|
writeConfig(this.workspaceRoot, config);
|
|
902
|
-
return
|
|
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
|
|
915
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) =>
|
|
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
|
|
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
|
|
1228
|
+
return path6.join(workspaceRoot, WEBHOOKS_FILENAME);
|
|
928
1229
|
}
|
|
929
1230
|
function loadWebhooks(workspaceRoot) {
|
|
930
1231
|
try {
|
|
931
|
-
const raw =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1026
|
-
|
|
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 = (
|
|
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:
|
|
4870
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
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(
|
|
5211
|
+
async _exploreDir(path10, depth) {
|
|
4911
5212
|
let files;
|
|
4912
5213
|
try {
|
|
4913
|
-
files = await (0, import_promises.readdir)(
|
|
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:
|
|
5218
|
+
return { files, depth, path: path10 };
|
|
4918
5219
|
}
|
|
4919
|
-
async _formatEntry(dirent,
|
|
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)(
|
|
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(
|
|
5281
|
+
function createFsWatchInstance(path10, options, listener, errHandler, emitRaw) {
|
|
4981
5282
|
const handleEvent = (rawEvent, evPath) => {
|
|
4982
|
-
listener(
|
|
4983
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
4984
|
-
if (evPath &&
|
|
4985
|
-
fsWatchBroadcast(sysPath.resolve(
|
|
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)(
|
|
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 = (
|
|
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(
|
|
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
|
-
|
|
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)(
|
|
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 = (
|
|
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(
|
|
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(
|
|
5750
|
+
_watchWithNodeFs(path10, listener) {
|
|
5450
5751
|
const opts = this.fsw.options;
|
|
5451
|
-
const directory = sysPath.dirname(
|
|
5452
|
-
const basename7 = sysPath.basename(
|
|
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(
|
|
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(
|
|
5766
|
+
closer = setFsWatchFileListener(path10, absolutePath, options, {
|
|
5466
5767
|
listener,
|
|
5467
5768
|
rawEmitter: this.fsw._emitRaw
|
|
5468
5769
|
});
|
|
5469
5770
|
} else {
|
|
5470
|
-
closer = setFsWatchListener(
|
|
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 (
|
|
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(
|
|
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(
|
|
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,
|
|
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)(
|
|
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,
|
|
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,
|
|
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
|
|
5897
|
+
let path10 = sysPath.join(directory, item);
|
|
5597
5898
|
current.add(item);
|
|
5598
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
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
|
-
|
|
5608
|
-
this._addToNodeFs(
|
|
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(
|
|
5978
|
+
async _addToNodeFs(path10, initialAdd, priorWh, depth, target) {
|
|
5678
5979
|
const ready = this.fsw._emitReady;
|
|
5679
|
-
if (this.fsw._isIgnored(
|
|
5980
|
+
if (this.fsw._isIgnored(path10) || this.fsw.closed) {
|
|
5680
5981
|
ready();
|
|
5681
5982
|
return false;
|
|
5682
5983
|
}
|
|
5683
|
-
const wh = this.fsw._getWatchHelpers(
|
|
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(
|
|
5700
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
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)(
|
|
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,
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
5768
|
-
if (typeof
|
|
6068
|
+
function normalizePath(path10) {
|
|
6069
|
+
if (typeof path10 !== "string")
|
|
5769
6070
|
throw new Error("string expected");
|
|
5770
|
-
|
|
5771
|
-
|
|
6071
|
+
path10 = sysPath2.normalize(path10);
|
|
6072
|
+
path10 = path10.replace(/\\/g, "/");
|
|
5772
6073
|
let prepend = false;
|
|
5773
|
-
if (
|
|
6074
|
+
if (path10.startsWith("//"))
|
|
5774
6075
|
prepend = true;
|
|
5775
6076
|
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
5776
|
-
while (
|
|
5777
|
-
|
|
6077
|
+
while (path10.match(DOUBLE_SLASH_RE2))
|
|
6078
|
+
path10 = path10.replace(DOUBLE_SLASH_RE2, "/");
|
|
5778
6079
|
if (prepend)
|
|
5779
|
-
|
|
5780
|
-
return
|
|
6080
|
+
path10 = "/" + path10;
|
|
6081
|
+
return path10;
|
|
5781
6082
|
}
|
|
5782
6083
|
function matchPatterns(patterns, testString, stats) {
|
|
5783
|
-
const
|
|
6084
|
+
const path10 = normalizePath(testString);
|
|
5784
6085
|
for (let index = 0; index < patterns.length; index++) {
|
|
5785
6086
|
const pattern = patterns[index];
|
|
5786
|
-
if (pattern(
|
|
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 = (
|
|
5851
|
-
normalizeIgnored = (cwd = "") => (
|
|
5852
|
-
if (typeof
|
|
5853
|
-
return normalizePathToUnix(sysPath2.isAbsolute(
|
|
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
|
|
6156
|
+
return path10;
|
|
5856
6157
|
}
|
|
5857
6158
|
};
|
|
5858
|
-
getAbsolutePath = (
|
|
5859
|
-
if (sysPath2.isAbsolute(
|
|
5860
|
-
return
|
|
6159
|
+
getAbsolutePath = (path10, cwd) => {
|
|
6160
|
+
if (sysPath2.isAbsolute(path10)) {
|
|
6161
|
+
return path10;
|
|
5861
6162
|
}
|
|
5862
|
-
return sysPath2.join(cwd,
|
|
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(
|
|
6218
|
+
constructor(path10, follow, fsw) {
|
|
5918
6219
|
this.fsw = fsw;
|
|
5919
|
-
const watchPath =
|
|
5920
|
-
this.path =
|
|
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((
|
|
6043
|
-
const absPath = getAbsolutePath(
|
|
6343
|
+
paths = paths.map((path10) => {
|
|
6344
|
+
const absPath = getAbsolutePath(path10, cwd);
|
|
6044
6345
|
return absPath;
|
|
6045
6346
|
});
|
|
6046
6347
|
}
|
|
6047
|
-
paths.forEach((
|
|
6048
|
-
this._removeIgnoredPath(
|
|
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 (
|
|
6055
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
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((
|
|
6078
|
-
if (!sysPath2.isAbsolute(
|
|
6378
|
+
paths.forEach((path10) => {
|
|
6379
|
+
if (!sysPath2.isAbsolute(path10) && !this._closers.has(path10)) {
|
|
6079
6380
|
if (cwd)
|
|
6080
|
-
|
|
6081
|
-
|
|
6381
|
+
path10 = sysPath2.join(cwd, path10);
|
|
6382
|
+
path10 = sysPath2.resolve(path10);
|
|
6082
6383
|
}
|
|
6083
|
-
this._closePath(
|
|
6084
|
-
this._addIgnoredPath(
|
|
6085
|
-
if (this._watched.has(
|
|
6384
|
+
this._closePath(path10);
|
|
6385
|
+
this._addIgnoredPath(path10);
|
|
6386
|
+
if (this._watched.has(path10)) {
|
|
6086
6387
|
this._addIgnoredPath({
|
|
6087
|
-
path:
|
|
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,
|
|
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
|
-
|
|
6457
|
+
path10 = sysPath2.normalize(path10);
|
|
6157
6458
|
if (opts.cwd)
|
|
6158
|
-
|
|
6159
|
-
const args = [
|
|
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(
|
|
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(
|
|
6471
|
+
this._pendingUnlinks.set(path10, [event, ...args]);
|
|
6171
6472
|
setTimeout(() => {
|
|
6172
|
-
this._pendingUnlinks.forEach((entry,
|
|
6473
|
+
this._pendingUnlinks.forEach((entry, path11) => {
|
|
6173
6474
|
this.emit(...entry);
|
|
6174
6475
|
this.emit(EVENTS.ALL, ...entry);
|
|
6175
|
-
this._pendingUnlinks.delete(
|
|
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(
|
|
6481
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path10)) {
|
|
6181
6482
|
event = EVENTS.CHANGE;
|
|
6182
|
-
this._pendingUnlinks.delete(
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
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(
|
|
6555
|
+
const item = action.get(path10);
|
|
6255
6556
|
const count = item ? item.count : 0;
|
|
6256
|
-
action.delete(
|
|
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(
|
|
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(
|
|
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 =
|
|
6285
|
-
if (this.options.cwd && !sysPath2.isAbsolute(
|
|
6286
|
-
fullPath = sysPath2.join(this.options.cwd,
|
|
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(
|
|
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(
|
|
6600
|
+
writes.get(path10).lastChange = now2;
|
|
6300
6601
|
}
|
|
6301
|
-
const pw = writes.get(
|
|
6602
|
+
const pw = writes.get(path10);
|
|
6302
6603
|
const df = now2 - pw.lastChange;
|
|
6303
6604
|
if (df >= threshold) {
|
|
6304
|
-
writes.delete(
|
|
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(
|
|
6312
|
-
writes.set(
|
|
6612
|
+
if (!writes.has(path10)) {
|
|
6613
|
+
writes.set(path10, {
|
|
6313
6614
|
lastChange: now,
|
|
6314
6615
|
cancelWait: () => {
|
|
6315
|
-
writes.delete(
|
|
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(
|
|
6327
|
-
if (this.options.atomic && DOT_RE.test(
|
|
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(
|
|
6638
|
+
return this._userIgnored(path10, stats);
|
|
6338
6639
|
}
|
|
6339
|
-
_isntIgnored(
|
|
6340
|
-
return !this._isIgnored(
|
|
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(
|
|
6347
|
-
return new WatchHelper(
|
|
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
|
|
6380
|
-
const fullPath = sysPath2.resolve(
|
|
6381
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
6382
|
-
if (!this._throttle("remove",
|
|
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(
|
|
6688
|
+
const wp = this._getWatchedDir(path10);
|
|
6388
6689
|
const nestedDirectoryChildren = wp.getChildren();
|
|
6389
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
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 =
|
|
6697
|
+
let relPath = path10;
|
|
6397
6698
|
if (this.options.cwd)
|
|
6398
|
-
relPath = sysPath2.relative(this.options.cwd,
|
|
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(
|
|
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(
|
|
6408
|
-
this._emit(eventName,
|
|
6409
|
-
this._closePath(
|
|
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(
|
|
6415
|
-
this._closeFile(
|
|
6416
|
-
const dir = sysPath2.dirname(
|
|
6417
|
-
this._getWatchedDir(dir).remove(sysPath2.basename(
|
|
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(
|
|
6423
|
-
const closers = this._closers.get(
|
|
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(
|
|
6728
|
+
this._closers.delete(path10);
|
|
6428
6729
|
}
|
|
6429
|
-
_addPathCloser(
|
|
6730
|
+
_addPathCloser(path10, closer) {
|
|
6430
6731
|
if (!closer)
|
|
6431
6732
|
return;
|
|
6432
|
-
let list = this._closers.get(
|
|
6733
|
+
let list = this._closers.get(path10);
|
|
6433
6734
|
if (!list) {
|
|
6434
6735
|
list = [];
|
|
6435
|
-
this._closers.set(
|
|
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 =
|
|
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
|
-
|
|
6473
|
-
const
|
|
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 =
|
|
6681
|
-
|
|
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,
|
|
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/
|
|
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 =
|
|
7069
|
-
const attachmentPath =
|
|
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 =
|
|
7580
|
+
const ext2 = path7.extname(attachName);
|
|
7076
7581
|
const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
|
|
7077
|
-
|
|
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 =
|
|
7293
|
-
const attachmentPath =
|
|
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 =
|
|
7804
|
+
const ext2 = path7.extname(filename);
|
|
7300
7805
|
const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
|
|
7301
|
-
|
|
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 =
|
|
7316
|
-
if (!
|
|
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 =
|
|
7826
|
+
const ext = path7.extname(filePath);
|
|
7322
7827
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
7323
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
7411
|
-
|
|
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
|
|
8022
|
-
var
|
|
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
|
|
8567
|
+
await fs12.access(path9.join(dir, ".git"));
|
|
8055
8568
|
return dir;
|
|
8056
8569
|
} catch {
|
|
8057
8570
|
}
|
|
8058
8571
|
try {
|
|
8059
|
-
await
|
|
8572
|
+
await fs12.access(path9.join(dir, "package.json"));
|
|
8060
8573
|
return dir;
|
|
8061
8574
|
} catch {
|
|
8062
8575
|
}
|
|
8063
|
-
const parent =
|
|
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
|
|
8584
|
+
return path9.resolve(flags.dir);
|
|
8072
8585
|
}
|
|
8073
8586
|
const root = await findWorkspaceRoot(process.cwd());
|
|
8074
|
-
return
|
|
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
|
-
|
|
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
|
|
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
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
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
|
-
|
|
8250
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8281
|
-
const
|
|
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
|
-
|
|
8300
|
-
|
|
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
|
-
|
|
8332
|
-
const
|
|
8333
|
-
|
|
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}: ${
|
|
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 =
|
|
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);
|