kanban-lite 1.0.9 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/cli.js +1015 -407
  2. package/dist/extension.js +897 -347
  3. package/dist/mcp-server.js +550 -189
  4. package/dist/sdk/index.cjs +1223 -0
  5. package/dist/sdk/index.mjs +1168 -0
  6. package/dist/sdk/sdk/KanbanSDK.d.ts +39 -21
  7. package/dist/sdk/sdk/index.d.ts +4 -3
  8. package/dist/sdk/sdk/migration.d.ts +6 -0
  9. package/dist/sdk/sdk/types.d.ts +3 -5
  10. package/dist/sdk/shared/config.d.ts +23 -10
  11. package/dist/sdk/shared/types.d.ts +18 -4
  12. package/dist/standalone-webview/index.js +27 -27
  13. package/dist/standalone-webview/index.js.map +1 -1
  14. package/dist/standalone-webview/style.css +1 -1
  15. package/dist/standalone.js +831 -328
  16. package/dist/webview/index.js +45 -45
  17. package/dist/webview/index.js.map +1 -1
  18. package/dist/webview/style.css +1 -1
  19. package/package.json +4 -3
  20. package/src/cli/index.ts +217 -95
  21. package/src/extension/KanbanPanel.ts +49 -22
  22. package/src/mcp-server/index.ts +138 -62
  23. package/src/sdk/KanbanSDK.ts +283 -77
  24. package/src/sdk/__tests__/KanbanSDK.test.ts +5 -5
  25. package/src/sdk/__tests__/migration.test.ts +269 -0
  26. package/src/sdk/__tests__/multi-board.test.ts +449 -0
  27. package/src/sdk/index.ts +4 -3
  28. package/src/sdk/migration.ts +52 -0
  29. package/src/sdk/types.ts +3 -6
  30. package/src/shared/config.ts +144 -22
  31. package/src/shared/types.ts +14 -5
  32. package/src/standalone/__tests__/server.integration.test.ts +38 -37
  33. package/src/standalone/server.ts +239 -21
  34. package/src/webview/App.tsx +17 -7
  35. package/src/webview/components/Toolbar.tsx +99 -3
  36. package/src/webview/store/index.ts +11 -3
  37. package/.kanban/backlog/1-test1.md +0 -30
  38. package/.kanban.json +0 -42
@@ -4334,8 +4334,8 @@ var init_open = __esm({
4334
4334
 
4335
4335
  // src/standalone/server.ts
4336
4336
  var http2 = __toESM(require("http"));
4337
- var fs5 = __toESM(require("fs"));
4338
- var path6 = __toESM(require("path"));
4337
+ var fs6 = __toESM(require("fs"));
4338
+ var path7 = __toESM(require("path"));
4339
4339
 
4340
4340
  // node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
4341
4341
  var import_stream = __toESM(require_stream(), 1);
@@ -4420,7 +4420,7 @@ var ReaddirpStream = class extends import_node_stream.Readable {
4420
4420
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
4421
4421
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
4422
4422
  if (wantBigintFsStats) {
4423
- this._stat = (path8) => statMethod(path8, { bigint: true });
4423
+ this._stat = (path9) => statMethod(path9, { bigint: true });
4424
4424
  } else {
4425
4425
  this._stat = statMethod;
4426
4426
  }
@@ -4445,8 +4445,8 @@ var ReaddirpStream = class extends import_node_stream.Readable {
4445
4445
  const par = this.parent;
4446
4446
  const fil = par && par.files;
4447
4447
  if (fil && fil.length > 0) {
4448
- const { path: path8, depth } = par;
4449
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path8));
4448
+ const { path: path9, depth } = par;
4449
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path9));
4450
4450
  const awaited = await Promise.all(slice);
4451
4451
  for (const entry of awaited) {
4452
4452
  if (!entry)
@@ -4486,20 +4486,20 @@ var ReaddirpStream = class extends import_node_stream.Readable {
4486
4486
  this.reading = false;
4487
4487
  }
4488
4488
  }
4489
- async _exploreDir(path8, depth) {
4489
+ async _exploreDir(path9, depth) {
4490
4490
  let files;
4491
4491
  try {
4492
- files = await (0, import_promises.readdir)(path8, this._rdOptions);
4492
+ files = await (0, import_promises.readdir)(path9, this._rdOptions);
4493
4493
  } catch (error) {
4494
4494
  this._onError(error);
4495
4495
  }
4496
- return { files, depth, path: path8 };
4496
+ return { files, depth, path: path9 };
4497
4497
  }
4498
- async _formatEntry(dirent, path8) {
4498
+ async _formatEntry(dirent, path9) {
4499
4499
  let entry;
4500
4500
  const basename6 = this._isDirent ? dirent.name : dirent;
4501
4501
  try {
4502
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path8, basename6));
4502
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path9, basename6));
4503
4503
  entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename6 };
4504
4504
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
4505
4505
  } catch (err) {
@@ -4899,16 +4899,16 @@ var delFromSet = (main, prop, item) => {
4899
4899
  };
4900
4900
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
4901
4901
  var FsWatchInstances = /* @__PURE__ */ new Map();
4902
- function createFsWatchInstance(path8, options, listener, errHandler, emitRaw) {
4902
+ function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
4903
4903
  const handleEvent = (rawEvent, evPath) => {
4904
- listener(path8);
4905
- emitRaw(rawEvent, evPath, { watchedPath: path8 });
4906
- if (evPath && path8 !== evPath) {
4907
- fsWatchBroadcast(sysPath.resolve(path8, evPath), KEY_LISTENERS, sysPath.join(path8, evPath));
4904
+ listener(path9);
4905
+ emitRaw(rawEvent, evPath, { watchedPath: path9 });
4906
+ if (evPath && path9 !== evPath) {
4907
+ fsWatchBroadcast(sysPath.resolve(path9, evPath), KEY_LISTENERS, sysPath.join(path9, evPath));
4908
4908
  }
4909
4909
  };
4910
4910
  try {
4911
- return (0, import_fs.watch)(path8, {
4911
+ return (0, import_fs.watch)(path9, {
4912
4912
  persistent: options.persistent
4913
4913
  }, handleEvent);
4914
4914
  } catch (error) {
@@ -4924,12 +4924,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
4924
4924
  listener(val1, val2, val3);
4925
4925
  });
4926
4926
  };
4927
- var setFsWatchListener = (path8, fullPath, options, handlers) => {
4927
+ var setFsWatchListener = (path9, fullPath, options, handlers) => {
4928
4928
  const { listener, errHandler, rawEmitter } = handlers;
4929
4929
  let cont = FsWatchInstances.get(fullPath);
4930
4930
  let watcher;
4931
4931
  if (!options.persistent) {
4932
- watcher = createFsWatchInstance(path8, options, listener, errHandler, rawEmitter);
4932
+ watcher = createFsWatchInstance(path9, options, listener, errHandler, rawEmitter);
4933
4933
  if (!watcher)
4934
4934
  return;
4935
4935
  return watcher.close.bind(watcher);
@@ -4940,7 +4940,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4940
4940
  addAndConvert(cont, KEY_RAW, rawEmitter);
4941
4941
  } else {
4942
4942
  watcher = createFsWatchInstance(
4943
- path8,
4943
+ path9,
4944
4944
  options,
4945
4945
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
4946
4946
  errHandler,
@@ -4955,7 +4955,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4955
4955
  cont.watcherUnusable = true;
4956
4956
  if (isWindows && error.code === "EPERM") {
4957
4957
  try {
4958
- const fd = await (0, import_promises2.open)(path8, "r");
4958
+ const fd = await (0, import_promises2.open)(path9, "r");
4959
4959
  await fd.close();
4960
4960
  broadcastErr(error);
4961
4961
  } catch (err) {
@@ -4986,7 +4986,7 @@ var setFsWatchListener = (path8, fullPath, options, handlers) => {
4986
4986
  };
4987
4987
  };
4988
4988
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
4989
- var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
4989
+ var setFsWatchFileListener = (path9, fullPath, options, handlers) => {
4990
4990
  const { listener, rawEmitter } = handlers;
4991
4991
  let cont = FsWatchFileInstances.get(fullPath);
4992
4992
  const copts = cont && cont.options;
@@ -5008,7 +5008,7 @@ var setFsWatchFileListener = (path8, fullPath, options, handlers) => {
5008
5008
  });
5009
5009
  const currmtime = curr.mtimeMs;
5010
5010
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
5011
- foreach(cont.listeners, (listener2) => listener2(path8, curr));
5011
+ foreach(cont.listeners, (listener2) => listener2(path9, curr));
5012
5012
  }
5013
5013
  })
5014
5014
  };
@@ -5036,13 +5036,13 @@ var NodeFsHandler = class {
5036
5036
  * @param listener on fs change
5037
5037
  * @returns closer for the watcher instance
5038
5038
  */
5039
- _watchWithNodeFs(path8, listener) {
5039
+ _watchWithNodeFs(path9, listener) {
5040
5040
  const opts = this.fsw.options;
5041
- const directory = sysPath.dirname(path8);
5042
- const basename6 = sysPath.basename(path8);
5041
+ const directory = sysPath.dirname(path9);
5042
+ const basename6 = sysPath.basename(path9);
5043
5043
  const parent = this.fsw._getWatchedDir(directory);
5044
5044
  parent.add(basename6);
5045
- const absolutePath = sysPath.resolve(path8);
5045
+ const absolutePath = sysPath.resolve(path9);
5046
5046
  const options = {
5047
5047
  persistent: opts.persistent
5048
5048
  };
@@ -5052,12 +5052,12 @@ var NodeFsHandler = class {
5052
5052
  if (opts.usePolling) {
5053
5053
  const enableBin = opts.interval !== opts.binaryInterval;
5054
5054
  options.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
5055
- closer = setFsWatchFileListener(path8, absolutePath, options, {
5055
+ closer = setFsWatchFileListener(path9, absolutePath, options, {
5056
5056
  listener,
5057
5057
  rawEmitter: this.fsw._emitRaw
5058
5058
  });
5059
5059
  } else {
5060
- closer = setFsWatchListener(path8, absolutePath, options, {
5060
+ closer = setFsWatchListener(path9, absolutePath, options, {
5061
5061
  listener,
5062
5062
  errHandler: this._boundHandleError,
5063
5063
  rawEmitter: this.fsw._emitRaw
@@ -5079,7 +5079,7 @@ var NodeFsHandler = class {
5079
5079
  let prevStats = stats;
5080
5080
  if (parent.has(basename6))
5081
5081
  return;
5082
- const listener = async (path8, newStats) => {
5082
+ const listener = async (path9, newStats) => {
5083
5083
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
5084
5084
  return;
5085
5085
  if (!newStats || newStats.mtimeMs === 0) {
@@ -5093,11 +5093,11 @@ var NodeFsHandler = class {
5093
5093
  this.fsw._emit(EV.CHANGE, file, newStats2);
5094
5094
  }
5095
5095
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
5096
- this.fsw._closeFile(path8);
5096
+ this.fsw._closeFile(path9);
5097
5097
  prevStats = newStats2;
5098
5098
  const closer2 = this._watchWithNodeFs(file, listener);
5099
5099
  if (closer2)
5100
- this.fsw._addPathCloser(path8, closer2);
5100
+ this.fsw._addPathCloser(path9, closer2);
5101
5101
  } else {
5102
5102
  prevStats = newStats2;
5103
5103
  }
@@ -5129,7 +5129,7 @@ var NodeFsHandler = class {
5129
5129
  * @param item basename of this item
5130
5130
  * @returns true if no more processing is needed for this entry.
5131
5131
  */
5132
- async _handleSymlink(entry, directory, path8, item) {
5132
+ async _handleSymlink(entry, directory, path9, item) {
5133
5133
  if (this.fsw.closed) {
5134
5134
  return;
5135
5135
  }
@@ -5139,7 +5139,7 @@ var NodeFsHandler = class {
5139
5139
  this.fsw._incrReadyCount();
5140
5140
  let linkPath;
5141
5141
  try {
5142
- linkPath = await (0, import_promises2.realpath)(path8);
5142
+ linkPath = await (0, import_promises2.realpath)(path9);
5143
5143
  } catch (e) {
5144
5144
  this.fsw._emitReady();
5145
5145
  return true;
@@ -5149,12 +5149,12 @@ var NodeFsHandler = class {
5149
5149
  if (dir2.has(item)) {
5150
5150
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
5151
5151
  this.fsw._symlinkPaths.set(full, linkPath);
5152
- this.fsw._emit(EV.CHANGE, path8, entry.stats);
5152
+ this.fsw._emit(EV.CHANGE, path9, entry.stats);
5153
5153
  }
5154
5154
  } else {
5155
5155
  dir2.add(item);
5156
5156
  this.fsw._symlinkPaths.set(full, linkPath);
5157
- this.fsw._emit(EV.ADD, path8, entry.stats);
5157
+ this.fsw._emit(EV.ADD, path9, entry.stats);
5158
5158
  }
5159
5159
  this.fsw._emitReady();
5160
5160
  return true;
@@ -5183,9 +5183,9 @@ var NodeFsHandler = class {
5183
5183
  return;
5184
5184
  }
5185
5185
  const item = entry.path;
5186
- let path8 = sysPath.join(directory, item);
5186
+ let path9 = sysPath.join(directory, item);
5187
5187
  current.add(item);
5188
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path8, item)) {
5188
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path9, item)) {
5189
5189
  return;
5190
5190
  }
5191
5191
  if (this.fsw.closed) {
@@ -5194,8 +5194,8 @@ var NodeFsHandler = class {
5194
5194
  }
5195
5195
  if (item === target || !target && !previous.has(item)) {
5196
5196
  this.fsw._incrReadyCount();
5197
- path8 = sysPath.join(dir2, sysPath.relative(dir2, path8));
5198
- this._addToNodeFs(path8, initialAdd, wh, depth + 1);
5197
+ path9 = sysPath.join(dir2, sysPath.relative(dir2, path9));
5198
+ this._addToNodeFs(path9, initialAdd, wh, depth + 1);
5199
5199
  }
5200
5200
  }).on(EV.ERROR, this._boundHandleError);
5201
5201
  return new Promise((resolve5, reject) => {
@@ -5264,13 +5264,13 @@ var NodeFsHandler = class {
5264
5264
  * @param depth Child path actually targeted for watch
5265
5265
  * @param target Child path actually targeted for watch
5266
5266
  */
5267
- async _addToNodeFs(path8, initialAdd, priorWh, depth, target) {
5267
+ async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
5268
5268
  const ready = this.fsw._emitReady;
5269
- if (this.fsw._isIgnored(path8) || this.fsw.closed) {
5269
+ if (this.fsw._isIgnored(path9) || this.fsw.closed) {
5270
5270
  ready();
5271
5271
  return false;
5272
5272
  }
5273
- const wh = this.fsw._getWatchHelpers(path8);
5273
+ const wh = this.fsw._getWatchHelpers(path9);
5274
5274
  if (priorWh) {
5275
5275
  wh.filterPath = (entry) => priorWh.filterPath(entry);
5276
5276
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -5286,8 +5286,8 @@ var NodeFsHandler = class {
5286
5286
  const follow = this.fsw.options.followSymlinks;
5287
5287
  let closer;
5288
5288
  if (stats.isDirectory()) {
5289
- const absPath = sysPath.resolve(path8);
5290
- const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
5289
+ const absPath = sysPath.resolve(path9);
5290
+ const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
5291
5291
  if (this.fsw.closed)
5292
5292
  return;
5293
5293
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -5297,29 +5297,29 @@ var NodeFsHandler = class {
5297
5297
  this.fsw._symlinkPaths.set(absPath, targetPath);
5298
5298
  }
5299
5299
  } else if (stats.isSymbolicLink()) {
5300
- const targetPath = follow ? await (0, import_promises2.realpath)(path8) : path8;
5300
+ const targetPath = follow ? await (0, import_promises2.realpath)(path9) : path9;
5301
5301
  if (this.fsw.closed)
5302
5302
  return;
5303
5303
  const parent = sysPath.dirname(wh.watchPath);
5304
5304
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
5305
5305
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
5306
- closer = await this._handleDir(parent, stats, initialAdd, depth, path8, wh, targetPath);
5306
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path9, wh, targetPath);
5307
5307
  if (this.fsw.closed)
5308
5308
  return;
5309
5309
  if (targetPath !== void 0) {
5310
- this.fsw._symlinkPaths.set(sysPath.resolve(path8), targetPath);
5310
+ this.fsw._symlinkPaths.set(sysPath.resolve(path9), targetPath);
5311
5311
  }
5312
5312
  } else {
5313
5313
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
5314
5314
  }
5315
5315
  ready();
5316
5316
  if (closer)
5317
- this.fsw._addPathCloser(path8, closer);
5317
+ this.fsw._addPathCloser(path9, closer);
5318
5318
  return false;
5319
5319
  } catch (error) {
5320
5320
  if (this.fsw._handleError(error)) {
5321
5321
  ready();
5322
- return path8;
5322
+ return path9;
5323
5323
  }
5324
5324
  }
5325
5325
  }
@@ -5362,26 +5362,26 @@ function createPattern(matcher) {
5362
5362
  }
5363
5363
  return () => false;
5364
5364
  }
5365
- function normalizePath(path8) {
5366
- if (typeof path8 !== "string")
5365
+ function normalizePath(path9) {
5366
+ if (typeof path9 !== "string")
5367
5367
  throw new Error("string expected");
5368
- path8 = sysPath2.normalize(path8);
5369
- path8 = path8.replace(/\\/g, "/");
5368
+ path9 = sysPath2.normalize(path9);
5369
+ path9 = path9.replace(/\\/g, "/");
5370
5370
  let prepend = false;
5371
- if (path8.startsWith("//"))
5371
+ if (path9.startsWith("//"))
5372
5372
  prepend = true;
5373
5373
  const DOUBLE_SLASH_RE2 = /\/\//;
5374
- while (path8.match(DOUBLE_SLASH_RE2))
5375
- path8 = path8.replace(DOUBLE_SLASH_RE2, "/");
5374
+ while (path9.match(DOUBLE_SLASH_RE2))
5375
+ path9 = path9.replace(DOUBLE_SLASH_RE2, "/");
5376
5376
  if (prepend)
5377
- path8 = "/" + path8;
5378
- return path8;
5377
+ path9 = "/" + path9;
5378
+ return path9;
5379
5379
  }
5380
5380
  function matchPatterns(patterns, testString, stats) {
5381
- const path8 = normalizePath(testString);
5381
+ const path9 = normalizePath(testString);
5382
5382
  for (let index = 0; index < patterns.length; index++) {
5383
5383
  const pattern = patterns[index];
5384
- if (pattern(path8, stats)) {
5384
+ if (pattern(path9, stats)) {
5385
5385
  return true;
5386
5386
  }
5387
5387
  }
@@ -5421,19 +5421,19 @@ var toUnix = (string) => {
5421
5421
  }
5422
5422
  return str;
5423
5423
  };
5424
- var normalizePathToUnix = (path8) => toUnix(sysPath2.normalize(toUnix(path8)));
5425
- var normalizeIgnored = (cwd = "") => (path8) => {
5426
- if (typeof path8 === "string") {
5427
- return normalizePathToUnix(sysPath2.isAbsolute(path8) ? path8 : sysPath2.join(cwd, path8));
5424
+ var normalizePathToUnix = (path9) => toUnix(sysPath2.normalize(toUnix(path9)));
5425
+ var normalizeIgnored = (cwd = "") => (path9) => {
5426
+ if (typeof path9 === "string") {
5427
+ return normalizePathToUnix(sysPath2.isAbsolute(path9) ? path9 : sysPath2.join(cwd, path9));
5428
5428
  } else {
5429
- return path8;
5429
+ return path9;
5430
5430
  }
5431
5431
  };
5432
- var getAbsolutePath = (path8, cwd) => {
5433
- if (sysPath2.isAbsolute(path8)) {
5434
- return path8;
5432
+ var getAbsolutePath = (path9, cwd) => {
5433
+ if (sysPath2.isAbsolute(path9)) {
5434
+ return path9;
5435
5435
  }
5436
- return sysPath2.join(cwd, path8);
5436
+ return sysPath2.join(cwd, path9);
5437
5437
  };
5438
5438
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
5439
5439
  var DirEntry = class {
@@ -5488,10 +5488,10 @@ var DirEntry = class {
5488
5488
  var STAT_METHOD_F = "stat";
5489
5489
  var STAT_METHOD_L = "lstat";
5490
5490
  var WatchHelper = class {
5491
- constructor(path8, follow, fsw) {
5491
+ constructor(path9, follow, fsw) {
5492
5492
  this.fsw = fsw;
5493
- const watchPath = path8;
5494
- this.path = path8 = path8.replace(REPLACER_RE, "");
5493
+ const watchPath = path9;
5494
+ this.path = path9 = path9.replace(REPLACER_RE, "");
5495
5495
  this.watchPath = watchPath;
5496
5496
  this.fullWatchPath = sysPath2.resolve(watchPath);
5497
5497
  this.dirParts = [];
@@ -5613,20 +5613,20 @@ var FSWatcher = class extends import_events.EventEmitter {
5613
5613
  this._closePromise = void 0;
5614
5614
  let paths = unifyPaths(paths_);
5615
5615
  if (cwd) {
5616
- paths = paths.map((path8) => {
5617
- const absPath = getAbsolutePath(path8, cwd);
5616
+ paths = paths.map((path9) => {
5617
+ const absPath = getAbsolutePath(path9, cwd);
5618
5618
  return absPath;
5619
5619
  });
5620
5620
  }
5621
- paths.forEach((path8) => {
5622
- this._removeIgnoredPath(path8);
5621
+ paths.forEach((path9) => {
5622
+ this._removeIgnoredPath(path9);
5623
5623
  });
5624
5624
  this._userIgnored = void 0;
5625
5625
  if (!this._readyCount)
5626
5626
  this._readyCount = 0;
5627
5627
  this._readyCount += paths.length;
5628
- Promise.all(paths.map(async (path8) => {
5629
- const res = await this._nodeFsHandler._addToNodeFs(path8, !_internal, void 0, 0, _origAdd);
5628
+ Promise.all(paths.map(async (path9) => {
5629
+ const res = await this._nodeFsHandler._addToNodeFs(path9, !_internal, void 0, 0, _origAdd);
5630
5630
  if (res)
5631
5631
  this._emitReady();
5632
5632
  return res;
@@ -5648,17 +5648,17 @@ var FSWatcher = class extends import_events.EventEmitter {
5648
5648
  return this;
5649
5649
  const paths = unifyPaths(paths_);
5650
5650
  const { cwd } = this.options;
5651
- paths.forEach((path8) => {
5652
- if (!sysPath2.isAbsolute(path8) && !this._closers.has(path8)) {
5651
+ paths.forEach((path9) => {
5652
+ if (!sysPath2.isAbsolute(path9) && !this._closers.has(path9)) {
5653
5653
  if (cwd)
5654
- path8 = sysPath2.join(cwd, path8);
5655
- path8 = sysPath2.resolve(path8);
5654
+ path9 = sysPath2.join(cwd, path9);
5655
+ path9 = sysPath2.resolve(path9);
5656
5656
  }
5657
- this._closePath(path8);
5658
- this._addIgnoredPath(path8);
5659
- if (this._watched.has(path8)) {
5657
+ this._closePath(path9);
5658
+ this._addIgnoredPath(path9);
5659
+ if (this._watched.has(path9)) {
5660
5660
  this._addIgnoredPath({
5661
- path: path8,
5661
+ path: path9,
5662
5662
  recursive: true
5663
5663
  });
5664
5664
  }
@@ -5722,38 +5722,38 @@ var FSWatcher = class extends import_events.EventEmitter {
5722
5722
  * @param stats arguments to be passed with event
5723
5723
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
5724
5724
  */
5725
- async _emit(event, path8, stats) {
5725
+ async _emit(event, path9, stats) {
5726
5726
  if (this.closed)
5727
5727
  return;
5728
5728
  const opts = this.options;
5729
5729
  if (isWindows)
5730
- path8 = sysPath2.normalize(path8);
5730
+ path9 = sysPath2.normalize(path9);
5731
5731
  if (opts.cwd)
5732
- path8 = sysPath2.relative(opts.cwd, path8);
5733
- const args = [path8];
5732
+ path9 = sysPath2.relative(opts.cwd, path9);
5733
+ const args = [path9];
5734
5734
  if (stats != null)
5735
5735
  args.push(stats);
5736
5736
  const awf = opts.awaitWriteFinish;
5737
5737
  let pw;
5738
- if (awf && (pw = this._pendingWrites.get(path8))) {
5738
+ if (awf && (pw = this._pendingWrites.get(path9))) {
5739
5739
  pw.lastChange = /* @__PURE__ */ new Date();
5740
5740
  return this;
5741
5741
  }
5742
5742
  if (opts.atomic) {
5743
5743
  if (event === EVENTS.UNLINK) {
5744
- this._pendingUnlinks.set(path8, [event, ...args]);
5744
+ this._pendingUnlinks.set(path9, [event, ...args]);
5745
5745
  setTimeout(() => {
5746
- this._pendingUnlinks.forEach((entry, path9) => {
5746
+ this._pendingUnlinks.forEach((entry, path10) => {
5747
5747
  this.emit(...entry);
5748
5748
  this.emit(EVENTS.ALL, ...entry);
5749
- this._pendingUnlinks.delete(path9);
5749
+ this._pendingUnlinks.delete(path10);
5750
5750
  });
5751
5751
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
5752
5752
  return this;
5753
5753
  }
5754
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path8)) {
5754
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
5755
5755
  event = EVENTS.CHANGE;
5756
- this._pendingUnlinks.delete(path8);
5756
+ this._pendingUnlinks.delete(path9);
5757
5757
  }
5758
5758
  }
5759
5759
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -5771,16 +5771,16 @@ var FSWatcher = class extends import_events.EventEmitter {
5771
5771
  this.emitWithAll(event, args);
5772
5772
  }
5773
5773
  };
5774
- this._awaitWriteFinish(path8, awf.stabilityThreshold, event, awfEmit);
5774
+ this._awaitWriteFinish(path9, awf.stabilityThreshold, event, awfEmit);
5775
5775
  return this;
5776
5776
  }
5777
5777
  if (event === EVENTS.CHANGE) {
5778
- const isThrottled = !this._throttle(EVENTS.CHANGE, path8, 50);
5778
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path9, 50);
5779
5779
  if (isThrottled)
5780
5780
  return this;
5781
5781
  }
5782
5782
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
5783
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path8) : path8;
5783
+ const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path9) : path9;
5784
5784
  let stats2;
5785
5785
  try {
5786
5786
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -5811,23 +5811,23 @@ var FSWatcher = class extends import_events.EventEmitter {
5811
5811
  * @param timeout duration of time to suppress duplicate actions
5812
5812
  * @returns tracking object or false if action should be suppressed
5813
5813
  */
5814
- _throttle(actionType, path8, timeout) {
5814
+ _throttle(actionType, path9, timeout) {
5815
5815
  if (!this._throttled.has(actionType)) {
5816
5816
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
5817
5817
  }
5818
5818
  const action = this._throttled.get(actionType);
5819
5819
  if (!action)
5820
5820
  throw new Error("invalid throttle");
5821
- const actionPath = action.get(path8);
5821
+ const actionPath = action.get(path9);
5822
5822
  if (actionPath) {
5823
5823
  actionPath.count++;
5824
5824
  return false;
5825
5825
  }
5826
5826
  let timeoutObject;
5827
5827
  const clear = () => {
5828
- const item = action.get(path8);
5828
+ const item = action.get(path9);
5829
5829
  const count = item ? item.count : 0;
5830
- action.delete(path8);
5830
+ action.delete(path9);
5831
5831
  clearTimeout(timeoutObject);
5832
5832
  if (item)
5833
5833
  clearTimeout(item.timeoutObject);
@@ -5835,7 +5835,7 @@ var FSWatcher = class extends import_events.EventEmitter {
5835
5835
  };
5836
5836
  timeoutObject = setTimeout(clear, timeout);
5837
5837
  const thr = { timeoutObject, clear, count: 0 };
5838
- action.set(path8, thr);
5838
+ action.set(path9, thr);
5839
5839
  return thr;
5840
5840
  }
5841
5841
  _incrReadyCount() {
@@ -5849,44 +5849,44 @@ var FSWatcher = class extends import_events.EventEmitter {
5849
5849
  * @param event
5850
5850
  * @param awfEmit Callback to be called when ready for event to be emitted.
5851
5851
  */
5852
- _awaitWriteFinish(path8, threshold, event, awfEmit) {
5852
+ _awaitWriteFinish(path9, threshold, event, awfEmit) {
5853
5853
  const awf = this.options.awaitWriteFinish;
5854
5854
  if (typeof awf !== "object")
5855
5855
  return;
5856
5856
  const pollInterval = awf.pollInterval;
5857
5857
  let timeoutHandler;
5858
- let fullPath = path8;
5859
- if (this.options.cwd && !sysPath2.isAbsolute(path8)) {
5860
- fullPath = sysPath2.join(this.options.cwd, path8);
5858
+ let fullPath = path9;
5859
+ if (this.options.cwd && !sysPath2.isAbsolute(path9)) {
5860
+ fullPath = sysPath2.join(this.options.cwd, path9);
5861
5861
  }
5862
5862
  const now = /* @__PURE__ */ new Date();
5863
5863
  const writes = this._pendingWrites;
5864
5864
  function awaitWriteFinishFn(prevStat) {
5865
5865
  (0, import_fs2.stat)(fullPath, (err, curStat) => {
5866
- if (err || !writes.has(path8)) {
5866
+ if (err || !writes.has(path9)) {
5867
5867
  if (err && err.code !== "ENOENT")
5868
5868
  awfEmit(err);
5869
5869
  return;
5870
5870
  }
5871
5871
  const now2 = Number(/* @__PURE__ */ new Date());
5872
5872
  if (prevStat && curStat.size !== prevStat.size) {
5873
- writes.get(path8).lastChange = now2;
5873
+ writes.get(path9).lastChange = now2;
5874
5874
  }
5875
- const pw = writes.get(path8);
5875
+ const pw = writes.get(path9);
5876
5876
  const df = now2 - pw.lastChange;
5877
5877
  if (df >= threshold) {
5878
- writes.delete(path8);
5878
+ writes.delete(path9);
5879
5879
  awfEmit(void 0, curStat);
5880
5880
  } else {
5881
5881
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
5882
5882
  }
5883
5883
  });
5884
5884
  }
5885
- if (!writes.has(path8)) {
5886
- writes.set(path8, {
5885
+ if (!writes.has(path9)) {
5886
+ writes.set(path9, {
5887
5887
  lastChange: now,
5888
5888
  cancelWait: () => {
5889
- writes.delete(path8);
5889
+ writes.delete(path9);
5890
5890
  clearTimeout(timeoutHandler);
5891
5891
  return event;
5892
5892
  }
@@ -5897,8 +5897,8 @@ var FSWatcher = class extends import_events.EventEmitter {
5897
5897
  /**
5898
5898
  * Determines whether user has asked to ignore this path.
5899
5899
  */
5900
- _isIgnored(path8, stats) {
5901
- if (this.options.atomic && DOT_RE.test(path8))
5900
+ _isIgnored(path9, stats) {
5901
+ if (this.options.atomic && DOT_RE.test(path9))
5902
5902
  return true;
5903
5903
  if (!this._userIgnored) {
5904
5904
  const { cwd } = this.options;
@@ -5908,17 +5908,17 @@ var FSWatcher = class extends import_events.EventEmitter {
5908
5908
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
5909
5909
  this._userIgnored = anymatch(list, void 0);
5910
5910
  }
5911
- return this._userIgnored(path8, stats);
5911
+ return this._userIgnored(path9, stats);
5912
5912
  }
5913
- _isntIgnored(path8, stat4) {
5914
- return !this._isIgnored(path8, stat4);
5913
+ _isntIgnored(path9, stat4) {
5914
+ return !this._isIgnored(path9, stat4);
5915
5915
  }
5916
5916
  /**
5917
5917
  * Provides a set of common helpers and properties relating to symlink handling.
5918
5918
  * @param path file or directory pattern being watched
5919
5919
  */
5920
- _getWatchHelpers(path8) {
5921
- return new WatchHelper(path8, this.options.followSymlinks, this);
5920
+ _getWatchHelpers(path9) {
5921
+ return new WatchHelper(path9, this.options.followSymlinks, this);
5922
5922
  }
5923
5923
  // Directory helpers
5924
5924
  // -----------------
@@ -5950,63 +5950,63 @@ var FSWatcher = class extends import_events.EventEmitter {
5950
5950
  * @param item base path of item/directory
5951
5951
  */
5952
5952
  _remove(directory, item, isDirectory) {
5953
- const path8 = sysPath2.join(directory, item);
5954
- const fullPath = sysPath2.resolve(path8);
5955
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path8) || this._watched.has(fullPath);
5956
- if (!this._throttle("remove", path8, 100))
5953
+ const path9 = sysPath2.join(directory, item);
5954
+ const fullPath = sysPath2.resolve(path9);
5955
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path9) || this._watched.has(fullPath);
5956
+ if (!this._throttle("remove", path9, 100))
5957
5957
  return;
5958
5958
  if (!isDirectory && this._watched.size === 1) {
5959
5959
  this.add(directory, item, true);
5960
5960
  }
5961
- const wp = this._getWatchedDir(path8);
5961
+ const wp = this._getWatchedDir(path9);
5962
5962
  const nestedDirectoryChildren = wp.getChildren();
5963
- nestedDirectoryChildren.forEach((nested) => this._remove(path8, nested));
5963
+ nestedDirectoryChildren.forEach((nested) => this._remove(path9, nested));
5964
5964
  const parent = this._getWatchedDir(directory);
5965
5965
  const wasTracked = parent.has(item);
5966
5966
  parent.remove(item);
5967
5967
  if (this._symlinkPaths.has(fullPath)) {
5968
5968
  this._symlinkPaths.delete(fullPath);
5969
5969
  }
5970
- let relPath = path8;
5970
+ let relPath = path9;
5971
5971
  if (this.options.cwd)
5972
- relPath = sysPath2.relative(this.options.cwd, path8);
5972
+ relPath = sysPath2.relative(this.options.cwd, path9);
5973
5973
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
5974
5974
  const event = this._pendingWrites.get(relPath).cancelWait();
5975
5975
  if (event === EVENTS.ADD)
5976
5976
  return;
5977
5977
  }
5978
- this._watched.delete(path8);
5978
+ this._watched.delete(path9);
5979
5979
  this._watched.delete(fullPath);
5980
5980
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
5981
- if (wasTracked && !this._isIgnored(path8))
5982
- this._emit(eventName, path8);
5983
- this._closePath(path8);
5981
+ if (wasTracked && !this._isIgnored(path9))
5982
+ this._emit(eventName, path9);
5983
+ this._closePath(path9);
5984
5984
  }
5985
5985
  /**
5986
5986
  * Closes all watchers for a path
5987
5987
  */
5988
- _closePath(path8) {
5989
- this._closeFile(path8);
5990
- const dir2 = sysPath2.dirname(path8);
5991
- this._getWatchedDir(dir2).remove(sysPath2.basename(path8));
5988
+ _closePath(path9) {
5989
+ this._closeFile(path9);
5990
+ const dir2 = sysPath2.dirname(path9);
5991
+ this._getWatchedDir(dir2).remove(sysPath2.basename(path9));
5992
5992
  }
5993
5993
  /**
5994
5994
  * Closes only file-specific watchers
5995
5995
  */
5996
- _closeFile(path8) {
5997
- const closers = this._closers.get(path8);
5996
+ _closeFile(path9) {
5997
+ const closers = this._closers.get(path9);
5998
5998
  if (!closers)
5999
5999
  return;
6000
6000
  closers.forEach((closer) => closer());
6001
- this._closers.delete(path8);
6001
+ this._closers.delete(path9);
6002
6002
  }
6003
- _addPathCloser(path8, closer) {
6003
+ _addPathCloser(path9, closer) {
6004
6004
  if (!closer)
6005
6005
  return;
6006
- let list = this._closers.get(path8);
6006
+ let list = this._closers.get(path9);
6007
6007
  if (!list) {
6008
6008
  list = [];
6009
- this._closers.set(path8, list);
6009
+ this._closers.set(path9, list);
6010
6010
  }
6011
6011
  list.push(closer);
6012
6012
  }
@@ -6035,9 +6035,36 @@ function watch(paths, options = {}) {
6035
6035
  }
6036
6036
  var esm_default = { watch, FSWatcher };
6037
6037
 
6038
+ // src/shared/types.ts
6039
+ function getTitleFromContent(content) {
6040
+ const match = content.match(/^#\s+(.+)$/m);
6041
+ if (match)
6042
+ return match[1].trim();
6043
+ const firstLine = content.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
6044
+ return firstLine || "Untitled";
6045
+ }
6046
+ function generateSlug(title) {
6047
+ return title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 50) || "feature";
6048
+ }
6049
+ function generateFeatureFilename(id, title) {
6050
+ const slug = generateSlug(title);
6051
+ return `${id}-${slug}`;
6052
+ }
6053
+ function extractNumericId(filenameOrId) {
6054
+ const match = filenameOrId.match(/^(\d+)(?:-|$)/);
6055
+ return match ? parseInt(match[1], 10) : null;
6056
+ }
6057
+ var DEFAULT_COLUMNS = [
6058
+ { id: "backlog", name: "Backlog", color: "#6b7280" },
6059
+ { id: "todo", name: "To Do", color: "#3b82f6" },
6060
+ { id: "in-progress", name: "In Progress", color: "#f59e0b" },
6061
+ { id: "review", name: "Review", color: "#8b5cf6" },
6062
+ { id: "done", name: "Done", color: "#22c55e" }
6063
+ ];
6064
+
6038
6065
  // src/sdk/KanbanSDK.ts
6039
- var fs3 = __toESM(require("fs/promises"));
6040
- var path4 = __toESM(require("path"));
6066
+ var fs4 = __toESM(require("fs/promises"));
6067
+ var path5 = __toESM(require("path"));
6041
6068
 
6042
6069
  // node_modules/.pnpm/fractional-indexing@3.2.0/node_modules/fractional-indexing/src/index.js
6043
6070
  var BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -6249,42 +6276,26 @@ function generateNKeysBetween(a, b, n, digits = BASE_62_DIGITS) {
6249
6276
  ];
6250
6277
  }
6251
6278
 
6252
- // src/shared/types.ts
6253
- function getTitleFromContent(content) {
6254
- const match = content.match(/^#\s+(.+)$/m);
6255
- if (match)
6256
- return match[1].trim();
6257
- const firstLine = content.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
6258
- return firstLine || "Untitled";
6259
- }
6260
- function generateSlug(title) {
6261
- return title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 50) || "feature";
6262
- }
6263
- function generateFeatureFilename(id, title) {
6264
- const slug = generateSlug(title);
6265
- return `${id}-${slug}`;
6266
- }
6267
- function extractNumericId(filenameOrId) {
6268
- const match = filenameOrId.match(/^(\d+)(?:-|$)/);
6269
- return match ? parseInt(match[1], 10) : null;
6270
- }
6271
-
6272
6279
  // src/shared/config.ts
6273
6280
  var fs = __toESM(require("fs"));
6274
6281
  var path = __toESM(require("path"));
6282
+ var DEFAULT_BOARD_CONFIG = {
6283
+ name: "Default",
6284
+ columns: [...DEFAULT_COLUMNS],
6285
+ nextCardId: 1,
6286
+ defaultStatus: "backlog",
6287
+ defaultPriority: "medium"
6288
+ };
6275
6289
  var DEFAULT_CONFIG = {
6290
+ version: 2,
6291
+ boards: {
6292
+ default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] }
6293
+ },
6294
+ defaultBoard: "default",
6276
6295
  featuresDirectory: ".kanban",
6296
+ aiAgent: "claude",
6277
6297
  defaultPriority: "medium",
6278
6298
  defaultStatus: "backlog",
6279
- columns: [
6280
- { id: "backlog", name: "Backlog", color: "#6b7280" },
6281
- { id: "todo", name: "To Do", color: "#3b82f6" },
6282
- { id: "in-progress", name: "In Progress", color: "#f59e0b" },
6283
- { id: "review", name: "Review", color: "#8b5cf6" },
6284
- { id: "done", name: "Done", color: "#22c55e" }
6285
- ],
6286
- aiAgent: "claude",
6287
- nextCardId: 1,
6288
6299
  showPriorityBadges: true,
6289
6300
  showAssignee: true,
6290
6301
  showDueDate: true,
@@ -6298,12 +6309,65 @@ var CONFIG_FILENAME = ".kanban.json";
6298
6309
  function configPath(workspaceRoot) {
6299
6310
  return path.join(workspaceRoot, CONFIG_FILENAME);
6300
6311
  }
6312
+ function migrateConfigV1ToV2(raw) {
6313
+ const v1Defaults = {
6314
+ featuresDirectory: ".kanban",
6315
+ defaultPriority: "medium",
6316
+ defaultStatus: "backlog",
6317
+ columns: [...DEFAULT_COLUMNS],
6318
+ aiAgent: "claude",
6319
+ nextCardId: 1,
6320
+ showPriorityBadges: true,
6321
+ showAssignee: true,
6322
+ showDueDate: true,
6323
+ showLabels: true,
6324
+ showBuildWithAI: true,
6325
+ showFileName: false,
6326
+ compactMode: false,
6327
+ markdownEditorMode: false
6328
+ };
6329
+ const v1 = { ...v1Defaults, ...raw };
6330
+ return {
6331
+ version: 2,
6332
+ boards: {
6333
+ default: {
6334
+ name: "Default",
6335
+ columns: v1.columns,
6336
+ nextCardId: v1.nextCardId,
6337
+ defaultStatus: v1.defaultStatus,
6338
+ defaultPriority: v1.defaultPriority
6339
+ }
6340
+ },
6341
+ defaultBoard: "default",
6342
+ featuresDirectory: v1.featuresDirectory,
6343
+ aiAgent: v1.aiAgent,
6344
+ defaultPriority: v1.defaultPriority,
6345
+ defaultStatus: v1.defaultStatus,
6346
+ showPriorityBadges: v1.showPriorityBadges,
6347
+ showAssignee: v1.showAssignee,
6348
+ showDueDate: v1.showDueDate,
6349
+ showLabels: v1.showLabels,
6350
+ showBuildWithAI: v1.showBuildWithAI,
6351
+ showFileName: v1.showFileName,
6352
+ compactMode: v1.compactMode,
6353
+ markdownEditorMode: v1.markdownEditorMode
6354
+ };
6355
+ }
6301
6356
  function readConfig(workspaceRoot) {
6302
6357
  const filePath = configPath(workspaceRoot);
6303
- const defaults = { ...DEFAULT_CONFIG, columns: [...DEFAULT_CONFIG.columns] };
6358
+ const defaults = { ...DEFAULT_CONFIG, boards: { default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] } } };
6304
6359
  try {
6305
6360
  const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
6306
- return { ...defaults, ...raw };
6361
+ if (!raw.version || raw.version === 1) {
6362
+ const v2 = migrateConfigV1ToV2(raw);
6363
+ writeConfig(workspaceRoot, v2);
6364
+ return v2;
6365
+ }
6366
+ const config = { ...defaults, ...raw };
6367
+ if (!config.boards || Object.keys(config.boards).length === 0) {
6368
+ config.boards = defaults.boards;
6369
+ }
6370
+ return config;
6307
6371
  } catch {
6308
6372
  return defaults;
6309
6373
  }
@@ -6312,19 +6376,39 @@ function writeConfig(workspaceRoot, config) {
6312
6376
  const filePath = configPath(workspaceRoot);
6313
6377
  fs.writeFileSync(filePath, JSON.stringify(config, null, 2) + "\n", "utf-8");
6314
6378
  }
6315
- function allocateCardId(workspaceRoot) {
6379
+ function getBoardConfig(workspaceRoot, boardId) {
6380
+ const config = readConfig(workspaceRoot);
6381
+ const resolvedId = boardId || config.defaultBoard;
6382
+ const board = config.boards[resolvedId];
6383
+ if (!board) {
6384
+ throw new Error(`Board '${resolvedId}' not found`);
6385
+ }
6386
+ return board;
6387
+ }
6388
+ function allocateCardId(workspaceRoot, boardId) {
6316
6389
  const config = readConfig(workspaceRoot);
6317
- const id = config.nextCardId;
6318
- writeConfig(workspaceRoot, { ...config, nextCardId: id + 1 });
6390
+ const resolvedId = boardId || config.defaultBoard;
6391
+ const board = config.boards[resolvedId];
6392
+ if (!board) {
6393
+ throw new Error(`Board '${resolvedId}' not found`);
6394
+ }
6395
+ const id = board.nextCardId;
6396
+ board.nextCardId = id + 1;
6397
+ writeConfig(workspaceRoot, config);
6319
6398
  return id;
6320
6399
  }
6321
- function syncCardIdCounter(workspaceRoot, existingIds) {
6400
+ function syncCardIdCounter(workspaceRoot, boardId, existingIds) {
6322
6401
  if (existingIds.length === 0)
6323
6402
  return;
6324
6403
  const maxId = Math.max(...existingIds);
6325
6404
  const config = readConfig(workspaceRoot);
6326
- if (config.nextCardId <= maxId) {
6327
- writeConfig(workspaceRoot, { ...config, nextCardId: maxId + 1 });
6405
+ const resolvedId = boardId || config.defaultBoard;
6406
+ const board = config.boards[resolvedId];
6407
+ if (!board)
6408
+ return;
6409
+ if (board.nextCardId <= maxId) {
6410
+ board.nextCardId = maxId + 1;
6411
+ writeConfig(workspaceRoot, config);
6328
6412
  }
6329
6413
  }
6330
6414
  function configToSettings(config) {
@@ -6542,30 +6626,203 @@ async function fileExists(filePath) {
6542
6626
  }
6543
6627
  }
6544
6628
 
6629
+ // src/sdk/migration.ts
6630
+ var fs3 = __toESM(require("fs/promises"));
6631
+ var path4 = __toESM(require("path"));
6632
+ async function migrateFileSystemToMultiBoard(featuresDir) {
6633
+ const boardsDir = path4.join(featuresDir, "boards");
6634
+ const defaultBoardDir = path4.join(boardsDir, "default");
6635
+ try {
6636
+ await fs3.access(boardsDir);
6637
+ return;
6638
+ } catch {
6639
+ }
6640
+ await fs3.mkdir(defaultBoardDir, { recursive: true });
6641
+ let entries;
6642
+ try {
6643
+ entries = await fs3.readdir(featuresDir, { withFileTypes: true });
6644
+ } catch {
6645
+ return;
6646
+ }
6647
+ for (const entry of entries) {
6648
+ if (!entry.isDirectory())
6649
+ continue;
6650
+ if (entry.name === "boards" || entry.name.startsWith("."))
6651
+ continue;
6652
+ const src = path4.join(featuresDir, entry.name);
6653
+ const dest = path4.join(defaultBoardDir, entry.name);
6654
+ await fs3.rename(src, dest);
6655
+ }
6656
+ const rootMdFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".md"));
6657
+ if (rootMdFiles.length > 0) {
6658
+ const backlogDir = path4.join(defaultBoardDir, "backlog");
6659
+ await fs3.mkdir(backlogDir, { recursive: true });
6660
+ for (const file of rootMdFiles) {
6661
+ await fs3.rename(
6662
+ path4.join(featuresDir, file.name),
6663
+ path4.join(backlogDir, file.name)
6664
+ );
6665
+ }
6666
+ }
6667
+ }
6668
+
6545
6669
  // src/sdk/KanbanSDK.ts
6546
6670
  var KanbanSDK = class {
6547
6671
  constructor(featuresDir) {
6548
6672
  this.featuresDir = featuresDir;
6673
+ this._migrated = false;
6549
6674
  }
6550
6675
  get workspaceRoot() {
6551
- return path4.dirname(this.featuresDir);
6676
+ return path5.dirname(this.featuresDir);
6677
+ }
6678
+ // --- Board resolution helpers ---
6679
+ _resolveBoardId(boardId) {
6680
+ const config = readConfig(this.workspaceRoot);
6681
+ return boardId || config.defaultBoard;
6682
+ }
6683
+ _boardDir(boardId) {
6684
+ const resolvedId = this._resolveBoardId(boardId);
6685
+ return path5.join(this.featuresDir, "boards", resolvedId);
6686
+ }
6687
+ _isCompletedStatus(status, boardId) {
6688
+ const config = readConfig(this.workspaceRoot);
6689
+ const resolvedId = boardId || config.defaultBoard;
6690
+ const board = config.boards[resolvedId];
6691
+ if (!board || board.columns.length === 0)
6692
+ return status === "done";
6693
+ return board.columns[board.columns.length - 1].id === status;
6694
+ }
6695
+ async _ensureMigrated() {
6696
+ if (this._migrated)
6697
+ return;
6698
+ await migrateFileSystemToMultiBoard(this.featuresDir);
6699
+ this._migrated = true;
6552
6700
  }
6553
6701
  async init() {
6554
- await ensureDirectories(this.featuresDir);
6702
+ await this._ensureMigrated();
6703
+ const boardDir = this._boardDir();
6704
+ await ensureDirectories(boardDir);
6705
+ }
6706
+ // --- Board management ---
6707
+ listBoards() {
6708
+ const config = readConfig(this.workspaceRoot);
6709
+ return Object.entries(config.boards).map(([id, board]) => ({
6710
+ id,
6711
+ name: board.name,
6712
+ description: board.description
6713
+ }));
6714
+ }
6715
+ createBoard(id, name, options) {
6716
+ const config = readConfig(this.workspaceRoot);
6717
+ if (config.boards[id]) {
6718
+ throw new Error(`Board already exists: ${id}`);
6719
+ }
6720
+ const columns = options?.columns || [...config.boards[config.defaultBoard]?.columns || [
6721
+ { id: "backlog", name: "Backlog", color: "#6b7280" },
6722
+ { id: "todo", name: "To Do", color: "#3b82f6" },
6723
+ { id: "in-progress", name: "In Progress", color: "#f59e0b" },
6724
+ { id: "review", name: "Review", color: "#8b5cf6" },
6725
+ { id: "done", name: "Done", color: "#22c55e" }
6726
+ ]];
6727
+ config.boards[id] = {
6728
+ name,
6729
+ description: options?.description,
6730
+ columns,
6731
+ nextCardId: 1,
6732
+ defaultStatus: options?.defaultStatus || columns[0]?.id || "backlog",
6733
+ defaultPriority: options?.defaultPriority || config.defaultPriority
6734
+ };
6735
+ writeConfig(this.workspaceRoot, config);
6736
+ return { id, name, description: options?.description };
6737
+ }
6738
+ async deleteBoard(boardId) {
6739
+ const config = readConfig(this.workspaceRoot);
6740
+ if (!config.boards[boardId]) {
6741
+ throw new Error(`Board not found: ${boardId}`);
6742
+ }
6743
+ if (config.defaultBoard === boardId) {
6744
+ throw new Error(`Cannot delete the default board: ${boardId}`);
6745
+ }
6746
+ const cards = await this.listCards(void 0, boardId);
6747
+ if (cards.length > 0) {
6748
+ throw new Error(`Cannot delete board "${boardId}": ${cards.length} card(s) still exist`);
6749
+ }
6750
+ const boardDir = this._boardDir(boardId);
6751
+ try {
6752
+ await fs4.rm(boardDir, { recursive: true });
6753
+ } catch {
6754
+ }
6755
+ delete config.boards[boardId];
6756
+ writeConfig(this.workspaceRoot, config);
6757
+ }
6758
+ getBoard(boardId) {
6759
+ return getBoardConfig(this.workspaceRoot, boardId);
6760
+ }
6761
+ updateBoard(boardId, updates) {
6762
+ const config = readConfig(this.workspaceRoot);
6763
+ const board = config.boards[boardId];
6764
+ if (!board) {
6765
+ throw new Error(`Board not found: ${boardId}`);
6766
+ }
6767
+ if (updates.name !== void 0)
6768
+ board.name = updates.name;
6769
+ if (updates.description !== void 0)
6770
+ board.description = updates.description;
6771
+ if (updates.columns !== void 0)
6772
+ board.columns = updates.columns;
6773
+ if (updates.defaultStatus !== void 0)
6774
+ board.defaultStatus = updates.defaultStatus;
6775
+ if (updates.defaultPriority !== void 0)
6776
+ board.defaultPriority = updates.defaultPriority;
6777
+ writeConfig(this.workspaceRoot, config);
6778
+ return board;
6779
+ }
6780
+ async transferCard(cardId, fromBoardId, toBoardId, targetStatus) {
6781
+ const toBoardDir = this._boardDir(toBoardId);
6782
+ const config = readConfig(this.workspaceRoot);
6783
+ if (!config.boards[fromBoardId])
6784
+ throw new Error(`Board not found: ${fromBoardId}`);
6785
+ if (!config.boards[toBoardId])
6786
+ throw new Error(`Board not found: ${toBoardId}`);
6787
+ const card = await this.getCard(cardId, fromBoardId);
6788
+ if (!card)
6789
+ throw new Error(`Card not found: ${cardId} in board ${fromBoardId}`);
6790
+ const toBoard = config.boards[toBoardId];
6791
+ const newStatus = targetStatus || toBoard.defaultStatus || toBoard.columns[0]?.id || "backlog";
6792
+ const targetDir = path5.join(toBoardDir, newStatus);
6793
+ await fs4.mkdir(targetDir, { recursive: true });
6794
+ const oldPath = card.filePath;
6795
+ const filename = path5.basename(oldPath);
6796
+ const newPath = path5.join(targetDir, filename);
6797
+ await fs4.rename(oldPath, newPath);
6798
+ card.status = newStatus;
6799
+ card.boardId = toBoardId;
6800
+ card.filePath = newPath;
6801
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
6802
+ card.completedAt = this._isCompletedStatus(newStatus, toBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
6803
+ const targetCards = await this.listCards(void 0, toBoardId);
6804
+ 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);
6805
+ const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
6806
+ card.order = generateKeyBetween(lastOrder, null);
6807
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6808
+ return card;
6555
6809
  }
6556
6810
  // --- Card CRUD ---
6557
- async listCards(columns) {
6558
- await ensureDirectories(this.featuresDir);
6811
+ async listCards(columns, boardId) {
6812
+ await this._ensureMigrated();
6813
+ const boardDir = this._boardDir(boardId);
6814
+ const resolvedBoardId = this._resolveBoardId(boardId);
6815
+ await ensureDirectories(boardDir);
6559
6816
  if (columns) {
6560
- await ensureStatusSubfolders(this.featuresDir, columns);
6817
+ await ensureStatusSubfolders(boardDir, columns);
6561
6818
  }
6562
6819
  try {
6563
- const rootFiles = await this._readMdFiles(this.featuresDir);
6820
+ const rootFiles = await this._readMdFiles(boardDir);
6564
6821
  for (const filePath of rootFiles) {
6565
6822
  try {
6566
6823
  const card = await this._loadCard(filePath);
6567
6824
  if (card) {
6568
- await moveFeatureFile(filePath, this.featuresDir, card.status, card.attachments);
6825
+ await moveFeatureFile(filePath, boardDir, card.status, card.attachments);
6569
6826
  }
6570
6827
  } catch {
6571
6828
  }
@@ -6573,26 +6830,33 @@ var KanbanSDK = class {
6573
6830
  } catch {
6574
6831
  }
6575
6832
  const cards = [];
6576
- const entries = await fs3.readdir(this.featuresDir, { withFileTypes: true });
6833
+ let entries;
6834
+ try {
6835
+ entries = await fs4.readdir(boardDir, { withFileTypes: true });
6836
+ } catch {
6837
+ return [];
6838
+ }
6577
6839
  for (const entry of entries) {
6578
6840
  if (!entry.isDirectory() || entry.name.startsWith("."))
6579
6841
  continue;
6580
- const subdir = path4.join(this.featuresDir, entry.name);
6842
+ const subdir = path5.join(boardDir, entry.name);
6581
6843
  try {
6582
6844
  const mdFiles = await this._readMdFiles(subdir);
6583
6845
  for (const filePath of mdFiles) {
6584
6846
  const card = await this._loadCard(filePath);
6585
- if (card)
6847
+ if (card) {
6848
+ card.boardId = resolvedBoardId;
6586
6849
  cards.push(card);
6850
+ }
6587
6851
  }
6588
6852
  } catch {
6589
6853
  }
6590
6854
  }
6591
6855
  for (const card of cards) {
6592
- const pathStatus = getStatusFromPath(card.filePath, this.featuresDir);
6856
+ const pathStatus = getStatusFromPath(card.filePath, boardDir);
6593
6857
  if (pathStatus !== null && pathStatus !== card.status) {
6594
6858
  try {
6595
- card.filePath = await moveFeatureFile(card.filePath, this.featuresDir, card.status, card.attachments);
6859
+ card.filePath = await moveFeatureFile(card.filePath, boardDir, card.status, card.attachments);
6596
6860
  } catch {
6597
6861
  }
6598
6862
  }
@@ -6610,65 +6874,72 @@ var KanbanSDK = class {
6610
6874
  const keys = generateNKeysBetween(null, null, columnCards.length);
6611
6875
  for (let i = 0; i < columnCards.length; i++) {
6612
6876
  columnCards[i].order = keys[i];
6613
- await fs3.writeFile(columnCards[i].filePath, serializeFeature(columnCards[i]), "utf-8");
6877
+ await fs4.writeFile(columnCards[i].filePath, serializeFeature(columnCards[i]), "utf-8");
6614
6878
  }
6615
6879
  }
6616
6880
  }
6617
6881
  const numericIds = cards.map((c) => parseInt(c.id, 10)).filter((n) => !Number.isNaN(n));
6618
6882
  if (numericIds.length > 0) {
6619
- syncCardIdCounter(path4.dirname(this.featuresDir), numericIds);
6883
+ syncCardIdCounter(this.workspaceRoot, resolvedBoardId, numericIds);
6620
6884
  }
6621
6885
  return cards.sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
6622
6886
  }
6623
- async getCard(cardId) {
6624
- const cards = await this.listCards();
6887
+ async getCard(cardId, boardId) {
6888
+ const cards = await this.listCards(void 0, boardId);
6625
6889
  return cards.find((c) => c.id === cardId) || null;
6626
6890
  }
6627
6891
  async createCard(data) {
6628
- await ensureDirectories(this.featuresDir);
6629
- const status = data.status || "backlog";
6630
- const priority = data.priority || "medium";
6892
+ await this._ensureMigrated();
6893
+ const resolvedBoardId = this._resolveBoardId(data.boardId);
6894
+ const boardDir = this._boardDir(resolvedBoardId);
6895
+ await ensureDirectories(boardDir);
6896
+ const config = readConfig(this.workspaceRoot);
6897
+ const board = config.boards[resolvedBoardId];
6898
+ const status = data.status || board?.defaultStatus || config.defaultStatus || "backlog";
6899
+ const priority = data.priority || board?.defaultPriority || config.defaultPriority || "medium";
6631
6900
  const title = getTitleFromContent(data.content);
6632
- const workspaceRoot = path4.dirname(this.featuresDir);
6633
- const numericId = allocateCardId(workspaceRoot);
6901
+ const numericId = allocateCardId(this.workspaceRoot, resolvedBoardId);
6634
6902
  const filename = generateFeatureFilename(numericId, title);
6635
6903
  const now = (/* @__PURE__ */ new Date()).toISOString();
6636
- const cards = await this.listCards();
6904
+ const cards = await this.listCards(void 0, resolvedBoardId);
6637
6905
  const cardsInStatus = cards.filter((c) => c.status === status).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
6638
6906
  const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
6639
6907
  const card = {
6640
6908
  id: String(numericId),
6909
+ boardId: resolvedBoardId,
6641
6910
  status,
6642
6911
  priority,
6643
6912
  assignee: data.assignee ?? null,
6644
6913
  dueDate: data.dueDate ?? null,
6645
6914
  created: now,
6646
6915
  modified: now,
6647
- completedAt: status === "done" ? now : null,
6916
+ completedAt: this._isCompletedStatus(status, resolvedBoardId) ? now : null,
6648
6917
  labels: data.labels || [],
6649
6918
  attachments: data.attachments || [],
6650
6919
  comments: [],
6651
6920
  order: generateKeyBetween(lastOrder, null),
6652
6921
  content: data.content,
6653
- filePath: getFeatureFilePath(this.featuresDir, status, filename)
6922
+ filePath: getFeatureFilePath(boardDir, status, filename)
6654
6923
  };
6655
- await fs3.mkdir(path4.dirname(card.filePath), { recursive: true });
6656
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
6924
+ await fs4.mkdir(path5.dirname(card.filePath), { recursive: true });
6925
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6657
6926
  return card;
6658
6927
  }
6659
- async updateCard(cardId, updates) {
6660
- const card = await this.getCard(cardId);
6928
+ async updateCard(cardId, updates, boardId) {
6929
+ const card = await this.getCard(cardId, boardId);
6661
6930
  if (!card)
6662
6931
  throw new Error(`Card not found: ${cardId}`);
6932
+ const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
6933
+ const boardDir = this._boardDir(resolvedBoardId);
6663
6934
  const oldStatus = card.status;
6664
6935
  const oldTitle = getTitleFromContent(card.content);
6665
- const { filePath: _fp, id: _id, ...safeUpdates } = updates;
6936
+ const { filePath: _fp, id: _id, boardId: _bid, ...safeUpdates } = updates;
6666
6937
  Object.assign(card, safeUpdates);
6667
6938
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
6668
6939
  if (oldStatus !== card.status) {
6669
- card.completedAt = card.status === "done" ? (/* @__PURE__ */ new Date()).toISOString() : null;
6940
+ card.completedAt = this._isCompletedStatus(card.status, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
6670
6941
  }
6671
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
6942
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6672
6943
  const newTitle = getTitleFromContent(card.content);
6673
6944
  const numericId = extractNumericId(card.id);
6674
6945
  if (numericId !== null && newTitle !== oldTitle) {
@@ -6676,46 +6947,48 @@ var KanbanSDK = class {
6676
6947
  card.filePath = await renameFeatureFile(card.filePath, newFilename);
6677
6948
  }
6678
6949
  if (oldStatus !== card.status) {
6679
- const newPath = await moveFeatureFile(card.filePath, this.featuresDir, card.status, card.attachments);
6950
+ const newPath = await moveFeatureFile(card.filePath, boardDir, card.status, card.attachments);
6680
6951
  card.filePath = newPath;
6681
6952
  }
6682
6953
  return card;
6683
6954
  }
6684
- async moveCard(cardId, newStatus, position) {
6685
- const cards = await this.listCards();
6955
+ async moveCard(cardId, newStatus, position, boardId) {
6956
+ const cards = await this.listCards(void 0, boardId);
6686
6957
  const card = cards.find((c) => c.id === cardId);
6687
6958
  if (!card)
6688
6959
  throw new Error(`Card not found: ${cardId}`);
6960
+ const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
6961
+ const boardDir = this._boardDir(resolvedBoardId);
6689
6962
  const oldStatus = card.status;
6690
6963
  card.status = newStatus;
6691
6964
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
6692
6965
  if (oldStatus !== newStatus) {
6693
- card.completedAt = newStatus === "done" ? (/* @__PURE__ */ new Date()).toISOString() : null;
6966
+ card.completedAt = this._isCompletedStatus(newStatus, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
6694
6967
  }
6695
6968
  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);
6696
6969
  const pos = position !== void 0 ? Math.max(0, Math.min(position, targetColumnCards.length)) : targetColumnCards.length;
6697
6970
  const before = pos > 0 ? targetColumnCards[pos - 1].order : null;
6698
6971
  const after = pos < targetColumnCards.length ? targetColumnCards[pos].order : null;
6699
6972
  card.order = generateKeyBetween(before, after);
6700
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
6973
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6701
6974
  if (oldStatus !== newStatus) {
6702
- const newPath = await moveFeatureFile(card.filePath, this.featuresDir, newStatus, card.attachments);
6975
+ const newPath = await moveFeatureFile(card.filePath, boardDir, newStatus, card.attachments);
6703
6976
  card.filePath = newPath;
6704
6977
  }
6705
6978
  return card;
6706
6979
  }
6707
- async deleteCard(cardId) {
6708
- const card = await this.getCard(cardId);
6980
+ async deleteCard(cardId, boardId) {
6981
+ const card = await this.getCard(cardId, boardId);
6709
6982
  if (!card)
6710
6983
  throw new Error(`Card not found: ${cardId}`);
6711
- await fs3.unlink(card.filePath);
6984
+ await fs4.unlink(card.filePath);
6712
6985
  }
6713
- async getCardsByStatus(status) {
6714
- const cards = await this.listCards();
6986
+ async getCardsByStatus(status, boardId) {
6987
+ const cards = await this.listCards(void 0, boardId);
6715
6988
  return cards.filter((c) => c.status === status);
6716
6989
  }
6717
- async getUniqueAssignees() {
6718
- const cards = await this.listCards();
6990
+ async getUniqueAssignees(boardId) {
6991
+ const cards = await this.listCards(void 0, boardId);
6719
6992
  const assignees = /* @__PURE__ */ new Set();
6720
6993
  for (const c of cards) {
6721
6994
  if (c.assignee)
@@ -6723,8 +6996,8 @@ var KanbanSDK = class {
6723
6996
  }
6724
6997
  return [...assignees].sort();
6725
6998
  }
6726
- async getUniqueLabels() {
6727
- const cards = await this.listCards();
6999
+ async getUniqueLabels(boardId) {
7000
+ const cards = await this.listCards(void 0, boardId);
6728
7001
  const labels = /* @__PURE__ */ new Set();
6729
7002
  for (const c of cards) {
6730
7003
  for (const l of c.labels)
@@ -6733,48 +7006,48 @@ var KanbanSDK = class {
6733
7006
  return [...labels].sort();
6734
7007
  }
6735
7008
  // --- Attachment management ---
6736
- async addAttachment(cardId, sourcePath) {
6737
- const card = await this.getCard(cardId);
7009
+ async addAttachment(cardId, sourcePath, boardId) {
7010
+ const card = await this.getCard(cardId, boardId);
6738
7011
  if (!card)
6739
7012
  throw new Error(`Card not found: ${cardId}`);
6740
- const fileName = path4.basename(sourcePath);
6741
- const cardDir = path4.dirname(card.filePath);
6742
- const destPath = path4.join(cardDir, fileName);
6743
- const sourceDir = path4.dirname(path4.resolve(sourcePath));
7013
+ const fileName = path5.basename(sourcePath);
7014
+ const cardDir = path5.dirname(card.filePath);
7015
+ const destPath = path5.join(cardDir, fileName);
7016
+ const sourceDir = path5.dirname(path5.resolve(sourcePath));
6744
7017
  if (sourceDir !== cardDir) {
6745
- await fs3.copyFile(path4.resolve(sourcePath), destPath);
7018
+ await fs4.copyFile(path5.resolve(sourcePath), destPath);
6746
7019
  }
6747
7020
  if (!card.attachments.includes(fileName)) {
6748
7021
  card.attachments.push(fileName);
6749
7022
  }
6750
7023
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
6751
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
7024
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6752
7025
  return card;
6753
7026
  }
6754
- async removeAttachment(cardId, attachment) {
6755
- const card = await this.getCard(cardId);
7027
+ async removeAttachment(cardId, attachment, boardId) {
7028
+ const card = await this.getCard(cardId, boardId);
6756
7029
  if (!card)
6757
7030
  throw new Error(`Card not found: ${cardId}`);
6758
7031
  card.attachments = card.attachments.filter((a) => a !== attachment);
6759
7032
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
6760
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
7033
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6761
7034
  return card;
6762
7035
  }
6763
- async listAttachments(cardId) {
6764
- const card = await this.getCard(cardId);
7036
+ async listAttachments(cardId, boardId) {
7037
+ const card = await this.getCard(cardId, boardId);
6765
7038
  if (!card)
6766
7039
  throw new Error(`Card not found: ${cardId}`);
6767
7040
  return card.attachments;
6768
7041
  }
6769
7042
  // --- Comment management ---
6770
- async listComments(cardId) {
6771
- const card = await this.getCard(cardId);
7043
+ async listComments(cardId, boardId) {
7044
+ const card = await this.getCard(cardId, boardId);
6772
7045
  if (!card)
6773
7046
  throw new Error(`Card not found: ${cardId}`);
6774
7047
  return card.comments || [];
6775
7048
  }
6776
- async addComment(cardId, author, content) {
6777
- const card = await this.getCard(cardId);
7049
+ async addComment(cardId, author, content, boardId) {
7050
+ const card = await this.getCard(cardId, boardId);
6778
7051
  if (!card)
6779
7052
  throw new Error(`Card not found: ${cardId}`);
6780
7053
  if (!card.comments)
@@ -6791,11 +7064,11 @@ var KanbanSDK = class {
6791
7064
  };
6792
7065
  card.comments.push(comment);
6793
7066
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
6794
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
7067
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6795
7068
  return card;
6796
7069
  }
6797
- async updateComment(cardId, commentId, content) {
6798
- const card = await this.getCard(cardId);
7070
+ async updateComment(cardId, commentId, content, boardId) {
7071
+ const card = await this.getCard(cardId, boardId);
6799
7072
  if (!card)
6800
7073
  throw new Error(`Card not found: ${cardId}`);
6801
7074
  const comment = (card.comments || []).find((c) => c.id === commentId);
@@ -6803,34 +7076,45 @@ var KanbanSDK = class {
6803
7076
  throw new Error(`Comment not found: ${commentId}`);
6804
7077
  comment.content = content;
6805
7078
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
6806
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
7079
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6807
7080
  return card;
6808
7081
  }
6809
- async deleteComment(cardId, commentId) {
6810
- const card = await this.getCard(cardId);
7082
+ async deleteComment(cardId, commentId, boardId) {
7083
+ const card = await this.getCard(cardId, boardId);
6811
7084
  if (!card)
6812
7085
  throw new Error(`Card not found: ${cardId}`);
6813
7086
  card.comments = (card.comments || []).filter((c) => c.id !== commentId);
6814
7087
  card.modified = (/* @__PURE__ */ new Date()).toISOString();
6815
- await fs3.writeFile(card.filePath, serializeFeature(card), "utf-8");
7088
+ await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
6816
7089
  return card;
6817
7090
  }
6818
- // --- Column management ---
6819
- listColumns() {
6820
- return readConfig(this.workspaceRoot).columns;
7091
+ // --- Column management (board-scoped) ---
7092
+ listColumns(boardId) {
7093
+ const config = readConfig(this.workspaceRoot);
7094
+ const resolvedId = boardId || config.defaultBoard;
7095
+ const board = config.boards[resolvedId];
7096
+ return board?.columns || [];
6821
7097
  }
6822
- addColumn(column) {
7098
+ addColumn(column, boardId) {
6823
7099
  const config = readConfig(this.workspaceRoot);
6824
- if (config.columns.some((c) => c.id === column.id)) {
7100
+ const resolvedId = boardId || config.defaultBoard;
7101
+ const board = config.boards[resolvedId];
7102
+ if (!board)
7103
+ throw new Error(`Board not found: ${resolvedId}`);
7104
+ if (board.columns.some((c) => c.id === column.id)) {
6825
7105
  throw new Error(`Column already exists: ${column.id}`);
6826
7106
  }
6827
- config.columns.push(column);
7107
+ board.columns.push(column);
6828
7108
  writeConfig(this.workspaceRoot, config);
6829
- return config.columns;
7109
+ return board.columns;
6830
7110
  }
6831
- updateColumn(columnId, updates) {
7111
+ updateColumn(columnId, updates, boardId) {
6832
7112
  const config = readConfig(this.workspaceRoot);
6833
- const col = config.columns.find((c) => c.id === columnId);
7113
+ const resolvedId = boardId || config.defaultBoard;
7114
+ const board = config.boards[resolvedId];
7115
+ if (!board)
7116
+ throw new Error(`Board not found: ${resolvedId}`);
7117
+ const col = board.columns.find((c) => c.id === columnId);
6834
7118
  if (!col)
6835
7119
  throw new Error(`Column not found: ${columnId}`);
6836
7120
  if (updates.name !== void 0)
@@ -6838,37 +7122,45 @@ var KanbanSDK = class {
6838
7122
  if (updates.color !== void 0)
6839
7123
  col.color = updates.color;
6840
7124
  writeConfig(this.workspaceRoot, config);
6841
- return config.columns;
7125
+ return board.columns;
6842
7126
  }
6843
- async removeColumn(columnId) {
7127
+ async removeColumn(columnId, boardId) {
6844
7128
  const config = readConfig(this.workspaceRoot);
6845
- const idx = config.columns.findIndex((c) => c.id === columnId);
7129
+ const resolvedId = boardId || config.defaultBoard;
7130
+ const board = config.boards[resolvedId];
7131
+ if (!board)
7132
+ throw new Error(`Board not found: ${resolvedId}`);
7133
+ const idx = board.columns.findIndex((c) => c.id === columnId);
6846
7134
  if (idx === -1)
6847
7135
  throw new Error(`Column not found: ${columnId}`);
6848
- const cards = await this.listCards();
7136
+ const cards = await this.listCards(void 0, resolvedId);
6849
7137
  const cardsInColumn = cards.filter((c) => c.status === columnId);
6850
7138
  if (cardsInColumn.length > 0) {
6851
7139
  throw new Error(`Cannot remove column "${columnId}": ${cardsInColumn.length} card(s) still in this column`);
6852
7140
  }
6853
- config.columns.splice(idx, 1);
7141
+ board.columns.splice(idx, 1);
6854
7142
  writeConfig(this.workspaceRoot, config);
6855
- return config.columns;
7143
+ return board.columns;
6856
7144
  }
6857
- reorderColumns(columnIds) {
7145
+ reorderColumns(columnIds, boardId) {
6858
7146
  const config = readConfig(this.workspaceRoot);
6859
- const colMap = new Map(config.columns.map((c) => [c.id, c]));
7147
+ const resolvedId = boardId || config.defaultBoard;
7148
+ const board = config.boards[resolvedId];
7149
+ if (!board)
7150
+ throw new Error(`Board not found: ${resolvedId}`);
7151
+ const colMap = new Map(board.columns.map((c) => [c.id, c]));
6860
7152
  for (const id of columnIds) {
6861
7153
  if (!colMap.has(id))
6862
7154
  throw new Error(`Column not found: ${id}`);
6863
7155
  }
6864
- if (columnIds.length !== config.columns.length) {
7156
+ if (columnIds.length !== board.columns.length) {
6865
7157
  throw new Error("Must include all column IDs when reordering");
6866
7158
  }
6867
- config.columns = columnIds.map((id) => colMap.get(id));
7159
+ board.columns = columnIds.map((id) => colMap.get(id));
6868
7160
  writeConfig(this.workspaceRoot, config);
6869
- return config.columns;
7161
+ return board.columns;
6870
7162
  }
6871
- // --- Settings management ---
7163
+ // --- Settings management (global) ---
6872
7164
  getSettings() {
6873
7165
  return configToSettings(readConfig(this.workspaceRoot));
6874
7166
  }
@@ -6878,28 +7170,28 @@ var KanbanSDK = class {
6878
7170
  }
6879
7171
  // --- Private helpers ---
6880
7172
  async _readMdFiles(dir2) {
6881
- const entries = await fs3.readdir(dir2, { withFileTypes: true });
6882
- return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => path4.join(dir2, e.name));
7173
+ const entries = await fs4.readdir(dir2, { withFileTypes: true });
7174
+ return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => path5.join(dir2, e.name));
6883
7175
  }
6884
7176
  async _loadCard(filePath) {
6885
- const content = await fs3.readFile(filePath, "utf-8");
7177
+ const content = await fs4.readFile(filePath, "utf-8");
6886
7178
  return parseFeatureFile(content, filePath);
6887
7179
  }
6888
7180
  };
6889
7181
 
6890
7182
  // src/standalone/webhooks.ts
6891
- var fs4 = __toESM(require("fs"));
6892
- var path5 = __toESM(require("path"));
7183
+ var fs5 = __toESM(require("fs"));
7184
+ var path6 = __toESM(require("path"));
6893
7185
  var http = __toESM(require("http"));
6894
7186
  var https = __toESM(require("https"));
6895
7187
  var crypto = __toESM(require("crypto"));
6896
7188
  var WEBHOOKS_FILENAME = ".kanban-webhooks.json";
6897
7189
  function webhooksPath(workspaceRoot) {
6898
- return path5.join(workspaceRoot, WEBHOOKS_FILENAME);
7190
+ return path6.join(workspaceRoot, WEBHOOKS_FILENAME);
6899
7191
  }
6900
7192
  function loadWebhooks(workspaceRoot) {
6901
7193
  try {
6902
- const raw = fs4.readFileSync(webhooksPath(workspaceRoot), "utf-8");
7194
+ const raw = fs5.readFileSync(webhooksPath(workspaceRoot), "utf-8");
6903
7195
  const data = JSON.parse(raw);
6904
7196
  return Array.isArray(data) ? data : [];
6905
7197
  } catch {
@@ -6907,7 +7199,7 @@ function loadWebhooks(workspaceRoot) {
6907
7199
  }
6908
7200
  }
6909
7201
  function saveWebhooks(workspaceRoot, webhooks) {
6910
- fs4.writeFileSync(webhooksPath(workspaceRoot), JSON.stringify(webhooks, null, 2) + "\n", "utf-8");
7202
+ fs5.writeFileSync(webhooksPath(workspaceRoot), JSON.stringify(webhooks, null, 2) + "\n", "utf-8");
6911
7203
  }
6912
7204
  function createWebhook(workspaceRoot, config) {
6913
7205
  const webhooks = loadWebhooks(workspaceRoot);
@@ -7002,13 +7294,14 @@ var MIME_TYPES = {
7002
7294
  ".map": "application/json"
7003
7295
  };
7004
7296
  function startServer(featuresDir, port2, webviewDir) {
7005
- const absoluteFeaturesDir = path6.resolve(featuresDir);
7297
+ const absoluteFeaturesDir = path7.resolve(featuresDir);
7006
7298
  let features = [];
7007
7299
  let migrating = false;
7008
7300
  let currentEditingFeatureId = null;
7009
7301
  let lastWrittenContent = "";
7010
- const resolvedWebviewDir = webviewDir || path6.join(__dirname, "standalone-webview");
7011
- const workspaceRoot = path6.dirname(absoluteFeaturesDir);
7302
+ let currentBoardId;
7303
+ const resolvedWebviewDir = webviewDir || path7.join(__dirname, "standalone-webview");
7304
+ const workspaceRoot = path7.dirname(absoluteFeaturesDir);
7012
7305
  const sdk = new KanbanSDK(absoluteFeaturesDir);
7013
7306
  function sanitizeFeature(feature) {
7014
7307
  const { filePath: _, ...rest } = feature;
@@ -7074,22 +7367,20 @@ function startServer(featuresDir, port2, webviewDir) {
7074
7367
  </body>
7075
7368
  </html>`;
7076
7369
  async function loadFeatures() {
7077
- migrating = true;
7078
- try {
7079
- features = await sdk.listCards(sdk.listColumns().map((c) => c.id));
7080
- } finally {
7081
- migrating = false;
7082
- }
7370
+ features = await sdk.listCards(sdk.listColumns(currentBoardId).map((c) => c.id), currentBoardId);
7083
7371
  }
7084
7372
  function buildInitMessage() {
7373
+ const config = readConfig(workspaceRoot);
7085
7374
  const settings = sdk.getSettings();
7086
7375
  settings.showBuildWithAI = false;
7087
7376
  settings.markdownEditorMode = false;
7088
7377
  return {
7089
7378
  type: "init",
7090
7379
  features,
7091
- columns: sdk.listColumns(),
7092
- settings
7380
+ columns: sdk.listColumns(currentBoardId),
7381
+ settings,
7382
+ boards: sdk.listBoards(),
7383
+ currentBoard: currentBoardId || config.defaultBoard
7093
7384
  };
7094
7385
  }
7095
7386
  function broadcast(message) {
@@ -7109,7 +7400,8 @@ function startServer(featuresDir, port2, webviewDir) {
7109
7400
  priority: data.priority,
7110
7401
  assignee: data.assignee,
7111
7402
  dueDate: data.dueDate,
7112
- labels: data.labels
7403
+ labels: data.labels,
7404
+ boardId: currentBoardId
7113
7405
  });
7114
7406
  await loadFeatures();
7115
7407
  broadcast(buildInitMessage());
@@ -7126,7 +7418,7 @@ function startServer(featuresDir, port2, webviewDir) {
7126
7418
  const oldStatus = feature.status;
7127
7419
  migrating = true;
7128
7420
  try {
7129
- const updated = await sdk.moveCard(featureId, newStatus, newOrder);
7421
+ const updated = await sdk.moveCard(featureId, newStatus, newOrder, currentBoardId);
7130
7422
  await loadFeatures();
7131
7423
  broadcast(buildInitMessage());
7132
7424
  fireWebhooks(workspaceRoot, "task.moved", {
@@ -7144,7 +7436,7 @@ function startServer(featuresDir, port2, webviewDir) {
7144
7436
  return null;
7145
7437
  migrating = true;
7146
7438
  try {
7147
- const updated = await sdk.updateCard(featureId, updates);
7439
+ const updated = await sdk.updateCard(featureId, updates, currentBoardId);
7148
7440
  lastWrittenContent = serializeFeature(updated);
7149
7441
  await loadFeatures();
7150
7442
  broadcast(buildInitMessage());
@@ -7160,7 +7452,7 @@ function startServer(featuresDir, port2, webviewDir) {
7160
7452
  return false;
7161
7453
  try {
7162
7454
  const deleted = sanitizeFeature(feature);
7163
- await sdk.deleteCard(featureId);
7455
+ await sdk.deleteCard(featureId, currentBoardId);
7164
7456
  await loadFeatures();
7165
7457
  broadcast(buildInitMessage());
7166
7458
  fireWebhooks(workspaceRoot, "task.deleted", deleted);
@@ -7171,7 +7463,7 @@ function startServer(featuresDir, port2, webviewDir) {
7171
7463
  }
7172
7464
  }
7173
7465
  function doAddColumn(name, color) {
7174
- const existingColumns = sdk.listColumns();
7466
+ const existingColumns = sdk.listColumns(currentBoardId);
7175
7467
  const id = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
7176
7468
  let uniqueId = id;
7177
7469
  let counter = 1;
@@ -7179,14 +7471,14 @@ function startServer(featuresDir, port2, webviewDir) {
7179
7471
  uniqueId = `${id}-${counter++}`;
7180
7472
  }
7181
7473
  const column = { id: uniqueId, name, color };
7182
- sdk.addColumn(column);
7474
+ sdk.addColumn(column, currentBoardId);
7183
7475
  broadcast(buildInitMessage());
7184
7476
  fireWebhooks(workspaceRoot, "column.created", column);
7185
7477
  return column;
7186
7478
  }
7187
7479
  function doEditColumn(columnId, updates) {
7188
7480
  try {
7189
- const columns = sdk.updateColumn(columnId, { name: updates.name, color: updates.color });
7481
+ const columns = sdk.updateColumn(columnId, { name: updates.name, color: updates.color }, currentBoardId);
7190
7482
  const updated = columns.find((c) => c.id === columnId) ?? null;
7191
7483
  broadcast(buildInitMessage());
7192
7484
  if (updated)
@@ -7198,13 +7490,13 @@ function startServer(featuresDir, port2, webviewDir) {
7198
7490
  }
7199
7491
  async function doRemoveColumn(columnId) {
7200
7492
  try {
7201
- const columns = sdk.listColumns();
7493
+ const columns = sdk.listColumns(currentBoardId);
7202
7494
  if (columns.length <= 1)
7203
7495
  return { removed: false, error: "Cannot remove last column" };
7204
7496
  const col = columns.find((c) => c.id === columnId);
7205
7497
  if (!col)
7206
7498
  return { removed: false, error: "Column not found" };
7207
- await sdk.removeColumn(columnId);
7499
+ await sdk.removeColumn(columnId, currentBoardId);
7208
7500
  broadcast(buildInitMessage());
7209
7501
  fireWebhooks(workspaceRoot, "column.deleted", col);
7210
7502
  return { removed: true };
@@ -7220,11 +7512,11 @@ function startServer(featuresDir, port2, webviewDir) {
7220
7512
  const feature = features.find((f) => f.id === featureId);
7221
7513
  if (!feature)
7222
7514
  return false;
7223
- const featureDir = path6.dirname(feature.filePath);
7224
- fs5.writeFileSync(path6.join(featureDir, filename), fileData);
7515
+ const featureDir = path7.dirname(feature.filePath);
7516
+ fs6.writeFileSync(path7.join(featureDir, filename), fileData);
7225
7517
  migrating = true;
7226
7518
  try {
7227
- const updated = await sdk.addAttachment(featureId, path6.join(featureDir, filename));
7519
+ const updated = await sdk.addAttachment(featureId, path7.join(featureDir, filename), currentBoardId);
7228
7520
  lastWrittenContent = serializeFeature(updated);
7229
7521
  await loadFeatures();
7230
7522
  } finally {
@@ -7238,7 +7530,7 @@ function startServer(featuresDir, port2, webviewDir) {
7238
7530
  return null;
7239
7531
  migrating = true;
7240
7532
  try {
7241
- const updated = await sdk.removeAttachment(featureId, attachment);
7533
+ const updated = await sdk.removeAttachment(featureId, attachment, currentBoardId);
7242
7534
  lastWrittenContent = serializeFeature(updated);
7243
7535
  await loadFeatures();
7244
7536
  broadcast(buildInitMessage());
@@ -7250,7 +7542,7 @@ function startServer(featuresDir, port2, webviewDir) {
7250
7542
  async function doAddComment(featureId, author, content) {
7251
7543
  migrating = true;
7252
7544
  try {
7253
- const updated = await sdk.addComment(featureId, author, content);
7545
+ const updated = await sdk.addComment(featureId, author, content, currentBoardId);
7254
7546
  lastWrittenContent = serializeFeature(updated);
7255
7547
  const comment = updated.comments[updated.comments.length - 1];
7256
7548
  await loadFeatures();
@@ -7266,7 +7558,7 @@ function startServer(featuresDir, port2, webviewDir) {
7266
7558
  async function doUpdateComment(featureId, commentId, content) {
7267
7559
  migrating = true;
7268
7560
  try {
7269
- const updated = await sdk.updateComment(featureId, commentId, content);
7561
+ const updated = await sdk.updateComment(featureId, commentId, content, currentBoardId);
7270
7562
  lastWrittenContent = serializeFeature(updated);
7271
7563
  const comment = (updated.comments || []).find((c) => c.id === commentId);
7272
7564
  await loadFeatures();
@@ -7289,7 +7581,7 @@ function startServer(featuresDir, port2, webviewDir) {
7289
7581
  return false;
7290
7582
  migrating = true;
7291
7583
  try {
7292
- const updated = await sdk.deleteComment(featureId, commentId);
7584
+ const updated = await sdk.deleteComment(featureId, commentId, currentBoardId);
7293
7585
  lastWrittenContent = serializeFeature(updated);
7294
7586
  await loadFeatures();
7295
7587
  broadcast(buildInitMessage());
@@ -7305,8 +7597,13 @@ function startServer(featuresDir, port2, webviewDir) {
7305
7597
  const msg = message;
7306
7598
  switch (msg.type) {
7307
7599
  case "ready":
7308
- await loadFeatures();
7309
- ws.send(JSON.stringify(buildInitMessage()));
7600
+ migrating = true;
7601
+ try {
7602
+ await loadFeatures();
7603
+ ws.send(JSON.stringify(buildInitMessage()));
7604
+ } finally {
7605
+ migrating = false;
7606
+ }
7310
7607
  break;
7311
7608
  case "createFeature":
7312
7609
  await doCreateFeature(msg.data);
@@ -7469,6 +7766,34 @@ function startServer(featuresDir, port2, webviewDir) {
7469
7766
  }
7470
7767
  break;
7471
7768
  }
7769
+ case "switchBoard":
7770
+ currentBoardId = msg.boardId;
7771
+ migrating = true;
7772
+ try {
7773
+ await loadFeatures();
7774
+ broadcast(buildInitMessage());
7775
+ } finally {
7776
+ migrating = false;
7777
+ }
7778
+ break;
7779
+ case "createBoard": {
7780
+ const boardName = msg.name;
7781
+ const boardId = generateSlug(boardName) || "board";
7782
+ try {
7783
+ sdk.createBoard(boardId, boardName);
7784
+ currentBoardId = boardId;
7785
+ migrating = true;
7786
+ try {
7787
+ await loadFeatures();
7788
+ broadcast(buildInitMessage());
7789
+ } finally {
7790
+ migrating = false;
7791
+ }
7792
+ } catch (err) {
7793
+ console.error("Failed to create board:", err);
7794
+ }
7795
+ break;
7796
+ }
7472
7797
  case "openFile":
7473
7798
  case "focusMenuBar":
7474
7799
  case "startWithAI":
@@ -7492,7 +7817,178 @@ function startServer(featuresDir, port2, webviewDir) {
7492
7817
  return;
7493
7818
  }
7494
7819
  const route = (expectedMethod, pattern) => matchRoute(expectedMethod, method, pathname, pattern);
7495
- let params = route("GET", "/api/tasks");
7820
+ let params = route("GET", "/api/boards");
7821
+ if (params) {
7822
+ return jsonOk(res, sdk.listBoards());
7823
+ }
7824
+ params = route("POST", "/api/boards");
7825
+ if (params) {
7826
+ try {
7827
+ const body = await readBody(req);
7828
+ const id = body.id;
7829
+ const name = body.name;
7830
+ if (!id)
7831
+ return jsonError(res, 400, "id is required");
7832
+ if (!name)
7833
+ return jsonError(res, 400, "name is required");
7834
+ const board = sdk.createBoard(id, name, {
7835
+ description: body.description,
7836
+ columns: body.columns
7837
+ });
7838
+ return jsonOk(res, board, 201);
7839
+ } catch (err) {
7840
+ return jsonError(res, 400, String(err));
7841
+ }
7842
+ }
7843
+ params = route("GET", "/api/boards/:boardId");
7844
+ if (params) {
7845
+ try {
7846
+ const { boardId } = params;
7847
+ const board = sdk.getBoard(boardId);
7848
+ return jsonOk(res, board);
7849
+ } catch (err) {
7850
+ return jsonError(res, 404, String(err));
7851
+ }
7852
+ }
7853
+ params = route("PUT", "/api/boards/:boardId");
7854
+ if (params) {
7855
+ try {
7856
+ const { boardId } = params;
7857
+ const body = await readBody(req);
7858
+ const board = sdk.updateBoard(boardId, body);
7859
+ return jsonOk(res, board);
7860
+ } catch (err) {
7861
+ return jsonError(res, 400, String(err));
7862
+ }
7863
+ }
7864
+ params = route("DELETE", "/api/boards/:boardId");
7865
+ if (params) {
7866
+ try {
7867
+ const { boardId } = params;
7868
+ await sdk.deleteBoard(boardId);
7869
+ return jsonOk(res, { deleted: true });
7870
+ } catch (err) {
7871
+ return jsonError(res, 400, String(err));
7872
+ }
7873
+ }
7874
+ params = route("POST", "/api/boards/:boardId/tasks/:id/transfer");
7875
+ if (params) {
7876
+ try {
7877
+ const { boardId, id } = params;
7878
+ const body = await readBody(req);
7879
+ const config = readConfig(workspaceRoot);
7880
+ const fromBoard = currentBoardId || config.defaultBoard;
7881
+ const targetStatus = body.targetStatus;
7882
+ const card = await sdk.transferCard(id, fromBoard, boardId, targetStatus);
7883
+ return jsonOk(res, sanitizeFeature(card));
7884
+ } catch (err) {
7885
+ return jsonError(res, 400, String(err));
7886
+ }
7887
+ }
7888
+ params = route("GET", "/api/boards/:boardId/tasks");
7889
+ if (params) {
7890
+ try {
7891
+ const { boardId } = params;
7892
+ const boardColumns = sdk.listColumns(boardId);
7893
+ const boardTasks = await sdk.listCards(boardColumns.map((c) => c.id), boardId);
7894
+ let result = boardTasks.map(sanitizeFeature);
7895
+ const status = url.searchParams.get("status");
7896
+ if (status)
7897
+ result = result.filter((f) => f.status === status);
7898
+ const priority = url.searchParams.get("priority");
7899
+ if (priority)
7900
+ result = result.filter((f) => f.priority === priority);
7901
+ const assignee = url.searchParams.get("assignee");
7902
+ if (assignee)
7903
+ result = result.filter((f) => f.assignee === assignee);
7904
+ const label = url.searchParams.get("label");
7905
+ if (label)
7906
+ result = result.filter((f) => f.labels.includes(label));
7907
+ return jsonOk(res, result);
7908
+ } catch (err) {
7909
+ return jsonError(res, 400, String(err));
7910
+ }
7911
+ }
7912
+ params = route("POST", "/api/boards/:boardId/tasks");
7913
+ if (params) {
7914
+ try {
7915
+ const { boardId } = params;
7916
+ const body = await readBody(req);
7917
+ const content = body.content || "";
7918
+ if (!content)
7919
+ return jsonError(res, 400, "content is required");
7920
+ const feature = await sdk.createCard({
7921
+ content,
7922
+ status: body.status || "backlog",
7923
+ priority: body.priority || "medium",
7924
+ assignee: body.assignee || null,
7925
+ dueDate: body.dueDate || null,
7926
+ labels: body.labels || [],
7927
+ boardId
7928
+ });
7929
+ return jsonOk(res, sanitizeFeature(feature), 201);
7930
+ } catch (err) {
7931
+ return jsonError(res, 400, String(err));
7932
+ }
7933
+ }
7934
+ params = route("GET", "/api/boards/:boardId/tasks/:id");
7935
+ if (params) {
7936
+ try {
7937
+ const { boardId, id } = params;
7938
+ const card = await sdk.getCard(id, boardId);
7939
+ if (!card)
7940
+ return jsonError(res, 404, "Task not found");
7941
+ return jsonOk(res, sanitizeFeature(card));
7942
+ } catch (err) {
7943
+ return jsonError(res, 400, String(err));
7944
+ }
7945
+ }
7946
+ params = route("PUT", "/api/boards/:boardId/tasks/:id");
7947
+ if (params) {
7948
+ try {
7949
+ const { boardId, id } = params;
7950
+ const body = await readBody(req);
7951
+ const feature = await sdk.updateCard(id, body, boardId);
7952
+ return jsonOk(res, sanitizeFeature(feature));
7953
+ } catch (err) {
7954
+ return jsonError(res, 400, String(err));
7955
+ }
7956
+ }
7957
+ params = route("PATCH", "/api/boards/:boardId/tasks/:id/move");
7958
+ if (params) {
7959
+ try {
7960
+ const { boardId, id } = params;
7961
+ const body = await readBody(req);
7962
+ const newStatus = body.status;
7963
+ const position = body.position ?? 0;
7964
+ if (!newStatus)
7965
+ return jsonError(res, 400, "status is required");
7966
+ const feature = await sdk.moveCard(id, newStatus, position, boardId);
7967
+ return jsonOk(res, sanitizeFeature(feature));
7968
+ } catch (err) {
7969
+ return jsonError(res, 400, String(err));
7970
+ }
7971
+ }
7972
+ params = route("DELETE", "/api/boards/:boardId/tasks/:id");
7973
+ if (params) {
7974
+ try {
7975
+ const { boardId, id } = params;
7976
+ await sdk.deleteCard(id, boardId);
7977
+ return jsonOk(res, { deleted: true });
7978
+ } catch (err) {
7979
+ return jsonError(res, 400, String(err));
7980
+ }
7981
+ }
7982
+ params = route("GET", "/api/boards/:boardId/columns");
7983
+ if (params) {
7984
+ try {
7985
+ const { boardId } = params;
7986
+ return jsonOk(res, sdk.listColumns(boardId));
7987
+ } catch (err) {
7988
+ return jsonError(res, 400, String(err));
7989
+ }
7990
+ }
7991
+ params = route("GET", "/api/tasks");
7496
7992
  if (params) {
7497
7993
  await loadFeatures();
7498
7994
  let result = features.map(sanitizeFeature);
@@ -7603,16 +8099,16 @@ function startServer(featuresDir, port2, webviewDir) {
7603
8099
  const feature = features.find((f) => f.id === id);
7604
8100
  if (!feature)
7605
8101
  return jsonError(res, 404, "Task not found");
7606
- const featureDir = path6.dirname(feature.filePath);
7607
- const attachmentPath = path6.resolve(featureDir, attachName);
8102
+ const featureDir = path7.dirname(feature.filePath);
8103
+ const attachmentPath = path7.resolve(featureDir, attachName);
7608
8104
  if (!attachmentPath.startsWith(absoluteFeaturesDir)) {
7609
8105
  res.writeHead(403, { "Content-Type": "text/plain" });
7610
8106
  res.end("Forbidden");
7611
8107
  return;
7612
8108
  }
7613
- const ext2 = path6.extname(attachName);
8109
+ const ext2 = path7.extname(attachName);
7614
8110
  const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
7615
- fs5.readFile(attachmentPath, (err, data) => {
8111
+ fs6.readFile(attachmentPath, (err, data) => {
7616
8112
  if (err) {
7617
8113
  res.writeHead(404);
7618
8114
  res.end("File not found");
@@ -7688,7 +8184,7 @@ function startServer(featuresDir, port2, webviewDir) {
7688
8184
  }
7689
8185
  params = route("GET", "/api/columns");
7690
8186
  if (params) {
7691
- return jsonOk(res, sdk.listColumns());
8187
+ return jsonOk(res, sdk.listColumns(currentBoardId));
7692
8188
  }
7693
8189
  params = route("POST", "/api/columns");
7694
8190
  if (params) {
@@ -7827,16 +8323,16 @@ function startServer(featuresDir, port2, webviewDir) {
7827
8323
  res.end("Feature not found");
7828
8324
  return;
7829
8325
  }
7830
- const featureDir = path6.dirname(feature.filePath);
7831
- const attachmentPath = path6.resolve(featureDir, filename);
8326
+ const featureDir = path7.dirname(feature.filePath);
8327
+ const attachmentPath = path7.resolve(featureDir, filename);
7832
8328
  if (!attachmentPath.startsWith(absoluteFeaturesDir)) {
7833
8329
  res.writeHead(403, { "Content-Type": "text/plain" });
7834
8330
  res.end("Forbidden");
7835
8331
  return;
7836
8332
  }
7837
- const ext2 = path6.extname(filename);
8333
+ const ext2 = path7.extname(filename);
7838
8334
  const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
7839
- fs5.readFile(attachmentPath, (err, data) => {
8335
+ fs6.readFile(attachmentPath, (err, data) => {
7840
8336
  if (err) {
7841
8337
  res.writeHead(404, { "Content-Type": "text/plain" });
7842
8338
  res.end("File not found");
@@ -7850,15 +8346,15 @@ function startServer(featuresDir, port2, webviewDir) {
7850
8346
  if (pathname.startsWith("/api/")) {
7851
8347
  return jsonError(res, 404, "Not found");
7852
8348
  }
7853
- const filePath = path6.join(resolvedWebviewDir, pathname === "/" ? "index.html" : pathname);
7854
- if (!path6.extname(filePath)) {
8349
+ const filePath = path7.join(resolvedWebviewDir, pathname === "/" ? "index.html" : pathname);
8350
+ if (!path7.extname(filePath)) {
7855
8351
  res.writeHead(200, { "Content-Type": "text/html" });
7856
8352
  res.end(indexHtml);
7857
8353
  return;
7858
8354
  }
7859
- const ext = path6.extname(filePath);
8355
+ const ext = path7.extname(filePath);
7860
8356
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
7861
- fs5.readFile(filePath, (err, data) => {
8357
+ fs6.readFile(filePath, (err, data) => {
7862
8358
  if (err) {
7863
8359
  res.writeHead(200, { "Content-Type": "text/html" });
7864
8360
  res.end(indexHtml);
@@ -7880,7 +8376,7 @@ function startServer(featuresDir, port2, webviewDir) {
7880
8376
  });
7881
8377
  });
7882
8378
  let debounceTimer;
7883
- fs5.mkdirSync(absoluteFeaturesDir, { recursive: true });
8379
+ fs6.mkdirSync(absoluteFeaturesDir, { recursive: true });
7884
8380
  const watcher = esm_default.watch(absoluteFeaturesDir, {
7885
8381
  ignoreInitial: true,
7886
8382
  awaitWriteFinish: { stabilityThreshold: 100 }
@@ -7893,8 +8389,15 @@ function startServer(featuresDir, port2, webviewDir) {
7893
8389
  if (debounceTimer)
7894
8390
  clearTimeout(debounceTimer);
7895
8391
  debounceTimer = setTimeout(async () => {
7896
- await loadFeatures();
7897
- broadcast(buildInitMessage());
8392
+ if (migrating)
8393
+ return;
8394
+ migrating = true;
8395
+ try {
8396
+ await loadFeatures();
8397
+ broadcast(buildInitMessage());
8398
+ } finally {
8399
+ migrating = false;
8400
+ }
7898
8401
  if (currentEditingFeatureId && changedPath) {
7899
8402
  const editingFeature = features.find((f) => f.id === currentEditingFeatureId);
7900
8403
  if (editingFeature && editingFeature.filePath === changedPath) {