kanban-lite 1.0.9 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1015 -407
- package/dist/extension.js +897 -347
- package/dist/mcp-server.js +550 -189
- package/dist/sdk/index.cjs +1223 -0
- package/dist/sdk/index.mjs +1168 -0
- package/dist/sdk/sdk/KanbanSDK.d.ts +39 -21
- package/dist/sdk/sdk/index.d.ts +4 -3
- package/dist/sdk/sdk/migration.d.ts +6 -0
- package/dist/sdk/sdk/types.d.ts +3 -5
- package/dist/sdk/shared/config.d.ts +23 -10
- package/dist/sdk/shared/types.d.ts +18 -4
- package/dist/standalone-webview/index.js +27 -27
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone-webview/style.css +1 -1
- package/dist/standalone.js +831 -328
- package/dist/webview/index.js +45 -45
- package/dist/webview/index.js.map +1 -1
- package/dist/webview/style.css +1 -1
- package/package.json +4 -3
- package/src/cli/index.ts +217 -95
- package/src/extension/KanbanPanel.ts +49 -22
- package/src/mcp-server/index.ts +138 -62
- package/src/sdk/KanbanSDK.ts +283 -77
- package/src/sdk/__tests__/KanbanSDK.test.ts +5 -5
- package/src/sdk/__tests__/migration.test.ts +269 -0
- package/src/sdk/__tests__/multi-board.test.ts +449 -0
- package/src/sdk/index.ts +4 -3
- package/src/sdk/migration.ts +52 -0
- package/src/sdk/types.ts +3 -6
- package/src/shared/config.ts +144 -22
- package/src/shared/types.ts +14 -5
- package/src/standalone/__tests__/server.integration.test.ts +38 -37
- package/src/standalone/server.ts +239 -21
- package/src/webview/App.tsx +17 -7
- package/src/webview/components/Toolbar.tsx +99 -3
- package/src/webview/store/index.ts +11 -3
- package/.kanban/backlog/1-test1.md +0 -30
- package/.kanban.json +0 -42
package/dist/standalone.js
CHANGED
|
@@ -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
|
|
4338
|
-
var
|
|
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 = (
|
|
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:
|
|
4449
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
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(
|
|
4489
|
+
async _exploreDir(path9, depth) {
|
|
4490
4490
|
let files;
|
|
4491
4491
|
try {
|
|
4492
|
-
files = await (0, import_promises.readdir)(
|
|
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:
|
|
4496
|
+
return { files, depth, path: path9 };
|
|
4497
4497
|
}
|
|
4498
|
-
async _formatEntry(dirent,
|
|
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)(
|
|
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(
|
|
4902
|
+
function createFsWatchInstance(path9, options, listener, errHandler, emitRaw) {
|
|
4903
4903
|
const handleEvent = (rawEvent, evPath) => {
|
|
4904
|
-
listener(
|
|
4905
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
4906
|
-
if (evPath &&
|
|
4907
|
-
fsWatchBroadcast(sysPath.resolve(
|
|
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)(
|
|
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 = (
|
|
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(
|
|
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
|
-
|
|
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)(
|
|
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 = (
|
|
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(
|
|
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(
|
|
5039
|
+
_watchWithNodeFs(path9, listener) {
|
|
5040
5040
|
const opts = this.fsw.options;
|
|
5041
|
-
const directory = sysPath.dirname(
|
|
5042
|
-
const basename6 = sysPath.basename(
|
|
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(
|
|
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(
|
|
5055
|
+
closer = setFsWatchFileListener(path9, absolutePath, options, {
|
|
5056
5056
|
listener,
|
|
5057
5057
|
rawEmitter: this.fsw._emitRaw
|
|
5058
5058
|
});
|
|
5059
5059
|
} else {
|
|
5060
|
-
closer = setFsWatchListener(
|
|
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 (
|
|
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(
|
|
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(
|
|
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,
|
|
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)(
|
|
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,
|
|
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,
|
|
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
|
|
5186
|
+
let path9 = sysPath.join(directory, item);
|
|
5187
5187
|
current.add(item);
|
|
5188
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
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
|
-
|
|
5198
|
-
this._addToNodeFs(
|
|
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(
|
|
5267
|
+
async _addToNodeFs(path9, initialAdd, priorWh, depth, target) {
|
|
5268
5268
|
const ready = this.fsw._emitReady;
|
|
5269
|
-
if (this.fsw._isIgnored(
|
|
5269
|
+
if (this.fsw._isIgnored(path9) || this.fsw.closed) {
|
|
5270
5270
|
ready();
|
|
5271
5271
|
return false;
|
|
5272
5272
|
}
|
|
5273
|
-
const wh = this.fsw._getWatchHelpers(
|
|
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(
|
|
5290
|
-
const targetPath = follow ? await (0, import_promises2.realpath)(
|
|
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)(
|
|
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,
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
5366
|
-
if (typeof
|
|
5365
|
+
function normalizePath(path9) {
|
|
5366
|
+
if (typeof path9 !== "string")
|
|
5367
5367
|
throw new Error("string expected");
|
|
5368
|
-
|
|
5369
|
-
|
|
5368
|
+
path9 = sysPath2.normalize(path9);
|
|
5369
|
+
path9 = path9.replace(/\\/g, "/");
|
|
5370
5370
|
let prepend = false;
|
|
5371
|
-
if (
|
|
5371
|
+
if (path9.startsWith("//"))
|
|
5372
5372
|
prepend = true;
|
|
5373
5373
|
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
5374
|
-
while (
|
|
5375
|
-
|
|
5374
|
+
while (path9.match(DOUBLE_SLASH_RE2))
|
|
5375
|
+
path9 = path9.replace(DOUBLE_SLASH_RE2, "/");
|
|
5376
5376
|
if (prepend)
|
|
5377
|
-
|
|
5378
|
-
return
|
|
5377
|
+
path9 = "/" + path9;
|
|
5378
|
+
return path9;
|
|
5379
5379
|
}
|
|
5380
5380
|
function matchPatterns(patterns, testString, stats) {
|
|
5381
|
-
const
|
|
5381
|
+
const path9 = normalizePath(testString);
|
|
5382
5382
|
for (let index = 0; index < patterns.length; index++) {
|
|
5383
5383
|
const pattern = patterns[index];
|
|
5384
|
-
if (pattern(
|
|
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 = (
|
|
5425
|
-
var normalizeIgnored = (cwd = "") => (
|
|
5426
|
-
if (typeof
|
|
5427
|
-
return normalizePathToUnix(sysPath2.isAbsolute(
|
|
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
|
|
5429
|
+
return path9;
|
|
5430
5430
|
}
|
|
5431
5431
|
};
|
|
5432
|
-
var getAbsolutePath = (
|
|
5433
|
-
if (sysPath2.isAbsolute(
|
|
5434
|
-
return
|
|
5432
|
+
var getAbsolutePath = (path9, cwd) => {
|
|
5433
|
+
if (sysPath2.isAbsolute(path9)) {
|
|
5434
|
+
return path9;
|
|
5435
5435
|
}
|
|
5436
|
-
return sysPath2.join(cwd,
|
|
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(
|
|
5491
|
+
constructor(path9, follow, fsw) {
|
|
5492
5492
|
this.fsw = fsw;
|
|
5493
|
-
const watchPath =
|
|
5494
|
-
this.path =
|
|
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((
|
|
5617
|
-
const absPath = getAbsolutePath(
|
|
5616
|
+
paths = paths.map((path9) => {
|
|
5617
|
+
const absPath = getAbsolutePath(path9, cwd);
|
|
5618
5618
|
return absPath;
|
|
5619
5619
|
});
|
|
5620
5620
|
}
|
|
5621
|
-
paths.forEach((
|
|
5622
|
-
this._removeIgnoredPath(
|
|
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 (
|
|
5629
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
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((
|
|
5652
|
-
if (!sysPath2.isAbsolute(
|
|
5651
|
+
paths.forEach((path9) => {
|
|
5652
|
+
if (!sysPath2.isAbsolute(path9) && !this._closers.has(path9)) {
|
|
5653
5653
|
if (cwd)
|
|
5654
|
-
|
|
5655
|
-
|
|
5654
|
+
path9 = sysPath2.join(cwd, path9);
|
|
5655
|
+
path9 = sysPath2.resolve(path9);
|
|
5656
5656
|
}
|
|
5657
|
-
this._closePath(
|
|
5658
|
-
this._addIgnoredPath(
|
|
5659
|
-
if (this._watched.has(
|
|
5657
|
+
this._closePath(path9);
|
|
5658
|
+
this._addIgnoredPath(path9);
|
|
5659
|
+
if (this._watched.has(path9)) {
|
|
5660
5660
|
this._addIgnoredPath({
|
|
5661
|
-
path:
|
|
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,
|
|
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
|
-
|
|
5730
|
+
path9 = sysPath2.normalize(path9);
|
|
5731
5731
|
if (opts.cwd)
|
|
5732
|
-
|
|
5733
|
-
const args = [
|
|
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(
|
|
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(
|
|
5744
|
+
this._pendingUnlinks.set(path9, [event, ...args]);
|
|
5745
5745
|
setTimeout(() => {
|
|
5746
|
-
this._pendingUnlinks.forEach((entry,
|
|
5746
|
+
this._pendingUnlinks.forEach((entry, path10) => {
|
|
5747
5747
|
this.emit(...entry);
|
|
5748
5748
|
this.emit(EVENTS.ALL, ...entry);
|
|
5749
|
-
this._pendingUnlinks.delete(
|
|
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(
|
|
5754
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path9)) {
|
|
5755
5755
|
event = EVENTS.CHANGE;
|
|
5756
|
-
this._pendingUnlinks.delete(
|
|
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(
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
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(
|
|
5828
|
+
const item = action.get(path9);
|
|
5829
5829
|
const count = item ? item.count : 0;
|
|
5830
|
-
action.delete(
|
|
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(
|
|
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(
|
|
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 =
|
|
5859
|
-
if (this.options.cwd && !sysPath2.isAbsolute(
|
|
5860
|
-
fullPath = sysPath2.join(this.options.cwd,
|
|
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(
|
|
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(
|
|
5873
|
+
writes.get(path9).lastChange = now2;
|
|
5874
5874
|
}
|
|
5875
|
-
const pw = writes.get(
|
|
5875
|
+
const pw = writes.get(path9);
|
|
5876
5876
|
const df = now2 - pw.lastChange;
|
|
5877
5877
|
if (df >= threshold) {
|
|
5878
|
-
writes.delete(
|
|
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(
|
|
5886
|
-
writes.set(
|
|
5885
|
+
if (!writes.has(path9)) {
|
|
5886
|
+
writes.set(path9, {
|
|
5887
5887
|
lastChange: now,
|
|
5888
5888
|
cancelWait: () => {
|
|
5889
|
-
writes.delete(
|
|
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(
|
|
5901
|
-
if (this.options.atomic && DOT_RE.test(
|
|
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(
|
|
5911
|
+
return this._userIgnored(path9, stats);
|
|
5912
5912
|
}
|
|
5913
|
-
_isntIgnored(
|
|
5914
|
-
return !this._isIgnored(
|
|
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(
|
|
5921
|
-
return new WatchHelper(
|
|
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
|
|
5954
|
-
const fullPath = sysPath2.resolve(
|
|
5955
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
5956
|
-
if (!this._throttle("remove",
|
|
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(
|
|
5961
|
+
const wp = this._getWatchedDir(path9);
|
|
5962
5962
|
const nestedDirectoryChildren = wp.getChildren();
|
|
5963
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
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 =
|
|
5970
|
+
let relPath = path9;
|
|
5971
5971
|
if (this.options.cwd)
|
|
5972
|
-
relPath = sysPath2.relative(this.options.cwd,
|
|
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(
|
|
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(
|
|
5982
|
-
this._emit(eventName,
|
|
5983
|
-
this._closePath(
|
|
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(
|
|
5989
|
-
this._closeFile(
|
|
5990
|
-
const dir2 = sysPath2.dirname(
|
|
5991
|
-
this._getWatchedDir(dir2).remove(sysPath2.basename(
|
|
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(
|
|
5997
|
-
const closers = this._closers.get(
|
|
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(
|
|
6001
|
+
this._closers.delete(path9);
|
|
6002
6002
|
}
|
|
6003
|
-
_addPathCloser(
|
|
6003
|
+
_addPathCloser(path9, closer) {
|
|
6004
6004
|
if (!closer)
|
|
6005
6005
|
return;
|
|
6006
|
-
let list = this._closers.get(
|
|
6006
|
+
let list = this._closers.get(path9);
|
|
6007
6007
|
if (!list) {
|
|
6008
6008
|
list = [];
|
|
6009
|
-
this._closers.set(
|
|
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
|
|
6040
|
-
var
|
|
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: [...
|
|
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
|
-
|
|
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
|
|
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
|
|
6318
|
-
|
|
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
|
-
|
|
6327
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
6817
|
+
await ensureStatusSubfolders(boardDir, columns);
|
|
6561
6818
|
}
|
|
6562
6819
|
try {
|
|
6563
|
-
const rootFiles = await this._readMdFiles(
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
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,
|
|
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
|
|
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(
|
|
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
|
|
6629
|
-
const
|
|
6630
|
-
const
|
|
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
|
|
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
|
|
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(
|
|
6922
|
+
filePath: getFeatureFilePath(boardDir, status, filename)
|
|
6654
6923
|
};
|
|
6655
|
-
await
|
|
6656
|
-
await
|
|
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
|
|
6940
|
+
card.completedAt = this._isCompletedStatus(card.status, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
|
|
6670
6941
|
}
|
|
6671
|
-
await
|
|
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,
|
|
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
|
|
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
|
|
6973
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
6701
6974
|
if (oldStatus !== newStatus) {
|
|
6702
|
-
const newPath = await moveFeatureFile(card.filePath,
|
|
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
|
|
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 =
|
|
6741
|
-
const cardDir =
|
|
6742
|
-
const destPath =
|
|
6743
|
-
const sourceDir =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
7088
|
+
await fs4.writeFile(card.filePath, serializeFeature(card), "utf-8");
|
|
6816
7089
|
return card;
|
|
6817
7090
|
}
|
|
6818
|
-
// --- Column management ---
|
|
6819
|
-
listColumns() {
|
|
6820
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7107
|
+
board.columns.push(column);
|
|
6828
7108
|
writeConfig(this.workspaceRoot, config);
|
|
6829
|
-
return
|
|
7109
|
+
return board.columns;
|
|
6830
7110
|
}
|
|
6831
|
-
updateColumn(columnId, updates) {
|
|
7111
|
+
updateColumn(columnId, updates, boardId) {
|
|
6832
7112
|
const config = readConfig(this.workspaceRoot);
|
|
6833
|
-
const
|
|
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
|
|
7125
|
+
return board.columns;
|
|
6842
7126
|
}
|
|
6843
|
-
async removeColumn(columnId) {
|
|
7127
|
+
async removeColumn(columnId, boardId) {
|
|
6844
7128
|
const config = readConfig(this.workspaceRoot);
|
|
6845
|
-
const
|
|
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
|
-
|
|
7141
|
+
board.columns.splice(idx, 1);
|
|
6854
7142
|
writeConfig(this.workspaceRoot, config);
|
|
6855
|
-
return
|
|
7143
|
+
return board.columns;
|
|
6856
7144
|
}
|
|
6857
|
-
reorderColumns(columnIds) {
|
|
7145
|
+
reorderColumns(columnIds, boardId) {
|
|
6858
7146
|
const config = readConfig(this.workspaceRoot);
|
|
6859
|
-
const
|
|
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 !==
|
|
7156
|
+
if (columnIds.length !== board.columns.length) {
|
|
6865
7157
|
throw new Error("Must include all column IDs when reordering");
|
|
6866
7158
|
}
|
|
6867
|
-
|
|
7159
|
+
board.columns = columnIds.map((id) => colMap.get(id));
|
|
6868
7160
|
writeConfig(this.workspaceRoot, config);
|
|
6869
|
-
return
|
|
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
|
|
6882
|
-
return entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) =>
|
|
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
|
|
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
|
|
6892
|
-
var
|
|
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
|
|
7190
|
+
return path6.join(workspaceRoot, WEBHOOKS_FILENAME);
|
|
6899
7191
|
}
|
|
6900
7192
|
function loadWebhooks(workspaceRoot) {
|
|
6901
7193
|
try {
|
|
6902
|
-
const raw =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
7011
|
-
const
|
|
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
|
-
|
|
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 =
|
|
7224
|
-
|
|
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,
|
|
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
|
-
|
|
7309
|
-
|
|
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/
|
|
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 =
|
|
7607
|
-
const attachmentPath =
|
|
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 =
|
|
8109
|
+
const ext2 = path7.extname(attachName);
|
|
7614
8110
|
const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
|
|
7615
|
-
|
|
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 =
|
|
7831
|
-
const attachmentPath =
|
|
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 =
|
|
8333
|
+
const ext2 = path7.extname(filename);
|
|
7838
8334
|
const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
|
|
7839
|
-
|
|
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 =
|
|
7854
|
-
if (!
|
|
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 =
|
|
8355
|
+
const ext = path7.extname(filePath);
|
|
7860
8356
|
const contentType = MIME_TYPES[ext] || "application/octet-stream";
|
|
7861
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7897
|
-
|
|
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) {
|