kanban-lite 1.0.39 → 1.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/cli.js +1779 -1473
  2. package/dist/extension.js +1483 -1257
  3. package/dist/mcp-server.js +1207 -953
  4. package/dist/sdk/index.cjs +1156 -774
  5. package/dist/sdk/index.mjs +1156 -774
  6. package/dist/sdk/sdk/KanbanSDK.d.ts +58 -20
  7. package/dist/sdk/sdk/modules/attachments.d.ts +18 -0
  8. package/dist/sdk/sdk/modules/boards.d.ts +49 -0
  9. package/dist/sdk/sdk/modules/cards.d.ts +47 -0
  10. package/dist/sdk/sdk/modules/columns.d.ts +30 -0
  11. package/dist/sdk/sdk/modules/comments.d.ts +18 -0
  12. package/dist/sdk/sdk/modules/context.d.ts +35 -0
  13. package/dist/sdk/sdk/modules/labels.d.ts +26 -0
  14. package/dist/sdk/sdk/modules/logs.d.ts +42 -0
  15. package/dist/sdk/sdk/modules/migration.d.ts +9 -0
  16. package/dist/sdk/sdk/modules/settings.d.ts +14 -0
  17. package/dist/sdk/sdk/webhooks.d.ts +61 -0
  18. package/package.json +1 -1
  19. package/src/cli/index.ts +45 -60
  20. package/src/extension/KanbanPanel.ts +10 -27
  21. package/src/mcp-server/index.ts +17 -34
  22. package/src/sdk/KanbanSDK.ts +132 -828
  23. package/src/sdk/modules/attachments.ts +60 -0
  24. package/src/sdk/modules/boards.ts +258 -0
  25. package/src/sdk/modules/cards.ts +323 -0
  26. package/src/sdk/modules/columns.ts +135 -0
  27. package/src/sdk/modules/comments.ts +96 -0
  28. package/src/sdk/modules/context.ts +40 -0
  29. package/src/sdk/modules/labels.ts +82 -0
  30. package/src/sdk/modules/logs.ts +195 -0
  31. package/src/sdk/modules/migration.ts +81 -0
  32. package/src/sdk/modules/settings.ts +31 -0
  33. package/src/sdk/webhooks.ts +192 -0
  34. package/src/standalone/__tests__/server.integration.test.ts +30 -28
  35. package/src/standalone/server.ts +43 -42
  36. package/src/standalone/webhooks.ts +2 -223
package/dist/cli.js CHANGED
@@ -33,221 +33,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
33
  mod
34
34
  ));
35
35
 
36
- // node_modules/.pnpm/fractional-indexing@3.2.0/node_modules/fractional-indexing/src/index.js
37
- function midpoint(a, b, digits) {
38
- const zero = digits[0];
39
- if (b != null && a >= b) {
40
- throw new Error(a + " >= " + b);
41
- }
42
- if (a.slice(-1) === zero || b && b.slice(-1) === zero) {
43
- throw new Error("trailing zero");
44
- }
45
- if (b) {
46
- let n = 0;
47
- while ((a[n] || zero) === b[n]) {
48
- n++;
49
- }
50
- if (n > 0) {
51
- return b.slice(0, n) + midpoint(a.slice(n), b.slice(n), digits);
52
- }
53
- }
54
- const digitA = a ? digits.indexOf(a[0]) : 0;
55
- const digitB = b != null ? digits.indexOf(b[0]) : digits.length;
56
- if (digitB - digitA > 1) {
57
- const midDigit = Math.round(0.5 * (digitA + digitB));
58
- return digits[midDigit];
59
- } else {
60
- if (b && b.length > 1) {
61
- return b.slice(0, 1);
62
- } else {
63
- return digits[digitA] + midpoint(a.slice(1), null, digits);
64
- }
65
- }
66
- }
67
- function validateInteger(int4) {
68
- if (int4.length !== getIntegerLength(int4[0])) {
69
- throw new Error("invalid integer part of order key: " + int4);
70
- }
71
- }
72
- function getIntegerLength(head) {
73
- if (head >= "a" && head <= "z") {
74
- return head.charCodeAt(0) - "a".charCodeAt(0) + 2;
75
- } else if (head >= "A" && head <= "Z") {
76
- return "Z".charCodeAt(0) - head.charCodeAt(0) + 2;
77
- } else {
78
- throw new Error("invalid order key head: " + head);
79
- }
80
- }
81
- function getIntegerPart(key) {
82
- const integerPartLength = getIntegerLength(key[0]);
83
- if (integerPartLength > key.length) {
84
- throw new Error("invalid order key: " + key);
85
- }
86
- return key.slice(0, integerPartLength);
87
- }
88
- function validateOrderKey(key, digits) {
89
- if (key === "A" + digits[0].repeat(26)) {
90
- throw new Error("invalid order key: " + key);
91
- }
92
- const i = getIntegerPart(key);
93
- const f = key.slice(i.length);
94
- if (f.slice(-1) === digits[0]) {
95
- throw new Error("invalid order key: " + key);
96
- }
97
- }
98
- function incrementInteger(x, digits) {
99
- validateInteger(x);
100
- const [head, ...digs] = x.split("");
101
- let carry = true;
102
- for (let i = digs.length - 1; carry && i >= 0; i--) {
103
- const d = digits.indexOf(digs[i]) + 1;
104
- if (d === digits.length) {
105
- digs[i] = digits[0];
106
- } else {
107
- digs[i] = digits[d];
108
- carry = false;
109
- }
110
- }
111
- if (carry) {
112
- if (head === "Z") {
113
- return "a" + digits[0];
114
- }
115
- if (head === "z") {
116
- return null;
117
- }
118
- const h = String.fromCharCode(head.charCodeAt(0) + 1);
119
- if (h > "a") {
120
- digs.push(digits[0]);
121
- } else {
122
- digs.pop();
123
- }
124
- return h + digs.join("");
125
- } else {
126
- return head + digs.join("");
127
- }
128
- }
129
- function decrementInteger(x, digits) {
130
- validateInteger(x);
131
- const [head, ...digs] = x.split("");
132
- let borrow = true;
133
- for (let i = digs.length - 1; borrow && i >= 0; i--) {
134
- const d = digits.indexOf(digs[i]) - 1;
135
- if (d === -1) {
136
- digs[i] = digits.slice(-1);
137
- } else {
138
- digs[i] = digits[d];
139
- borrow = false;
140
- }
141
- }
142
- if (borrow) {
143
- if (head === "a") {
144
- return "Z" + digits.slice(-1);
145
- }
146
- if (head === "A") {
147
- return null;
148
- }
149
- const h = String.fromCharCode(head.charCodeAt(0) - 1);
150
- if (h < "Z") {
151
- digs.push(digits.slice(-1));
152
- } else {
153
- digs.pop();
154
- }
155
- return h + digs.join("");
156
- } else {
157
- return head + digs.join("");
158
- }
159
- }
160
- function generateKeyBetween(a, b, digits = BASE_62_DIGITS) {
161
- if (a != null) {
162
- validateOrderKey(a, digits);
163
- }
164
- if (b != null) {
165
- validateOrderKey(b, digits);
166
- }
167
- if (a != null && b != null && a >= b) {
168
- throw new Error(a + " >= " + b);
169
- }
170
- if (a == null) {
171
- if (b == null) {
172
- return "a" + digits[0];
173
- }
174
- const ib2 = getIntegerPart(b);
175
- const fb2 = b.slice(ib2.length);
176
- if (ib2 === "A" + digits[0].repeat(26)) {
177
- return ib2 + midpoint("", fb2, digits);
178
- }
179
- if (ib2 < b) {
180
- return ib2;
181
- }
182
- const res = decrementInteger(ib2, digits);
183
- if (res == null) {
184
- throw new Error("cannot decrement any more");
185
- }
186
- return res;
187
- }
188
- if (b == null) {
189
- const ia2 = getIntegerPart(a);
190
- const fa2 = a.slice(ia2.length);
191
- const i2 = incrementInteger(ia2, digits);
192
- return i2 == null ? ia2 + midpoint(fa2, null, digits) : i2;
193
- }
194
- const ia = getIntegerPart(a);
195
- const fa = a.slice(ia.length);
196
- const ib = getIntegerPart(b);
197
- const fb = b.slice(ib.length);
198
- if (ia === ib) {
199
- return ia + midpoint(fa, fb, digits);
200
- }
201
- const i = incrementInteger(ia, digits);
202
- if (i == null) {
203
- throw new Error("cannot increment any more");
204
- }
205
- if (i < b) {
206
- return i;
207
- }
208
- return ia + midpoint(fa, null, digits);
209
- }
210
- function generateNKeysBetween(a, b, n, digits = BASE_62_DIGITS) {
211
- if (n === 0) {
212
- return [];
213
- }
214
- if (n === 1) {
215
- return [generateKeyBetween(a, b, digits)];
216
- }
217
- if (b == null) {
218
- let c2 = generateKeyBetween(a, b, digits);
219
- const result = [c2];
220
- for (let i = 0; i < n - 1; i++) {
221
- c2 = generateKeyBetween(c2, b, digits);
222
- result.push(c2);
223
- }
224
- return result;
225
- }
226
- if (a == null) {
227
- let c2 = generateKeyBetween(a, b, digits);
228
- const result = [c2];
229
- for (let i = 0; i < n - 1; i++) {
230
- c2 = generateKeyBetween(a, c2, digits);
231
- result.push(c2);
232
- }
233
- result.reverse();
234
- return result;
235
- }
236
- const mid = Math.floor(n / 2);
237
- const c = generateKeyBetween(a, b, digits);
238
- return [
239
- ...generateNKeysBetween(a, c, mid, digits),
240
- c,
241
- ...generateNKeysBetween(c, b, n - mid - 1, digits)
242
- ];
243
- }
244
- var BASE_62_DIGITS;
245
- var init_src = __esm({
246
- "node_modules/.pnpm/fractional-indexing@3.2.0/node_modules/fractional-indexing/src/index.js"() {
247
- BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
248
- }
249
- });
250
-
251
36
  // src/shared/types.ts
252
37
  function getTitleFromContent(content) {
253
38
  const match = content.match(/^#\s+(.+)$/m);
@@ -3224,11 +3009,11 @@ var init_js_yaml = __esm({
3224
3009
 
3225
3010
  // src/sdk/parser.ts
3226
3011
  function extractIdFromFilename(filePath) {
3227
- const basename9 = path3.basename(filePath, ".md");
3228
- const numericMatch = basename9.match(/^(\d+)-/);
3012
+ const basename10 = path3.basename(filePath, ".md");
3013
+ const numericMatch = basename10.match(/^(\d+)-/);
3229
3014
  if (numericMatch)
3230
3015
  return numericMatch[1];
3231
- return basename9;
3016
+ return basename10;
3232
3017
  }
3233
3018
  function parseCommentBlock(header, body) {
3234
3019
  const getValue = (key) => {
@@ -3611,21 +3396,21 @@ var require_file_uri_to_path = __commonJS({
3611
3396
  var rest = decodeURI(uri.substring(7));
3612
3397
  var firstSlash = rest.indexOf("/");
3613
3398
  var host = rest.substring(0, firstSlash);
3614
- var path13 = rest.substring(firstSlash + 1);
3399
+ var path17 = rest.substring(firstSlash + 1);
3615
3400
  if ("localhost" == host)
3616
3401
  host = "";
3617
3402
  if (host) {
3618
3403
  host = sep2 + sep2 + host;
3619
3404
  }
3620
- path13 = path13.replace(/^(.+)\|/, "$1:");
3405
+ path17 = path17.replace(/^(.+)\|/, "$1:");
3621
3406
  if (sep2 == "\\") {
3622
- path13 = path13.replace(/\//g, "\\");
3407
+ path17 = path17.replace(/\//g, "\\");
3623
3408
  }
3624
- if (/^.+\:/.test(path13)) {
3409
+ if (/^.+\:/.test(path17)) {
3625
3410
  } else {
3626
- path13 = sep2 + path13;
3411
+ path17 = sep2 + path17;
3627
3412
  }
3628
- return host + path13;
3413
+ return host + path17;
3629
3414
  }
3630
3415
  }
3631
3416
  });
@@ -3633,19 +3418,19 @@ var require_file_uri_to_path = __commonJS({
3633
3418
  // node_modules/.pnpm/bindings@1.5.0/node_modules/bindings/bindings.js
3634
3419
  var require_bindings = __commonJS({
3635
3420
  "node_modules/.pnpm/bindings@1.5.0/node_modules/bindings/bindings.js"(exports2, module2) {
3636
- var fs15 = require("fs");
3637
- var path13 = require("path");
3421
+ var fs16 = require("fs");
3422
+ var path17 = require("path");
3638
3423
  var fileURLToPath2 = require_file_uri_to_path();
3639
- var join13 = path13.join;
3640
- var dirname11 = path13.dirname;
3641
- var exists = fs15.accessSync && function(path14) {
3424
+ var join16 = path17.join;
3425
+ var dirname12 = path17.dirname;
3426
+ var exists = fs16.accessSync && function(path18) {
3642
3427
  try {
3643
- fs15.accessSync(path14);
3428
+ fs16.accessSync(path18);
3644
3429
  } catch (e) {
3645
3430
  return false;
3646
3431
  }
3647
3432
  return true;
3648
- } || fs15.existsSync || path13.existsSync;
3433
+ } || fs16.existsSync || path17.existsSync;
3649
3434
  var defaults = {
3650
3435
  arrow: process.env.NODE_BINDINGS_ARROW || " \u2192 ",
3651
3436
  compiled: process.env.NODE_BINDINGS_COMPILED_DIR || "compiled",
@@ -3691,13 +3476,13 @@ var require_bindings = __commonJS({
3691
3476
  if (!opts.module_root) {
3692
3477
  opts.module_root = exports2.getRoot(exports2.getFileName());
3693
3478
  }
3694
- if (path13.extname(opts.bindings) != ".node") {
3479
+ if (path17.extname(opts.bindings) != ".node") {
3695
3480
  opts.bindings += ".node";
3696
3481
  }
3697
3482
  var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
3698
3483
  var tries = [], i = 0, l = opts.try.length, n, b, err;
3699
3484
  for (; i < l; i++) {
3700
- n = join13.apply(
3485
+ n = join16.apply(
3701
3486
  null,
3702
3487
  opts.try[i].map(function(p) {
3703
3488
  return opts[p] || p;
@@ -3753,12 +3538,12 @@ var require_bindings = __commonJS({
3753
3538
  return fileName;
3754
3539
  };
3755
3540
  exports2.getRoot = function getRoot(file3) {
3756
- var dir = dirname11(file3), prev;
3541
+ var dir = dirname12(file3), prev;
3757
3542
  while (true) {
3758
3543
  if (dir === ".") {
3759
3544
  dir = process.cwd();
3760
3545
  }
3761
- if (exists(join13(dir, "package.json")) || exists(join13(dir, "node_modules"))) {
3546
+ if (exists(join16(dir, "package.json")) || exists(join16(dir, "node_modules"))) {
3762
3547
  return dir;
3763
3548
  }
3764
3549
  if (prev === dir) {
@@ -3767,7 +3552,7 @@ var require_bindings = __commonJS({
3767
3552
  );
3768
3553
  }
3769
3554
  prev = dir;
3770
- dir = join13(dir, "..");
3555
+ dir = join16(dir, "..");
3771
3556
  }
3772
3557
  };
3773
3558
  }
@@ -3934,11 +3719,11 @@ var require_pragma = __commonJS({
3934
3719
  var require_backup = __commonJS({
3935
3720
  "node_modules/.pnpm/better-sqlite3@12.6.2/node_modules/better-sqlite3/lib/methods/backup.js"(exports2, module2) {
3936
3721
  "use strict";
3937
- var fs15 = require("fs");
3938
- var path13 = require("path");
3722
+ var fs16 = require("fs");
3723
+ var path17 = require("path");
3939
3724
  var { promisify: promisify6 } = require("util");
3940
3725
  var { cppdb } = require_util();
3941
- var fsAccess = promisify6(fs15.access);
3726
+ var fsAccess = promisify6(fs16.access);
3942
3727
  module2.exports = async function backup(filename, options) {
3943
3728
  if (options == null)
3944
3729
  options = {};
@@ -3959,7 +3744,7 @@ var require_backup = __commonJS({
3959
3744
  throw new TypeError('The "attached" option cannot be an empty string');
3960
3745
  if (handler != null && typeof handler !== "function")
3961
3746
  throw new TypeError('Expected the "progress" option to be a function');
3962
- await fsAccess(path13.dirname(filename)).catch(() => {
3747
+ await fsAccess(path17.dirname(filename)).catch(() => {
3963
3748
  throw new TypeError("Cannot save backup because the directory does not exist");
3964
3749
  });
3965
3750
  const isNewFile = await fsAccess(filename).then(() => false, () => true);
@@ -4289,8 +4074,8 @@ var require_inspect = __commonJS({
4289
4074
  var require_database = __commonJS({
4290
4075
  "node_modules/.pnpm/better-sqlite3@12.6.2/node_modules/better-sqlite3/lib/database.js"(exports2, module2) {
4291
4076
  "use strict";
4292
- var fs15 = require("fs");
4293
- var path13 = require("path");
4077
+ var fs16 = require("fs");
4078
+ var path17 = require("path");
4294
4079
  var util2 = require_util();
4295
4080
  var SqliteError = require_sqlite_error();
4296
4081
  var DEFAULT_ADDON;
@@ -4337,7 +4122,7 @@ var require_database = __commonJS({
4337
4122
  addon = DEFAULT_ADDON || (DEFAULT_ADDON = require_bindings()("better_sqlite3.node"));
4338
4123
  } else if (typeof nativeBinding === "string") {
4339
4124
  const requireFunc = typeof __non_webpack_require__ === "function" ? __non_webpack_require__ : require;
4340
- addon = requireFunc(path13.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
4125
+ addon = requireFunc(path17.resolve(nativeBinding).replace(/(\.node)?$/, ".node"));
4341
4126
  } else {
4342
4127
  addon = nativeBinding;
4343
4128
  }
@@ -4345,7 +4130,7 @@ var require_database = __commonJS({
4345
4130
  addon.setErrorConstructor(SqliteError);
4346
4131
  addon.isInitialized = true;
4347
4132
  }
4348
- if (!anonymous && !filename.startsWith("file:") && !fs15.existsSync(path13.dirname(filename))) {
4133
+ if (!anonymous && !filename.startsWith("file:") && !fs16.existsSync(path17.dirname(filename))) {
4349
4134
  throw new TypeError("Cannot open database because the directory does not exist");
4350
4135
  }
4351
4136
  Object.defineProperties(this, {
@@ -4617,7 +4402,7 @@ CREATE INDEX IF NOT EXISTS idx_comments_card ON comments (card_id, board_i
4617
4402
  default_status = excluded.default_status,
4618
4403
  default_priority = excluded.default_priority
4619
4404
  `);
4620
- const deleteBoard = this.db.prepare("DELETE FROM boards WHERE id = ?");
4405
+ const deleteBoard2 = this.db.prepare("DELETE FROM boards WHERE id = ?");
4621
4406
  const existingBoardIds = this.db.prepare("SELECT id FROM boards").all().map((r) => r.id);
4622
4407
  const newBoardIds = new Set(Object.keys(config3.boards));
4623
4408
  const doWrite = this.db.transaction(() => {
@@ -4634,7 +4419,7 @@ CREATE INDEX IF NOT EXISTS idx_comments_card ON comments (card_id, board_i
4634
4419
  }
4635
4420
  for (const id of existingBoardIds) {
4636
4421
  if (!newBoardIds.has(id))
4637
- deleteBoard.run(id);
4422
+ deleteBoard2.run(id);
4638
4423
  }
4639
4424
  this.db.prepare("DELETE FROM labels").run();
4640
4425
  const insertLabel = this.db.prepare("INSERT INTO labels (name, color, group_name) VALUES (?, ?, ?)");
@@ -4866,9 +4651,9 @@ function createStorageEngine(kanbanDir, options = {}) {
4866
4651
  }
4867
4652
  function readBootstrapConfig(workspaceRoot) {
4868
4653
  try {
4869
- const fs15 = require("fs");
4654
+ const fs16 = require("fs");
4870
4655
  const configFile = path7.join(workspaceRoot, ".kanban.json");
4871
- const raw = JSON.parse(fs15.readFileSync(configFile, "utf-8"));
4656
+ const raw = JSON.parse(fs16.readFileSync(configFile, "utf-8"));
4872
4657
  return {
4873
4658
  storageEngine: raw.storageEngine,
4874
4659
  sqlitePath: raw.sqlitePath
@@ -4889,6 +4674,339 @@ var init_storage = __esm({
4889
4674
  }
4890
4675
  });
4891
4676
 
4677
+ // src/sdk/webhooks.ts
4678
+ function fireWebhooks(workspaceRoot, event, data) {
4679
+ const config3 = readConfig(workspaceRoot);
4680
+ const webhooks = config3.webhooks || [];
4681
+ const matching = webhooks.filter(
4682
+ (w) => w.active && (w.events.includes("*") || w.events.includes(event))
4683
+ );
4684
+ if (matching.length === 0)
4685
+ return;
4686
+ const payload = JSON.stringify({
4687
+ event,
4688
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4689
+ data
4690
+ });
4691
+ for (const webhook of matching) {
4692
+ deliverWebhook(webhook, event, payload).catch((err) => {
4693
+ console.error(`Webhook delivery failed for ${webhook.id} (${webhook.url}):`, err.message || err);
4694
+ });
4695
+ }
4696
+ }
4697
+ async function deliverWebhook(webhook, event, payload) {
4698
+ const url3 = new URL(webhook.url);
4699
+ const isHttps = url3.protocol === "https:";
4700
+ const transport = isHttps ? https : http;
4701
+ const headers = {
4702
+ "Content-Type": "application/json",
4703
+ "Content-Length": Buffer.byteLength(payload).toString(),
4704
+ "X-Webhook-Event": event
4705
+ };
4706
+ if (webhook.secret) {
4707
+ const signature = crypto.createHmac("sha256", webhook.secret).update(payload).digest("hex");
4708
+ headers["X-Webhook-Signature"] = `sha256=${signature}`;
4709
+ }
4710
+ return new Promise((resolve9, reject) => {
4711
+ const req = transport.request(
4712
+ {
4713
+ hostname: url3.hostname,
4714
+ port: url3.port || (isHttps ? 443 : 80),
4715
+ path: url3.pathname + url3.search,
4716
+ method: "POST",
4717
+ headers,
4718
+ timeout: 1e4
4719
+ },
4720
+ (res) => {
4721
+ res.resume();
4722
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
4723
+ resolve9();
4724
+ } else {
4725
+ reject(new Error(`HTTP ${res.statusCode}`));
4726
+ }
4727
+ }
4728
+ );
4729
+ req.on("error", reject);
4730
+ req.on("timeout", () => {
4731
+ req.destroy();
4732
+ reject(new Error("Timeout"));
4733
+ });
4734
+ req.write(payload);
4735
+ req.end();
4736
+ });
4737
+ }
4738
+ function loadWebhooks(workspaceRoot) {
4739
+ const config3 = readConfig(workspaceRoot);
4740
+ return config3.webhooks || [];
4741
+ }
4742
+ function saveWebhooks(workspaceRoot, webhooks) {
4743
+ const config3 = readConfig(workspaceRoot);
4744
+ config3.webhooks = webhooks;
4745
+ writeConfig(workspaceRoot, config3);
4746
+ }
4747
+ function createWebhook(workspaceRoot, webhookConfig) {
4748
+ const webhooks = loadWebhooks(workspaceRoot);
4749
+ const webhook = {
4750
+ id: "wh_" + crypto.randomBytes(8).toString("hex"),
4751
+ url: webhookConfig.url,
4752
+ events: webhookConfig.events,
4753
+ secret: webhookConfig.secret,
4754
+ active: true
4755
+ };
4756
+ webhooks.push(webhook);
4757
+ saveWebhooks(workspaceRoot, webhooks);
4758
+ return webhook;
4759
+ }
4760
+ function deleteWebhook(workspaceRoot, id) {
4761
+ const webhooks = loadWebhooks(workspaceRoot);
4762
+ const filtered = webhooks.filter((w) => w.id !== id);
4763
+ if (filtered.length === webhooks.length)
4764
+ return false;
4765
+ saveWebhooks(workspaceRoot, filtered);
4766
+ return true;
4767
+ }
4768
+ function updateWebhook(workspaceRoot, id, updates) {
4769
+ const webhooks = loadWebhooks(workspaceRoot);
4770
+ const webhook = webhooks.find((w) => w.id === id);
4771
+ if (!webhook)
4772
+ return null;
4773
+ if (updates.url !== void 0)
4774
+ webhook.url = updates.url;
4775
+ if (updates.events !== void 0)
4776
+ webhook.events = updates.events;
4777
+ if (updates.secret !== void 0)
4778
+ webhook.secret = updates.secret;
4779
+ if (updates.active !== void 0)
4780
+ webhook.active = updates.active;
4781
+ saveWebhooks(workspaceRoot, webhooks);
4782
+ return webhook;
4783
+ }
4784
+ var crypto, http, https;
4785
+ var init_webhooks = __esm({
4786
+ "src/sdk/webhooks.ts"() {
4787
+ "use strict";
4788
+ crypto = __toESM(require("crypto"));
4789
+ http = __toESM(require("http"));
4790
+ https = __toESM(require("https"));
4791
+ init_config();
4792
+ }
4793
+ });
4794
+
4795
+ // node_modules/.pnpm/fractional-indexing@3.2.0/node_modules/fractional-indexing/src/index.js
4796
+ function midpoint(a, b, digits) {
4797
+ const zero = digits[0];
4798
+ if (b != null && a >= b) {
4799
+ throw new Error(a + " >= " + b);
4800
+ }
4801
+ if (a.slice(-1) === zero || b && b.slice(-1) === zero) {
4802
+ throw new Error("trailing zero");
4803
+ }
4804
+ if (b) {
4805
+ let n = 0;
4806
+ while ((a[n] || zero) === b[n]) {
4807
+ n++;
4808
+ }
4809
+ if (n > 0) {
4810
+ return b.slice(0, n) + midpoint(a.slice(n), b.slice(n), digits);
4811
+ }
4812
+ }
4813
+ const digitA = a ? digits.indexOf(a[0]) : 0;
4814
+ const digitB = b != null ? digits.indexOf(b[0]) : digits.length;
4815
+ if (digitB - digitA > 1) {
4816
+ const midDigit = Math.round(0.5 * (digitA + digitB));
4817
+ return digits[midDigit];
4818
+ } else {
4819
+ if (b && b.length > 1) {
4820
+ return b.slice(0, 1);
4821
+ } else {
4822
+ return digits[digitA] + midpoint(a.slice(1), null, digits);
4823
+ }
4824
+ }
4825
+ }
4826
+ function validateInteger(int4) {
4827
+ if (int4.length !== getIntegerLength(int4[0])) {
4828
+ throw new Error("invalid integer part of order key: " + int4);
4829
+ }
4830
+ }
4831
+ function getIntegerLength(head) {
4832
+ if (head >= "a" && head <= "z") {
4833
+ return head.charCodeAt(0) - "a".charCodeAt(0) + 2;
4834
+ } else if (head >= "A" && head <= "Z") {
4835
+ return "Z".charCodeAt(0) - head.charCodeAt(0) + 2;
4836
+ } else {
4837
+ throw new Error("invalid order key head: " + head);
4838
+ }
4839
+ }
4840
+ function getIntegerPart(key) {
4841
+ const integerPartLength = getIntegerLength(key[0]);
4842
+ if (integerPartLength > key.length) {
4843
+ throw new Error("invalid order key: " + key);
4844
+ }
4845
+ return key.slice(0, integerPartLength);
4846
+ }
4847
+ function validateOrderKey(key, digits) {
4848
+ if (key === "A" + digits[0].repeat(26)) {
4849
+ throw new Error("invalid order key: " + key);
4850
+ }
4851
+ const i = getIntegerPart(key);
4852
+ const f = key.slice(i.length);
4853
+ if (f.slice(-1) === digits[0]) {
4854
+ throw new Error("invalid order key: " + key);
4855
+ }
4856
+ }
4857
+ function incrementInteger(x, digits) {
4858
+ validateInteger(x);
4859
+ const [head, ...digs] = x.split("");
4860
+ let carry = true;
4861
+ for (let i = digs.length - 1; carry && i >= 0; i--) {
4862
+ const d = digits.indexOf(digs[i]) + 1;
4863
+ if (d === digits.length) {
4864
+ digs[i] = digits[0];
4865
+ } else {
4866
+ digs[i] = digits[d];
4867
+ carry = false;
4868
+ }
4869
+ }
4870
+ if (carry) {
4871
+ if (head === "Z") {
4872
+ return "a" + digits[0];
4873
+ }
4874
+ if (head === "z") {
4875
+ return null;
4876
+ }
4877
+ const h = String.fromCharCode(head.charCodeAt(0) + 1);
4878
+ if (h > "a") {
4879
+ digs.push(digits[0]);
4880
+ } else {
4881
+ digs.pop();
4882
+ }
4883
+ return h + digs.join("");
4884
+ } else {
4885
+ return head + digs.join("");
4886
+ }
4887
+ }
4888
+ function decrementInteger(x, digits) {
4889
+ validateInteger(x);
4890
+ const [head, ...digs] = x.split("");
4891
+ let borrow = true;
4892
+ for (let i = digs.length - 1; borrow && i >= 0; i--) {
4893
+ const d = digits.indexOf(digs[i]) - 1;
4894
+ if (d === -1) {
4895
+ digs[i] = digits.slice(-1);
4896
+ } else {
4897
+ digs[i] = digits[d];
4898
+ borrow = false;
4899
+ }
4900
+ }
4901
+ if (borrow) {
4902
+ if (head === "a") {
4903
+ return "Z" + digits.slice(-1);
4904
+ }
4905
+ if (head === "A") {
4906
+ return null;
4907
+ }
4908
+ const h = String.fromCharCode(head.charCodeAt(0) - 1);
4909
+ if (h < "Z") {
4910
+ digs.push(digits.slice(-1));
4911
+ } else {
4912
+ digs.pop();
4913
+ }
4914
+ return h + digs.join("");
4915
+ } else {
4916
+ return head + digs.join("");
4917
+ }
4918
+ }
4919
+ function generateKeyBetween(a, b, digits = BASE_62_DIGITS) {
4920
+ if (a != null) {
4921
+ validateOrderKey(a, digits);
4922
+ }
4923
+ if (b != null) {
4924
+ validateOrderKey(b, digits);
4925
+ }
4926
+ if (a != null && b != null && a >= b) {
4927
+ throw new Error(a + " >= " + b);
4928
+ }
4929
+ if (a == null) {
4930
+ if (b == null) {
4931
+ return "a" + digits[0];
4932
+ }
4933
+ const ib2 = getIntegerPart(b);
4934
+ const fb2 = b.slice(ib2.length);
4935
+ if (ib2 === "A" + digits[0].repeat(26)) {
4936
+ return ib2 + midpoint("", fb2, digits);
4937
+ }
4938
+ if (ib2 < b) {
4939
+ return ib2;
4940
+ }
4941
+ const res = decrementInteger(ib2, digits);
4942
+ if (res == null) {
4943
+ throw new Error("cannot decrement any more");
4944
+ }
4945
+ return res;
4946
+ }
4947
+ if (b == null) {
4948
+ const ia2 = getIntegerPart(a);
4949
+ const fa2 = a.slice(ia2.length);
4950
+ const i2 = incrementInteger(ia2, digits);
4951
+ return i2 == null ? ia2 + midpoint(fa2, null, digits) : i2;
4952
+ }
4953
+ const ia = getIntegerPart(a);
4954
+ const fa = a.slice(ia.length);
4955
+ const ib = getIntegerPart(b);
4956
+ const fb = b.slice(ib.length);
4957
+ if (ia === ib) {
4958
+ return ia + midpoint(fa, fb, digits);
4959
+ }
4960
+ const i = incrementInteger(ia, digits);
4961
+ if (i == null) {
4962
+ throw new Error("cannot increment any more");
4963
+ }
4964
+ if (i < b) {
4965
+ return i;
4966
+ }
4967
+ return ia + midpoint(fa, null, digits);
4968
+ }
4969
+ function generateNKeysBetween(a, b, n, digits = BASE_62_DIGITS) {
4970
+ if (n === 0) {
4971
+ return [];
4972
+ }
4973
+ if (n === 1) {
4974
+ return [generateKeyBetween(a, b, digits)];
4975
+ }
4976
+ if (b == null) {
4977
+ let c2 = generateKeyBetween(a, b, digits);
4978
+ const result = [c2];
4979
+ for (let i = 0; i < n - 1; i++) {
4980
+ c2 = generateKeyBetween(c2, b, digits);
4981
+ result.push(c2);
4982
+ }
4983
+ return result;
4984
+ }
4985
+ if (a == null) {
4986
+ let c2 = generateKeyBetween(a, b, digits);
4987
+ const result = [c2];
4988
+ for (let i = 0; i < n - 1; i++) {
4989
+ c2 = generateKeyBetween(a, c2, digits);
4990
+ result.push(c2);
4991
+ }
4992
+ result.reverse();
4993
+ return result;
4994
+ }
4995
+ const mid = Math.floor(n / 2);
4996
+ const c = generateKeyBetween(a, b, digits);
4997
+ return [
4998
+ ...generateNKeysBetween(a, c, mid, digits),
4999
+ c,
5000
+ ...generateNKeysBetween(c, b, n - mid - 1, digits)
5001
+ ];
5002
+ }
5003
+ var BASE_62_DIGITS;
5004
+ var init_src = __esm({
5005
+ "node_modules/.pnpm/fractional-indexing@3.2.0/node_modules/fractional-indexing/src/index.js"() {
5006
+ BASE_62_DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5007
+ }
5008
+ });
5009
+
4892
5010
  // src/sdk/types.ts
4893
5011
  function sanitizeCard(card) {
4894
5012
  const { filePath: _, ...rest } = card;
@@ -4903,15 +5021,210 @@ var init_types2 = __esm({
4903
5021
  }
4904
5022
  });
4905
5023
 
5024
+ // src/sdk/modules/boards.ts
5025
+ function listBoards(ctx) {
5026
+ const config3 = readConfig(ctx.workspaceRoot);
5027
+ return Object.entries(config3.boards).map(([id, board]) => ({
5028
+ id,
5029
+ name: board.name,
5030
+ description: board.description,
5031
+ columns: board.columns,
5032
+ actions: board.actions
5033
+ }));
5034
+ }
5035
+ function createBoard(ctx, id, name, options) {
5036
+ const config3 = readConfig(ctx.workspaceRoot);
5037
+ if (!id) {
5038
+ const base = generateSlug(name) || "board";
5039
+ let uniqueId = base;
5040
+ let counter = 1;
5041
+ while (config3.boards[uniqueId]) {
5042
+ uniqueId = `${base}-${counter++}`;
5043
+ }
5044
+ id = uniqueId;
5045
+ }
5046
+ if (config3.boards[id]) {
5047
+ throw new Error(`Board already exists: ${id}`);
5048
+ }
5049
+ const columns = options?.columns || [...config3.boards[config3.defaultBoard]?.columns || [
5050
+ { id: "backlog", name: "Backlog", color: "#6b7280" },
5051
+ { id: "todo", name: "To Do", color: "#3b82f6" },
5052
+ { id: "in-progress", name: "In Progress", color: "#f59e0b" },
5053
+ { id: "review", name: "Review", color: "#8b5cf6" },
5054
+ { id: "done", name: "Done", color: "#22c55e" }
5055
+ ]];
5056
+ config3.boards[id] = {
5057
+ name,
5058
+ description: options?.description,
5059
+ columns,
5060
+ nextCardId: 1,
5061
+ defaultStatus: options?.defaultStatus || columns[0]?.id || "backlog",
5062
+ defaultPriority: options?.defaultPriority || config3.defaultPriority
5063
+ };
5064
+ writeConfig(ctx.workspaceRoot, config3);
5065
+ const boardInfo = { id, name, description: options?.description };
5066
+ ctx.emitEvent("board.created", boardInfo);
5067
+ return boardInfo;
5068
+ }
5069
+ async function deleteBoard(ctx, boardId) {
5070
+ const config3 = readConfig(ctx.workspaceRoot);
5071
+ if (!config3.boards[boardId]) {
5072
+ throw new Error(`Board not found: ${boardId}`);
5073
+ }
5074
+ if (config3.defaultBoard === boardId) {
5075
+ throw new Error(`Cannot delete the default board: ${boardId}`);
5076
+ }
5077
+ const cards = await ctx.listCards(void 0, boardId);
5078
+ if (cards.length > 0) {
5079
+ throw new Error(`Cannot delete board "${boardId}": ${cards.length} card(s) still exist`);
5080
+ }
5081
+ const boardDir = ctx._boardDir(boardId);
5082
+ await ctx._storage.deleteBoardData(boardDir, boardId);
5083
+ delete config3.boards[boardId];
5084
+ writeConfig(ctx.workspaceRoot, config3);
5085
+ ctx.emitEvent("board.deleted", { id: boardId });
5086
+ }
5087
+ function getBoard(ctx, boardId) {
5088
+ return getBoardConfig(ctx.workspaceRoot, boardId);
5089
+ }
5090
+ function updateBoard(ctx, boardId, updates) {
5091
+ const config3 = readConfig(ctx.workspaceRoot);
5092
+ const board = config3.boards[boardId];
5093
+ if (!board) {
5094
+ throw new Error(`Board not found: ${boardId}`);
5095
+ }
5096
+ if (updates.name !== void 0)
5097
+ board.name = updates.name;
5098
+ if (updates.description !== void 0)
5099
+ board.description = updates.description;
5100
+ if (updates.columns !== void 0)
5101
+ board.columns = updates.columns;
5102
+ if (updates.defaultStatus !== void 0)
5103
+ board.defaultStatus = updates.defaultStatus;
5104
+ if (updates.defaultPriority !== void 0)
5105
+ board.defaultPriority = updates.defaultPriority;
5106
+ writeConfig(ctx.workspaceRoot, config3);
5107
+ ctx.emitEvent("board.updated", { id: boardId, ...board });
5108
+ return board;
5109
+ }
5110
+ function getBoardActions(ctx, boardId) {
5111
+ const config3 = readConfig(ctx.workspaceRoot);
5112
+ const resolvedId = boardId || config3.defaultBoard;
5113
+ const board = config3.boards[resolvedId];
5114
+ if (!board)
5115
+ throw new Error(`Board not found: ${resolvedId}`);
5116
+ return board.actions ?? {};
5117
+ }
5118
+ function addBoardAction(ctx, boardId, key, title) {
5119
+ const config3 = readConfig(ctx.workspaceRoot);
5120
+ const board = config3.boards[boardId];
5121
+ if (!board)
5122
+ throw new Error(`Board not found: ${boardId}`);
5123
+ board.actions ??= {};
5124
+ board.actions[key] = title;
5125
+ writeConfig(ctx.workspaceRoot, config3);
5126
+ ctx.emitEvent("board.updated", { id: boardId, ...board });
5127
+ return board.actions;
5128
+ }
5129
+ function removeBoardAction(ctx, boardId, key) {
5130
+ const config3 = readConfig(ctx.workspaceRoot);
5131
+ const board = config3.boards[boardId];
5132
+ if (!board)
5133
+ throw new Error(`Board not found: ${boardId}`);
5134
+ if (!board.actions || !(key in board.actions)) {
5135
+ throw new Error(`Action "${key}" not found on board "${boardId}"`);
5136
+ }
5137
+ delete board.actions[key];
5138
+ if (Object.keys(board.actions).length === 0)
5139
+ delete board.actions;
5140
+ writeConfig(ctx.workspaceRoot, config3);
5141
+ ctx.emitEvent("board.updated", { id: boardId, ...board });
5142
+ return board.actions ?? {};
5143
+ }
5144
+ async function triggerBoardAction(ctx, boardId, actionKey) {
5145
+ const config3 = readConfig(ctx.workspaceRoot);
5146
+ const resolvedId = boardId || config3.defaultBoard;
5147
+ const board = config3.boards[resolvedId];
5148
+ if (!board)
5149
+ throw new Error(`Board not found: ${resolvedId}`);
5150
+ const actions = board.actions ?? {};
5151
+ if (!(actionKey in actions)) {
5152
+ throw new Error(`Action "${actionKey}" not defined on board "${resolvedId}"`);
5153
+ }
5154
+ const actionTitle = actions[actionKey];
5155
+ ctx.emitEvent("board.action", { boardId: resolvedId, action: actionKey, title: actionTitle });
5156
+ }
5157
+ async function transferCard(ctx, cardId, fromBoardId, toBoardId, targetStatus) {
5158
+ const toBoardDir = ctx._boardDir(toBoardId);
5159
+ const config3 = readConfig(ctx.workspaceRoot);
5160
+ if (!config3.boards[fromBoardId])
5161
+ throw new Error(`Board not found: ${fromBoardId}`);
5162
+ if (!config3.boards[toBoardId])
5163
+ throw new Error(`Board not found: ${toBoardId}`);
5164
+ const card = await ctx.getCard(cardId, fromBoardId);
5165
+ if (!card)
5166
+ throw new Error(`Card not found: ${cardId} in board ${fromBoardId}`);
5167
+ const previousStatus = card.status;
5168
+ const toBoard = config3.boards[toBoardId];
5169
+ const newStatus = targetStatus || toBoard.defaultStatus || toBoard.columns[0]?.id || "backlog";
5170
+ await ctx._storage.ensureBoardDirs(toBoardDir, [newStatus]);
5171
+ const srcAttachDir = ctx._storage.getCardDir(card);
5172
+ await ctx._storage.deleteCard(card);
5173
+ card.status = newStatus;
5174
+ card.boardId = toBoardId;
5175
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5176
+ card.completedAt = ctx._isCompletedStatus(newStatus, toBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
5177
+ if (ctx._storage.type === "markdown") {
5178
+ card.filePath = path8.join(toBoardDir, newStatus, path8.basename(card.filePath));
5179
+ } else {
5180
+ card.filePath = "";
5181
+ }
5182
+ const targetCards = await ctx.listCards(void 0, toBoardId);
5183
+ 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);
5184
+ const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
5185
+ card.order = generateKeyBetween(lastOrder, null);
5186
+ await ctx._storage.writeCard(card);
5187
+ if (card.attachments.length > 0) {
5188
+ const dstAttachDir = ctx._storage.getCardDir(card);
5189
+ if (srcAttachDir !== dstAttachDir) {
5190
+ await fs6.mkdir(dstAttachDir, { recursive: true });
5191
+ await Promise.all(
5192
+ card.attachments.map(async (filename) => {
5193
+ const src = path8.join(srcAttachDir, filename);
5194
+ const dst = path8.join(dstAttachDir, filename);
5195
+ await fs6.rename(src, dst).catch(() => {
5196
+ });
5197
+ })
5198
+ );
5199
+ }
5200
+ }
5201
+ ctx.emitEvent("task.moved", { ...sanitizeCard(card), previousStatus, fromBoard: fromBoardId, toBoard: toBoardId });
5202
+ await ctx.addLog(card.id, `Status changed: \`${previousStatus}\` \u2192 \`${newStatus}\``, { source: "system" }, toBoardId).catch(() => {
5203
+ });
5204
+ return card;
5205
+ }
5206
+ var path8, fs6;
5207
+ var init_boards = __esm({
5208
+ "src/sdk/modules/boards.ts"() {
5209
+ "use strict";
5210
+ path8 = __toESM(require("path"));
5211
+ fs6 = __toESM(require("fs/promises"));
5212
+ init_src();
5213
+ init_types();
5214
+ init_config();
5215
+ init_types2();
5216
+ }
5217
+ });
5218
+
4906
5219
  // src/sdk/metaUtils.ts
4907
- function getNestedValue(obj, path13) {
4908
- return path13.split(".").reduce((curr, key) => curr != null && typeof curr === "object" ? curr[key] : void 0, obj);
5220
+ function getNestedValue(obj, path17) {
5221
+ return path17.split(".").reduce((curr, key) => curr != null && typeof curr === "object" ? curr[key] : void 0, obj);
4909
5222
  }
4910
5223
  function matchesMetaFilter(metadata, filter) {
4911
5224
  if (!metadata)
4912
5225
  return false;
4913
- for (const [path13, needle] of Object.entries(filter)) {
4914
- const value = getNestedValue(metadata, path13);
5226
+ for (const [path17, needle] of Object.entries(filter)) {
5227
+ const value = getNestedValue(metadata, path17);
4915
5228
  if (value == null)
4916
5229
  return false;
4917
5230
  if (!String(value).toLowerCase().includes(needle.toLowerCase()))
@@ -4925,20 +5238,769 @@ var init_metaUtils = __esm({
4925
5238
  }
4926
5239
  });
4927
5240
 
4928
- // src/sdk/KanbanSDK.ts
4929
- var path8, fs6, KanbanSDK;
4930
- var init_KanbanSDK = __esm({
4931
- "src/sdk/KanbanSDK.ts"() {
5241
+ // src/sdk/modules/cards.ts
5242
+ async function listCards(ctx, columns, boardId, metaFilter, sort) {
5243
+ await ctx._ensureMigrated();
5244
+ const boardDir = ctx._boardDir(boardId);
5245
+ const resolvedBoardId = ctx._resolveBoardId(boardId);
5246
+ await ctx._storage.ensureBoardDirs(boardDir, columns);
5247
+ const cards = await ctx._storage.scanCards(boardDir, resolvedBoardId);
5248
+ const hasLegacyOrder = cards.some((c) => /^\d+$/.test(c.order));
5249
+ if (hasLegacyOrder) {
5250
+ const byStatus = /* @__PURE__ */ new Map();
5251
+ for (const c of cards) {
5252
+ const list = byStatus.get(c.status) || [];
5253
+ list.push(c);
5254
+ byStatus.set(c.status, list);
5255
+ }
5256
+ for (const columnCards of byStatus.values()) {
5257
+ columnCards.sort((a, b) => parseInt(a.order) - parseInt(b.order));
5258
+ const keys = generateNKeysBetween(null, null, columnCards.length);
5259
+ for (let i = 0; i < columnCards.length; i++) {
5260
+ columnCards[i].order = keys[i];
5261
+ await ctx._storage.writeCard(columnCards[i]);
5262
+ }
5263
+ }
5264
+ }
5265
+ const numericIds = cards.map((c) => parseInt(c.id, 10)).filter((n) => !Number.isNaN(n));
5266
+ if (numericIds.length > 0) {
5267
+ syncCardIdCounter(ctx.workspaceRoot, resolvedBoardId, numericIds);
5268
+ }
5269
+ const filtered = metaFilter && Object.keys(metaFilter).length > 0 ? cards.filter((c) => matchesMetaFilter(c.metadata, metaFilter)) : cards;
5270
+ if (sort) {
5271
+ const [field, dir] = sort.split(":");
5272
+ return filtered.sort((a, b) => {
5273
+ const aVal = field === "created" ? a.created : a.modified;
5274
+ const bVal = field === "created" ? b.created : b.modified;
5275
+ return dir === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
5276
+ });
5277
+ }
5278
+ return filtered.sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
5279
+ }
5280
+ async function getCard(ctx, cardId, boardId) {
5281
+ const cards = await listCards(ctx, void 0, boardId);
5282
+ return cards.find((c) => c.id === cardId) || null;
5283
+ }
5284
+ async function createCard(ctx, data) {
5285
+ await ctx._ensureMigrated();
5286
+ const resolvedBoardId = ctx._resolveBoardId(data.boardId);
5287
+ const boardDir = ctx._boardDir(resolvedBoardId);
5288
+ await ctx._storage.ensureBoardDirs(boardDir);
5289
+ const config3 = readConfig(ctx.workspaceRoot);
5290
+ const board = config3.boards[resolvedBoardId];
5291
+ const status = data.status || board?.defaultStatus || config3.defaultStatus || "backlog";
5292
+ const priority = data.priority || board?.defaultPriority || config3.defaultPriority || "medium";
5293
+ const title = getTitleFromContent(data.content);
5294
+ const numericId = allocateCardId(ctx.workspaceRoot, resolvedBoardId);
5295
+ const filename = generateCardFilename(numericId, title);
5296
+ const now = (/* @__PURE__ */ new Date()).toISOString();
5297
+ const cards = await listCards(ctx, void 0, resolvedBoardId);
5298
+ const cardsInStatus = cards.filter((c) => c.status === status).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
5299
+ const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
5300
+ const card = {
5301
+ version: CARD_FORMAT_VERSION,
5302
+ id: String(numericId),
5303
+ boardId: resolvedBoardId,
5304
+ status,
5305
+ priority,
5306
+ assignee: data.assignee ?? null,
5307
+ dueDate: data.dueDate ?? null,
5308
+ created: now,
5309
+ modified: now,
5310
+ completedAt: ctx._isCompletedStatus(status, resolvedBoardId) ? now : null,
5311
+ labels: data.labels || [],
5312
+ attachments: data.attachments || [],
5313
+ comments: [],
5314
+ order: generateKeyBetween(lastOrder, null),
5315
+ content: data.content,
5316
+ ...data.metadata && Object.keys(data.metadata).length > 0 ? { metadata: data.metadata } : {},
5317
+ ...data.actions && (Array.isArray(data.actions) ? data.actions.length > 0 : Object.keys(data.actions).length > 0) ? { actions: data.actions } : {},
5318
+ filePath: ctx._storage.type === "markdown" ? getCardFilePath(boardDir, status, filename) : ""
5319
+ };
5320
+ await ctx._storage.writeCard(card);
5321
+ ctx.emitEvent("task.created", sanitizeCard(card));
5322
+ return card;
5323
+ }
5324
+ async function updateCard(ctx, cardId, updates, boardId) {
5325
+ const card = await getCard(ctx, cardId, boardId);
5326
+ if (!card)
5327
+ throw new Error(`Card not found: ${cardId}`);
5328
+ const resolvedBoardId = card.boardId || ctx._resolveBoardId(boardId);
5329
+ const boardDir = ctx._boardDir(resolvedBoardId);
5330
+ const oldStatus = card.status;
5331
+ const oldTitle = getTitleFromContent(card.content);
5332
+ const { filePath: _fp, id: _id, boardId: _bid, ...safeUpdates } = updates;
5333
+ Object.assign(card, safeUpdates);
5334
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5335
+ if (oldStatus !== card.status) {
5336
+ card.completedAt = ctx._isCompletedStatus(card.status, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
5337
+ }
5338
+ await ctx._storage.writeCard(card);
5339
+ const newTitle = getTitleFromContent(card.content);
5340
+ const numericId = extractNumericId(card.id);
5341
+ if (numericId !== null && newTitle !== oldTitle) {
5342
+ const newFilename = generateCardFilename(numericId, newTitle);
5343
+ const newPath = await ctx._storage.renameCard(card, newFilename);
5344
+ if (newPath)
5345
+ card.filePath = newPath;
5346
+ }
5347
+ if (oldStatus !== card.status) {
5348
+ const newPath = await ctx._storage.moveCard(card, boardDir, card.status);
5349
+ if (newPath)
5350
+ card.filePath = newPath;
5351
+ }
5352
+ ctx.emitEvent("task.updated", sanitizeCard(card));
5353
+ if (oldStatus !== card.status) {
5354
+ await ctx.addLog(card.id, `Status changed: \`${oldStatus}\` \u2192 \`${card.status}\``, { source: "system" }, resolvedBoardId).catch(() => {
5355
+ });
5356
+ }
5357
+ return card;
5358
+ }
5359
+ async function triggerAction(ctx, cardId, action, boardId) {
5360
+ const config3 = readConfig(ctx.workspaceRoot);
5361
+ const { actionWebhookUrl } = config3;
5362
+ if (!actionWebhookUrl) {
5363
+ throw new Error("No action webhook URL configured. Set actionWebhookUrl in .kanban.json");
5364
+ }
5365
+ const card = await getCard(ctx, cardId, boardId);
5366
+ if (!card)
5367
+ throw new Error(`Card not found: ${cardId}`);
5368
+ const resolvedBoardId = card.boardId || ctx._resolveBoardId(boardId);
5369
+ const payload = {
5370
+ action,
5371
+ board: resolvedBoardId,
5372
+ list: card.status,
5373
+ card: sanitizeCard(card)
5374
+ };
5375
+ const response = await fetch(actionWebhookUrl, {
5376
+ method: "POST",
5377
+ headers: { "Content-Type": "application/json" },
5378
+ body: JSON.stringify(payload)
5379
+ });
5380
+ if (!response.ok) {
5381
+ throw new Error(`Action webhook responded with ${response.status}: ${response.statusText}`);
5382
+ }
5383
+ await ctx.addLog(cardId, `Action triggered: \`${action}\``, { source: "system" }, resolvedBoardId).catch(() => {
5384
+ });
5385
+ }
5386
+ async function moveCard(ctx, cardId, newStatus, position, boardId) {
5387
+ const cards = await listCards(ctx, void 0, boardId);
5388
+ const card = cards.find((c) => c.id === cardId);
5389
+ if (!card)
5390
+ throw new Error(`Card not found: ${cardId}`);
5391
+ const resolvedBoardId = card.boardId || ctx._resolveBoardId(boardId);
5392
+ const boardDir = ctx._boardDir(resolvedBoardId);
5393
+ const oldStatus = card.status;
5394
+ card.status = newStatus;
5395
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5396
+ if (oldStatus !== newStatus) {
5397
+ card.completedAt = ctx._isCompletedStatus(newStatus, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
5398
+ }
5399
+ 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);
5400
+ const pos = position !== void 0 ? Math.max(0, Math.min(position, targetColumnCards.length)) : targetColumnCards.length;
5401
+ const before = pos > 0 ? targetColumnCards[pos - 1].order : null;
5402
+ const after = pos < targetColumnCards.length ? targetColumnCards[pos].order : null;
5403
+ card.order = generateKeyBetween(before, after);
5404
+ await ctx._storage.writeCard(card);
5405
+ if (oldStatus !== newStatus) {
5406
+ const newPath = await ctx._storage.moveCard(card, boardDir, newStatus);
5407
+ if (newPath)
5408
+ card.filePath = newPath;
5409
+ }
5410
+ ctx.emitEvent("task.moved", { ...sanitizeCard(card), previousStatus: oldStatus });
5411
+ if (oldStatus !== newStatus) {
5412
+ await ctx.addLog(card.id, `Status changed: \`${oldStatus}\` \u2192 \`${newStatus}\``, { source: "system" }, resolvedBoardId).catch(() => {
5413
+ });
5414
+ }
5415
+ return card;
5416
+ }
5417
+ async function deleteCard(ctx, cardId, boardId) {
5418
+ const card = await getCard(ctx, cardId, boardId);
5419
+ if (!card)
5420
+ throw new Error(`Card not found: ${cardId}`);
5421
+ if (card.status === DELETED_STATUS_ID)
5422
+ return;
5423
+ await updateCard(ctx, cardId, { status: DELETED_STATUS_ID }, boardId);
5424
+ }
5425
+ async function permanentlyDeleteCard(ctx, cardId, boardId) {
5426
+ const card = await getCard(ctx, cardId, boardId);
5427
+ if (!card)
5428
+ throw new Error(`Card not found: ${cardId}`);
5429
+ const snapshot = sanitizeCard(card);
5430
+ await ctx._storage.deleteCard(card);
5431
+ ctx.emitEvent("task.deleted", snapshot);
5432
+ }
5433
+ async function getCardsByStatus(ctx, status, boardId) {
5434
+ const cards = await listCards(ctx, void 0, boardId);
5435
+ return cards.filter((c) => c.status === status);
5436
+ }
5437
+ async function getUniqueAssignees(ctx, boardId) {
5438
+ const cards = await listCards(ctx, void 0, boardId);
5439
+ const assignees = /* @__PURE__ */ new Set();
5440
+ for (const c of cards) {
5441
+ if (c.assignee)
5442
+ assignees.add(c.assignee);
5443
+ }
5444
+ return [...assignees].sort();
5445
+ }
5446
+ async function getUniqueLabels(ctx, boardId) {
5447
+ const cards = await listCards(ctx, void 0, boardId);
5448
+ const labels = /* @__PURE__ */ new Set();
5449
+ for (const c of cards) {
5450
+ for (const l of c.labels)
5451
+ labels.add(l);
5452
+ }
5453
+ return [...labels].sort();
5454
+ }
5455
+ var init_cards = __esm({
5456
+ "src/sdk/modules/cards.ts"() {
4932
5457
  "use strict";
4933
- path8 = __toESM(require("path"));
4934
- fs6 = __toESM(require("fs/promises"));
4935
5458
  init_src();
4936
5459
  init_types();
4937
5460
  init_config();
4938
5461
  init_fileUtils();
5462
+ init_metaUtils();
4939
5463
  init_types2();
5464
+ }
5465
+ });
5466
+
5467
+ // src/sdk/modules/labels.ts
5468
+ function getLabels(ctx) {
5469
+ const config3 = readConfig(ctx.workspaceRoot);
5470
+ return config3.labels || {};
5471
+ }
5472
+ function setLabel(ctx, name, definition) {
5473
+ const config3 = readConfig(ctx.workspaceRoot);
5474
+ if (!config3.labels)
5475
+ config3.labels = {};
5476
+ config3.labels[name] = definition;
5477
+ writeConfig(ctx.workspaceRoot, config3);
5478
+ }
5479
+ async function deleteLabel(ctx, name) {
5480
+ const config3 = readConfig(ctx.workspaceRoot);
5481
+ if (config3.labels) {
5482
+ delete config3.labels[name];
5483
+ writeConfig(ctx.workspaceRoot, config3);
5484
+ }
5485
+ const cards = await ctx.listCards();
5486
+ for (const card of cards) {
5487
+ if (card.labels.includes(name)) {
5488
+ await ctx.updateCard(card.id, { labels: card.labels.filter((l) => l !== name) });
5489
+ }
5490
+ }
5491
+ }
5492
+ async function renameLabel(ctx, oldName, newName) {
5493
+ const config3 = readConfig(ctx.workspaceRoot);
5494
+ if (config3.labels && config3.labels[oldName]) {
5495
+ config3.labels[newName] = config3.labels[oldName];
5496
+ delete config3.labels[oldName];
5497
+ writeConfig(ctx.workspaceRoot, config3);
5498
+ }
5499
+ const cards = await ctx.listCards();
5500
+ for (const card of cards) {
5501
+ if (card.labels.includes(oldName)) {
5502
+ const newLabels = card.labels.map((l) => l === oldName ? newName : l);
5503
+ await ctx.updateCard(card.id, { labels: newLabels });
5504
+ }
5505
+ }
5506
+ }
5507
+ function getLabelsInGroup(ctx, group) {
5508
+ const labels = getLabels(ctx);
5509
+ return Object.entries(labels).filter(([, def]) => def.group === group).map(([name]) => name).sort();
5510
+ }
5511
+ async function filterCardsByLabelGroup(ctx, group, boardId) {
5512
+ const groupLabels = getLabelsInGroup(ctx, group);
5513
+ if (groupLabels.length === 0)
5514
+ return [];
5515
+ const cards = await ctx.listCards(void 0, boardId);
5516
+ return cards.filter((c) => c.labels.some((l) => groupLabels.includes(l)));
5517
+ }
5518
+ var init_labels = __esm({
5519
+ "src/sdk/modules/labels.ts"() {
5520
+ "use strict";
5521
+ init_config();
5522
+ }
5523
+ });
5524
+
5525
+ // src/sdk/modules/attachments.ts
5526
+ async function addAttachment(ctx, cardId, sourcePath, boardId) {
5527
+ const card = await ctx.getCard(cardId, boardId);
5528
+ if (!card)
5529
+ throw new Error(`Card not found: ${cardId}`);
5530
+ const fileName = path9.basename(sourcePath);
5531
+ await ctx._storage.copyAttachment(sourcePath, card);
5532
+ if (!card.attachments.includes(fileName)) {
5533
+ card.attachments.push(fileName);
5534
+ }
5535
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5536
+ await ctx._storage.writeCard(card);
5537
+ ctx.emitEvent("attachment.added", { cardId, attachment: fileName });
5538
+ return card;
5539
+ }
5540
+ async function removeAttachment(ctx, cardId, attachment, boardId) {
5541
+ const card = await ctx.getCard(cardId, boardId);
5542
+ if (!card)
5543
+ throw new Error(`Card not found: ${cardId}`);
5544
+ card.attachments = card.attachments.filter((a) => a !== attachment);
5545
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5546
+ await ctx._storage.writeCard(card);
5547
+ ctx.emitEvent("attachment.removed", { cardId, attachment });
5548
+ return card;
5549
+ }
5550
+ async function listAttachments(ctx, cardId, boardId) {
5551
+ const card = await ctx.getCard(cardId, boardId);
5552
+ if (!card)
5553
+ throw new Error(`Card not found: ${cardId}`);
5554
+ return card.attachments;
5555
+ }
5556
+ async function getAttachmentDir(ctx, cardId, boardId) {
5557
+ const card = await ctx.getCard(cardId, boardId);
5558
+ if (!card)
5559
+ return null;
5560
+ return ctx._storage.getCardDir(card);
5561
+ }
5562
+ var path9;
5563
+ var init_attachments = __esm({
5564
+ "src/sdk/modules/attachments.ts"() {
5565
+ "use strict";
5566
+ path9 = __toESM(require("path"));
5567
+ }
5568
+ });
5569
+
5570
+ // src/sdk/modules/comments.ts
5571
+ async function listComments(ctx, cardId, boardId) {
5572
+ const card = await ctx.getCard(cardId, boardId);
5573
+ if (!card)
5574
+ throw new Error(`Card not found: ${cardId}`);
5575
+ return card.comments || [];
5576
+ }
5577
+ async function addComment(ctx, cardId, author, content, boardId) {
5578
+ if (!content?.trim())
5579
+ throw new Error("Comment content cannot be empty");
5580
+ const card = await ctx.getCard(cardId, boardId);
5581
+ if (!card)
5582
+ throw new Error(`Card not found: ${cardId}`);
5583
+ if (!card.comments)
5584
+ card.comments = [];
5585
+ const maxId = card.comments.reduce((max, c) => {
5586
+ const num = parseInt(c.id.replace("c", ""), 10);
5587
+ return Number.isNaN(num) ? max : Math.max(max, num);
5588
+ }, 0);
5589
+ const comment = {
5590
+ id: `c${maxId + 1}`,
5591
+ author,
5592
+ created: (/* @__PURE__ */ new Date()).toISOString(),
5593
+ content
5594
+ };
5595
+ card.comments.push(comment);
5596
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5597
+ await ctx._storage.writeCard(card);
5598
+ ctx.emitEvent("comment.created", { ...comment, cardId });
5599
+ return card;
5600
+ }
5601
+ async function updateComment(ctx, cardId, commentId, content, boardId) {
5602
+ const card = await ctx.getCard(cardId, boardId);
5603
+ if (!card)
5604
+ throw new Error(`Card not found: ${cardId}`);
5605
+ const comment = (card.comments || []).find((c) => c.id === commentId);
5606
+ if (!comment)
5607
+ throw new Error(`Comment not found: ${commentId}`);
5608
+ comment.content = content;
5609
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5610
+ await ctx._storage.writeCard(card);
5611
+ ctx.emitEvent("comment.updated", { ...comment, cardId });
5612
+ return card;
5613
+ }
5614
+ async function deleteComment(ctx, cardId, commentId, boardId) {
5615
+ const card = await ctx.getCard(cardId, boardId);
5616
+ if (!card)
5617
+ throw new Error(`Card not found: ${cardId}`);
5618
+ const comment = (card.comments || []).find((c) => c.id === commentId);
5619
+ card.comments = (card.comments || []).filter((c) => c.id !== commentId);
5620
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5621
+ await ctx._storage.writeCard(card);
5622
+ if (comment) {
5623
+ ctx.emitEvent("comment.deleted", { ...comment, cardId });
5624
+ }
5625
+ return card;
5626
+ }
5627
+ var init_comments = __esm({
5628
+ "src/sdk/modules/comments.ts"() {
5629
+ "use strict";
5630
+ }
5631
+ });
5632
+
5633
+ // src/sdk/modules/logs.ts
5634
+ function parseLogLine(line) {
5635
+ const trimmed = line.trim();
5636
+ if (!trimmed)
5637
+ return null;
5638
+ const match = trimmed.match(/^(\S+)\s+\[([^\]]+)\]\s+(.+)$/);
5639
+ if (!match)
5640
+ return null;
5641
+ const [, timestamp2, source, rest] = match;
5642
+ const jsonMatch = rest.match(/^(.*?)\s+(\{.+\})\s*$/);
5643
+ if (jsonMatch) {
5644
+ try {
5645
+ const obj = JSON.parse(jsonMatch[2]);
5646
+ return { timestamp: timestamp2, source, text: jsonMatch[1], object: obj };
5647
+ } catch {
5648
+ }
5649
+ }
5650
+ return { timestamp: timestamp2, source, text: rest };
5651
+ }
5652
+ function serializeLogEntry(entry) {
5653
+ let line = `${entry.timestamp} [${entry.source}] ${entry.text}`;
5654
+ if (entry.object && Object.keys(entry.object).length > 0) {
5655
+ line += ` ${JSON.stringify(entry.object)}`;
5656
+ }
5657
+ return line;
5658
+ }
5659
+ async function getLogFilePath(ctx, cardId, boardId) {
5660
+ const card = await ctx.getCard(cardId, boardId);
5661
+ if (!card)
5662
+ return null;
5663
+ const dir = ctx._storage.getCardDir(card);
5664
+ return path10.join(dir, `${card.id}.log`);
5665
+ }
5666
+ async function listLogs(ctx, cardId, boardId) {
5667
+ const logPath = await getLogFilePath(ctx, cardId, boardId);
5668
+ if (!logPath)
5669
+ throw new Error(`Card not found: ${cardId}`);
5670
+ try {
5671
+ const content = await fs7.readFile(logPath, "utf-8");
5672
+ const entries = [];
5673
+ for (const line of content.split("\n")) {
5674
+ const entry = parseLogLine(line);
5675
+ if (entry)
5676
+ entries.push(entry);
5677
+ }
5678
+ return entries;
5679
+ } catch {
5680
+ return [];
5681
+ }
5682
+ }
5683
+ async function addLog(ctx, cardId, text, options, boardId) {
5684
+ if (!text?.trim())
5685
+ throw new Error("Log text cannot be empty");
5686
+ const card = await ctx.getCard(cardId, boardId);
5687
+ if (!card)
5688
+ throw new Error(`Card not found: ${cardId}`);
5689
+ const entry = {
5690
+ timestamp: options?.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
5691
+ source: options?.source || "default",
5692
+ text: text.trim(),
5693
+ ...options?.object && Object.keys(options.object).length > 0 ? { object: options.object } : {}
5694
+ };
5695
+ const dir = ctx._storage.getCardDir(card);
5696
+ const logFileName = `${card.id}.log`;
5697
+ const logPath = path10.join(dir, logFileName);
5698
+ await fs7.mkdir(dir, { recursive: true });
5699
+ const line = serializeLogEntry(entry) + "\n";
5700
+ await fs7.appendFile(logPath, line, "utf-8");
5701
+ if (!card.attachments.includes(logFileName)) {
5702
+ card.attachments.push(logFileName);
5703
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5704
+ await ctx._storage.writeCard(card);
5705
+ }
5706
+ ctx.emitEvent("log.added", { cardId, entry });
5707
+ return entry;
5708
+ }
5709
+ async function clearLogs(ctx, cardId, boardId) {
5710
+ const card = await ctx.getCard(cardId, boardId);
5711
+ if (!card)
5712
+ throw new Error(`Card not found: ${cardId}`);
5713
+ const logFileName = `${card.id}.log`;
5714
+ const dir = ctx._storage.getCardDir(card);
5715
+ const logPath = path10.join(dir, logFileName);
5716
+ try {
5717
+ await fs7.unlink(logPath);
5718
+ } catch {
5719
+ }
5720
+ if (card.attachments.includes(logFileName)) {
5721
+ card.attachments = card.attachments.filter((a) => a !== logFileName);
5722
+ card.modified = (/* @__PURE__ */ new Date()).toISOString();
5723
+ await ctx._storage.writeCard(card);
5724
+ }
5725
+ ctx.emitEvent("log.cleared", { cardId });
5726
+ }
5727
+ function getBoardLogFilePath(ctx, boardId) {
5728
+ return path10.join(ctx._boardDir(boardId), "board.log");
5729
+ }
5730
+ async function listBoardLogs(ctx, boardId) {
5731
+ const logPath = getBoardLogFilePath(ctx, boardId);
5732
+ let content;
5733
+ try {
5734
+ content = await fs7.readFile(logPath, "utf-8");
5735
+ } catch {
5736
+ return [];
5737
+ }
5738
+ const entries = [];
5739
+ for (const line of content.split("\n")) {
5740
+ const trimmed = line.trim();
5741
+ if (!trimmed)
5742
+ continue;
5743
+ const entry = parseLogLine(trimmed);
5744
+ if (entry)
5745
+ entries.push(entry);
5746
+ }
5747
+ return entries;
5748
+ }
5749
+ async function addBoardLog(ctx, text, options, boardId) {
5750
+ const entry = {
5751
+ timestamp: options?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
5752
+ source: options?.source ?? "sdk",
5753
+ text,
5754
+ ...options?.object ? { object: options.object } : {}
5755
+ };
5756
+ const logPath = getBoardLogFilePath(ctx, boardId);
5757
+ await fs7.mkdir(path10.dirname(logPath), { recursive: true });
5758
+ const line = serializeLogEntry(entry) + "\n";
5759
+ await fs7.appendFile(logPath, line, "utf-8");
5760
+ ctx.emitEvent("board.log.added", { boardId, entry });
5761
+ return entry;
5762
+ }
5763
+ async function clearBoardLogs(ctx, boardId) {
5764
+ const logPath = getBoardLogFilePath(ctx, boardId);
5765
+ try {
5766
+ await fs7.unlink(logPath);
5767
+ } catch {
5768
+ }
5769
+ ctx.emitEvent("board.log.cleared", { boardId });
5770
+ }
5771
+ var path10, fs7;
5772
+ var init_logs = __esm({
5773
+ "src/sdk/modules/logs.ts"() {
5774
+ "use strict";
5775
+ path10 = __toESM(require("path"));
5776
+ fs7 = __toESM(require("fs/promises"));
5777
+ }
5778
+ });
5779
+
5780
+ // src/sdk/modules/columns.ts
5781
+ function listColumns(ctx, boardId) {
5782
+ const config3 = readConfig(ctx.workspaceRoot);
5783
+ const resolvedId = boardId || config3.defaultBoard;
5784
+ const board = config3.boards[resolvedId];
5785
+ return board?.columns || [];
5786
+ }
5787
+ function addColumn(ctx, column, boardId) {
5788
+ const config3 = readConfig(ctx.workspaceRoot);
5789
+ const resolvedId = boardId || config3.defaultBoard;
5790
+ const board = config3.boards[resolvedId];
5791
+ if (!board)
5792
+ throw new Error(`Board not found: ${resolvedId}`);
5793
+ if (!column.id) {
5794
+ const base = generateSlug(column.name) || "column";
5795
+ let uniqueId = base;
5796
+ let counter = 1;
5797
+ while (board.columns.some((c) => c.id === uniqueId)) {
5798
+ uniqueId = `${base}-${counter++}`;
5799
+ }
5800
+ column = { ...column, id: uniqueId };
5801
+ }
5802
+ if (column.id === DELETED_STATUS_ID)
5803
+ throw new Error(`"${DELETED_STATUS_ID}" is a reserved column ID`);
5804
+ if (board.columns.some((c) => c.id === column.id)) {
5805
+ throw new Error(`Column already exists: ${column.id}`);
5806
+ }
5807
+ board.columns.push(column);
5808
+ writeConfig(ctx.workspaceRoot, config3);
5809
+ ctx.emitEvent("column.created", column);
5810
+ return board.columns;
5811
+ }
5812
+ function updateColumn(ctx, columnId, updates, boardId) {
5813
+ const config3 = readConfig(ctx.workspaceRoot);
5814
+ const resolvedId = boardId || config3.defaultBoard;
5815
+ const board = config3.boards[resolvedId];
5816
+ if (!board)
5817
+ throw new Error(`Board not found: ${resolvedId}`);
5818
+ const col = board.columns.find((c) => c.id === columnId);
5819
+ if (!col)
5820
+ throw new Error(`Column not found: ${columnId}`);
5821
+ if (updates.name !== void 0)
5822
+ col.name = updates.name;
5823
+ if (updates.color !== void 0)
5824
+ col.color = updates.color;
5825
+ writeConfig(ctx.workspaceRoot, config3);
5826
+ ctx.emitEvent("column.updated", col);
5827
+ return board.columns;
5828
+ }
5829
+ async function removeColumn(ctx, columnId, boardId) {
5830
+ const config3 = readConfig(ctx.workspaceRoot);
5831
+ const resolvedId = boardId || config3.defaultBoard;
5832
+ const board = config3.boards[resolvedId];
5833
+ if (!board)
5834
+ throw new Error(`Board not found: ${resolvedId}`);
5835
+ if (columnId === DELETED_STATUS_ID)
5836
+ throw new Error(`Cannot remove the reserved "${DELETED_STATUS_ID}" column`);
5837
+ const idx = board.columns.findIndex((c) => c.id === columnId);
5838
+ if (idx === -1)
5839
+ throw new Error(`Column not found: ${columnId}`);
5840
+ const cards = await ctx.listCards(void 0, resolvedId);
5841
+ const cardsInColumn = cards.filter((c) => c.status === columnId);
5842
+ if (cardsInColumn.length > 0) {
5843
+ throw new Error(`Cannot remove column "${columnId}": ${cardsInColumn.length} card(s) still in this column`);
5844
+ }
5845
+ const removed = board.columns[idx];
5846
+ board.columns.splice(idx, 1);
5847
+ writeConfig(ctx.workspaceRoot, config3);
5848
+ ctx.emitEvent("column.deleted", removed);
5849
+ return board.columns;
5850
+ }
5851
+ async function cleanupColumn(ctx, columnId, boardId) {
5852
+ if (columnId === DELETED_STATUS_ID)
5853
+ return 0;
5854
+ const cards = await ctx.listCards(void 0, boardId);
5855
+ const cardsToMove = cards.filter((c) => c.status === columnId);
5856
+ for (const card of cardsToMove) {
5857
+ await ctx.moveCard(card.id, DELETED_STATUS_ID, 0, boardId);
5858
+ }
5859
+ return cardsToMove.length;
5860
+ }
5861
+ async function purgeDeletedCards(ctx, boardId) {
5862
+ const cards = await ctx.listCards(void 0, boardId);
5863
+ const deleted = cards.filter((c) => c.status === DELETED_STATUS_ID);
5864
+ for (const card of deleted) {
5865
+ await ctx.permanentlyDeleteCard(card.id, boardId);
5866
+ }
5867
+ return deleted.length;
5868
+ }
5869
+ function reorderColumns(ctx, columnIds, boardId) {
5870
+ const config3 = readConfig(ctx.workspaceRoot);
5871
+ const resolvedId = boardId || config3.defaultBoard;
5872
+ const board = config3.boards[resolvedId];
5873
+ if (!board)
5874
+ throw new Error(`Board not found: ${resolvedId}`);
5875
+ const colMap = new Map(board.columns.map((c) => [c.id, c]));
5876
+ for (const id of columnIds) {
5877
+ if (!colMap.has(id))
5878
+ throw new Error(`Column not found: ${id}`);
5879
+ }
5880
+ if (columnIds.length !== board.columns.length) {
5881
+ throw new Error("Must include all column IDs when reordering");
5882
+ }
5883
+ board.columns = columnIds.map((id) => colMap.get(id));
5884
+ writeConfig(ctx.workspaceRoot, config3);
5885
+ return board.columns;
5886
+ }
5887
+ var init_columns = __esm({
5888
+ "src/sdk/modules/columns.ts"() {
5889
+ "use strict";
5890
+ init_config();
5891
+ init_types();
5892
+ }
5893
+ });
5894
+
5895
+ // src/sdk/modules/settings.ts
5896
+ function getSettings(ctx) {
5897
+ return configToSettings(readConfig(ctx.workspaceRoot));
5898
+ }
5899
+ function updateSettings(ctx, settings) {
5900
+ const config3 = readConfig(ctx.workspaceRoot);
5901
+ writeConfig(ctx.workspaceRoot, settingsToConfig(config3, settings));
5902
+ ctx.emitEvent("settings.updated", settings);
5903
+ }
5904
+ function setDefaultBoard(ctx, boardId) {
5905
+ const config3 = readConfig(ctx.workspaceRoot);
5906
+ if (!config3.boards[boardId])
5907
+ throw new Error(`Board not found: ${boardId}`);
5908
+ config3.defaultBoard = boardId;
5909
+ writeConfig(ctx.workspaceRoot, config3);
5910
+ }
5911
+ var init_settings = __esm({
5912
+ "src/sdk/modules/settings.ts"() {
5913
+ "use strict";
5914
+ init_config();
5915
+ }
5916
+ });
5917
+
5918
+ // src/sdk/modules/migration.ts
5919
+ async function migrateToSqlite(ctx, dbPath) {
5920
+ if (ctx._storage.type === "sqlite") {
5921
+ throw new Error("Storage engine is already sqlite");
5922
+ }
5923
+ await ctx._ensureMigrated();
5924
+ const resolvedDbPath = dbPath ?? ".kanban/kanban.db";
5925
+ const absDbPath = path11.resolve(ctx.workspaceRoot, resolvedDbPath);
5926
+ const { SqliteStorageEngine: SqliteStorageEngine2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
5927
+ const sqliteEngine = new SqliteStorageEngine2(ctx._storage.kanbanDir, absDbPath);
5928
+ await sqliteEngine.init();
5929
+ const config3 = readConfig(ctx.workspaceRoot);
5930
+ const boardIds = Object.keys(config3.boards);
5931
+ let count = 0;
5932
+ for (const boardId of boardIds) {
5933
+ const boardDir = path11.join(ctx._storage.kanbanDir, "boards", boardId);
5934
+ const cards = await ctx._storage.scanCards(boardDir, boardId);
5935
+ for (const card of cards) {
5936
+ await sqliteEngine.writeCard({ ...card, filePath: "" });
5937
+ count++;
5938
+ }
5939
+ }
5940
+ sqliteEngine.close();
5941
+ writeConfig(ctx.workspaceRoot, { ...config3, storageEngine: "sqlite", sqlitePath: resolvedDbPath });
5942
+ ctx.emitEvent("storage.migrated", { from: "markdown", to: "sqlite", count });
5943
+ return count;
5944
+ }
5945
+ async function migrateToMarkdown(ctx) {
5946
+ if (ctx._storage.type === "markdown") {
5947
+ throw new Error("Storage engine is already markdown");
5948
+ }
5949
+ await ctx._ensureMigrated();
5950
+ const { MarkdownStorageEngine: MarkdownStorageEngine2 } = await Promise.resolve().then(() => (init_markdown(), markdown_exports));
5951
+ const mdEngine = new MarkdownStorageEngine2(ctx._storage.kanbanDir);
5952
+ await mdEngine.init();
5953
+ const config3 = readConfig(ctx.workspaceRoot);
5954
+ const boardIds = Object.keys(config3.boards);
5955
+ let count = 0;
5956
+ for (const boardId of boardIds) {
5957
+ const boardDir = path11.join(ctx._storage.kanbanDir, "boards", boardId);
5958
+ const cards = await ctx._storage.scanCards(boardDir, boardId);
5959
+ for (const card of cards) {
5960
+ const numericId = Number(card.id) || 0;
5961
+ const title = getTitleFromContent(card.content) || card.id;
5962
+ const filename = generateCardFilename(numericId, title);
5963
+ const filePath = getCardFilePath(boardDir, card.status, filename);
5964
+ await mdEngine.writeCard({ ...card, filePath });
5965
+ count++;
5966
+ }
5967
+ }
5968
+ mdEngine.close();
5969
+ const { storageEngine: _se, sqlitePath: _sp, ...restConfig } = config3;
5970
+ writeConfig(ctx.workspaceRoot, restConfig);
5971
+ ctx.emitEvent("storage.migrated", { from: "sqlite", to: "markdown", count });
5972
+ return count;
5973
+ }
5974
+ var path11;
5975
+ var init_migration2 = __esm({
5976
+ "src/sdk/modules/migration.ts"() {
5977
+ "use strict";
5978
+ path11 = __toESM(require("path"));
5979
+ init_config();
5980
+ init_types();
5981
+ init_fileUtils();
5982
+ }
5983
+ });
5984
+
5985
+ // src/sdk/KanbanSDK.ts
5986
+ var path12, KanbanSDK;
5987
+ var init_KanbanSDK = __esm({
5988
+ "src/sdk/KanbanSDK.ts"() {
5989
+ "use strict";
5990
+ path12 = __toESM(require("path"));
5991
+ init_types();
5992
+ init_config();
4940
5993
  init_storage();
4941
- init_metaUtils();
5994
+ init_webhooks();
5995
+ init_boards();
5996
+ init_cards();
5997
+ init_labels();
5998
+ init_attachments();
5999
+ init_comments();
6000
+ init_logs();
6001
+ init_columns();
6002
+ init_settings();
6003
+ init_migration2();
4942
6004
  KanbanSDK = class {
4943
6005
  /**
4944
6006
  * Creates a new KanbanSDK instance.
@@ -4990,6 +6052,7 @@ var init_KanbanSDK = __esm({
4990
6052
  * Emits an event to the registered handler, if one exists.
4991
6053
  * Called internally after every successful mutating operation.
4992
6054
  */
6055
+ /** @internal */
4993
6056
  emitEvent(event, data) {
4994
6057
  if (this._onEvent) {
4995
6058
  try {
@@ -4997,6 +6060,8 @@ var init_KanbanSDK = __esm({
4997
6060
  } catch (err) {
4998
6061
  console.error(`SDK event handler error for ${event}:`, err);
4999
6062
  }
6063
+ } else {
6064
+ fireWebhooks(this.workspaceRoot, event, data);
5000
6065
  }
5001
6066
  }
5002
6067
  /**
@@ -5013,17 +6078,20 @@ var init_KanbanSDK = __esm({
5013
6078
  * ```
5014
6079
  */
5015
6080
  get workspaceRoot() {
5016
- return path8.dirname(this.kanbanDir);
6081
+ return path12.dirname(this.kanbanDir);
5017
6082
  }
5018
6083
  // --- Board resolution helpers ---
6084
+ /** @internal */
5019
6085
  _resolveBoardId(boardId) {
5020
6086
  const config3 = readConfig(this.workspaceRoot);
5021
6087
  return boardId || config3.defaultBoard;
5022
6088
  }
6089
+ /** @internal */
5023
6090
  _boardDir(boardId) {
5024
6091
  const resolvedId = this._resolveBoardId(boardId);
5025
- return path8.join(this.kanbanDir, "boards", resolvedId);
6092
+ return path12.join(this.kanbanDir, "boards", resolvedId);
5026
6093
  }
6094
+ /** @internal */
5027
6095
  _isCompletedStatus(status, boardId) {
5028
6096
  const config3 = readConfig(this.workspaceRoot);
5029
6097
  const resolvedId = boardId || config3.defaultBoard;
@@ -5032,6 +6100,7 @@ var init_KanbanSDK = __esm({
5032
6100
  return status === "done";
5033
6101
  return board.columns[board.columns.length - 1].id === status;
5034
6102
  }
6103
+ /** @internal */
5035
6104
  async _ensureMigrated() {
5036
6105
  if (this._migrated)
5037
6106
  return;
@@ -5073,14 +6142,7 @@ var init_KanbanSDK = __esm({
5073
6142
  * ```
5074
6143
  */
5075
6144
  listBoards() {
5076
- const config3 = readConfig(this.workspaceRoot);
5077
- return Object.entries(config3.boards).map(([id, board]) => ({
5078
- id,
5079
- name: board.name,
5080
- description: board.description,
5081
- columns: board.columns,
5082
- actions: board.actions
5083
- }));
6145
+ return listBoards(this);
5084
6146
  }
5085
6147
  /**
5086
6148
  * Creates a new board with the given ID and name.
@@ -5108,29 +6170,7 @@ var init_KanbanSDK = __esm({
5108
6170
  * ```
5109
6171
  */
5110
6172
  createBoard(id, name, options) {
5111
- const config3 = readConfig(this.workspaceRoot);
5112
- if (config3.boards[id]) {
5113
- throw new Error(`Board already exists: ${id}`);
5114
- }
5115
- const columns = options?.columns || [...config3.boards[config3.defaultBoard]?.columns || [
5116
- { id: "backlog", name: "Backlog", color: "#6b7280" },
5117
- { id: "todo", name: "To Do", color: "#3b82f6" },
5118
- { id: "in-progress", name: "In Progress", color: "#f59e0b" },
5119
- { id: "review", name: "Review", color: "#8b5cf6" },
5120
- { id: "done", name: "Done", color: "#22c55e" }
5121
- ]];
5122
- config3.boards[id] = {
5123
- name,
5124
- description: options?.description,
5125
- columns,
5126
- nextCardId: 1,
5127
- defaultStatus: options?.defaultStatus || columns[0]?.id || "backlog",
5128
- defaultPriority: options?.defaultPriority || config3.defaultPriority
5129
- };
5130
- writeConfig(this.workspaceRoot, config3);
5131
- const boardInfo = { id, name, description: options?.description };
5132
- this.emitEvent("board.created", boardInfo);
5133
- return boardInfo;
6173
+ return createBoard(this, id, name, options);
5134
6174
  }
5135
6175
  /**
5136
6176
  * Deletes a board and its directory from the filesystem.
@@ -5151,22 +6191,7 @@ var init_KanbanSDK = __esm({
5151
6191
  * ```
5152
6192
  */
5153
6193
  async deleteBoard(boardId) {
5154
- const config3 = readConfig(this.workspaceRoot);
5155
- if (!config3.boards[boardId]) {
5156
- throw new Error(`Board not found: ${boardId}`);
5157
- }
5158
- if (config3.defaultBoard === boardId) {
5159
- throw new Error(`Cannot delete the default board: ${boardId}`);
5160
- }
5161
- const cards = await this.listCards(void 0, boardId);
5162
- if (cards.length > 0) {
5163
- throw new Error(`Cannot delete board "${boardId}": ${cards.length} card(s) still exist`);
5164
- }
5165
- const boardDir = this._boardDir(boardId);
5166
- await this._storage.deleteBoardData(boardDir, boardId);
5167
- delete config3.boards[boardId];
5168
- writeConfig(this.workspaceRoot, config3);
5169
- this.emitEvent("board.deleted", { id: boardId });
6194
+ return deleteBoard(this, boardId);
5170
6195
  }
5171
6196
  /**
5172
6197
  * Retrieves the full configuration for a specific board.
@@ -5182,7 +6207,7 @@ var init_KanbanSDK = __esm({
5182
6207
  * ```
5183
6208
  */
5184
6209
  getBoard(boardId) {
5185
- return getBoardConfig(this.workspaceRoot, boardId);
6210
+ return getBoard(this, boardId);
5186
6211
  }
5187
6212
  /**
5188
6213
  * Updates properties of an existing board.
@@ -5209,24 +6234,7 @@ var init_KanbanSDK = __esm({
5209
6234
  * ```
5210
6235
  */
5211
6236
  updateBoard(boardId, updates) {
5212
- const config3 = readConfig(this.workspaceRoot);
5213
- const board = config3.boards[boardId];
5214
- if (!board) {
5215
- throw new Error(`Board not found: ${boardId}`);
5216
- }
5217
- if (updates.name !== void 0)
5218
- board.name = updates.name;
5219
- if (updates.description !== void 0)
5220
- board.description = updates.description;
5221
- if (updates.columns !== void 0)
5222
- board.columns = updates.columns;
5223
- if (updates.defaultStatus !== void 0)
5224
- board.defaultStatus = updates.defaultStatus;
5225
- if (updates.defaultPriority !== void 0)
5226
- board.defaultPriority = updates.defaultPriority;
5227
- writeConfig(this.workspaceRoot, config3);
5228
- this.emitEvent("board.updated", { id: boardId, ...board });
5229
- return board;
6237
+ return updateBoard(this, boardId, updates);
5230
6238
  }
5231
6239
  /**
5232
6240
  * Returns the named actions defined on a board.
@@ -5236,12 +6244,7 @@ var init_KanbanSDK = __esm({
5236
6244
  * @throws {Error} If the board does not exist.
5237
6245
  */
5238
6246
  getBoardActions(boardId) {
5239
- const config3 = readConfig(this.workspaceRoot);
5240
- const resolvedId = boardId || config3.defaultBoard;
5241
- const board = config3.boards[resolvedId];
5242
- if (!board)
5243
- throw new Error(`Board not found: ${resolvedId}`);
5244
- return board.actions ?? {};
6247
+ return getBoardActions(this, boardId);
5245
6248
  }
5246
6249
  /**
5247
6250
  * Adds or updates a named action on a board.
@@ -5253,15 +6256,7 @@ var init_KanbanSDK = __esm({
5253
6256
  * @throws {Error} If the board does not exist.
5254
6257
  */
5255
6258
  addBoardAction(boardId, key, title) {
5256
- const config3 = readConfig(this.workspaceRoot);
5257
- const board = config3.boards[boardId];
5258
- if (!board)
5259
- throw new Error(`Board not found: ${boardId}`);
5260
- board.actions ??= {};
5261
- board.actions[key] = title;
5262
- writeConfig(this.workspaceRoot, config3);
5263
- this.emitEvent("board.updated", { id: boardId, ...board });
5264
- return board.actions;
6259
+ return addBoardAction(this, boardId, key, title);
5265
6260
  }
5266
6261
  /**
5267
6262
  * Removes a named action from a board.
@@ -5273,19 +6268,7 @@ var init_KanbanSDK = __esm({
5273
6268
  * @throws {Error} If the action key is not found on the board.
5274
6269
  */
5275
6270
  removeBoardAction(boardId, key) {
5276
- const config3 = readConfig(this.workspaceRoot);
5277
- const board = config3.boards[boardId];
5278
- if (!board)
5279
- throw new Error(`Board not found: ${boardId}`);
5280
- if (!board.actions || !(key in board.actions)) {
5281
- throw new Error(`Action "${key}" not found on board "${boardId}"`);
5282
- }
5283
- delete board.actions[key];
5284
- if (Object.keys(board.actions).length === 0)
5285
- delete board.actions;
5286
- writeConfig(this.workspaceRoot, config3);
5287
- this.emitEvent("board.updated", { id: boardId, ...board });
5288
- return board.actions ?? {};
6271
+ return removeBoardAction(this, boardId, key);
5289
6272
  }
5290
6273
  /**
5291
6274
  * Fires the `board.action` webhook event for a named board action.
@@ -5296,17 +6279,7 @@ var init_KanbanSDK = __esm({
5296
6279
  * @throws {Error} If the action key is not defined on the board.
5297
6280
  */
5298
6281
  async triggerBoardAction(boardId, actionKey) {
5299
- const config3 = readConfig(this.workspaceRoot);
5300
- const resolvedId = boardId || config3.defaultBoard;
5301
- const board = config3.boards[resolvedId];
5302
- if (!board)
5303
- throw new Error(`Board not found: ${resolvedId}`);
5304
- const actions = board.actions ?? {};
5305
- if (!(actionKey in actions)) {
5306
- throw new Error(`Action "${actionKey}" not defined on board "${resolvedId}"`);
5307
- }
5308
- const actionTitle = actions[actionKey];
5309
- this.emitEvent("board.action", { boardId: resolvedId, action: actionKey, title: actionTitle });
6282
+ return triggerBoardAction(this, boardId, actionKey);
5310
6283
  }
5311
6284
  /**
5312
6285
  * Transfers a card from one board to another.
@@ -5334,55 +6307,7 @@ var init_KanbanSDK = __esm({
5334
6307
  * ```
5335
6308
  */
5336
6309
  async transferCard(cardId, fromBoardId, toBoardId, targetStatus) {
5337
- const toBoardDir = this._boardDir(toBoardId);
5338
- const config3 = readConfig(this.workspaceRoot);
5339
- if (!config3.boards[fromBoardId])
5340
- throw new Error(`Board not found: ${fromBoardId}`);
5341
- if (!config3.boards[toBoardId])
5342
- throw new Error(`Board not found: ${toBoardId}`);
5343
- const card = await this.getCard(cardId, fromBoardId);
5344
- if (!card)
5345
- throw new Error(`Card not found: ${cardId} in board ${fromBoardId}`);
5346
- const previousStatus = card.status;
5347
- const toBoard = config3.boards[toBoardId];
5348
- const newStatus = targetStatus || toBoard.defaultStatus || toBoard.columns[0]?.id || "backlog";
5349
- await this._storage.ensureBoardDirs(toBoardDir, [newStatus]);
5350
- const srcAttachDir = this._storage.getCardDir(card);
5351
- await this._storage.deleteCard(card);
5352
- card.status = newStatus;
5353
- card.boardId = toBoardId;
5354
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
5355
- card.completedAt = this._isCompletedStatus(newStatus, toBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
5356
- if (this._storage.type === "markdown") {
5357
- card.filePath = path8.join(toBoardDir, newStatus, path8.basename(card.filePath));
5358
- } else {
5359
- card.filePath = "";
5360
- }
5361
- const targetCards = await this.listCards(void 0, toBoardId);
5362
- 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);
5363
- const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
5364
- card.order = generateKeyBetween(lastOrder, null);
5365
- await this._storage.writeCard(card);
5366
- if (card.attachments.length > 0) {
5367
- const dstAttachDir = this._storage.getCardDir(card);
5368
- if (srcAttachDir !== dstAttachDir) {
5369
- await fs6.mkdir(dstAttachDir, { recursive: true });
5370
- await Promise.all(
5371
- card.attachments.map(async (filename) => {
5372
- const src = path8.join(srcAttachDir, filename);
5373
- const dst = path8.join(dstAttachDir, filename);
5374
- await fs6.rename(src, dst).catch(() => {
5375
- });
5376
- })
5377
- );
5378
- }
5379
- }
5380
- this.emitEvent("task.moved", { ...sanitizeCard(card), previousStatus, fromBoard: fromBoardId, toBoard: toBoardId });
5381
- if (previousStatus !== newStatus) {
5382
- await this.addLog(card.id, `Status changed: \`${previousStatus}\` \u2192 \`${newStatus}\``, { source: "system" }, toBoardId).catch(() => {
5383
- });
5384
- }
5385
- return card;
6310
+ return transferCard(this, cardId, fromBoardId, toBoardId, targetStatus);
5386
6311
  }
5387
6312
  // --- Card CRUD ---
5388
6313
  /**
@@ -5426,42 +6351,7 @@ var init_KanbanSDK = __esm({
5426
6351
  * ```
5427
6352
  */
5428
6353
  async listCards(columns, boardId, metaFilter, sort) {
5429
- await this._ensureMigrated();
5430
- const boardDir = this._boardDir(boardId);
5431
- const resolvedBoardId = this._resolveBoardId(boardId);
5432
- await this._storage.ensureBoardDirs(boardDir, columns);
5433
- const cards = await this._storage.scanCards(boardDir, resolvedBoardId);
5434
- const hasLegacyOrder = cards.some((c) => /^\d+$/.test(c.order));
5435
- if (hasLegacyOrder) {
5436
- const byStatus = /* @__PURE__ */ new Map();
5437
- for (const c of cards) {
5438
- const list = byStatus.get(c.status) || [];
5439
- list.push(c);
5440
- byStatus.set(c.status, list);
5441
- }
5442
- for (const columnCards of byStatus.values()) {
5443
- columnCards.sort((a, b) => parseInt(a.order) - parseInt(b.order));
5444
- const keys = generateNKeysBetween(null, null, columnCards.length);
5445
- for (let i = 0; i < columnCards.length; i++) {
5446
- columnCards[i].order = keys[i];
5447
- await this._storage.writeCard(columnCards[i]);
5448
- }
5449
- }
5450
- }
5451
- const numericIds = cards.map((c) => parseInt(c.id, 10)).filter((n) => !Number.isNaN(n));
5452
- if (numericIds.length > 0) {
5453
- syncCardIdCounter(this.workspaceRoot, resolvedBoardId, numericIds);
5454
- }
5455
- const filtered = metaFilter && Object.keys(metaFilter).length > 0 ? cards.filter((c) => matchesMetaFilter(c.metadata, metaFilter)) : cards;
5456
- if (sort) {
5457
- const [field, dir] = sort.split(":");
5458
- return filtered.sort((a, b) => {
5459
- const aVal = field === "created" ? a.created : a.modified;
5460
- const bVal = field === "created" ? b.created : b.modified;
5461
- return dir === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
5462
- });
5463
- }
5464
- return filtered.sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
6354
+ return listCards(this, columns, boardId, metaFilter, sort);
5465
6355
  }
5466
6356
  /**
5467
6357
  * Retrieves a single card by its ID.
@@ -5482,8 +6372,7 @@ var init_KanbanSDK = __esm({
5482
6372
  * ```
5483
6373
  */
5484
6374
  async getCard(cardId, boardId) {
5485
- const cards = await this.listCards(void 0, boardId);
5486
- return cards.find((c) => c.id === cardId) || null;
6375
+ return getCard(this, cardId, boardId);
5487
6376
  }
5488
6377
  /**
5489
6378
  * Creates a new card on a board.
@@ -5518,44 +6407,7 @@ var init_KanbanSDK = __esm({
5518
6407
  * ```
5519
6408
  */
5520
6409
  async createCard(data) {
5521
- await this._ensureMigrated();
5522
- const resolvedBoardId = this._resolveBoardId(data.boardId);
5523
- const boardDir = this._boardDir(resolvedBoardId);
5524
- await this._storage.ensureBoardDirs(boardDir);
5525
- const config3 = readConfig(this.workspaceRoot);
5526
- const board = config3.boards[resolvedBoardId];
5527
- const status = data.status || board?.defaultStatus || config3.defaultStatus || "backlog";
5528
- const priority = data.priority || board?.defaultPriority || config3.defaultPriority || "medium";
5529
- const title = getTitleFromContent(data.content);
5530
- const numericId = allocateCardId(this.workspaceRoot, resolvedBoardId);
5531
- const filename = generateCardFilename(numericId, title);
5532
- const now = (/* @__PURE__ */ new Date()).toISOString();
5533
- const cards = await this.listCards(void 0, resolvedBoardId);
5534
- const cardsInStatus = cards.filter((c) => c.status === status).sort((a, b) => a.order < b.order ? -1 : a.order > b.order ? 1 : 0);
5535
- const lastOrder = cardsInStatus.length > 0 ? cardsInStatus[cardsInStatus.length - 1].order : null;
5536
- const card = {
5537
- version: CARD_FORMAT_VERSION,
5538
- id: String(numericId),
5539
- boardId: resolvedBoardId,
5540
- status,
5541
- priority,
5542
- assignee: data.assignee ?? null,
5543
- dueDate: data.dueDate ?? null,
5544
- created: now,
5545
- modified: now,
5546
- completedAt: this._isCompletedStatus(status, resolvedBoardId) ? now : null,
5547
- labels: data.labels || [],
5548
- attachments: data.attachments || [],
5549
- comments: [],
5550
- order: generateKeyBetween(lastOrder, null),
5551
- content: data.content,
5552
- ...data.metadata && Object.keys(data.metadata).length > 0 ? { metadata: data.metadata } : {},
5553
- ...data.actions && (Array.isArray(data.actions) ? data.actions.length > 0 : Object.keys(data.actions).length > 0) ? { actions: data.actions } : {},
5554
- filePath: this._storage.type === "markdown" ? getCardFilePath(boardDir, status, filename) : ""
5555
- };
5556
- await this._storage.writeCard(card);
5557
- this.emitEvent("task.created", sanitizeCard(card));
5558
- return card;
6410
+ return createCard(this, data);
5559
6411
  }
5560
6412
  /**
5561
6413
  * Updates an existing card's properties.
@@ -5582,39 +6434,7 @@ var init_KanbanSDK = __esm({
5582
6434
  * ```
5583
6435
  */
5584
6436
  async updateCard(cardId, updates, boardId) {
5585
- const card = await this.getCard(cardId, boardId);
5586
- if (!card)
5587
- throw new Error(`Card not found: ${cardId}`);
5588
- const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
5589
- const boardDir = this._boardDir(resolvedBoardId);
5590
- const oldStatus = card.status;
5591
- const oldTitle = getTitleFromContent(card.content);
5592
- const { filePath: _fp, id: _id, boardId: _bid, ...safeUpdates } = updates;
5593
- Object.assign(card, safeUpdates);
5594
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
5595
- if (oldStatus !== card.status) {
5596
- card.completedAt = this._isCompletedStatus(card.status, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
5597
- }
5598
- await this._storage.writeCard(card);
5599
- const newTitle = getTitleFromContent(card.content);
5600
- const numericId = extractNumericId(card.id);
5601
- if (numericId !== null && newTitle !== oldTitle) {
5602
- const newFilename = generateCardFilename(numericId, newTitle);
5603
- const newPath = await this._storage.renameCard(card, newFilename);
5604
- if (newPath)
5605
- card.filePath = newPath;
5606
- }
5607
- if (oldStatus !== card.status) {
5608
- const newPath = await this._storage.moveCard(card, boardDir, card.status);
5609
- if (newPath)
5610
- card.filePath = newPath;
5611
- }
5612
- this.emitEvent("task.updated", sanitizeCard(card));
5613
- if (oldStatus !== card.status) {
5614
- await this.addLog(card.id, `Status changed: \`${oldStatus}\` \u2192 \`${card.status}\``, { source: "system" }, resolvedBoardId).catch(() => {
5615
- });
5616
- }
5617
- return card;
6437
+ return updateCard(this, cardId, updates, boardId);
5618
6438
  }
5619
6439
  /**
5620
6440
  * Triggers a named action for a card by POSTing to the global `actionWebhookUrl`
@@ -5640,31 +6460,7 @@ var init_KanbanSDK = __esm({
5640
6460
  * ```
5641
6461
  */
5642
6462
  async triggerAction(cardId, action, boardId) {
5643
- const config3 = readConfig(this.workspaceRoot);
5644
- const { actionWebhookUrl } = config3;
5645
- if (!actionWebhookUrl) {
5646
- throw new Error("No action webhook URL configured. Set actionWebhookUrl in .kanban.json");
5647
- }
5648
- const card = await this.getCard(cardId, boardId);
5649
- if (!card)
5650
- throw new Error(`Card not found: ${cardId}`);
5651
- const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
5652
- const payload = {
5653
- action,
5654
- board: resolvedBoardId,
5655
- list: card.status,
5656
- card: sanitizeCard(card)
5657
- };
5658
- const response = await fetch(actionWebhookUrl, {
5659
- method: "POST",
5660
- headers: { "Content-Type": "application/json" },
5661
- body: JSON.stringify(payload)
5662
- });
5663
- if (!response.ok) {
5664
- throw new Error(`Action webhook responded with ${response.status}: ${response.statusText}`);
5665
- }
5666
- await this.addLog(cardId, `Action triggered: \`${action}\``, { source: "system" }, resolvedBoardId).catch(() => {
5667
- });
6463
+ return triggerAction(this, cardId, action, boardId);
5668
6464
  }
5669
6465
  /**
5670
6466
  * Moves a card to a different status column and/or position within that column.
@@ -5691,35 +6487,7 @@ var init_KanbanSDK = __esm({
5691
6487
  * ```
5692
6488
  */
5693
6489
  async moveCard(cardId, newStatus, position, boardId) {
5694
- const cards = await this.listCards(void 0, boardId);
5695
- const card = cards.find((c) => c.id === cardId);
5696
- if (!card)
5697
- throw new Error(`Card not found: ${cardId}`);
5698
- const resolvedBoardId = card.boardId || this._resolveBoardId(boardId);
5699
- const boardDir = this._boardDir(resolvedBoardId);
5700
- const oldStatus = card.status;
5701
- card.status = newStatus;
5702
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
5703
- if (oldStatus !== newStatus) {
5704
- card.completedAt = this._isCompletedStatus(newStatus, resolvedBoardId) ? (/* @__PURE__ */ new Date()).toISOString() : null;
5705
- }
5706
- 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);
5707
- const pos = position !== void 0 ? Math.max(0, Math.min(position, targetColumnCards.length)) : targetColumnCards.length;
5708
- const before = pos > 0 ? targetColumnCards[pos - 1].order : null;
5709
- const after = pos < targetColumnCards.length ? targetColumnCards[pos].order : null;
5710
- card.order = generateKeyBetween(before, after);
5711
- await this._storage.writeCard(card);
5712
- if (oldStatus !== newStatus) {
5713
- const newPath = await this._storage.moveCard(card, boardDir, newStatus);
5714
- if (newPath)
5715
- card.filePath = newPath;
5716
- }
5717
- this.emitEvent("task.moved", { ...sanitizeCard(card), previousStatus: oldStatus });
5718
- if (oldStatus !== newStatus) {
5719
- await this.addLog(card.id, `Status changed: \`${oldStatus}\` \u2192 \`${newStatus}\``, { source: "system" }, resolvedBoardId).catch(() => {
5720
- });
5721
- }
5722
- return card;
6490
+ return moveCard(this, cardId, newStatus, position, boardId);
5723
6491
  }
5724
6492
  /**
5725
6493
  * Soft-deletes a card by moving it to the `deleted` status column.
@@ -5736,12 +6504,7 @@ var init_KanbanSDK = __esm({
5736
6504
  * ```
5737
6505
  */
5738
6506
  async deleteCard(cardId, boardId) {
5739
- const card = await this.getCard(cardId, boardId);
5740
- if (!card)
5741
- throw new Error(`Card not found: ${cardId}`);
5742
- if (card.status === DELETED_STATUS_ID)
5743
- return;
5744
- await this.updateCard(cardId, { status: DELETED_STATUS_ID }, boardId);
6507
+ return deleteCard(this, cardId, boardId);
5745
6508
  }
5746
6509
  /**
5747
6510
  * Permanently deletes a card's markdown file from disk.
@@ -5758,12 +6521,7 @@ var init_KanbanSDK = __esm({
5758
6521
  * ```
5759
6522
  */
5760
6523
  async permanentlyDeleteCard(cardId, boardId) {
5761
- const card = await this.getCard(cardId, boardId);
5762
- if (!card)
5763
- throw new Error(`Card not found: ${cardId}`);
5764
- const snapshot = sanitizeCard(card);
5765
- await this._storage.deleteCard(card);
5766
- this.emitEvent("task.deleted", snapshot);
6524
+ return permanentlyDeleteCard(this, cardId, boardId);
5767
6525
  }
5768
6526
  /**
5769
6527
  * Returns all cards in a specific status column.
@@ -5782,8 +6540,7 @@ var init_KanbanSDK = __esm({
5782
6540
  * ```
5783
6541
  */
5784
6542
  async getCardsByStatus(status, boardId) {
5785
- const cards = await this.listCards(void 0, boardId);
5786
- return cards.filter((c) => c.status === status);
6543
+ return getCardsByStatus(this, status, boardId);
5787
6544
  }
5788
6545
  /**
5789
6546
  * Returns a sorted list of unique assignee names across all cards on a board.
@@ -5800,13 +6557,7 @@ var init_KanbanSDK = __esm({
5800
6557
  * ```
5801
6558
  */
5802
6559
  async getUniqueAssignees(boardId) {
5803
- const cards = await this.listCards(void 0, boardId);
5804
- const assignees = /* @__PURE__ */ new Set();
5805
- for (const c of cards) {
5806
- if (c.assignee)
5807
- assignees.add(c.assignee);
5808
- }
5809
- return [...assignees].sort();
6560
+ return getUniqueAssignees(this, boardId);
5810
6561
  }
5811
6562
  /**
5812
6563
  * Returns a sorted list of unique labels across all cards on a board.
@@ -5821,13 +6572,7 @@ var init_KanbanSDK = __esm({
5821
6572
  * ```
5822
6573
  */
5823
6574
  async getUniqueLabels(boardId) {
5824
- const cards = await this.listCards(void 0, boardId);
5825
- const labels = /* @__PURE__ */ new Set();
5826
- for (const c of cards) {
5827
- for (const l of c.labels)
5828
- labels.add(l);
5829
- }
5830
- return [...labels].sort();
6575
+ return getUniqueLabels(this, boardId);
5831
6576
  }
5832
6577
  // --- Label definition management ---
5833
6578
  /**
@@ -5845,8 +6590,7 @@ var init_KanbanSDK = __esm({
5845
6590
  * ```
5846
6591
  */
5847
6592
  getLabels() {
5848
- const config3 = readConfig(this.workspaceRoot);
5849
- return config3.labels || {};
6593
+ return getLabels(this);
5850
6594
  }
5851
6595
  /**
5852
6596
  * Creates or updates a label definition in the workspace configuration.
@@ -5864,11 +6608,7 @@ var init_KanbanSDK = __esm({
5864
6608
  * ```
5865
6609
  */
5866
6610
  setLabel(name, definition) {
5867
- const config3 = readConfig(this.workspaceRoot);
5868
- if (!config3.labels)
5869
- config3.labels = {};
5870
- config3.labels[name] = definition;
5871
- writeConfig(this.workspaceRoot, config3);
6611
+ setLabel(this, name, definition);
5872
6612
  }
5873
6613
  /**
5874
6614
  * Removes a label definition from the workspace configuration and cascades
@@ -5882,17 +6622,7 @@ var init_KanbanSDK = __esm({
5882
6622
  * ```
5883
6623
  */
5884
6624
  async deleteLabel(name) {
5885
- const config3 = readConfig(this.workspaceRoot);
5886
- if (config3.labels) {
5887
- delete config3.labels[name];
5888
- writeConfig(this.workspaceRoot, config3);
5889
- }
5890
- const cards = await this.listCards();
5891
- for (const card of cards) {
5892
- if (card.labels.includes(name)) {
5893
- await this.updateCard(card.id, { labels: card.labels.filter((l) => l !== name) });
5894
- }
5895
- }
6625
+ return deleteLabel(this, name);
5896
6626
  }
5897
6627
  /**
5898
6628
  * Renames a label in the configuration and cascades the change to all cards.
@@ -5911,19 +6641,7 @@ var init_KanbanSDK = __esm({
5911
6641
  * ```
5912
6642
  */
5913
6643
  async renameLabel(oldName, newName) {
5914
- const config3 = readConfig(this.workspaceRoot);
5915
- if (config3.labels && config3.labels[oldName]) {
5916
- config3.labels[newName] = config3.labels[oldName];
5917
- delete config3.labels[oldName];
5918
- writeConfig(this.workspaceRoot, config3);
5919
- }
5920
- const cards = await this.listCards();
5921
- for (const card of cards) {
5922
- if (card.labels.includes(oldName)) {
5923
- const newLabels = card.labels.map((l) => l === oldName ? newName : l);
5924
- await this.updateCard(card.id, { labels: newLabels });
5925
- }
5926
- }
6644
+ return renameLabel(this, oldName, newName);
5927
6645
  }
5928
6646
  /**
5929
6647
  * Returns a sorted list of label names that belong to the given group.
@@ -5944,8 +6662,7 @@ var init_KanbanSDK = __esm({
5944
6662
  * ```
5945
6663
  */
5946
6664
  getLabelsInGroup(group) {
5947
- const labels = this.getLabels();
5948
- return Object.entries(labels).filter(([, def]) => def.group === group).map(([name]) => name).sort();
6665
+ return getLabelsInGroup(this, group);
5949
6666
  }
5950
6667
  /**
5951
6668
  * Returns all cards that have at least one label belonging to the given group.
@@ -5964,11 +6681,7 @@ var init_KanbanSDK = __esm({
5964
6681
  * ```
5965
6682
  */
5966
6683
  async filterCardsByLabelGroup(group, boardId) {
5967
- const groupLabels = this.getLabelsInGroup(group);
5968
- if (groupLabels.length === 0)
5969
- return [];
5970
- const cards = await this.listCards(void 0, boardId);
5971
- return cards.filter((c) => c.labels.some((l) => groupLabels.includes(l)));
6684
+ return filterCardsByLabelGroup(this, group, boardId);
5972
6685
  }
5973
6686
  // --- Attachment management ---
5974
6687
  /**
@@ -5991,18 +6704,7 @@ var init_KanbanSDK = __esm({
5991
6704
  * ```
5992
6705
  */
5993
6706
  async addAttachment(cardId, sourcePath, boardId) {
5994
- const card = await this.getCard(cardId, boardId);
5995
- if (!card)
5996
- throw new Error(`Card not found: ${cardId}`);
5997
- const fileName = path8.basename(sourcePath);
5998
- await this._storage.copyAttachment(sourcePath, card);
5999
- if (!card.attachments.includes(fileName)) {
6000
- card.attachments.push(fileName);
6001
- }
6002
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
6003
- await this._storage.writeCard(card);
6004
- this.emitEvent("attachment.added", { cardId, attachment: fileName });
6005
- return card;
6707
+ return addAttachment(this, cardId, sourcePath, boardId);
6006
6708
  }
6007
6709
  /**
6008
6710
  * Removes an attachment reference from a card's metadata.
@@ -6022,14 +6724,7 @@ var init_KanbanSDK = __esm({
6022
6724
  * ```
6023
6725
  */
6024
6726
  async removeAttachment(cardId, attachment, boardId) {
6025
- const card = await this.getCard(cardId, boardId);
6026
- if (!card)
6027
- throw new Error(`Card not found: ${cardId}`);
6028
- card.attachments = card.attachments.filter((a) => a !== attachment);
6029
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
6030
- await this._storage.writeCard(card);
6031
- this.emitEvent("attachment.removed", { cardId, attachment });
6032
- return card;
6727
+ return removeAttachment(this, cardId, attachment, boardId);
6033
6728
  }
6034
6729
  /**
6035
6730
  * Lists all attachment filenames for a card.
@@ -6046,10 +6741,7 @@ var init_KanbanSDK = __esm({
6046
6741
  * ```
6047
6742
  */
6048
6743
  async listAttachments(cardId, boardId) {
6049
- const card = await this.getCard(cardId, boardId);
6050
- if (!card)
6051
- throw new Error(`Card not found: ${cardId}`);
6052
- return card.attachments;
6744
+ return listAttachments(this, cardId, boardId);
6053
6745
  }
6054
6746
  /**
6055
6747
  * Returns the absolute path to the attachment directory for a card.
@@ -6068,10 +6760,7 @@ var init_KanbanSDK = __esm({
6068
6760
  * ```
6069
6761
  */
6070
6762
  async getAttachmentDir(cardId, boardId) {
6071
- const card = await this.getCard(cardId, boardId);
6072
- if (!card)
6073
- return null;
6074
- return this._storage.getCardDir(card);
6763
+ return getAttachmentDir(this, cardId, boardId);
6075
6764
  }
6076
6765
  // --- Comment management ---
6077
6766
  /**
@@ -6091,10 +6780,7 @@ var init_KanbanSDK = __esm({
6091
6780
  * ```
6092
6781
  */
6093
6782
  async listComments(cardId, boardId) {
6094
- const card = await this.getCard(cardId, boardId);
6095
- if (!card)
6096
- throw new Error(`Card not found: ${cardId}`);
6097
- return card.comments || [];
6783
+ return listComments(this, cardId, boardId);
6098
6784
  }
6099
6785
  /**
6100
6786
  * Adds a comment to a card.
@@ -6116,28 +6802,7 @@ var init_KanbanSDK = __esm({
6116
6802
  * ```
6117
6803
  */
6118
6804
  async addComment(cardId, author, content, boardId) {
6119
- if (!content?.trim())
6120
- throw new Error("Comment content cannot be empty");
6121
- const card = await this.getCard(cardId, boardId);
6122
- if (!card)
6123
- throw new Error(`Card not found: ${cardId}`);
6124
- if (!card.comments)
6125
- card.comments = [];
6126
- const maxId = card.comments.reduce((max, c) => {
6127
- const num = parseInt(c.id.replace("c", ""), 10);
6128
- return Number.isNaN(num) ? max : Math.max(max, num);
6129
- }, 0);
6130
- const comment = {
6131
- id: `c${maxId + 1}`,
6132
- author,
6133
- created: (/* @__PURE__ */ new Date()).toISOString(),
6134
- content
6135
- };
6136
- card.comments.push(comment);
6137
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
6138
- await this._storage.writeCard(card);
6139
- this.emitEvent("comment.created", { ...comment, cardId });
6140
- return card;
6805
+ return addComment(this, cardId, author, content, boardId);
6141
6806
  }
6142
6807
  /**
6143
6808
  * Updates the content of an existing comment on a card.
@@ -6156,17 +6821,7 @@ var init_KanbanSDK = __esm({
6156
6821
  * ```
6157
6822
  */
6158
6823
  async updateComment(cardId, commentId, content, boardId) {
6159
- const card = await this.getCard(cardId, boardId);
6160
- if (!card)
6161
- throw new Error(`Card not found: ${cardId}`);
6162
- const comment = (card.comments || []).find((c) => c.id === commentId);
6163
- if (!comment)
6164
- throw new Error(`Comment not found: ${commentId}`);
6165
- comment.content = content;
6166
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
6167
- await this._storage.writeCard(card);
6168
- this.emitEvent("comment.updated", { ...comment, cardId });
6169
- return card;
6824
+ return updateComment(this, cardId, commentId, content, boardId);
6170
6825
  }
6171
6826
  /**
6172
6827
  * Deletes a comment from a card.
@@ -6183,17 +6838,7 @@ var init_KanbanSDK = __esm({
6183
6838
  * ```
6184
6839
  */
6185
6840
  async deleteComment(cardId, commentId, boardId) {
6186
- const card = await this.getCard(cardId, boardId);
6187
- if (!card)
6188
- throw new Error(`Card not found: ${cardId}`);
6189
- const comment = (card.comments || []).find((c) => c.id === commentId);
6190
- card.comments = (card.comments || []).filter((c) => c.id !== commentId);
6191
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
6192
- await this._storage.writeCard(card);
6193
- if (comment) {
6194
- this.emitEvent("comment.deleted", { ...comment, cardId });
6195
- }
6196
- return card;
6841
+ return deleteComment(this, cardId, commentId, boardId);
6197
6842
  }
6198
6843
  // --- Log management ---
6199
6844
  /**
@@ -6207,44 +6852,7 @@ var init_KanbanSDK = __esm({
6207
6852
  * @returns A promise resolving to the log file path, or `null` if the card is not found.
6208
6853
  */
6209
6854
  async getLogFilePath(cardId, boardId) {
6210
- const card = await this.getCard(cardId, boardId);
6211
- if (!card)
6212
- return null;
6213
- const dir = this._storage.getCardDir(card);
6214
- return path8.join(dir, `${card.id}.log`);
6215
- }
6216
- /**
6217
- * Parses a single log line into a {@link LogEntry}.
6218
- *
6219
- * Expected format: `timestamp [source] text {json}`
6220
- */
6221
- _parseLogLine(line) {
6222
- const trimmed = line.trim();
6223
- if (!trimmed)
6224
- return null;
6225
- const match = trimmed.match(/^(\S+)\s+\[([^\]]+)\]\s+(.+)$/);
6226
- if (!match)
6227
- return null;
6228
- const [, timestamp2, source, rest] = match;
6229
- const jsonMatch = rest.match(/^(.*?)\s+(\{.+\})\s*$/);
6230
- if (jsonMatch) {
6231
- try {
6232
- const obj = JSON.parse(jsonMatch[2]);
6233
- return { timestamp: timestamp2, source, text: jsonMatch[1], object: obj };
6234
- } catch {
6235
- }
6236
- }
6237
- return { timestamp: timestamp2, source, text: rest };
6238
- }
6239
- /**
6240
- * Serializes a {@link LogEntry} into a single log line.
6241
- */
6242
- _serializeLogEntry(entry) {
6243
- let line = `${entry.timestamp} [${entry.source}] ${entry.text}`;
6244
- if (entry.object && Object.keys(entry.object).length > 0) {
6245
- line += ` ${JSON.stringify(entry.object)}`;
6246
- }
6247
- return line;
6855
+ return getLogFilePath(this, cardId, boardId);
6248
6856
  }
6249
6857
  /**
6250
6858
  * Lists all log entries for a card.
@@ -6266,21 +6874,7 @@ var init_KanbanSDK = __esm({
6266
6874
  * ```
6267
6875
  */
6268
6876
  async listLogs(cardId, boardId) {
6269
- const logPath = await this.getLogFilePath(cardId, boardId);
6270
- if (!logPath)
6271
- throw new Error(`Card not found: ${cardId}`);
6272
- try {
6273
- const content = await fs6.readFile(logPath, "utf-8");
6274
- const entries = [];
6275
- for (const line of content.split("\n")) {
6276
- const entry = this._parseLogLine(line);
6277
- if (entry)
6278
- entries.push(entry);
6279
- }
6280
- return entries;
6281
- } catch {
6282
- return [];
6283
- }
6877
+ return listLogs(this, cardId, boardId);
6284
6878
  }
6285
6879
  /**
6286
6880
  * Adds a log entry to a card.
@@ -6310,30 +6904,7 @@ var init_KanbanSDK = __esm({
6310
6904
  * ```
6311
6905
  */
6312
6906
  async addLog(cardId, text, options, boardId) {
6313
- if (!text?.trim())
6314
- throw new Error("Log text cannot be empty");
6315
- const card = await this.getCard(cardId, boardId);
6316
- if (!card)
6317
- throw new Error(`Card not found: ${cardId}`);
6318
- const entry = {
6319
- timestamp: options?.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
6320
- source: options?.source || "default",
6321
- text: text.trim(),
6322
- ...options?.object && Object.keys(options.object).length > 0 ? { object: options.object } : {}
6323
- };
6324
- const dir = this._storage.getCardDir(card);
6325
- const logFileName = `${card.id}.log`;
6326
- const logPath = path8.join(dir, logFileName);
6327
- await fs6.mkdir(dir, { recursive: true });
6328
- const line = this._serializeLogEntry(entry) + "\n";
6329
- await fs6.appendFile(logPath, line, "utf-8");
6330
- if (!card.attachments.includes(logFileName)) {
6331
- card.attachments.push(logFileName);
6332
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
6333
- await this._storage.writeCard(card);
6334
- }
6335
- this.emitEvent("log.added", { cardId, entry });
6336
- return entry;
6907
+ return addLog(this, cardId, text, options, boardId);
6337
6908
  }
6338
6909
  /**
6339
6910
  * Clears all log entries for a card by deleting the `.log` file.
@@ -6352,22 +6923,7 @@ var init_KanbanSDK = __esm({
6352
6923
  * ```
6353
6924
  */
6354
6925
  async clearLogs(cardId, boardId) {
6355
- const card = await this.getCard(cardId, boardId);
6356
- if (!card)
6357
- throw new Error(`Card not found: ${cardId}`);
6358
- const logFileName = `${card.id}.log`;
6359
- const dir = this._storage.getCardDir(card);
6360
- const logPath = path8.join(dir, logFileName);
6361
- try {
6362
- await fs6.unlink(logPath);
6363
- } catch {
6364
- }
6365
- if (card.attachments.includes(logFileName)) {
6366
- card.attachments = card.attachments.filter((a) => a !== logFileName);
6367
- card.modified = (/* @__PURE__ */ new Date()).toISOString();
6368
- await this._storage.writeCard(card);
6369
- }
6370
- this.emitEvent("log.cleared", { cardId });
6926
+ return clearLogs(this, cardId, boardId);
6371
6927
  }
6372
6928
  // --- Board-level log management ---
6373
6929
  /**
@@ -6386,7 +6942,7 @@ var init_KanbanSDK = __esm({
6386
6942
  * ```
6387
6943
  */
6388
6944
  getBoardLogFilePath(boardId) {
6389
- return path8.join(this._boardDir(boardId), "board.log");
6945
+ return getBoardLogFilePath(this, boardId);
6390
6946
  }
6391
6947
  /**
6392
6948
  * Lists all log entries from the board-level log file.
@@ -6403,23 +6959,7 @@ var init_KanbanSDK = __esm({
6403
6959
  * ```
6404
6960
  */
6405
6961
  async listBoardLogs(boardId) {
6406
- const logPath = this.getBoardLogFilePath(boardId);
6407
- let content;
6408
- try {
6409
- content = await fs6.readFile(logPath, "utf-8");
6410
- } catch {
6411
- return [];
6412
- }
6413
- const entries = [];
6414
- for (const line of content.split("\n")) {
6415
- const trimmed = line.trim();
6416
- if (!trimmed)
6417
- continue;
6418
- const entry = this._parseLogLine(trimmed);
6419
- if (entry)
6420
- entries.push(entry);
6421
- }
6422
- return entries;
6962
+ return listBoardLogs(this, boardId);
6423
6963
  }
6424
6964
  /**
6425
6965
  * Appends a new log entry to the board-level log file.
@@ -6437,18 +6977,7 @@ var init_KanbanSDK = __esm({
6437
6977
  * ```
6438
6978
  */
6439
6979
  async addBoardLog(text, options, boardId) {
6440
- const entry = {
6441
- timestamp: options?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
6442
- source: options?.source ?? "sdk",
6443
- text,
6444
- ...options?.object ? { object: options.object } : {}
6445
- };
6446
- const logPath = this.getBoardLogFilePath(boardId);
6447
- await fs6.mkdir(path8.dirname(logPath), { recursive: true });
6448
- const line = this._serializeLogEntry(entry) + "\n";
6449
- await fs6.appendFile(logPath, line, "utf-8");
6450
- this.emitEvent("board.log.added", { boardId, entry });
6451
- return entry;
6980
+ return addBoardLog(this, text, options, boardId);
6452
6981
  }
6453
6982
  /**
6454
6983
  * Clears all log entries for a board by deleting the board-level `board.log` file.
@@ -6465,12 +6994,7 @@ var init_KanbanSDK = __esm({
6465
6994
  * ```
6466
6995
  */
6467
6996
  async clearBoardLogs(boardId) {
6468
- const logPath = this.getBoardLogFilePath(boardId);
6469
- try {
6470
- await fs6.unlink(logPath);
6471
- } catch {
6472
- }
6473
- this.emitEvent("board.log.cleared", { boardId });
6997
+ return clearBoardLogs(this, boardId);
6474
6998
  }
6475
6999
  // --- Column management (board-scoped) ---
6476
7000
  /**
@@ -6486,10 +7010,7 @@ var init_KanbanSDK = __esm({
6486
7010
  * ```
6487
7011
  */
6488
7012
  listColumns(boardId) {
6489
- const config3 = readConfig(this.workspaceRoot);
6490
- const resolvedId = boardId || config3.defaultBoard;
6491
- const board = config3.boards[resolvedId];
6492
- return board?.columns || [];
7013
+ return listColumns(this, boardId);
6493
7014
  }
6494
7015
  /**
6495
7016
  * Adds a new column to a board.
@@ -6515,20 +7036,7 @@ var init_KanbanSDK = __esm({
6515
7036
  * ```
6516
7037
  */
6517
7038
  addColumn(column, boardId) {
6518
- const config3 = readConfig(this.workspaceRoot);
6519
- const resolvedId = boardId || config3.defaultBoard;
6520
- const board = config3.boards[resolvedId];
6521
- if (!board)
6522
- throw new Error(`Board not found: ${resolvedId}`);
6523
- if (column.id === DELETED_STATUS_ID)
6524
- throw new Error(`"${DELETED_STATUS_ID}" is a reserved column ID`);
6525
- if (board.columns.some((c) => c.id === column.id)) {
6526
- throw new Error(`Column already exists: ${column.id}`);
6527
- }
6528
- board.columns.push(column);
6529
- writeConfig(this.workspaceRoot, config3);
6530
- this.emitEvent("column.created", column);
6531
- return board.columns;
7039
+ return addColumn(this, column, boardId);
6532
7040
  }
6533
7041
  /**
6534
7042
  * Updates the properties of an existing column.
@@ -6554,21 +7062,7 @@ var init_KanbanSDK = __esm({
6554
7062
  * ```
6555
7063
  */
6556
7064
  updateColumn(columnId, updates, boardId) {
6557
- const config3 = readConfig(this.workspaceRoot);
6558
- const resolvedId = boardId || config3.defaultBoard;
6559
- const board = config3.boards[resolvedId];
6560
- if (!board)
6561
- throw new Error(`Board not found: ${resolvedId}`);
6562
- const col = board.columns.find((c) => c.id === columnId);
6563
- if (!col)
6564
- throw new Error(`Column not found: ${columnId}`);
6565
- if (updates.name !== void 0)
6566
- col.name = updates.name;
6567
- if (updates.color !== void 0)
6568
- col.color = updates.color;
6569
- writeConfig(this.workspaceRoot, config3);
6570
- this.emitEvent("column.updated", col);
6571
- return board.columns;
7065
+ return updateColumn(this, columnId, updates, boardId);
6572
7066
  }
6573
7067
  /**
6574
7068
  * Removes a column from a board.
@@ -6590,26 +7084,7 @@ var init_KanbanSDK = __esm({
6590
7084
  * ```
6591
7085
  */
6592
7086
  async removeColumn(columnId, boardId) {
6593
- const config3 = readConfig(this.workspaceRoot);
6594
- const resolvedId = boardId || config3.defaultBoard;
6595
- const board = config3.boards[resolvedId];
6596
- if (!board)
6597
- throw new Error(`Board not found: ${resolvedId}`);
6598
- if (columnId === DELETED_STATUS_ID)
6599
- throw new Error(`Cannot remove the reserved "${DELETED_STATUS_ID}" column`);
6600
- const idx = board.columns.findIndex((c) => c.id === columnId);
6601
- if (idx === -1)
6602
- throw new Error(`Column not found: ${columnId}`);
6603
- const cards = await this.listCards(void 0, resolvedId);
6604
- const cardsInColumn = cards.filter((c) => c.status === columnId);
6605
- if (cardsInColumn.length > 0) {
6606
- throw new Error(`Cannot remove column "${columnId}": ${cardsInColumn.length} card(s) still in this column`);
6607
- }
6608
- const removed = board.columns[idx];
6609
- board.columns.splice(idx, 1);
6610
- writeConfig(this.workspaceRoot, config3);
6611
- this.emitEvent("column.deleted", removed);
6612
- return board.columns;
7087
+ return removeColumn(this, columnId, boardId);
6613
7088
  }
6614
7089
  /**
6615
7090
  * Moves all cards in the specified column to the `deleted` (soft-delete) column.
@@ -6630,14 +7105,7 @@ var init_KanbanSDK = __esm({
6630
7105
  * ```
6631
7106
  */
6632
7107
  async cleanupColumn(columnId, boardId) {
6633
- if (columnId === DELETED_STATUS_ID)
6634
- return 0;
6635
- const cards = await this.listCards(void 0, boardId);
6636
- const cardsToMove = cards.filter((c) => c.status === columnId);
6637
- for (const card of cardsToMove) {
6638
- await this.moveCard(card.id, DELETED_STATUS_ID, 0, boardId);
6639
- }
6640
- return cardsToMove.length;
7108
+ return cleanupColumn(this, columnId, boardId);
6641
7109
  }
6642
7110
  /**
6643
7111
  * Permanently deletes all cards currently in the `deleted` column.
@@ -6655,12 +7123,7 @@ var init_KanbanSDK = __esm({
6655
7123
  * ```
6656
7124
  */
6657
7125
  async purgeDeletedCards(boardId) {
6658
- const cards = await this.listCards(void 0, boardId);
6659
- const deleted = cards.filter((c) => c.status === DELETED_STATUS_ID);
6660
- for (const card of deleted) {
6661
- await this.permanentlyDeleteCard(card.id, boardId);
6662
- }
6663
- return deleted.length;
7126
+ return purgeDeletedCards(this, boardId);
6664
7127
  }
6665
7128
  /**
6666
7129
  * Reorders the columns of a board.
@@ -6684,22 +7147,7 @@ var init_KanbanSDK = __esm({
6684
7147
  * ```
6685
7148
  */
6686
7149
  reorderColumns(columnIds, boardId) {
6687
- const config3 = readConfig(this.workspaceRoot);
6688
- const resolvedId = boardId || config3.defaultBoard;
6689
- const board = config3.boards[resolvedId];
6690
- if (!board)
6691
- throw new Error(`Board not found: ${resolvedId}`);
6692
- const colMap = new Map(board.columns.map((c) => [c.id, c]));
6693
- for (const id of columnIds) {
6694
- if (!colMap.has(id))
6695
- throw new Error(`Column not found: ${id}`);
6696
- }
6697
- if (columnIds.length !== board.columns.length) {
6698
- throw new Error("Must include all column IDs when reordering");
6699
- }
6700
- board.columns = columnIds.map((id) => colMap.get(id));
6701
- writeConfig(this.workspaceRoot, config3);
6702
- return board.columns;
7150
+ return reorderColumns(this, columnIds, boardId);
6703
7151
  }
6704
7152
  // --- Settings management (global) ---
6705
7153
  /**
@@ -6717,7 +7165,7 @@ var init_KanbanSDK = __esm({
6717
7165
  * ```
6718
7166
  */
6719
7167
  getSettings() {
6720
- return configToSettings(readConfig(this.workspaceRoot));
7168
+ return getSettings(this);
6721
7169
  }
6722
7170
  /**
6723
7171
  * Updates the global card display settings for the workspace.
@@ -6738,9 +7186,7 @@ var init_KanbanSDK = __esm({
6738
7186
  * ```
6739
7187
  */
6740
7188
  updateSettings(settings) {
6741
- const config3 = readConfig(this.workspaceRoot);
6742
- writeConfig(this.workspaceRoot, settingsToConfig(config3, settings));
6743
- this.emitEvent("settings.updated", settings);
7189
+ updateSettings(this, settings);
6744
7190
  }
6745
7191
  // ---------------------------------------------------------------------------
6746
7192
  // Storage migration
@@ -6768,30 +7214,7 @@ var init_KanbanSDK = __esm({
6768
7214
  * ```
6769
7215
  */
6770
7216
  async migrateToSqlite(dbPath) {
6771
- if (this._storage.type === "sqlite") {
6772
- throw new Error("Storage engine is already sqlite");
6773
- }
6774
- await this._ensureMigrated();
6775
- const resolvedDbPath = dbPath ?? ".kanban/kanban.db";
6776
- const absDbPath = path8.resolve(this.workspaceRoot, resolvedDbPath);
6777
- const { SqliteStorageEngine: SqliteStorageEngine2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
6778
- const sqliteEngine = new SqliteStorageEngine2(this._storage.kanbanDir, absDbPath);
6779
- await sqliteEngine.init();
6780
- const config3 = readConfig(this.workspaceRoot);
6781
- const boardIds = Object.keys(config3.boards);
6782
- let count = 0;
6783
- for (const boardId of boardIds) {
6784
- const boardDir = path8.join(this._storage.kanbanDir, "boards", boardId);
6785
- const cards = await this._storage.scanCards(boardDir, boardId);
6786
- for (const card of cards) {
6787
- await sqliteEngine.writeCard({ ...card, filePath: "" });
6788
- count++;
6789
- }
6790
- }
6791
- sqliteEngine.close();
6792
- writeConfig(this.workspaceRoot, { ...config3, storageEngine: "sqlite", sqlitePath: resolvedDbPath });
6793
- this.emitEvent("storage.migrated", { from: "markdown", to: "sqlite", count });
6794
- return count;
7217
+ return migrateToSqlite(this, dbPath);
6795
7218
  }
6796
7219
  /**
6797
7220
  * Migrates all card data from the current SQLite engine back to markdown files.
@@ -6814,152 +7237,59 @@ var init_KanbanSDK = __esm({
6814
7237
  * ```
6815
7238
  */
6816
7239
  async migrateToMarkdown() {
6817
- if (this._storage.type === "markdown") {
6818
- throw new Error("Storage engine is already markdown");
6819
- }
6820
- await this._ensureMigrated();
6821
- const { MarkdownStorageEngine: MarkdownStorageEngine2 } = await Promise.resolve().then(() => (init_markdown(), markdown_exports));
6822
- const mdEngine = new MarkdownStorageEngine2(this._storage.kanbanDir);
6823
- await mdEngine.init();
6824
- const config3 = readConfig(this.workspaceRoot);
6825
- const boardIds = Object.keys(config3.boards);
6826
- let count = 0;
6827
- for (const boardId of boardIds) {
6828
- const boardDir = path8.join(this._storage.kanbanDir, "boards", boardId);
6829
- const cards = await this._storage.scanCards(boardDir, boardId);
6830
- for (const card of cards) {
6831
- const numericId = Number(card.id) || 0;
6832
- const title = getTitleFromContent(card.content) || card.id;
6833
- const filename = generateCardFilename(numericId, title);
6834
- const filePath = getCardFilePath(boardDir, card.status, filename);
6835
- await mdEngine.writeCard({ ...card, filePath });
6836
- count++;
6837
- }
6838
- }
6839
- mdEngine.close();
6840
- const { storageEngine: _se, sqlitePath: _sp, ...restConfig } = config3;
6841
- writeConfig(this.workspaceRoot, restConfig);
6842
- this.emitEvent("storage.migrated", { from: "sqlite", to: "markdown", count });
6843
- return count;
7240
+ return migrateToMarkdown(this);
6844
7241
  }
6845
- };
6846
- }
6847
- });
6848
-
6849
- // src/standalone/webhooks.ts
6850
- function loadWebhooks(workspaceRoot) {
6851
- const config3 = readConfig(workspaceRoot);
6852
- return config3.webhooks || [];
6853
- }
6854
- function saveWebhooks(workspaceRoot, webhooks) {
6855
- const config3 = readConfig(workspaceRoot);
6856
- config3.webhooks = webhooks;
6857
- writeConfig(workspaceRoot, config3);
6858
- }
6859
- function createWebhook(workspaceRoot, webhookConfig) {
6860
- const webhooks = loadWebhooks(workspaceRoot);
6861
- const webhook = {
6862
- id: "wh_" + crypto.randomBytes(8).toString("hex"),
6863
- url: webhookConfig.url,
6864
- events: webhookConfig.events,
6865
- secret: webhookConfig.secret,
6866
- active: true
6867
- };
6868
- webhooks.push(webhook);
6869
- saveWebhooks(workspaceRoot, webhooks);
6870
- return webhook;
6871
- }
6872
- function deleteWebhook(workspaceRoot, id) {
6873
- const webhooks = loadWebhooks(workspaceRoot);
6874
- const filtered = webhooks.filter((w) => w.id !== id);
6875
- if (filtered.length === webhooks.length)
6876
- return false;
6877
- saveWebhooks(workspaceRoot, filtered);
6878
- return true;
6879
- }
6880
- function updateWebhook(workspaceRoot, id, updates) {
6881
- const webhooks = loadWebhooks(workspaceRoot);
6882
- const webhook = webhooks.find((w) => w.id === id);
6883
- if (!webhook)
6884
- return null;
6885
- if (updates.url !== void 0)
6886
- webhook.url = updates.url;
6887
- if (updates.events !== void 0)
6888
- webhook.events = updates.events;
6889
- if (updates.secret !== void 0)
6890
- webhook.secret = updates.secret;
6891
- if (updates.active !== void 0)
6892
- webhook.active = updates.active;
6893
- saveWebhooks(workspaceRoot, webhooks);
6894
- return webhook;
6895
- }
6896
- function fireWebhooks(workspaceRoot, event, data) {
6897
- const webhooks = loadWebhooks(workspaceRoot);
6898
- const matching = webhooks.filter(
6899
- (w) => w.active && (w.events.includes("*") || w.events.includes(event))
6900
- );
6901
- if (matching.length === 0)
6902
- return;
6903
- const payload = JSON.stringify({
6904
- event,
6905
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
6906
- data
6907
- });
6908
- for (const webhook of matching) {
6909
- deliverWebhook(webhook, event, payload).catch((err) => {
6910
- console.error(`Webhook delivery failed for ${webhook.id} (${webhook.url}):`, err.message || err);
6911
- });
6912
- }
6913
- }
6914
- async function deliverWebhook(webhook, event, payload) {
6915
- const url3 = new URL(webhook.url);
6916
- const isHttps = url3.protocol === "https:";
6917
- const transport = isHttps ? https : http;
6918
- const headers = {
6919
- "Content-Type": "application/json",
6920
- "Content-Length": Buffer.byteLength(payload).toString(),
6921
- "X-Webhook-Event": event
6922
- };
6923
- if (webhook.secret) {
6924
- const signature = crypto.createHmac("sha256", webhook.secret).update(payload).digest("hex");
6925
- headers["X-Webhook-Signature"] = `sha256=${signature}`;
6926
- }
6927
- return new Promise((resolve9, reject) => {
6928
- const req = transport.request(
6929
- {
6930
- hostname: url3.hostname,
6931
- port: url3.port || (isHttps ? 443 : 80),
6932
- path: url3.pathname + url3.search,
6933
- method: "POST",
6934
- headers,
6935
- timeout: 1e4
6936
- },
6937
- (res) => {
6938
- res.resume();
6939
- if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
6940
- resolve9();
6941
- } else {
6942
- reject(new Error(`HTTP ${res.statusCode}`));
6943
- }
7242
+ /**
7243
+ * Sets the default board for the workspace.
7244
+ *
7245
+ * @param boardId - The ID of the board to set as the default.
7246
+ * @throws {Error} If the board does not exist.
7247
+ *
7248
+ * @example
7249
+ * ```ts
7250
+ * sdk.setDefaultBoard('sprint-2')
7251
+ * ```
7252
+ */
7253
+ setDefaultBoard(boardId) {
7254
+ setDefaultBoard(this, boardId);
6944
7255
  }
6945
- );
6946
- req.on("error", reject);
6947
- req.on("timeout", () => {
6948
- req.destroy();
6949
- reject(new Error("Timeout"));
6950
- });
6951
- req.write(payload);
6952
- req.end();
6953
- });
6954
- }
6955
- var crypto, http, https;
6956
- var init_webhooks = __esm({
6957
- "src/standalone/webhooks.ts"() {
6958
- "use strict";
6959
- crypto = __toESM(require("crypto"));
6960
- http = __toESM(require("http"));
6961
- https = __toESM(require("https"));
6962
- init_config();
7256
+ /**
7257
+ * Lists all registered webhooks.
7258
+ *
7259
+ * @returns Array of {@link Webhook} objects.
7260
+ */
7261
+ listWebhooks() {
7262
+ return loadWebhooks(this.workspaceRoot);
7263
+ }
7264
+ /**
7265
+ * Creates and persists a new webhook.
7266
+ *
7267
+ * @param webhookConfig - The webhook configuration.
7268
+ * @returns The newly created {@link Webhook}.
7269
+ */
7270
+ createWebhook(webhookConfig) {
7271
+ return createWebhook(this.workspaceRoot, webhookConfig);
7272
+ }
7273
+ /**
7274
+ * Deletes a webhook by its ID.
7275
+ *
7276
+ * @param id - The webhook ID to delete.
7277
+ * @returns `true` if deleted, `false` if not found.
7278
+ */
7279
+ deleteWebhook(id) {
7280
+ return deleteWebhook(this.workspaceRoot, id);
7281
+ }
7282
+ /**
7283
+ * Updates an existing webhook's configuration.
7284
+ *
7285
+ * @param id - The webhook ID to update.
7286
+ * @param updates - Partial webhook fields to merge.
7287
+ * @returns The updated {@link Webhook}, or `null` if not found.
7288
+ */
7289
+ updateWebhook(id, updates) {
7290
+ return updateWebhook(this.workspaceRoot, id, updates);
7291
+ }
7292
+ };
6963
7293
  }
6964
7294
  });
6965
7295
 
@@ -7368,8 +7698,8 @@ var init_parseUtil = __esm({
7368
7698
  init_errors();
7369
7699
  init_en();
7370
7700
  makeIssue = (params) => {
7371
- const { data, path: path13, errorMaps, issueData } = params;
7372
- const fullPath = [...path13, ...issueData.path || []];
7701
+ const { data, path: path17, errorMaps, issueData } = params;
7702
+ const fullPath = [...path17, ...issueData.path || []];
7373
7703
  const fullIssue = {
7374
7704
  ...issueData,
7375
7705
  path: fullPath
@@ -7649,11 +7979,11 @@ var init_types3 = __esm({
7649
7979
  init_parseUtil();
7650
7980
  init_util();
7651
7981
  ParseInputLazyPath = class {
7652
- constructor(parent, value, path13, key) {
7982
+ constructor(parent, value, path17, key) {
7653
7983
  this._cachedPath = [];
7654
7984
  this.parent = parent;
7655
7985
  this.data = value;
7656
- this._path = path13;
7986
+ this._path = path17;
7657
7987
  this._key = key;
7658
7988
  }
7659
7989
  get path() {
@@ -11159,10 +11489,10 @@ function mergeDefs(...defs) {
11159
11489
  function cloneDef(schema2) {
11160
11490
  return mergeDefs(schema2._zod.def);
11161
11491
  }
11162
- function getElementAtPath(obj, path13) {
11163
- if (!path13)
11492
+ function getElementAtPath(obj, path17) {
11493
+ if (!path17)
11164
11494
  return obj;
11165
- return path13.reduce((acc, key) => acc?.[key], obj);
11495
+ return path17.reduce((acc, key) => acc?.[key], obj);
11166
11496
  }
11167
11497
  function promiseAllObject(promisesObj) {
11168
11498
  const keys = Object.keys(promisesObj);
@@ -11474,11 +11804,11 @@ function aborted(x, startIndex = 0) {
11474
11804
  }
11475
11805
  return false;
11476
11806
  }
11477
- function prefixIssues(path13, issues) {
11807
+ function prefixIssues(path17, issues) {
11478
11808
  return issues.map((iss) => {
11479
11809
  var _a3;
11480
11810
  (_a3 = iss).path ?? (_a3.path = []);
11481
- iss.path.unshift(path13);
11811
+ iss.path.unshift(path17);
11482
11812
  return iss;
11483
11813
  });
11484
11814
  }
@@ -25968,8 +26298,8 @@ var require_utils = __commonJS({
25968
26298
  }
25969
26299
  return ind;
25970
26300
  }
25971
- function removeDotSegments(path13) {
25972
- let input = path13;
26301
+ function removeDotSegments(path17) {
26302
+ let input = path17;
25973
26303
  const output = [];
25974
26304
  let nextSlash = -1;
25975
26305
  let len = 0;
@@ -26168,8 +26498,8 @@ var require_schemes = __commonJS({
26168
26498
  wsComponent.secure = void 0;
26169
26499
  }
26170
26500
  if (wsComponent.resourceName) {
26171
- const [path13, query] = wsComponent.resourceName.split("?");
26172
- wsComponent.path = path13 && path13 !== "/" ? path13 : void 0;
26501
+ const [path17, query] = wsComponent.resourceName.split("?");
26502
+ wsComponent.path = path17 && path17 !== "/" ? path17 : void 0;
26173
26503
  wsComponent.query = query;
26174
26504
  wsComponent.resourceName = void 0;
26175
26505
  }
@@ -29532,12 +29862,12 @@ var require_dist = __commonJS({
29532
29862
  throw new Error(`Unknown format "${name}"`);
29533
29863
  return f;
29534
29864
  };
29535
- function addFormats(ajv, list, fs15, exportName) {
29865
+ function addFormats(ajv, list, fs16, exportName) {
29536
29866
  var _a3;
29537
29867
  var _b;
29538
29868
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
29539
29869
  for (const f of list)
29540
- ajv.addFormat(f, fs15[f]);
29870
+ ajv.addFormat(f, fs16[f]);
29541
29871
  }
29542
29872
  module2.exports = exports2 = formatsPlugin;
29543
29873
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -31394,10 +31724,10 @@ function mergeDefs2(...defs) {
31394
31724
  function cloneDef2(schema2) {
31395
31725
  return mergeDefs2(schema2._zod.def);
31396
31726
  }
31397
- function getElementAtPath2(obj, path13) {
31398
- if (!path13)
31727
+ function getElementAtPath2(obj, path17) {
31728
+ if (!path17)
31399
31729
  return obj;
31400
- return path13.reduce((acc, key) => acc?.[key], obj);
31730
+ return path17.reduce((acc, key) => acc?.[key], obj);
31401
31731
  }
31402
31732
  function promiseAllObject2(promisesObj) {
31403
31733
  const keys = Object.keys(promisesObj);
@@ -31709,11 +32039,11 @@ function aborted2(x, startIndex = 0) {
31709
32039
  }
31710
32040
  return false;
31711
32041
  }
31712
- function prefixIssues2(path13, issues) {
32042
+ function prefixIssues2(path17, issues) {
31713
32043
  return issues.map((iss) => {
31714
32044
  var _a3;
31715
32045
  (_a3 = iss).path ?? (_a3.path = []);
31716
- iss.path.unshift(path13);
32046
+ iss.path.unshift(path17);
31717
32047
  return iss;
31718
32048
  });
31719
32049
  }
@@ -31955,7 +32285,7 @@ function formatError3(error49, mapper = (issue3) => issue3.message) {
31955
32285
  }
31956
32286
  function treeifyError2(error49, mapper = (issue3) => issue3.message) {
31957
32287
  const result = { errors: [] };
31958
- const processError = (error50, path13 = []) => {
32288
+ const processError = (error50, path17 = []) => {
31959
32289
  var _a3, _b;
31960
32290
  for (const issue3 of error50.issues) {
31961
32291
  if (issue3.code === "invalid_union" && issue3.errors.length) {
@@ -31965,7 +32295,7 @@ function treeifyError2(error49, mapper = (issue3) => issue3.message) {
31965
32295
  } else if (issue3.code === "invalid_element") {
31966
32296
  processError({ issues: issue3.issues }, issue3.path);
31967
32297
  } else {
31968
- const fullpath = [...path13, ...issue3.path];
32298
+ const fullpath = [...path17, ...issue3.path];
31969
32299
  if (fullpath.length === 0) {
31970
32300
  result.errors.push(mapper(issue3));
31971
32301
  continue;
@@ -31997,8 +32327,8 @@ function treeifyError2(error49, mapper = (issue3) => issue3.message) {
31997
32327
  }
31998
32328
  function toDotPath(_path) {
31999
32329
  const segs = [];
32000
- const path13 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
32001
- for (const seg of path13) {
32330
+ const path17 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
32331
+ for (const seg of path17) {
32002
32332
  if (typeof seg === "number")
32003
32333
  segs.push(`[${seg}]`);
32004
32334
  else if (typeof seg === "symbol")
@@ -44691,13 +45021,13 @@ function resolveRef(ref, ctx) {
44691
45021
  if (!ref.startsWith("#")) {
44692
45022
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
44693
45023
  }
44694
- const path13 = ref.slice(1).split("/").filter(Boolean);
44695
- if (path13.length === 0) {
45024
+ const path17 = ref.slice(1).split("/").filter(Boolean);
45025
+ if (path17.length === 0) {
44696
45026
  return ctx.rootSchema;
44697
45027
  }
44698
45028
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
44699
- if (path13[0] === defsKey) {
44700
- const key = path13[1];
45029
+ if (path17[0] === defsKey) {
45030
+ const key = path17[1];
44701
45031
  if (!key || !ctx.defs[key]) {
44702
45032
  throw new Error(`Reference not found: ${ref}`);
44703
45033
  }
@@ -45462,16 +45792,16 @@ async function findWorkspaceRoot(startDir) {
45462
45792
  let dir = startDir;
45463
45793
  while (true) {
45464
45794
  try {
45465
- await fs7.access(path9.join(dir, ".git"));
45795
+ await fs8.access(path13.join(dir, ".git"));
45466
45796
  return dir;
45467
45797
  } catch {
45468
45798
  }
45469
45799
  try {
45470
- await fs7.access(path9.join(dir, "package.json"));
45800
+ await fs8.access(path13.join(dir, "package.json"));
45471
45801
  return dir;
45472
45802
  } catch {
45473
45803
  }
45474
- const parent = path9.dirname(dir);
45804
+ const parent = path13.dirname(dir);
45475
45805
  if (parent === dir)
45476
45806
  return startDir;
45477
45807
  dir = parent;
@@ -45480,25 +45810,18 @@ async function findWorkspaceRoot(startDir) {
45480
45810
  async function resolveKanbanDir() {
45481
45811
  const dirIndex = process.argv.indexOf("--dir");
45482
45812
  if (dirIndex !== -1 && process.argv[dirIndex + 1]) {
45483
- return path9.resolve(process.argv[dirIndex + 1]);
45813
+ return path13.resolve(process.argv[dirIndex + 1]);
45484
45814
  }
45485
45815
  const envDir = process.env.KANBAN_DIR || process.env.KANBAN_FEATURES_DIR;
45486
45816
  if (envDir) {
45487
- return path9.resolve(envDir);
45817
+ return path13.resolve(envDir);
45488
45818
  }
45489
45819
  const root = await findWorkspaceRoot(process.cwd());
45490
- return path9.join(root, ".devtool", "cards");
45491
- }
45492
- function getTitleFromContent2(content) {
45493
- const match = content.match(/^#\s+(.+)$/m);
45494
- if (match)
45495
- return match[1].trim();
45496
- const firstLine = content.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
45497
- return firstLine || "Untitled";
45820
+ return path13.join(root, ".kanban");
45498
45821
  }
45499
45822
  async function main() {
45500
45823
  const kanbanDir = await resolveKanbanDir();
45501
- const workspaceRoot = path9.dirname(kanbanDir);
45824
+ const workspaceRoot = path13.dirname(kanbanDir);
45502
45825
  const sdk = new KanbanSDK(kanbanDir, {
45503
45826
  onEvent: (event, data) => fireWebhooks(workspaceRoot, event, data)
45504
45827
  });
@@ -45571,7 +45894,12 @@ async function main() {
45571
45894
  sort: external_exports4.enum(["created:asc", "created:desc", "modified:asc", "modified:desc"]).optional().describe("Sort order: created:asc, created:desc, modified:asc, or modified:desc. Defaults to board order.")
45572
45895
  },
45573
45896
  async ({ boardId, status, priority, assignee, label, labelGroup, includeDeleted, metaFilter, sort }) => {
45574
- let cards = await sdk.listCards(void 0, boardId);
45897
+ let cards = await sdk.listCards(
45898
+ void 0,
45899
+ boardId,
45900
+ metaFilter && Object.keys(metaFilter).length > 0 ? metaFilter : void 0,
45901
+ sort || void 0
45902
+ );
45575
45903
  if (!includeDeleted)
45576
45904
  cards = cards.filter((c) => c.status !== DELETED_STATUS_ID);
45577
45905
  if (status)
@@ -45586,19 +45914,9 @@ async function main() {
45586
45914
  const groupLabels = sdk.getLabelsInGroup(labelGroup);
45587
45915
  cards = cards.filter((c) => c.labels.some((l) => groupLabels.includes(l)));
45588
45916
  }
45589
- if (metaFilter && Object.keys(metaFilter).length > 0)
45590
- cards = cards.filter((c) => matchesMetaFilter(c.metadata, metaFilter));
45591
- if (sort) {
45592
- const [field, dir] = sort.split(":");
45593
- cards = [...cards].sort((a, b) => {
45594
- const aVal = field === "created" ? a.created : a.modified;
45595
- const bVal = field === "created" ? b.created : b.modified;
45596
- return dir === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
45597
- });
45598
- }
45599
45917
  const summary = cards.map((c) => ({
45600
45918
  id: c.id,
45601
- title: getTitleFromContent2(c.content),
45919
+ title: getTitleFromContent(c.content),
45602
45920
  status: c.status,
45603
45921
  priority: c.priority,
45604
45922
  assignee: c.assignee,
@@ -46506,8 +46824,7 @@ async function main() {
46506
46824
  "Get the current kanban board display settings.",
46507
46825
  {},
46508
46826
  async () => {
46509
- const config3 = readConfig(workspaceRoot);
46510
- const settings = configToSettings(config3);
46827
+ const settings = sdk.getSettings();
46511
46828
  return {
46512
46829
  content: [{
46513
46830
  type: "text",
@@ -46531,20 +46848,18 @@ async function main() {
46531
46848
  defaultStatus: external_exports4.string().optional().describe("Default status for new cards")
46532
46849
  },
46533
46850
  async (updates) => {
46534
- const config3 = readConfig(workspaceRoot);
46535
- const settings = configToSettings(config3);
46851
+ const settings = sdk.getSettings();
46536
46852
  const merged = { ...settings };
46537
46853
  for (const [key, value] of Object.entries(updates)) {
46538
46854
  if (value !== void 0) {
46539
46855
  merged[key] = value;
46540
46856
  }
46541
46857
  }
46542
- writeConfig(workspaceRoot, settingsToConfig(config3, merged));
46543
- const updated = configToSettings(readConfig(workspaceRoot));
46858
+ sdk.updateSettings(merged);
46544
46859
  return {
46545
46860
  content: [{
46546
46861
  type: "text",
46547
- text: JSON.stringify(updated, null, 2)
46862
+ text: JSON.stringify(sdk.getSettings(), null, 2)
46548
46863
  }]
46549
46864
  };
46550
46865
  }
@@ -46554,7 +46869,7 @@ async function main() {
46554
46869
  "List all registered webhooks.",
46555
46870
  {},
46556
46871
  async () => {
46557
- const webhooks = loadWebhooks(workspaceRoot);
46872
+ const webhooks = sdk.listWebhooks();
46558
46873
  return {
46559
46874
  content: [{
46560
46875
  type: "text",
@@ -46572,7 +46887,7 @@ async function main() {
46572
46887
  secret: external_exports4.string().optional().describe("Optional HMAC-SHA256 signing secret")
46573
46888
  },
46574
46889
  async ({ url: url3, events, secret }) => {
46575
- const webhook = createWebhook(workspaceRoot, {
46890
+ const webhook = sdk.createWebhook({
46576
46891
  url: url3,
46577
46892
  events: events || ["*"],
46578
46893
  secret
@@ -46592,7 +46907,7 @@ async function main() {
46592
46907
  webhookId: external_exports4.string().describe('Webhook ID (e.g. "wh_abc123")')
46593
46908
  },
46594
46909
  async ({ webhookId }) => {
46595
- const removed = deleteWebhook(workspaceRoot, webhookId);
46910
+ const removed = sdk.deleteWebhook(webhookId);
46596
46911
  if (!removed) {
46597
46912
  return {
46598
46913
  content: [{ type: "text", text: `Webhook not found: ${webhookId}` }],
@@ -46627,7 +46942,7 @@ async function main() {
46627
46942
  updates.secret = secret;
46628
46943
  if (active !== void 0)
46629
46944
  updates.active = active;
46630
- const updated = updateWebhook(workspaceRoot, webhookId, updates);
46945
+ const updated = sdk.updateWebhook(webhookId, updates);
46631
46946
  if (!updated) {
46632
46947
  return {
46633
46948
  content: [{ type: "text", text: `Webhook not found: ${webhookId}` }],
@@ -46726,12 +47041,12 @@ async function main() {
46726
47041
  const transport = new StdioServerTransport();
46727
47042
  await server.connect(transport);
46728
47043
  }
46729
- var path9, fs7;
47044
+ var path13, fs8;
46730
47045
  var init_mcp_server2 = __esm({
46731
47046
  "src/mcp-server/index.ts"() {
46732
47047
  "use strict";
46733
- path9 = __toESM(require("path"));
46734
- fs7 = __toESM(require("fs/promises"));
47048
+ path13 = __toESM(require("path"));
47049
+ fs8 = __toESM(require("fs/promises"));
46735
47050
  init_mcp();
46736
47051
  init_stdio2();
46737
47052
  init_zod2();
@@ -46739,7 +47054,6 @@ var init_mcp_server2 = __esm({
46739
47054
  init_types();
46740
47055
  init_config();
46741
47056
  init_webhooks();
46742
- init_metaUtils();
46743
47057
  main().catch((err) => {
46744
47058
  console.error(`MCP Server error: ${err.message}`);
46745
47059
  process.exit(1);
@@ -50557,7 +50871,7 @@ var init_esm2 = __esm({
50557
50871
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
50558
50872
  const statMethod = opts.lstat ? import_promises.lstat : import_promises.stat;
50559
50873
  if (wantBigintFsStats) {
50560
- this._stat = (path13) => statMethod(path13, { bigint: true });
50874
+ this._stat = (path17) => statMethod(path17, { bigint: true });
50561
50875
  } else {
50562
50876
  this._stat = statMethod;
50563
50877
  }
@@ -50582,8 +50896,8 @@ var init_esm2 = __esm({
50582
50896
  const par = this.parent;
50583
50897
  const fil = par && par.files;
50584
50898
  if (fil && fil.length > 0) {
50585
- const { path: path13, depth } = par;
50586
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path13));
50899
+ const { path: path17, depth } = par;
50900
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path17));
50587
50901
  const awaited = await Promise.all(slice);
50588
50902
  for (const entry of awaited) {
50589
50903
  if (!entry)
@@ -50623,21 +50937,21 @@ var init_esm2 = __esm({
50623
50937
  this.reading = false;
50624
50938
  }
50625
50939
  }
50626
- async _exploreDir(path13, depth) {
50940
+ async _exploreDir(path17, depth) {
50627
50941
  let files;
50628
50942
  try {
50629
- files = await (0, import_promises.readdir)(path13, this._rdOptions);
50943
+ files = await (0, import_promises.readdir)(path17, this._rdOptions);
50630
50944
  } catch (error49) {
50631
50945
  this._onError(error49);
50632
50946
  }
50633
- return { files, depth, path: path13 };
50947
+ return { files, depth, path: path17 };
50634
50948
  }
50635
- async _formatEntry(dirent, path13) {
50949
+ async _formatEntry(dirent, path17) {
50636
50950
  let entry;
50637
- const basename9 = this._isDirent ? dirent.name : dirent;
50951
+ const basename10 = this._isDirent ? dirent.name : dirent;
50638
50952
  try {
50639
- const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path13, basename9));
50640
- entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename9 };
50953
+ const fullPath = (0, import_node_path.resolve)((0, import_node_path.join)(path17, basename10));
50954
+ entry = { path: (0, import_node_path.relative)(this._root, fullPath), fullPath, basename: basename10 };
50641
50955
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
50642
50956
  } catch (err) {
50643
50957
  this._onError(err);
@@ -50693,16 +51007,16 @@ var init_esm2 = __esm({
50693
51007
  });
50694
51008
 
50695
51009
  // node_modules/.pnpm/chokidar@4.0.3/node_modules/chokidar/esm/handler.js
50696
- function createFsWatchInstance(path13, options, listener, errHandler, emitRaw) {
51010
+ function createFsWatchInstance(path17, options, listener, errHandler, emitRaw) {
50697
51011
  const handleEvent = (rawEvent, evPath) => {
50698
- listener(path13);
50699
- emitRaw(rawEvent, evPath, { watchedPath: path13 });
50700
- if (evPath && path13 !== evPath) {
50701
- fsWatchBroadcast(sysPath.resolve(path13, evPath), KEY_LISTENERS, sysPath.join(path13, evPath));
51012
+ listener(path17);
51013
+ emitRaw(rawEvent, evPath, { watchedPath: path17 });
51014
+ if (evPath && path17 !== evPath) {
51015
+ fsWatchBroadcast(sysPath.resolve(path17, evPath), KEY_LISTENERS, sysPath.join(path17, evPath));
50702
51016
  }
50703
51017
  };
50704
51018
  try {
50705
- return (0, import_fs.watch)(path13, {
51019
+ return (0, import_fs.watch)(path17, {
50706
51020
  persistent: options.persistent
50707
51021
  }, handleEvent);
50708
51022
  } catch (error49) {
@@ -51050,12 +51364,12 @@ var init_handler = __esm({
51050
51364
  listener(val1, val2, val3);
51051
51365
  });
51052
51366
  };
51053
- setFsWatchListener = (path13, fullPath, options, handlers) => {
51367
+ setFsWatchListener = (path17, fullPath, options, handlers) => {
51054
51368
  const { listener, errHandler, rawEmitter } = handlers;
51055
51369
  let cont = FsWatchInstances.get(fullPath);
51056
51370
  let watcher;
51057
51371
  if (!options.persistent) {
51058
- watcher = createFsWatchInstance(path13, options, listener, errHandler, rawEmitter);
51372
+ watcher = createFsWatchInstance(path17, options, listener, errHandler, rawEmitter);
51059
51373
  if (!watcher)
51060
51374
  return;
51061
51375
  return watcher.close.bind(watcher);
@@ -51066,7 +51380,7 @@ var init_handler = __esm({
51066
51380
  addAndConvert(cont, KEY_RAW, rawEmitter);
51067
51381
  } else {
51068
51382
  watcher = createFsWatchInstance(
51069
- path13,
51383
+ path17,
51070
51384
  options,
51071
51385
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
51072
51386
  errHandler,
@@ -51081,7 +51395,7 @@ var init_handler = __esm({
51081
51395
  cont.watcherUnusable = true;
51082
51396
  if (isWindows && error49.code === "EPERM") {
51083
51397
  try {
51084
- const fd = await (0, import_promises2.open)(path13, "r");
51398
+ const fd = await (0, import_promises2.open)(path17, "r");
51085
51399
  await fd.close();
51086
51400
  broadcastErr(error49);
51087
51401
  } catch (err) {
@@ -51112,7 +51426,7 @@ var init_handler = __esm({
51112
51426
  };
51113
51427
  };
51114
51428
  FsWatchFileInstances = /* @__PURE__ */ new Map();
51115
- setFsWatchFileListener = (path13, fullPath, options, handlers) => {
51429
+ setFsWatchFileListener = (path17, fullPath, options, handlers) => {
51116
51430
  const { listener, rawEmitter } = handlers;
51117
51431
  let cont = FsWatchFileInstances.get(fullPath);
51118
51432
  const copts = cont && cont.options;
@@ -51134,7 +51448,7 @@ var init_handler = __esm({
51134
51448
  });
51135
51449
  const currmtime = curr.mtimeMs;
51136
51450
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
51137
- foreach(cont.listeners, (listener2) => listener2(path13, curr));
51451
+ foreach(cont.listeners, (listener2) => listener2(path17, curr));
51138
51452
  }
51139
51453
  })
51140
51454
  };
@@ -51162,13 +51476,13 @@ var init_handler = __esm({
51162
51476
  * @param listener on fs change
51163
51477
  * @returns closer for the watcher instance
51164
51478
  */
51165
- _watchWithNodeFs(path13, listener) {
51479
+ _watchWithNodeFs(path17, listener) {
51166
51480
  const opts = this.fsw.options;
51167
- const directory = sysPath.dirname(path13);
51168
- const basename9 = sysPath.basename(path13);
51481
+ const directory = sysPath.dirname(path17);
51482
+ const basename10 = sysPath.basename(path17);
51169
51483
  const parent = this.fsw._getWatchedDir(directory);
51170
- parent.add(basename9);
51171
- const absolutePath = sysPath.resolve(path13);
51484
+ parent.add(basename10);
51485
+ const absolutePath = sysPath.resolve(path17);
51172
51486
  const options = {
51173
51487
  persistent: opts.persistent
51174
51488
  };
@@ -51177,13 +51491,13 @@ var init_handler = __esm({
51177
51491
  let closer;
51178
51492
  if (opts.usePolling) {
51179
51493
  const enableBin = opts.interval !== opts.binaryInterval;
51180
- options.interval = enableBin && isBinaryPath(basename9) ? opts.binaryInterval : opts.interval;
51181
- closer = setFsWatchFileListener(path13, absolutePath, options, {
51494
+ options.interval = enableBin && isBinaryPath(basename10) ? opts.binaryInterval : opts.interval;
51495
+ closer = setFsWatchFileListener(path17, absolutePath, options, {
51182
51496
  listener,
51183
51497
  rawEmitter: this.fsw._emitRaw
51184
51498
  });
51185
51499
  } else {
51186
- closer = setFsWatchListener(path13, absolutePath, options, {
51500
+ closer = setFsWatchListener(path17, absolutePath, options, {
51187
51501
  listener,
51188
51502
  errHandler: this._boundHandleError,
51189
51503
  rawEmitter: this.fsw._emitRaw
@@ -51199,13 +51513,13 @@ var init_handler = __esm({
51199
51513
  if (this.fsw.closed) {
51200
51514
  return;
51201
51515
  }
51202
- const dirname11 = sysPath.dirname(file3);
51203
- const basename9 = sysPath.basename(file3);
51204
- const parent = this.fsw._getWatchedDir(dirname11);
51516
+ const dirname12 = sysPath.dirname(file3);
51517
+ const basename10 = sysPath.basename(file3);
51518
+ const parent = this.fsw._getWatchedDir(dirname12);
51205
51519
  let prevStats = stats;
51206
- if (parent.has(basename9))
51520
+ if (parent.has(basename10))
51207
51521
  return;
51208
- const listener = async (path13, newStats) => {
51522
+ const listener = async (path17, newStats) => {
51209
51523
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file3, 5))
51210
51524
  return;
51211
51525
  if (!newStats || newStats.mtimeMs === 0) {
@@ -51219,18 +51533,18 @@ var init_handler = __esm({
51219
51533
  this.fsw._emit(EV.CHANGE, file3, newStats2);
51220
51534
  }
51221
51535
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
51222
- this.fsw._closeFile(path13);
51536
+ this.fsw._closeFile(path17);
51223
51537
  prevStats = newStats2;
51224
51538
  const closer2 = this._watchWithNodeFs(file3, listener);
51225
51539
  if (closer2)
51226
- this.fsw._addPathCloser(path13, closer2);
51540
+ this.fsw._addPathCloser(path17, closer2);
51227
51541
  } else {
51228
51542
  prevStats = newStats2;
51229
51543
  }
51230
51544
  } catch (error49) {
51231
- this.fsw._remove(dirname11, basename9);
51545
+ this.fsw._remove(dirname12, basename10);
51232
51546
  }
51233
- } else if (parent.has(basename9)) {
51547
+ } else if (parent.has(basename10)) {
51234
51548
  const at = newStats.atimeMs;
51235
51549
  const mt = newStats.mtimeMs;
51236
51550
  if (!at || at <= mt || mt !== prevStats.mtimeMs) {
@@ -51255,7 +51569,7 @@ var init_handler = __esm({
51255
51569
  * @param item basename of this item
51256
51570
  * @returns true if no more processing is needed for this entry.
51257
51571
  */
51258
- async _handleSymlink(entry, directory, path13, item) {
51572
+ async _handleSymlink(entry, directory, path17, item) {
51259
51573
  if (this.fsw.closed) {
51260
51574
  return;
51261
51575
  }
@@ -51265,7 +51579,7 @@ var init_handler = __esm({
51265
51579
  this.fsw._incrReadyCount();
51266
51580
  let linkPath;
51267
51581
  try {
51268
- linkPath = await (0, import_promises2.realpath)(path13);
51582
+ linkPath = await (0, import_promises2.realpath)(path17);
51269
51583
  } catch (e) {
51270
51584
  this.fsw._emitReady();
51271
51585
  return true;
@@ -51275,12 +51589,12 @@ var init_handler = __esm({
51275
51589
  if (dir.has(item)) {
51276
51590
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
51277
51591
  this.fsw._symlinkPaths.set(full, linkPath);
51278
- this.fsw._emit(EV.CHANGE, path13, entry.stats);
51592
+ this.fsw._emit(EV.CHANGE, path17, entry.stats);
51279
51593
  }
51280
51594
  } else {
51281
51595
  dir.add(item);
51282
51596
  this.fsw._symlinkPaths.set(full, linkPath);
51283
- this.fsw._emit(EV.ADD, path13, entry.stats);
51597
+ this.fsw._emit(EV.ADD, path17, entry.stats);
51284
51598
  }
51285
51599
  this.fsw._emitReady();
51286
51600
  return true;
@@ -51309,9 +51623,9 @@ var init_handler = __esm({
51309
51623
  return;
51310
51624
  }
51311
51625
  const item = entry.path;
51312
- let path13 = sysPath.join(directory, item);
51626
+ let path17 = sysPath.join(directory, item);
51313
51627
  current.add(item);
51314
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path13, item)) {
51628
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path17, item)) {
51315
51629
  return;
51316
51630
  }
51317
51631
  if (this.fsw.closed) {
@@ -51320,8 +51634,8 @@ var init_handler = __esm({
51320
51634
  }
51321
51635
  if (item === target || !target && !previous.has(item)) {
51322
51636
  this.fsw._incrReadyCount();
51323
- path13 = sysPath.join(dir, sysPath.relative(dir, path13));
51324
- this._addToNodeFs(path13, initialAdd, wh, depth + 1);
51637
+ path17 = sysPath.join(dir, sysPath.relative(dir, path17));
51638
+ this._addToNodeFs(path17, initialAdd, wh, depth + 1);
51325
51639
  }
51326
51640
  }).on(EV.ERROR, this._boundHandleError);
51327
51641
  return new Promise((resolve9, reject) => {
@@ -51390,13 +51704,13 @@ var init_handler = __esm({
51390
51704
  * @param depth Child path actually targeted for watch
51391
51705
  * @param target Child path actually targeted for watch
51392
51706
  */
51393
- async _addToNodeFs(path13, initialAdd, priorWh, depth, target) {
51707
+ async _addToNodeFs(path17, initialAdd, priorWh, depth, target) {
51394
51708
  const ready = this.fsw._emitReady;
51395
- if (this.fsw._isIgnored(path13) || this.fsw.closed) {
51709
+ if (this.fsw._isIgnored(path17) || this.fsw.closed) {
51396
51710
  ready();
51397
51711
  return false;
51398
51712
  }
51399
- const wh = this.fsw._getWatchHelpers(path13);
51713
+ const wh = this.fsw._getWatchHelpers(path17);
51400
51714
  if (priorWh) {
51401
51715
  wh.filterPath = (entry) => priorWh.filterPath(entry);
51402
51716
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -51412,8 +51726,8 @@ var init_handler = __esm({
51412
51726
  const follow = this.fsw.options.followSymlinks;
51413
51727
  let closer;
51414
51728
  if (stats.isDirectory()) {
51415
- const absPath = sysPath.resolve(path13);
51416
- const targetPath = follow ? await (0, import_promises2.realpath)(path13) : path13;
51729
+ const absPath = sysPath.resolve(path17);
51730
+ const targetPath = follow ? await (0, import_promises2.realpath)(path17) : path17;
51417
51731
  if (this.fsw.closed)
51418
51732
  return;
51419
51733
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -51423,29 +51737,29 @@ var init_handler = __esm({
51423
51737
  this.fsw._symlinkPaths.set(absPath, targetPath);
51424
51738
  }
51425
51739
  } else if (stats.isSymbolicLink()) {
51426
- const targetPath = follow ? await (0, import_promises2.realpath)(path13) : path13;
51740
+ const targetPath = follow ? await (0, import_promises2.realpath)(path17) : path17;
51427
51741
  if (this.fsw.closed)
51428
51742
  return;
51429
51743
  const parent = sysPath.dirname(wh.watchPath);
51430
51744
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
51431
51745
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
51432
- closer = await this._handleDir(parent, stats, initialAdd, depth, path13, wh, targetPath);
51746
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path17, wh, targetPath);
51433
51747
  if (this.fsw.closed)
51434
51748
  return;
51435
51749
  if (targetPath !== void 0) {
51436
- this.fsw._symlinkPaths.set(sysPath.resolve(path13), targetPath);
51750
+ this.fsw._symlinkPaths.set(sysPath.resolve(path17), targetPath);
51437
51751
  }
51438
51752
  } else {
51439
51753
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
51440
51754
  }
51441
51755
  ready();
51442
51756
  if (closer)
51443
- this.fsw._addPathCloser(path13, closer);
51757
+ this.fsw._addPathCloser(path17, closer);
51444
51758
  return false;
51445
51759
  } catch (error49) {
51446
51760
  if (this.fsw._handleError(error49)) {
51447
51761
  ready();
51448
- return path13;
51762
+ return path17;
51449
51763
  }
51450
51764
  }
51451
51765
  }
@@ -51480,26 +51794,26 @@ function createPattern(matcher) {
51480
51794
  }
51481
51795
  return () => false;
51482
51796
  }
51483
- function normalizePath(path13) {
51484
- if (typeof path13 !== "string")
51797
+ function normalizePath(path17) {
51798
+ if (typeof path17 !== "string")
51485
51799
  throw new Error("string expected");
51486
- path13 = sysPath2.normalize(path13);
51487
- path13 = path13.replace(/\\/g, "/");
51800
+ path17 = sysPath2.normalize(path17);
51801
+ path17 = path17.replace(/\\/g, "/");
51488
51802
  let prepend = false;
51489
- if (path13.startsWith("//"))
51803
+ if (path17.startsWith("//"))
51490
51804
  prepend = true;
51491
51805
  const DOUBLE_SLASH_RE2 = /\/\//;
51492
- while (path13.match(DOUBLE_SLASH_RE2))
51493
- path13 = path13.replace(DOUBLE_SLASH_RE2, "/");
51806
+ while (path17.match(DOUBLE_SLASH_RE2))
51807
+ path17 = path17.replace(DOUBLE_SLASH_RE2, "/");
51494
51808
  if (prepend)
51495
- path13 = "/" + path13;
51496
- return path13;
51809
+ path17 = "/" + path17;
51810
+ return path17;
51497
51811
  }
51498
51812
  function matchPatterns(patterns, testString, stats) {
51499
- const path13 = normalizePath(testString);
51813
+ const path17 = normalizePath(testString);
51500
51814
  for (let index = 0; index < patterns.length; index++) {
51501
51815
  const pattern = patterns[index];
51502
- if (pattern(path13, stats)) {
51816
+ if (pattern(path17, stats)) {
51503
51817
  return true;
51504
51818
  }
51505
51819
  }
@@ -51563,19 +51877,19 @@ var init_esm3 = __esm({
51563
51877
  }
51564
51878
  return str2;
51565
51879
  };
51566
- normalizePathToUnix = (path13) => toUnix(sysPath2.normalize(toUnix(path13)));
51567
- normalizeIgnored = (cwd = "") => (path13) => {
51568
- if (typeof path13 === "string") {
51569
- return normalizePathToUnix(sysPath2.isAbsolute(path13) ? path13 : sysPath2.join(cwd, path13));
51880
+ normalizePathToUnix = (path17) => toUnix(sysPath2.normalize(toUnix(path17)));
51881
+ normalizeIgnored = (cwd = "") => (path17) => {
51882
+ if (typeof path17 === "string") {
51883
+ return normalizePathToUnix(sysPath2.isAbsolute(path17) ? path17 : sysPath2.join(cwd, path17));
51570
51884
  } else {
51571
- return path13;
51885
+ return path17;
51572
51886
  }
51573
51887
  };
51574
- getAbsolutePath = (path13, cwd) => {
51575
- if (sysPath2.isAbsolute(path13)) {
51576
- return path13;
51888
+ getAbsolutePath = (path17, cwd) => {
51889
+ if (sysPath2.isAbsolute(path17)) {
51890
+ return path17;
51577
51891
  }
51578
- return sysPath2.join(cwd, path13);
51892
+ return sysPath2.join(cwd, path17);
51579
51893
  };
51580
51894
  EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
51581
51895
  DirEntry = class {
@@ -51630,10 +51944,10 @@ var init_esm3 = __esm({
51630
51944
  STAT_METHOD_F = "stat";
51631
51945
  STAT_METHOD_L = "lstat";
51632
51946
  WatchHelper = class {
51633
- constructor(path13, follow, fsw) {
51947
+ constructor(path17, follow, fsw) {
51634
51948
  this.fsw = fsw;
51635
- const watchPath = path13;
51636
- this.path = path13 = path13.replace(REPLACER_RE, "");
51949
+ const watchPath = path17;
51950
+ this.path = path17 = path17.replace(REPLACER_RE, "");
51637
51951
  this.watchPath = watchPath;
51638
51952
  this.fullWatchPath = sysPath2.resolve(watchPath);
51639
51953
  this.dirParts = [];
@@ -51755,20 +52069,20 @@ var init_esm3 = __esm({
51755
52069
  this._closePromise = void 0;
51756
52070
  let paths = unifyPaths(paths_);
51757
52071
  if (cwd) {
51758
- paths = paths.map((path13) => {
51759
- const absPath = getAbsolutePath(path13, cwd);
52072
+ paths = paths.map((path17) => {
52073
+ const absPath = getAbsolutePath(path17, cwd);
51760
52074
  return absPath;
51761
52075
  });
51762
52076
  }
51763
- paths.forEach((path13) => {
51764
- this._removeIgnoredPath(path13);
52077
+ paths.forEach((path17) => {
52078
+ this._removeIgnoredPath(path17);
51765
52079
  });
51766
52080
  this._userIgnored = void 0;
51767
52081
  if (!this._readyCount)
51768
52082
  this._readyCount = 0;
51769
52083
  this._readyCount += paths.length;
51770
- Promise.all(paths.map(async (path13) => {
51771
- const res = await this._nodeFsHandler._addToNodeFs(path13, !_internal, void 0, 0, _origAdd);
52084
+ Promise.all(paths.map(async (path17) => {
52085
+ const res = await this._nodeFsHandler._addToNodeFs(path17, !_internal, void 0, 0, _origAdd);
51772
52086
  if (res)
51773
52087
  this._emitReady();
51774
52088
  return res;
@@ -51790,17 +52104,17 @@ var init_esm3 = __esm({
51790
52104
  return this;
51791
52105
  const paths = unifyPaths(paths_);
51792
52106
  const { cwd } = this.options;
51793
- paths.forEach((path13) => {
51794
- if (!sysPath2.isAbsolute(path13) && !this._closers.has(path13)) {
52107
+ paths.forEach((path17) => {
52108
+ if (!sysPath2.isAbsolute(path17) && !this._closers.has(path17)) {
51795
52109
  if (cwd)
51796
- path13 = sysPath2.join(cwd, path13);
51797
- path13 = sysPath2.resolve(path13);
52110
+ path17 = sysPath2.join(cwd, path17);
52111
+ path17 = sysPath2.resolve(path17);
51798
52112
  }
51799
- this._closePath(path13);
51800
- this._addIgnoredPath(path13);
51801
- if (this._watched.has(path13)) {
52113
+ this._closePath(path17);
52114
+ this._addIgnoredPath(path17);
52115
+ if (this._watched.has(path17)) {
51802
52116
  this._addIgnoredPath({
51803
- path: path13,
52117
+ path: path17,
51804
52118
  recursive: true
51805
52119
  });
51806
52120
  }
@@ -51864,38 +52178,38 @@ var init_esm3 = __esm({
51864
52178
  * @param stats arguments to be passed with event
51865
52179
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
51866
52180
  */
51867
- async _emit(event, path13, stats) {
52181
+ async _emit(event, path17, stats) {
51868
52182
  if (this.closed)
51869
52183
  return;
51870
52184
  const opts = this.options;
51871
52185
  if (isWindows)
51872
- path13 = sysPath2.normalize(path13);
52186
+ path17 = sysPath2.normalize(path17);
51873
52187
  if (opts.cwd)
51874
- path13 = sysPath2.relative(opts.cwd, path13);
51875
- const args = [path13];
52188
+ path17 = sysPath2.relative(opts.cwd, path17);
52189
+ const args = [path17];
51876
52190
  if (stats != null)
51877
52191
  args.push(stats);
51878
52192
  const awf = opts.awaitWriteFinish;
51879
52193
  let pw;
51880
- if (awf && (pw = this._pendingWrites.get(path13))) {
52194
+ if (awf && (pw = this._pendingWrites.get(path17))) {
51881
52195
  pw.lastChange = /* @__PURE__ */ new Date();
51882
52196
  return this;
51883
52197
  }
51884
52198
  if (opts.atomic) {
51885
52199
  if (event === EVENTS.UNLINK) {
51886
- this._pendingUnlinks.set(path13, [event, ...args]);
52200
+ this._pendingUnlinks.set(path17, [event, ...args]);
51887
52201
  setTimeout(() => {
51888
- this._pendingUnlinks.forEach((entry, path14) => {
52202
+ this._pendingUnlinks.forEach((entry, path18) => {
51889
52203
  this.emit(...entry);
51890
52204
  this.emit(EVENTS.ALL, ...entry);
51891
- this._pendingUnlinks.delete(path14);
52205
+ this._pendingUnlinks.delete(path18);
51892
52206
  });
51893
52207
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
51894
52208
  return this;
51895
52209
  }
51896
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path13)) {
52210
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path17)) {
51897
52211
  event = EVENTS.CHANGE;
51898
- this._pendingUnlinks.delete(path13);
52212
+ this._pendingUnlinks.delete(path17);
51899
52213
  }
51900
52214
  }
51901
52215
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -51913,16 +52227,16 @@ var init_esm3 = __esm({
51913
52227
  this.emitWithAll(event, args);
51914
52228
  }
51915
52229
  };
51916
- this._awaitWriteFinish(path13, awf.stabilityThreshold, event, awfEmit);
52230
+ this._awaitWriteFinish(path17, awf.stabilityThreshold, event, awfEmit);
51917
52231
  return this;
51918
52232
  }
51919
52233
  if (event === EVENTS.CHANGE) {
51920
- const isThrottled = !this._throttle(EVENTS.CHANGE, path13, 50);
52234
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path17, 50);
51921
52235
  if (isThrottled)
51922
52236
  return this;
51923
52237
  }
51924
52238
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
51925
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path13) : path13;
52239
+ const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path17) : path17;
51926
52240
  let stats2;
51927
52241
  try {
51928
52242
  stats2 = await (0, import_promises3.stat)(fullPath);
@@ -51953,23 +52267,23 @@ var init_esm3 = __esm({
51953
52267
  * @param timeout duration of time to suppress duplicate actions
51954
52268
  * @returns tracking object or false if action should be suppressed
51955
52269
  */
51956
- _throttle(actionType, path13, timeout) {
52270
+ _throttle(actionType, path17, timeout) {
51957
52271
  if (!this._throttled.has(actionType)) {
51958
52272
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
51959
52273
  }
51960
52274
  const action = this._throttled.get(actionType);
51961
52275
  if (!action)
51962
52276
  throw new Error("invalid throttle");
51963
- const actionPath = action.get(path13);
52277
+ const actionPath = action.get(path17);
51964
52278
  if (actionPath) {
51965
52279
  actionPath.count++;
51966
52280
  return false;
51967
52281
  }
51968
52282
  let timeoutObject;
51969
52283
  const clear = () => {
51970
- const item = action.get(path13);
52284
+ const item = action.get(path17);
51971
52285
  const count = item ? item.count : 0;
51972
- action.delete(path13);
52286
+ action.delete(path17);
51973
52287
  clearTimeout(timeoutObject);
51974
52288
  if (item)
51975
52289
  clearTimeout(item.timeoutObject);
@@ -51977,7 +52291,7 @@ var init_esm3 = __esm({
51977
52291
  };
51978
52292
  timeoutObject = setTimeout(clear, timeout);
51979
52293
  const thr = { timeoutObject, clear, count: 0 };
51980
- action.set(path13, thr);
52294
+ action.set(path17, thr);
51981
52295
  return thr;
51982
52296
  }
51983
52297
  _incrReadyCount() {
@@ -51991,44 +52305,44 @@ var init_esm3 = __esm({
51991
52305
  * @param event
51992
52306
  * @param awfEmit Callback to be called when ready for event to be emitted.
51993
52307
  */
51994
- _awaitWriteFinish(path13, threshold, event, awfEmit) {
52308
+ _awaitWriteFinish(path17, threshold, event, awfEmit) {
51995
52309
  const awf = this.options.awaitWriteFinish;
51996
52310
  if (typeof awf !== "object")
51997
52311
  return;
51998
52312
  const pollInterval = awf.pollInterval;
51999
52313
  let timeoutHandler;
52000
- let fullPath = path13;
52001
- if (this.options.cwd && !sysPath2.isAbsolute(path13)) {
52002
- fullPath = sysPath2.join(this.options.cwd, path13);
52314
+ let fullPath = path17;
52315
+ if (this.options.cwd && !sysPath2.isAbsolute(path17)) {
52316
+ fullPath = sysPath2.join(this.options.cwd, path17);
52003
52317
  }
52004
52318
  const now = /* @__PURE__ */ new Date();
52005
52319
  const writes = this._pendingWrites;
52006
52320
  function awaitWriteFinishFn(prevStat) {
52007
52321
  (0, import_fs2.stat)(fullPath, (err, curStat) => {
52008
- if (err || !writes.has(path13)) {
52322
+ if (err || !writes.has(path17)) {
52009
52323
  if (err && err.code !== "ENOENT")
52010
52324
  awfEmit(err);
52011
52325
  return;
52012
52326
  }
52013
52327
  const now2 = Number(/* @__PURE__ */ new Date());
52014
52328
  if (prevStat && curStat.size !== prevStat.size) {
52015
- writes.get(path13).lastChange = now2;
52329
+ writes.get(path17).lastChange = now2;
52016
52330
  }
52017
- const pw = writes.get(path13);
52331
+ const pw = writes.get(path17);
52018
52332
  const df = now2 - pw.lastChange;
52019
52333
  if (df >= threshold) {
52020
- writes.delete(path13);
52334
+ writes.delete(path17);
52021
52335
  awfEmit(void 0, curStat);
52022
52336
  } else {
52023
52337
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
52024
52338
  }
52025
52339
  });
52026
52340
  }
52027
- if (!writes.has(path13)) {
52028
- writes.set(path13, {
52341
+ if (!writes.has(path17)) {
52342
+ writes.set(path17, {
52029
52343
  lastChange: now,
52030
52344
  cancelWait: () => {
52031
- writes.delete(path13);
52345
+ writes.delete(path17);
52032
52346
  clearTimeout(timeoutHandler);
52033
52347
  return event;
52034
52348
  }
@@ -52039,8 +52353,8 @@ var init_esm3 = __esm({
52039
52353
  /**
52040
52354
  * Determines whether user has asked to ignore this path.
52041
52355
  */
52042
- _isIgnored(path13, stats) {
52043
- if (this.options.atomic && DOT_RE.test(path13))
52356
+ _isIgnored(path17, stats) {
52357
+ if (this.options.atomic && DOT_RE.test(path17))
52044
52358
  return true;
52045
52359
  if (!this._userIgnored) {
52046
52360
  const { cwd } = this.options;
@@ -52050,17 +52364,17 @@ var init_esm3 = __esm({
52050
52364
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
52051
52365
  this._userIgnored = anymatch(list, void 0);
52052
52366
  }
52053
- return this._userIgnored(path13, stats);
52367
+ return this._userIgnored(path17, stats);
52054
52368
  }
52055
- _isntIgnored(path13, stat4) {
52056
- return !this._isIgnored(path13, stat4);
52369
+ _isntIgnored(path17, stat4) {
52370
+ return !this._isIgnored(path17, stat4);
52057
52371
  }
52058
52372
  /**
52059
52373
  * Provides a set of common helpers and properties relating to symlink handling.
52060
52374
  * @param path file or directory pattern being watched
52061
52375
  */
52062
- _getWatchHelpers(path13) {
52063
- return new WatchHelper(path13, this.options.followSymlinks, this);
52376
+ _getWatchHelpers(path17) {
52377
+ return new WatchHelper(path17, this.options.followSymlinks, this);
52064
52378
  }
52065
52379
  // Directory helpers
52066
52380
  // -----------------
@@ -52092,63 +52406,63 @@ var init_esm3 = __esm({
52092
52406
  * @param item base path of item/directory
52093
52407
  */
52094
52408
  _remove(directory, item, isDirectory) {
52095
- const path13 = sysPath2.join(directory, item);
52096
- const fullPath = sysPath2.resolve(path13);
52097
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path13) || this._watched.has(fullPath);
52098
- if (!this._throttle("remove", path13, 100))
52409
+ const path17 = sysPath2.join(directory, item);
52410
+ const fullPath = sysPath2.resolve(path17);
52411
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path17) || this._watched.has(fullPath);
52412
+ if (!this._throttle("remove", path17, 100))
52099
52413
  return;
52100
52414
  if (!isDirectory && this._watched.size === 1) {
52101
52415
  this.add(directory, item, true);
52102
52416
  }
52103
- const wp = this._getWatchedDir(path13);
52417
+ const wp = this._getWatchedDir(path17);
52104
52418
  const nestedDirectoryChildren = wp.getChildren();
52105
- nestedDirectoryChildren.forEach((nested) => this._remove(path13, nested));
52419
+ nestedDirectoryChildren.forEach((nested) => this._remove(path17, nested));
52106
52420
  const parent = this._getWatchedDir(directory);
52107
52421
  const wasTracked = parent.has(item);
52108
52422
  parent.remove(item);
52109
52423
  if (this._symlinkPaths.has(fullPath)) {
52110
52424
  this._symlinkPaths.delete(fullPath);
52111
52425
  }
52112
- let relPath = path13;
52426
+ let relPath = path17;
52113
52427
  if (this.options.cwd)
52114
- relPath = sysPath2.relative(this.options.cwd, path13);
52428
+ relPath = sysPath2.relative(this.options.cwd, path17);
52115
52429
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
52116
52430
  const event = this._pendingWrites.get(relPath).cancelWait();
52117
52431
  if (event === EVENTS.ADD)
52118
52432
  return;
52119
52433
  }
52120
- this._watched.delete(path13);
52434
+ this._watched.delete(path17);
52121
52435
  this._watched.delete(fullPath);
52122
52436
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
52123
- if (wasTracked && !this._isIgnored(path13))
52124
- this._emit(eventName, path13);
52125
- this._closePath(path13);
52437
+ if (wasTracked && !this._isIgnored(path17))
52438
+ this._emit(eventName, path17);
52439
+ this._closePath(path17);
52126
52440
  }
52127
52441
  /**
52128
52442
  * Closes all watchers for a path
52129
52443
  */
52130
- _closePath(path13) {
52131
- this._closeFile(path13);
52132
- const dir = sysPath2.dirname(path13);
52133
- this._getWatchedDir(dir).remove(sysPath2.basename(path13));
52444
+ _closePath(path17) {
52445
+ this._closeFile(path17);
52446
+ const dir = sysPath2.dirname(path17);
52447
+ this._getWatchedDir(dir).remove(sysPath2.basename(path17));
52134
52448
  }
52135
52449
  /**
52136
52450
  * Closes only file-specific watchers
52137
52451
  */
52138
- _closeFile(path13) {
52139
- const closers = this._closers.get(path13);
52452
+ _closeFile(path17) {
52453
+ const closers = this._closers.get(path17);
52140
52454
  if (!closers)
52141
52455
  return;
52142
52456
  closers.forEach((closer) => closer());
52143
- this._closers.delete(path13);
52457
+ this._closers.delete(path17);
52144
52458
  }
52145
- _addPathCloser(path13, closer) {
52459
+ _addPathCloser(path17, closer) {
52146
52460
  if (!closer)
52147
52461
  return;
52148
- let list = this._closers.get(path13);
52462
+ let list = this._closers.get(path17);
52149
52463
  if (!list) {
52150
52464
  list = [];
52151
- this._closers.set(path13, list);
52465
+ this._closers.set(path17, list);
52152
52466
  }
52153
52467
  list.push(closer);
52154
52468
  }
@@ -52174,13 +52488,21 @@ var init_esm3 = __esm({
52174
52488
  }
52175
52489
  });
52176
52490
 
52491
+ // src/standalone/webhooks.ts
52492
+ var init_webhooks2 = __esm({
52493
+ "src/standalone/webhooks.ts"() {
52494
+ "use strict";
52495
+ init_webhooks();
52496
+ }
52497
+ });
52498
+
52177
52499
  // src/standalone/server.ts
52178
52500
  var server_exports = {};
52179
52501
  __export(server_exports, {
52180
52502
  startServer: () => startServer
52181
52503
  });
52182
52504
  function startServer(kanbanDir, port, webviewDir) {
52183
- const absoluteKanbanDir = path10.resolve(kanbanDir);
52505
+ const absoluteKanbanDir = path14.resolve(kanbanDir);
52184
52506
  let cards = [];
52185
52507
  let migrating = false;
52186
52508
  let currentEditingCardId = null;
@@ -52197,15 +52519,15 @@ function startServer(kanbanDir, port, webviewDir) {
52197
52519
  }
52198
52520
  if (tempFilePath) {
52199
52521
  try {
52200
- fs8.unlinkSync(tempFilePath);
52522
+ fs9.unlinkSync(tempFilePath);
52201
52523
  } catch {
52202
52524
  }
52203
52525
  tempFilePath = void 0;
52204
52526
  }
52205
52527
  tempFileCardId = void 0;
52206
52528
  }
52207
- const resolvedWebviewDir = webviewDir || path10.join(__dirname, "standalone-webview");
52208
- const workspaceRoot = path10.dirname(absoluteKanbanDir);
52529
+ const resolvedWebviewDir = webviewDir || path14.join(__dirname, "standalone-webview");
52530
+ const workspaceRoot = path14.dirname(absoluteKanbanDir);
52209
52531
  const sdk = new KanbanSDK(absoluteKanbanDir, {
52210
52532
  onEvent: (event, data) => {
52211
52533
  fireWebhooks(workspaceRoot, event, data);
@@ -52413,15 +52735,8 @@ function startServer(kanbanDir, port, webviewDir) {
52413
52735
  }
52414
52736
  }
52415
52737
  function doAddColumn(name, color) {
52416
- const existingColumns = sdk.listColumns(currentBoardId);
52417
- const id = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
52418
- let uniqueId = id;
52419
- let counter = 1;
52420
- while (existingColumns.some((c) => c.id === uniqueId)) {
52421
- uniqueId = `${id}-${counter++}`;
52422
- }
52423
- const column = { id: uniqueId, name, color };
52424
- sdk.addColumn(column, currentBoardId);
52738
+ const columns = sdk.addColumn({ id: "", name, color }, currentBoardId);
52739
+ const column = columns[columns.length - 1];
52425
52740
  broadcast(buildInitMessage());
52426
52741
  return column;
52427
52742
  }
@@ -52473,11 +52788,11 @@ function startServer(kanbanDir, port, webviewDir) {
52473
52788
  if (!card)
52474
52789
  return false;
52475
52790
  const cardDir = sdk.storageEngine.getCardDir(card);
52476
- fs8.mkdirSync(cardDir, { recursive: true });
52477
- fs8.writeFileSync(path10.join(cardDir, filename), fileData);
52791
+ fs9.mkdirSync(cardDir, { recursive: true });
52792
+ fs9.writeFileSync(path14.join(cardDir, filename), fileData);
52478
52793
  migrating = true;
52479
52794
  try {
52480
- const updated = await sdk.addAttachment(cardId, path10.join(cardDir, filename), currentBoardId);
52795
+ const updated = await sdk.addAttachment(cardId, path14.join(cardDir, filename), currentBoardId);
52481
52796
  lastWrittenContent = serializeCard(updated);
52482
52797
  await loadCards();
52483
52798
  } finally {
@@ -52880,10 +53195,9 @@ function startServer(kanbanDir, port, webviewDir) {
52880
53195
  break;
52881
53196
  case "createBoard": {
52882
53197
  const boardName = msg.name;
52883
- const boardId = generateSlug(boardName) || "board";
52884
53198
  try {
52885
- sdk.createBoard(boardId, boardName);
52886
- currentBoardId = boardId;
53199
+ const createdBoard = sdk.createBoard("", boardName);
53200
+ currentBoardId = createdBoard.id;
52887
53201
  migrating = true;
52888
53202
  try {
52889
53203
  await loadCards();
@@ -53027,13 +53341,15 @@ function startServer(kanbanDir, port, webviewDir) {
53027
53341
  const { boardId } = params;
53028
53342
  const body = await readBody(req);
53029
53343
  const actions = body.actions;
53030
- const config3 = readConfig(workspaceRoot);
53031
- const board = config3.boards[boardId];
53032
- if (!board)
53033
- return jsonError(res, 404, `Board not found: ${boardId}`);
53034
- board.actions = actions;
53035
- writeConfig(workspaceRoot, config3);
53036
- return jsonOk(res, actions);
53344
+ const existing = sdk.getBoardActions(boardId);
53345
+ for (const key of Object.keys(existing)) {
53346
+ if (!(key in actions))
53347
+ sdk.removeBoardAction(boardId, key);
53348
+ }
53349
+ for (const [key, title] of Object.entries(actions)) {
53350
+ sdk.addBoardAction(boardId, key, title);
53351
+ }
53352
+ return jsonOk(res, sdk.getBoardActions(boardId));
53037
53353
  } catch (err) {
53038
53354
  return jsonError(res, 400, String(err));
53039
53355
  }
@@ -53092,7 +53408,18 @@ function startServer(kanbanDir, port, webviewDir) {
53092
53408
  try {
53093
53409
  const { boardId } = params;
53094
53410
  const boardColumns = sdk.listColumns(boardId);
53095
- const boardTasks = await sdk.listCards(boardColumns.map((c) => c.id), boardId);
53411
+ const metaFilter = {};
53412
+ for (const [param, value] of url3.searchParams.entries()) {
53413
+ if (param.startsWith("meta."))
53414
+ metaFilter[param.slice(5)] = value;
53415
+ }
53416
+ const sortParam = url3.searchParams.get("sort");
53417
+ const boardTasks = await sdk.listCards(
53418
+ boardColumns.map((c) => c.id),
53419
+ boardId,
53420
+ Object.keys(metaFilter).length > 0 ? metaFilter : void 0,
53421
+ sortParam || void 0
53422
+ );
53096
53423
  let result = boardTasks.map(sanitizeCard);
53097
53424
  if (url3.searchParams.get("includeDeleted") !== "true") {
53098
53425
  result = result.filter((f) => f.status !== "deleted");
@@ -53114,14 +53441,6 @@ function startServer(kanbanDir, port, webviewDir) {
53114
53441
  const groupLabels = sdk.getLabelsInGroup(labelGroup);
53115
53442
  result = result.filter((f) => f.labels.some((l) => groupLabels.includes(l)));
53116
53443
  }
53117
- const metaFilter = {};
53118
- for (const [param, value] of url3.searchParams.entries()) {
53119
- if (param.startsWith("meta."))
53120
- metaFilter[param.slice(5)] = value;
53121
- }
53122
- if (Object.keys(metaFilter).length > 0)
53123
- result = result.filter((f) => matchesMetaFilter(f.metadata, metaFilter));
53124
- result = applySortParam(result, url3.searchParams.get("sort"));
53125
53444
  return jsonOk(res, result);
53126
53445
  } catch (err) {
53127
53446
  return jsonError(res, 400, String(err));
@@ -53239,6 +53558,12 @@ function startServer(kanbanDir, port, webviewDir) {
53239
53558
  }
53240
53559
  params = route("GET", "/api/tasks");
53241
53560
  if (params) {
53561
+ const metaFilter2 = {};
53562
+ for (const [param, value] of url3.searchParams.entries()) {
53563
+ if (param.startsWith("meta."))
53564
+ metaFilter2[param.slice(5)] = value;
53565
+ }
53566
+ const sortParam2 = url3.searchParams.get("sort");
53242
53567
  await loadCards();
53243
53568
  let result = cards.map(sanitizeCard);
53244
53569
  if (url3.searchParams.get("includeDeleted") !== "true") {
@@ -53261,14 +53586,9 @@ function startServer(kanbanDir, port, webviewDir) {
53261
53586
  const groupLabels = sdk.getLabelsInGroup(labelGroup);
53262
53587
  result = result.filter((f) => f.labels.some((l) => groupLabels.includes(l)));
53263
53588
  }
53264
- const metaFilter = {};
53265
- for (const [param, value] of url3.searchParams.entries()) {
53266
- if (param.startsWith("meta."))
53267
- metaFilter[param.slice(5)] = value;
53268
- }
53269
- if (Object.keys(metaFilter).length > 0)
53270
- result = result.filter((f) => matchesMetaFilter(f.metadata, metaFilter));
53271
- result = applySortParam(result, url3.searchParams.get("sort"));
53589
+ if (Object.keys(metaFilter2).length > 0)
53590
+ result = result.filter((f) => matchesMetaFilter(f.metadata, metaFilter2));
53591
+ result = applySortParam(result, sortParam2);
53272
53592
  return jsonOk(res, result);
53273
53593
  }
53274
53594
  params = route("POST", "/api/tasks");
@@ -53388,7 +53708,7 @@ function startServer(kanbanDir, port, webviewDir) {
53388
53708
  const rawPath = url3.searchParams.get("path") ?? "";
53389
53709
  if (!rawPath)
53390
53710
  return jsonError(res, 400, "path is required");
53391
- const resolved = /^([/~]|[A-Za-z]:[/\\])/.test(rawPath) ? rawPath.replace(/^~/, process.env.HOME ?? os.homedir()) : path10.resolve(workspaceRoot, rawPath);
53711
+ const resolved = /^([/~]|[A-Za-z]:[/\\])/.test(rawPath) ? rawPath.replace(/^~/, process.env.HOME ?? os.homedir()) : path14.resolve(workspaceRoot, rawPath);
53392
53712
  return jsonOk(res, { path: resolved });
53393
53713
  }
53394
53714
  params = route("GET", "/api/card-file");
@@ -53407,16 +53727,16 @@ function startServer(kanbanDir, port, webviewDir) {
53407
53727
  if (tempFileCardId === cardId && tempFilePath) {
53408
53728
  tempFileWriting = true;
53409
53729
  try {
53410
- fs8.writeFileSync(tempFilePath, serializeCard(openCard), "utf-8");
53730
+ fs9.writeFileSync(tempFilePath, serializeCard(openCard), "utf-8");
53411
53731
  } finally {
53412
53732
  tempFileWriting = false;
53413
53733
  }
53414
53734
  return jsonOk(res, { path: tempFilePath });
53415
53735
  }
53416
- const tmpPath = path10.join(os.tmpdir(), `kanban-card-${openCard.id}.md`);
53736
+ const tmpPath = path14.join(os.tmpdir(), `kanban-card-${openCard.id}.md`);
53417
53737
  tempFileWriting = true;
53418
53738
  try {
53419
- fs8.writeFileSync(tmpPath, serializeCard(openCard), "utf-8");
53739
+ fs9.writeFileSync(tmpPath, serializeCard(openCard), "utf-8");
53420
53740
  } finally {
53421
53741
  tempFileWriting = false;
53422
53742
  }
@@ -53435,7 +53755,7 @@ function startServer(kanbanDir, port, webviewDir) {
53435
53755
  if (!cid || !fp)
53436
53756
  return;
53437
53757
  try {
53438
- const raw = fs8.readFileSync(fp, "utf-8");
53758
+ const raw = fs9.readFileSync(fp, "utf-8");
53439
53759
  const parsed = parseCardFile(raw, `${cid}.md`);
53440
53760
  if (!parsed)
53441
53761
  return;
@@ -53475,15 +53795,15 @@ function startServer(kanbanDir, port, webviewDir) {
53475
53795
  if (!card)
53476
53796
  return jsonError(res, 404, "Task not found");
53477
53797
  const cardDir = sdk.storageEngine.getCardDir(card);
53478
- const attachmentPath = path10.resolve(cardDir, attachName);
53798
+ const attachmentPath = path14.resolve(cardDir, attachName);
53479
53799
  if (!attachmentPath.startsWith(absoluteKanbanDir)) {
53480
53800
  res.writeHead(403, { "Content-Type": "text/plain" });
53481
53801
  res.end("Forbidden");
53482
53802
  return;
53483
53803
  }
53484
- const ext2 = path10.extname(attachName);
53804
+ const ext2 = path14.extname(attachName);
53485
53805
  const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
53486
- fs8.readFile(attachmentPath, (err, data) => {
53806
+ fs9.readFile(attachmentPath, (err, data) => {
53487
53807
  if (err) {
53488
53808
  res.writeHead(404);
53489
53809
  res.end("File not found");
@@ -53687,7 +54007,7 @@ function startServer(kanbanDir, port, webviewDir) {
53687
54007
  }
53688
54008
  params = route("GET", "/api/webhooks");
53689
54009
  if (params) {
53690
- return jsonOk(res, loadWebhooks(workspaceRoot));
54010
+ return jsonOk(res, sdk.listWebhooks());
53691
54011
  }
53692
54012
  params = route("POST", "/api/webhooks");
53693
54013
  if (params) {
@@ -53701,7 +54021,7 @@ function startServer(kanbanDir, port, webviewDir) {
53701
54021
  if (!events || !Array.isArray(events) || events.length === 0) {
53702
54022
  return jsonError(res, 400, "events array is required");
53703
54023
  }
53704
- const webhook = createWebhook(workspaceRoot, { url: webhookUrl, events, secret });
54024
+ const webhook = sdk.createWebhook({ url: webhookUrl, events, secret });
53705
54025
  return jsonOk(res, webhook, 201);
53706
54026
  } catch (err) {
53707
54027
  return jsonError(res, 400, String(err));
@@ -53712,7 +54032,7 @@ function startServer(kanbanDir, port, webviewDir) {
53712
54032
  try {
53713
54033
  const { id } = params;
53714
54034
  const body = await readBody(req);
53715
- const webhook = updateWebhook(workspaceRoot, id, body);
54035
+ const webhook = sdk.updateWebhook(id, body);
53716
54036
  if (!webhook)
53717
54037
  return jsonError(res, 404, "Webhook not found");
53718
54038
  return jsonOk(res, webhook);
@@ -53723,7 +54043,7 @@ function startServer(kanbanDir, port, webviewDir) {
53723
54043
  params = route("DELETE", "/api/webhooks/:id");
53724
54044
  if (params) {
53725
54045
  const { id } = params;
53726
- const ok = deleteWebhook(workspaceRoot, id);
54046
+ const ok = sdk.deleteWebhook(id);
53727
54047
  if (!ok)
53728
54048
  return jsonError(res, 404, "Webhook not found");
53729
54049
  return jsonOk(res, { deleted: true });
@@ -53866,15 +54186,15 @@ function startServer(kanbanDir, port, webviewDir) {
53866
54186
  return;
53867
54187
  }
53868
54188
  const cardDir = sdk.storageEngine.getCardDir(card);
53869
- const attachmentPath = path10.resolve(cardDir, filename);
54189
+ const attachmentPath = path14.resolve(cardDir, filename);
53870
54190
  if (!attachmentPath.startsWith(absoluteKanbanDir)) {
53871
54191
  res.writeHead(403, { "Content-Type": "text/plain" });
53872
54192
  res.end("Forbidden");
53873
54193
  return;
53874
54194
  }
53875
- const ext2 = path10.extname(filename);
54195
+ const ext2 = path14.extname(filename);
53876
54196
  const contentType2 = MIME_TYPES[ext2] || "application/octet-stream";
53877
- fs8.readFile(attachmentPath, (err, data) => {
54197
+ fs9.readFile(attachmentPath, (err, data) => {
53878
54198
  if (err) {
53879
54199
  res.writeHead(404, { "Content-Type": "text/plain" });
53880
54200
  res.end("File not found");
@@ -53888,15 +54208,15 @@ function startServer(kanbanDir, port, webviewDir) {
53888
54208
  if (pathname.startsWith("/api/")) {
53889
54209
  return jsonError(res, 404, "Not found");
53890
54210
  }
53891
- const filePath = path10.join(resolvedWebviewDir, pathname === "/" ? "index.html" : pathname);
53892
- if (!path10.extname(filePath)) {
54211
+ const filePath = path14.join(resolvedWebviewDir, pathname === "/" ? "index.html" : pathname);
54212
+ if (!path14.extname(filePath)) {
53893
54213
  res.writeHead(200, { "Content-Type": "text/html" });
53894
54214
  res.end(indexHtml);
53895
54215
  return;
53896
54216
  }
53897
- const ext = path10.extname(filePath);
54217
+ const ext = path14.extname(filePath);
53898
54218
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
53899
- fs8.readFile(filePath, (err, data) => {
54219
+ fs9.readFile(filePath, (err, data) => {
53900
54220
  if (err) {
53901
54221
  res.writeHead(200, { "Content-Type": "text/html" });
53902
54222
  res.end(indexHtml);
@@ -53918,7 +54238,7 @@ function startServer(kanbanDir, port, webviewDir) {
53918
54238
  });
53919
54239
  });
53920
54240
  let debounceTimer;
53921
- fs8.mkdirSync(absoluteKanbanDir, { recursive: true });
54241
+ fs9.mkdirSync(absoluteKanbanDir, { recursive: true });
53922
54242
  const handleFileChange = (changedPath) => {
53923
54243
  if (changedPath && !changedPath.endsWith(".md"))
53924
54244
  return;
@@ -53964,13 +54284,17 @@ function startServer(kanbanDir, port, webviewDir) {
53964
54284
  }, 100);
53965
54285
  };
53966
54286
  if (sdk.storageEngine.type === "markdown") {
54287
+ let watcherReady = false;
53967
54288
  const watcher = esm_default.watch(absoluteKanbanDir, {
53968
54289
  ignoreInitial: true,
53969
54290
  awaitWriteFinish: { stabilityThreshold: 100 }
53970
54291
  });
53971
- watcher.on("change", handleFileChange);
53972
- watcher.on("add", handleFileChange);
53973
- watcher.on("unlink", handleFileChange);
54292
+ watcher.on("ready", () => {
54293
+ watcherReady = true;
54294
+ });
54295
+ watcher.on("change", (p) => watcherReady && handleFileChange(p));
54296
+ watcher.on("add", (p) => watcherReady && handleFileChange(p));
54297
+ watcher.on("unlink", (p) => watcherReady && handleFileChange(p));
53974
54298
  server.on("close", () => {
53975
54299
  watcher.close();
53976
54300
  wss.close();
@@ -53988,22 +54312,21 @@ function startServer(kanbanDir, port, webviewDir) {
53988
54312
  });
53989
54313
  return server;
53990
54314
  }
53991
- var http2, fs8, os, path10, MIME_TYPES;
54315
+ var http2, fs9, os, path14, MIME_TYPES;
53992
54316
  var init_server3 = __esm({
53993
54317
  "src/standalone/server.ts"() {
53994
54318
  "use strict";
53995
54319
  http2 = __toESM(require("http"));
53996
- fs8 = __toESM(require("fs"));
54320
+ fs9 = __toESM(require("fs"));
53997
54321
  os = __toESM(require("os"));
53998
- path10 = __toESM(require("path"));
54322
+ path14 = __toESM(require("path"));
53999
54323
  init_wrapper();
54000
54324
  init_esm3();
54001
- init_types();
54002
54325
  init_KanbanSDK();
54003
54326
  init_parser();
54004
54327
  init_config();
54005
54328
  init_types2();
54006
- init_webhooks();
54329
+ init_webhooks2();
54007
54330
  init_metaUtils();
54008
54331
  MIME_TYPES = {
54009
54332
  ".html": "text/html",
@@ -54617,12 +54940,11 @@ var init_open = __esm({
54617
54940
  });
54618
54941
 
54619
54942
  // src/cli/index.ts
54620
- var path12 = __toESM(require("path"));
54621
- var fs14 = __toESM(require("fs/promises"));
54943
+ var path16 = __toESM(require("path"));
54944
+ var fs15 = __toESM(require("fs/promises"));
54622
54945
  init_KanbanSDK();
54623
- init_webhooks();
54946
+ init_types();
54624
54947
  init_config();
54625
- init_metaUtils();
54626
54948
  var VALID_PRIORITIES = ["critical", "high", "medium", "low"];
54627
54949
  function parseArgs(argv) {
54628
54950
  const args = argv.slice(2);
@@ -54665,16 +54987,16 @@ async function findWorkspaceRoot2(startDir) {
54665
54987
  let dir = startDir;
54666
54988
  while (true) {
54667
54989
  try {
54668
- await fs14.access(path12.join(dir, ".git"));
54990
+ await fs15.access(path16.join(dir, ".git"));
54669
54991
  return dir;
54670
54992
  } catch {
54671
54993
  }
54672
54994
  try {
54673
- await fs14.access(path12.join(dir, "package.json"));
54995
+ await fs15.access(path16.join(dir, "package.json"));
54674
54996
  return dir;
54675
54997
  } catch {
54676
54998
  }
54677
- const parent = path12.dirname(dir);
54999
+ const parent = path16.dirname(dir);
54678
55000
  if (parent === dir)
54679
55001
  return startDir;
54680
55002
  dir = parent;
@@ -54682,10 +55004,10 @@ async function findWorkspaceRoot2(startDir) {
54682
55004
  }
54683
55005
  async function resolveKanbanDir2(flags) {
54684
55006
  if (typeof flags.dir === "string") {
54685
- return path12.resolve(flags.dir);
55007
+ return path16.resolve(flags.dir);
54686
55008
  }
54687
55009
  const root = await findWorkspaceRoot2(process.cwd());
54688
- return path12.join(root, ".kanban");
55010
+ return path16.join(root, ".kanban");
54689
55011
  }
54690
55012
  var bold = (s) => `\x1B[1m${s}\x1B[0m`;
54691
55013
  var dim = (s) => `\x1B[2m${s}\x1B[0m`;
@@ -54724,21 +55046,14 @@ function colorPriority(priority) {
54724
55046
  return priority;
54725
55047
  }
54726
55048
  }
54727
- function getTitleFromContent3(content) {
54728
- const match = content.match(/^#\s+(.+)$/m);
54729
- if (match)
54730
- return match[1].trim();
54731
- const firstLine = content.split("\n").map((l) => l.trim()).find((l) => l.length > 0);
54732
- return firstLine || "Untitled";
54733
- }
54734
55049
  function formatCardRow(c) {
54735
- const title = getTitleFromContent3(c.content);
55050
+ const title = getTitleFromContent(c.content);
54736
55051
  const truncTitle = title.length > 40 ? title.slice(0, 37) + "..." : title;
54737
55052
  const assignee = c.assignee || "-";
54738
55053
  return ` ${bold(c.id.slice(0, 30).padEnd(30))} ${colorStatus(c.status.padEnd(12))} ${colorPriority(c.priority.padEnd(8))} ${assignee.padEnd(12)} ${truncTitle}`;
54739
55054
  }
54740
55055
  function formatCardDetail(c) {
54741
- const title = getTitleFromContent3(c.content);
55056
+ const title = getTitleFromContent(c.content);
54742
55057
  const lines = [
54743
55058
  `${bold(title)}`,
54744
55059
  "",
@@ -54769,7 +55084,29 @@ function formatCardDetail(c) {
54769
55084
  }
54770
55085
  async function cmdList(sdk, flags) {
54771
55086
  const boardId = getBoardId(flags);
54772
- let cards = await sdk.listCards(void 0, boardId);
55087
+ const metaFilter = Array.isArray(flags.meta) ? (() => {
55088
+ const mf = {};
55089
+ for (const entry of flags.meta) {
55090
+ const eq = entry.indexOf("=");
55091
+ if (eq < 1) {
55092
+ console.error(red(`Invalid --meta format: "${entry}". Expected key=value`));
55093
+ process.exit(1);
55094
+ }
55095
+ mf[entry.slice(0, eq)] = entry.slice(eq + 1);
55096
+ }
55097
+ return Object.keys(mf).length > 0 ? mf : void 0;
55098
+ })() : void 0;
55099
+ const VALID_SORTS = ["created:asc", "created:desc", "modified:asc", "modified:desc"];
55100
+ let sortOpt;
55101
+ if (typeof flags.sort === "string") {
55102
+ const sort = flags.sort;
55103
+ if (!VALID_SORTS.includes(sort)) {
55104
+ console.error(red(`Invalid --sort value: "${sort}". Must be one of: ${VALID_SORTS.join(", ")}`));
55105
+ process.exit(1);
55106
+ }
55107
+ sortOpt = sort;
55108
+ }
55109
+ let cards = await sdk.listCards(void 0, boardId, metaFilter, sortOpt);
54773
55110
  if (!flags["include-deleted"]) {
54774
55111
  cards = cards.filter((c) => c.status !== "deleted");
54775
55112
  }
@@ -54789,32 +55126,6 @@ async function cmdList(sdk, flags) {
54789
55126
  const groupLabels = sdk.getLabelsInGroup(flags["label-group"]);
54790
55127
  cards = cards.filter((c) => c.labels.some((l) => groupLabels.includes(l)));
54791
55128
  }
54792
- if (Array.isArray(flags.meta)) {
54793
- const metaFilter = {};
54794
- for (const entry of flags.meta) {
54795
- const eq = entry.indexOf("=");
54796
- if (eq < 1) {
54797
- console.error(red(`Invalid --meta format: "${entry}". Expected key=value`));
54798
- process.exit(1);
54799
- }
54800
- metaFilter[entry.slice(0, eq)] = entry.slice(eq + 1);
54801
- }
54802
- cards = cards.filter((c) => matchesMetaFilter(c.metadata, metaFilter));
54803
- }
54804
- const VALID_SORTS = ["created:asc", "created:desc", "modified:asc", "modified:desc"];
54805
- if (typeof flags.sort === "string") {
54806
- const sort = flags.sort;
54807
- if (!VALID_SORTS.includes(sort)) {
54808
- console.error(red(`Invalid --sort value: "${sort}". Must be one of: ${VALID_SORTS.join(", ")}`));
54809
- process.exit(1);
54810
- }
54811
- const [field, dir] = sort.split(":");
54812
- cards = [...cards].sort((a, b) => {
54813
- const aVal = field === "created" ? a.created : a.modified;
54814
- const bVal = field === "created" ? b.created : b.modified;
54815
- return dir === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
54816
- });
54817
- }
54818
55129
  if (flags.json) {
54819
55130
  console.log(JSON.stringify(cards, null, 2));
54820
55131
  return;
@@ -55068,17 +55379,16 @@ async function cmdBoards(sdk, positional, flags, workspaceRoot) {
55068
55379
  case "default": {
55069
55380
  const boardId = positional[1];
55070
55381
  if (!boardId) {
55071
- const config4 = readConfig(workspaceRoot);
55072
- console.log(config4.defaultBoard);
55382
+ const config3 = readConfig(workspaceRoot);
55383
+ console.log(config3.defaultBoard);
55073
55384
  break;
55074
55385
  }
55075
- const config3 = readConfig(workspaceRoot);
55076
- if (!config3.boards[boardId]) {
55077
- console.error(red(`Board not found: ${boardId}`));
55386
+ try {
55387
+ sdk.setDefaultBoard(boardId);
55388
+ } catch (err) {
55389
+ console.error(red(String(err)));
55078
55390
  process.exit(1);
55079
55391
  }
55080
- config3.defaultBoard = boardId;
55081
- writeConfig(workspaceRoot, config3);
55082
55392
  console.log(green(`Default board set to: ${boardId}`));
55083
55393
  break;
55084
55394
  }
@@ -55214,7 +55524,7 @@ async function cmdAttach(sdk, positional, flags) {
55214
55524
  }
55215
55525
  const resolvedId = await resolveCardId(sdk, cardId, boardId);
55216
55526
  const updated = await sdk.addAttachment(resolvedId, filePath, boardId);
55217
- console.log(green(`Attached to ${updated.id}: ${path12.basename(filePath)}`));
55527
+ console.log(green(`Attached to ${updated.id}: ${path16.basename(filePath)}`));
55218
55528
  break;
55219
55529
  }
55220
55530
  case "remove":
@@ -55682,11 +55992,11 @@ async function cmdAction(sdk, positional, flags) {
55682
55992
  process.exit(1);
55683
55993
  }
55684
55994
  }
55685
- async function cmdWebhooks(positional, flags, workspaceRoot) {
55995
+ async function cmdWebhooks(positional, flags, sdk) {
55686
55996
  const subcommand = positional[0] || "list";
55687
55997
  switch (subcommand) {
55688
55998
  case "list": {
55689
- const webhooks = loadWebhooks(workspaceRoot);
55999
+ const webhooks = sdk.listWebhooks();
55690
56000
  if (flags.json) {
55691
56001
  console.log(JSON.stringify(webhooks, null, 2));
55692
56002
  } else if (webhooks.length === 0) {
@@ -55710,7 +56020,7 @@ async function cmdWebhooks(positional, flags, workspaceRoot) {
55710
56020
  }
55711
56021
  const events = typeof flags.events === "string" ? flags.events.split(",").map((e) => e.trim()) : ["*"];
55712
56022
  const secret = typeof flags.secret === "string" ? flags.secret : void 0;
55713
- const webhook = createWebhook(workspaceRoot, { url: url3, events, secret });
56023
+ const webhook = sdk.createWebhook({ url: url3, events, secret });
55714
56024
  if (flags.json) {
55715
56025
  console.log(JSON.stringify(webhook, null, 2));
55716
56026
  } else {
@@ -55729,7 +56039,7 @@ async function cmdWebhooks(positional, flags, workspaceRoot) {
55729
56039
  console.error(red("Usage: kl webhooks remove <id>"));
55730
56040
  process.exit(1);
55731
56041
  }
55732
- const removed = deleteWebhook(workspaceRoot, webhookId);
56042
+ const removed = sdk.deleteWebhook(webhookId);
55733
56043
  if (removed) {
55734
56044
  console.log(green(`Removed webhook: ${webhookId}`));
55735
56045
  } else {
@@ -55753,7 +56063,7 @@ async function cmdWebhooks(positional, flags, workspaceRoot) {
55753
56063
  updates.secret = flags.secret;
55754
56064
  if (typeof flags.active === "string")
55755
56065
  updates.active = flags.active === "true";
55756
- const updated = updateWebhook(workspaceRoot, webhookId, updates);
56066
+ const updated = sdk.updateWebhook(webhookId, updates);
55757
56067
  if (!updated) {
55758
56068
  console.error(red(`Webhook not found: ${webhookId}`));
55759
56069
  process.exit(1);
@@ -55785,13 +56095,12 @@ var SETTINGS_KEYS = [
55785
56095
  "defaultPriority",
55786
56096
  "defaultStatus"
55787
56097
  ];
55788
- async function cmdSettings(positional, flags, workspaceRoot) {
56098
+ async function cmdSettings(positional, flags, sdk) {
55789
56099
  const subcommand = positional[0] || "show";
55790
56100
  switch (subcommand) {
55791
56101
  case "show":
55792
56102
  case "list": {
55793
- const config3 = readConfig(workspaceRoot);
55794
- const settings = configToSettings(config3);
56103
+ const settings = sdk.getSettings();
55795
56104
  if (flags.json) {
55796
56105
  console.log(JSON.stringify(settings, null, 2));
55797
56106
  } else {
@@ -55805,8 +56114,7 @@ async function cmdSettings(positional, flags, workspaceRoot) {
55805
56114
  }
55806
56115
  case "update":
55807
56116
  case "set": {
55808
- const config3 = readConfig(workspaceRoot);
55809
- const settings = configToSettings(config3);
56117
+ const settings = sdk.getSettings();
55810
56118
  let changed = false;
55811
56119
  const settingsAny = settings;
55812
56120
  for (const key of SETTINGS_KEYS) {
@@ -55827,10 +56135,10 @@ async function cmdSettings(positional, flags, workspaceRoot) {
55827
56135
  console.error(`Available: ${SETTINGS_KEYS.join(", ")}`);
55828
56136
  process.exit(1);
55829
56137
  }
55830
- writeConfig(workspaceRoot, settingsToConfig(config3, settings));
56138
+ sdk.updateSettings(settings);
55831
56139
  console.log(green("Settings updated."));
55832
56140
  if (flags.json) {
55833
- console.log(JSON.stringify(configToSettings(readConfig(workspaceRoot)), null, 2));
56141
+ console.log(JSON.stringify(sdk.getSettings(), null, 2));
55834
56142
  }
55835
56143
  break;
55836
56144
  }
@@ -55842,7 +56150,7 @@ async function cmdSettings(positional, flags, workspaceRoot) {
55842
56150
  }
55843
56151
  async function cmdMcp(flags) {
55844
56152
  if (typeof flags.dir === "string") {
55845
- process.env.KANBAN_DIR = path12.resolve(flags.dir);
56153
+ process.env.KANBAN_DIR = path16.resolve(flags.dir);
55846
56154
  }
55847
56155
  await Promise.resolve().then(() => (init_mcp_server2(), mcp_server_exports));
55848
56156
  }
@@ -55895,10 +56203,10 @@ async function findPackageRoot() {
55895
56203
  let dir = __dirname;
55896
56204
  while (true) {
55897
56205
  try {
55898
- await fs14.access(path12.join(dir, "package.json"));
56206
+ await fs15.access(path16.join(dir, "package.json"));
55899
56207
  return dir;
55900
56208
  } catch {
55901
- const parent = path12.dirname(dir);
56209
+ const parent = path16.dirname(dir);
55902
56210
  if (parent === dir)
55903
56211
  return __dirname;
55904
56212
  dir = parent;
@@ -55913,9 +56221,9 @@ async function showDocHelp(topic) {
55913
56221
  process.exit(1);
55914
56222
  }
55915
56223
  const pkgRoot = await findPackageRoot();
55916
- const docPath = path12.join(pkgRoot, relativePath);
56224
+ const docPath = path16.join(pkgRoot, relativePath);
55917
56225
  try {
55918
- const content = await fs14.readFile(docPath, "utf-8");
56226
+ const content = await fs15.readFile(docPath, "utf-8");
55919
56227
  console.log(formatMarkdownForTerminal(content));
55920
56228
  } catch {
55921
56229
  console.error(red(`Documentation not found: ${docPath}`));
@@ -56091,7 +56399,7 @@ async function main2() {
56091
56399
  const { command, positional, flags } = parseArgs(process.argv);
56092
56400
  if (command === "version" || flags.version) {
56093
56401
  const pkgRoot = await findPackageRoot();
56094
- const pkg = JSON.parse(await fs14.readFile(path12.join(pkgRoot, "package.json"), "utf-8"));
56402
+ const pkg = JSON.parse(await fs15.readFile(path16.join(pkgRoot, "package.json"), "utf-8"));
56095
56403
  console.log(pkg.version);
56096
56404
  return;
56097
56405
  }
@@ -56113,10 +56421,8 @@ async function main2() {
56113
56421
  return;
56114
56422
  }
56115
56423
  const kanbanDir = await resolveKanbanDir2(flags);
56116
- const workspaceRoot = path12.dirname(kanbanDir);
56117
- const sdk = new KanbanSDK(kanbanDir, {
56118
- onEvent: (event, data) => fireWebhooks(workspaceRoot, event, data)
56119
- });
56424
+ const workspaceRoot = path16.dirname(kanbanDir);
56425
+ const sdk = new KanbanSDK(kanbanDir);
56120
56426
  switch (command) {
56121
56427
  case "list":
56122
56428
  case "ls":
@@ -56187,10 +56493,10 @@ async function main2() {
56187
56493
  case "webhooks":
56188
56494
  case "webhook":
56189
56495
  case "wh":
56190
- await cmdWebhooks(positional, flags, workspaceRoot);
56496
+ await cmdWebhooks(positional, flags, sdk);
56191
56497
  break;
56192
56498
  case "settings":
56193
- await cmdSettings(positional, flags, workspaceRoot);
56499
+ await cmdSettings(positional, flags, sdk);
56194
56500
  break;
56195
56501
  case "pwd":
56196
56502
  if (flags.json) {