@raythunder/vibe-kanban 0.1.45-main.1

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 (3) hide show
  1. package/README.md +184 -0
  2. package/bin/cli.js +1377 -0
  3. package/package.json +34 -0
package/bin/cli.js ADDED
@@ -0,0 +1,1377 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __commonJS = (cb, mod) => function __require() {
10
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+
29
+ // package.json
30
+ var require_package = __commonJS({
31
+ "package.json"(exports2, module2) {
32
+ module2.exports = {
33
+ name: "@raythunder/vibe-kanban",
34
+ private: false,
35
+ version: "0.1.45-main.1",
36
+ main: "index.js",
37
+ bin: {
38
+ "vibe-kanban": "bin/cli.js"
39
+ },
40
+ scripts: {
41
+ build: 'esbuild src/cli.ts --bundle --platform=node --target=node20 --format=cjs --outfile=bin/cli.js --external:adm-zip --banner:js="#!/usr/bin/env node"',
42
+ check: "tsc --noEmit -p tsconfig.json"
43
+ },
44
+ keywords: [],
45
+ author: "bloop",
46
+ repository: {
47
+ type: "git",
48
+ url: "https://github.com/raythunder/vibe-kanban"
49
+ },
50
+ engines: {
51
+ node: ">=20.19.0"
52
+ },
53
+ license: "",
54
+ description: "NPX wrapper around vibe-kanban and vibe-kanban-mcp",
55
+ devDependencies: {
56
+ esbuild: "^0.27.2"
57
+ },
58
+ dependencies: {
59
+ "adm-zip": "^0.5.16",
60
+ cac: "^7.0.0"
61
+ },
62
+ files: [
63
+ "bin"
64
+ ]
65
+ };
66
+ }
67
+ });
68
+
69
+ // src/cli.ts
70
+ var import_child_process2 = require("child_process");
71
+ var import_path3 = __toESM(require("path"));
72
+ var import_fs3 = __toESM(require("fs"));
73
+
74
+ // node_modules/cac/dist/index.js
75
+ function toArr(any) {
76
+ return any == null ? [] : Array.isArray(any) ? any : [any];
77
+ }
78
+ function toVal(out, key, val, opts) {
79
+ var x, old = out[key], nxt = !!~opts.string.indexOf(key) ? val == null || val === true ? "" : String(val) : typeof val === "boolean" ? val : !!~opts.boolean.indexOf(key) ? val === "false" ? false : val === "true" || (out._.push((x = +val, x * 0 === 0) ? x : val), !!val) : (x = +val, x * 0 === 0) ? x : val;
80
+ out[key] = old == null ? nxt : Array.isArray(old) ? old.concat(nxt) : [old, nxt];
81
+ }
82
+ function lib_default(args, opts) {
83
+ args = args || [];
84
+ opts = opts || {};
85
+ var k, arr, arg, name, val, out = { _: [] };
86
+ var i = 0, j = 0, idx = 0, len = args.length;
87
+ const alibi = opts.alias !== void 0;
88
+ const strict = opts.unknown !== void 0;
89
+ const defaults = opts.default !== void 0;
90
+ opts.alias = opts.alias || {};
91
+ opts.string = toArr(opts.string);
92
+ opts.boolean = toArr(opts.boolean);
93
+ if (alibi) for (k in opts.alias) {
94
+ arr = opts.alias[k] = toArr(opts.alias[k]);
95
+ for (i = 0; i < arr.length; i++) (opts.alias[arr[i]] = arr.concat(k)).splice(i, 1);
96
+ }
97
+ for (i = opts.boolean.length; i-- > 0; ) {
98
+ arr = opts.alias[opts.boolean[i]] || [];
99
+ for (j = arr.length; j-- > 0; ) opts.boolean.push(arr[j]);
100
+ }
101
+ for (i = opts.string.length; i-- > 0; ) {
102
+ arr = opts.alias[opts.string[i]] || [];
103
+ for (j = arr.length; j-- > 0; ) opts.string.push(arr[j]);
104
+ }
105
+ if (defaults) for (k in opts.default) {
106
+ name = typeof opts.default[k];
107
+ arr = opts.alias[k] = opts.alias[k] || [];
108
+ if (opts[name] !== void 0) {
109
+ opts[name].push(k);
110
+ for (i = 0; i < arr.length; i++) opts[name].push(arr[i]);
111
+ }
112
+ }
113
+ const keys = strict ? Object.keys(opts.alias) : [];
114
+ for (i = 0; i < len; i++) {
115
+ arg = args[i];
116
+ if (arg === "--") {
117
+ out._ = out._.concat(args.slice(++i));
118
+ break;
119
+ }
120
+ for (j = 0; j < arg.length; j++) if (arg.charCodeAt(j) !== 45) break;
121
+ if (j === 0) out._.push(arg);
122
+ else if (arg.substring(j, j + 3) === "no-") {
123
+ name = arg.substring(j + 3);
124
+ if (strict && !~keys.indexOf(name)) return opts.unknown(arg);
125
+ out[name] = false;
126
+ } else {
127
+ for (idx = j + 1; idx < arg.length; idx++) if (arg.charCodeAt(idx) === 61) break;
128
+ name = arg.substring(j, idx);
129
+ val = arg.substring(++idx) || i + 1 === len || ("" + args[i + 1]).charCodeAt(0) === 45 || args[++i];
130
+ arr = j === 2 ? [name] : name;
131
+ for (idx = 0; idx < arr.length; idx++) {
132
+ name = arr[idx];
133
+ if (strict && !~keys.indexOf(name)) return opts.unknown("-".repeat(j) + name);
134
+ toVal(out, name, idx + 1 < arr.length || val, opts);
135
+ }
136
+ }
137
+ }
138
+ if (defaults) {
139
+ for (k in opts.default) if (out[k] === void 0) out[k] = opts.default[k];
140
+ }
141
+ if (alibi) for (k in out) {
142
+ arr = opts.alias[k] || [];
143
+ while (arr.length > 0) out[arr.shift()] = out[k];
144
+ }
145
+ return out;
146
+ }
147
+ function removeBrackets(v) {
148
+ return v.replace(/[<[].+/, "").trim();
149
+ }
150
+ function findAllBrackets(v) {
151
+ const ANGLED_BRACKET_RE_GLOBAL = /<([^>]+)>/g;
152
+ const SQUARE_BRACKET_RE_GLOBAL = /\[([^\]]+)\]/g;
153
+ const res = [];
154
+ const parse = (match) => {
155
+ let variadic = false;
156
+ let value = match[1];
157
+ if (value.startsWith("...")) {
158
+ value = value.slice(3);
159
+ variadic = true;
160
+ }
161
+ return {
162
+ required: match[0].startsWith("<"),
163
+ value,
164
+ variadic
165
+ };
166
+ };
167
+ let angledMatch;
168
+ while (angledMatch = ANGLED_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(angledMatch));
169
+ let squareMatch;
170
+ while (squareMatch = SQUARE_BRACKET_RE_GLOBAL.exec(v)) res.push(parse(squareMatch));
171
+ return res;
172
+ }
173
+ function getMriOptions(options) {
174
+ const result = {
175
+ alias: {},
176
+ boolean: []
177
+ };
178
+ for (const [index, option] of options.entries()) {
179
+ if (option.names.length > 1) result.alias[option.names[0]] = option.names.slice(1);
180
+ if (option.isBoolean) if (option.negated) {
181
+ if (!options.some((o, i) => {
182
+ return i !== index && o.names.some((name) => option.names.includes(name)) && typeof o.required === "boolean";
183
+ })) result.boolean.push(option.names[0]);
184
+ } else result.boolean.push(option.names[0]);
185
+ }
186
+ return result;
187
+ }
188
+ function findLongest(arr) {
189
+ return arr.sort((a, b) => {
190
+ return a.length > b.length ? -1 : 1;
191
+ })[0];
192
+ }
193
+ function padRight(str, length) {
194
+ return str.length >= length ? str : `${str}${" ".repeat(length - str.length)}`;
195
+ }
196
+ function camelcase(input) {
197
+ return input.replaceAll(/([a-z])-([a-z])/g, (_, p1, p2) => {
198
+ return p1 + p2.toUpperCase();
199
+ });
200
+ }
201
+ function setDotProp(obj, keys, val) {
202
+ let current = obj;
203
+ for (let i = 0; i < keys.length; i++) {
204
+ const key = keys[i];
205
+ if (i === keys.length - 1) {
206
+ current[key] = val;
207
+ return;
208
+ }
209
+ if (current[key] == null) {
210
+ const nextKeyIsArrayIndex = +keys[i + 1] > -1;
211
+ current[key] = nextKeyIsArrayIndex ? [] : {};
212
+ }
213
+ current = current[key];
214
+ }
215
+ }
216
+ function setByType(obj, transforms) {
217
+ for (const key of Object.keys(transforms)) {
218
+ const transform = transforms[key];
219
+ if (transform.shouldTransform) {
220
+ obj[key] = [obj[key]].flat();
221
+ if (typeof transform.transformFunction === "function") obj[key] = obj[key].map(transform.transformFunction);
222
+ }
223
+ }
224
+ }
225
+ function getFileName(input) {
226
+ const m = /([^\\/]+)$/.exec(input);
227
+ return m ? m[1] : "";
228
+ }
229
+ function camelcaseOptionName(name) {
230
+ return name.split(".").map((v, i) => {
231
+ return i === 0 ? camelcase(v) : v;
232
+ }).join(".");
233
+ }
234
+ var CACError = class extends Error {
235
+ constructor(message) {
236
+ super(message);
237
+ this.name = "CACError";
238
+ if (typeof Error.captureStackTrace !== "function") this.stack = new Error(message).stack;
239
+ }
240
+ };
241
+ var Option = class {
242
+ rawName;
243
+ description;
244
+ /** Option name */
245
+ name;
246
+ /** Option name and aliases */
247
+ names;
248
+ isBoolean;
249
+ required;
250
+ config;
251
+ negated;
252
+ constructor(rawName, description, config) {
253
+ this.rawName = rawName;
254
+ this.description = description;
255
+ this.config = Object.assign({}, config);
256
+ rawName = rawName.replaceAll(".*", "");
257
+ this.negated = false;
258
+ this.names = removeBrackets(rawName).split(",").map((v) => {
259
+ let name = v.trim().replace(/^-{1,2}/, "");
260
+ if (name.startsWith("no-")) {
261
+ this.negated = true;
262
+ name = name.replace(/^no-/, "");
263
+ }
264
+ return camelcaseOptionName(name);
265
+ }).sort((a, b) => a.length > b.length ? 1 : -1);
266
+ this.name = this.names.at(-1);
267
+ if (this.negated && this.config.default == null) this.config.default = true;
268
+ if (rawName.includes("<")) this.required = true;
269
+ else if (rawName.includes("[")) this.required = false;
270
+ else this.isBoolean = true;
271
+ }
272
+ };
273
+ var runtimeProcessArgs;
274
+ var runtimeInfo;
275
+ if (typeof process !== "undefined") {
276
+ let runtimeName;
277
+ if (typeof Deno !== "undefined" && typeof Deno.version?.deno === "string") runtimeName = "deno";
278
+ else if (typeof Bun !== "undefined" && typeof Bun.version === "string") runtimeName = "bun";
279
+ else runtimeName = "node";
280
+ runtimeInfo = `${process.platform}-${process.arch} ${runtimeName}-${process.version}`;
281
+ runtimeProcessArgs = process.argv;
282
+ } else if (typeof navigator === "undefined") runtimeInfo = `unknown`;
283
+ else runtimeInfo = `${navigator.platform} ${navigator.userAgent}`;
284
+ var Command = class {
285
+ rawName;
286
+ description;
287
+ config;
288
+ cli;
289
+ options;
290
+ aliasNames;
291
+ name;
292
+ args;
293
+ commandAction;
294
+ usageText;
295
+ versionNumber;
296
+ examples;
297
+ helpCallback;
298
+ globalCommand;
299
+ constructor(rawName, description, config = {}, cli) {
300
+ this.rawName = rawName;
301
+ this.description = description;
302
+ this.config = config;
303
+ this.cli = cli;
304
+ this.options = [];
305
+ this.aliasNames = [];
306
+ this.name = removeBrackets(rawName);
307
+ this.args = findAllBrackets(rawName);
308
+ this.examples = [];
309
+ }
310
+ usage(text) {
311
+ this.usageText = text;
312
+ return this;
313
+ }
314
+ allowUnknownOptions() {
315
+ this.config.allowUnknownOptions = true;
316
+ return this;
317
+ }
318
+ ignoreOptionDefaultValue() {
319
+ this.config.ignoreOptionDefaultValue = true;
320
+ return this;
321
+ }
322
+ version(version, customFlags = "-v, --version") {
323
+ this.versionNumber = version;
324
+ this.option(customFlags, "Display version number");
325
+ return this;
326
+ }
327
+ example(example) {
328
+ this.examples.push(example);
329
+ return this;
330
+ }
331
+ /**
332
+ * Add a option for this command
333
+ * @param rawName Raw option name(s)
334
+ * @param description Option description
335
+ * @param config Option config
336
+ */
337
+ option(rawName, description, config) {
338
+ const option = new Option(rawName, description, config);
339
+ this.options.push(option);
340
+ return this;
341
+ }
342
+ alias(name) {
343
+ this.aliasNames.push(name);
344
+ return this;
345
+ }
346
+ action(callback) {
347
+ this.commandAction = callback;
348
+ return this;
349
+ }
350
+ /**
351
+ * Check if a command name is matched by this command
352
+ * @param name Command name
353
+ */
354
+ isMatched(name) {
355
+ return this.name === name || this.aliasNames.includes(name);
356
+ }
357
+ get isDefaultCommand() {
358
+ return this.name === "" || this.aliasNames.includes("!");
359
+ }
360
+ get isGlobalCommand() {
361
+ return this instanceof GlobalCommand;
362
+ }
363
+ /**
364
+ * Check if an option is registered in this command
365
+ * @param name Option name
366
+ */
367
+ hasOption(name) {
368
+ name = name.split(".")[0];
369
+ return this.options.find((option) => {
370
+ return option.names.includes(name);
371
+ });
372
+ }
373
+ outputHelp() {
374
+ const { name, commands } = this.cli;
375
+ const { versionNumber, options: globalOptions, helpCallback } = this.cli.globalCommand;
376
+ let sections = [{ body: `${name}${versionNumber ? `/${versionNumber}` : ""}` }];
377
+ sections.push({
378
+ title: "Usage",
379
+ body: ` $ ${name} ${this.usageText || this.rawName}`
380
+ });
381
+ if ((this.isGlobalCommand || this.isDefaultCommand) && commands.length > 0) {
382
+ const longestCommandName = findLongest(commands.map((command) => command.rawName));
383
+ sections.push({
384
+ title: "Commands",
385
+ body: commands.map((command) => {
386
+ return ` ${padRight(command.rawName, longestCommandName.length)} ${command.description}`;
387
+ }).join("\n")
388
+ }, {
389
+ title: `For more info, run any command with the \`--help\` flag`,
390
+ body: commands.map((command) => ` $ ${name}${command.name === "" ? "" : ` ${command.name}`} --help`).join("\n")
391
+ });
392
+ }
393
+ let options = this.isGlobalCommand ? globalOptions : [...this.options, ...globalOptions || []];
394
+ if (!this.isGlobalCommand && !this.isDefaultCommand) options = options.filter((option) => option.name !== "version");
395
+ if (options.length > 0) {
396
+ const longestOptionName = findLongest(options.map((option) => option.rawName));
397
+ sections.push({
398
+ title: "Options",
399
+ body: options.map((option) => {
400
+ return ` ${padRight(option.rawName, longestOptionName.length)} ${option.description} ${option.config.default === void 0 ? "" : `(default: ${option.config.default})`}`;
401
+ }).join("\n")
402
+ });
403
+ }
404
+ if (this.examples.length > 0) sections.push({
405
+ title: "Examples",
406
+ body: this.examples.map((example) => {
407
+ if (typeof example === "function") return example(name);
408
+ return example;
409
+ }).join("\n")
410
+ });
411
+ if (helpCallback) sections = helpCallback(sections) || sections;
412
+ console.info(sections.map((section) => {
413
+ return section.title ? `${section.title}:
414
+ ${section.body}` : section.body;
415
+ }).join("\n\n"));
416
+ }
417
+ outputVersion() {
418
+ const { name } = this.cli;
419
+ const { versionNumber } = this.cli.globalCommand;
420
+ if (versionNumber) console.info(`${name}/${versionNumber} ${runtimeInfo}`);
421
+ }
422
+ checkRequiredArgs() {
423
+ const minimalArgsCount = this.args.filter((arg) => arg.required).length;
424
+ if (this.cli.args.length < minimalArgsCount) throw new CACError(`missing required args for command \`${this.rawName}\``);
425
+ }
426
+ /**
427
+ * Check if the parsed options contain any unknown options
428
+ *
429
+ * Exit and output error when true
430
+ */
431
+ checkUnknownOptions() {
432
+ const { options, globalCommand } = this.cli;
433
+ if (!this.config.allowUnknownOptions) {
434
+ for (const name of Object.keys(options)) if (name !== "--" && !this.hasOption(name) && !globalCommand.hasOption(name)) throw new CACError(`Unknown option \`${name.length > 1 ? `--${name}` : `-${name}`}\``);
435
+ }
436
+ }
437
+ /**
438
+ * Check if the required string-type options exist
439
+ */
440
+ checkOptionValue() {
441
+ const { options: parsedOptions, globalCommand } = this.cli;
442
+ const options = [...globalCommand.options, ...this.options];
443
+ for (const option of options) {
444
+ const value = parsedOptions[option.name.split(".")[0]];
445
+ if (option.required) {
446
+ const hasNegated = options.some((o) => o.negated && o.names.includes(option.name));
447
+ if (value === true || value === false && !hasNegated) throw new CACError(`option \`${option.rawName}\` value is missing`);
448
+ }
449
+ }
450
+ }
451
+ /**
452
+ * Check if the number of args is more than expected
453
+ */
454
+ checkUnusedArgs() {
455
+ const maximumArgsCount = this.args.some((arg) => arg.variadic) ? Infinity : this.args.length;
456
+ if (maximumArgsCount < this.cli.args.length) throw new CACError(`Unused args: ${this.cli.args.slice(maximumArgsCount).map((arg) => `\`${arg}\``).join(", ")}`);
457
+ }
458
+ };
459
+ var GlobalCommand = class extends Command {
460
+ constructor(cli) {
461
+ super("@@global@@", "", {}, cli);
462
+ }
463
+ };
464
+ var CAC = class extends EventTarget {
465
+ /** The program name to display in help and version message */
466
+ name;
467
+ commands;
468
+ globalCommand;
469
+ matchedCommand;
470
+ matchedCommandName;
471
+ /**
472
+ * Raw CLI arguments
473
+ */
474
+ rawArgs;
475
+ /**
476
+ * Parsed CLI arguments
477
+ */
478
+ args;
479
+ /**
480
+ * Parsed CLI options, camelCased
481
+ */
482
+ options;
483
+ showHelpOnExit;
484
+ showVersionOnExit;
485
+ /**
486
+ * @param name The program name to display in help and version message
487
+ */
488
+ constructor(name = "") {
489
+ super();
490
+ this.name = name;
491
+ this.commands = [];
492
+ this.rawArgs = [];
493
+ this.args = [];
494
+ this.options = {};
495
+ this.globalCommand = new GlobalCommand(this);
496
+ this.globalCommand.usage("<command> [options]");
497
+ }
498
+ /**
499
+ * Add a global usage text.
500
+ *
501
+ * This is not used by sub-commands.
502
+ */
503
+ usage(text) {
504
+ this.globalCommand.usage(text);
505
+ return this;
506
+ }
507
+ /**
508
+ * Add a sub-command
509
+ */
510
+ command(rawName, description, config) {
511
+ const command = new Command(rawName, description || "", config, this);
512
+ command.globalCommand = this.globalCommand;
513
+ this.commands.push(command);
514
+ return command;
515
+ }
516
+ /**
517
+ * Add a global CLI option.
518
+ *
519
+ * Which is also applied to sub-commands.
520
+ */
521
+ option(rawName, description, config) {
522
+ this.globalCommand.option(rawName, description, config);
523
+ return this;
524
+ }
525
+ /**
526
+ * Show help message when `-h, --help` flags appear.
527
+ *
528
+ */
529
+ help(callback) {
530
+ this.globalCommand.option("-h, --help", "Display this message");
531
+ this.globalCommand.helpCallback = callback;
532
+ this.showHelpOnExit = true;
533
+ return this;
534
+ }
535
+ /**
536
+ * Show version number when `-v, --version` flags appear.
537
+ *
538
+ */
539
+ version(version, customFlags = "-v, --version") {
540
+ this.globalCommand.version(version, customFlags);
541
+ this.showVersionOnExit = true;
542
+ return this;
543
+ }
544
+ /**
545
+ * Add a global example.
546
+ *
547
+ * This example added here will not be used by sub-commands.
548
+ */
549
+ example(example) {
550
+ this.globalCommand.example(example);
551
+ return this;
552
+ }
553
+ /**
554
+ * Output the corresponding help message
555
+ * When a sub-command is matched, output the help message for the command
556
+ * Otherwise output the global one.
557
+ *
558
+ */
559
+ outputHelp() {
560
+ if (this.matchedCommand) this.matchedCommand.outputHelp();
561
+ else this.globalCommand.outputHelp();
562
+ }
563
+ /**
564
+ * Output the version number.
565
+ *
566
+ */
567
+ outputVersion() {
568
+ this.globalCommand.outputVersion();
569
+ }
570
+ setParsedInfo({ args, options }, matchedCommand, matchedCommandName) {
571
+ this.args = args;
572
+ this.options = options;
573
+ if (matchedCommand) this.matchedCommand = matchedCommand;
574
+ if (matchedCommandName) this.matchedCommandName = matchedCommandName;
575
+ return this;
576
+ }
577
+ unsetMatchedCommand() {
578
+ this.matchedCommand = void 0;
579
+ this.matchedCommandName = void 0;
580
+ }
581
+ /**
582
+ * Parse argv
583
+ */
584
+ parse(argv, { run = true } = {}) {
585
+ if (!argv) {
586
+ if (!runtimeProcessArgs) throw new Error("No argv provided and runtime process argv is not available.");
587
+ argv = runtimeProcessArgs;
588
+ }
589
+ this.rawArgs = argv;
590
+ if (!this.name) this.name = argv[1] ? getFileName(argv[1]) : "cli";
591
+ let shouldParse = true;
592
+ for (const command of this.commands) {
593
+ const parsed = this.mri(argv.slice(2), command);
594
+ const commandName = parsed.args[0];
595
+ if (command.isMatched(commandName)) {
596
+ shouldParse = false;
597
+ const parsedInfo = {
598
+ ...parsed,
599
+ args: parsed.args.slice(1)
600
+ };
601
+ this.setParsedInfo(parsedInfo, command, commandName);
602
+ this.dispatchEvent(new CustomEvent(`command:${commandName}`, { detail: command }));
603
+ }
604
+ }
605
+ if (shouldParse) {
606
+ for (const command of this.commands) if (command.isDefaultCommand) {
607
+ shouldParse = false;
608
+ const parsed = this.mri(argv.slice(2), command);
609
+ this.setParsedInfo(parsed, command);
610
+ this.dispatchEvent(new CustomEvent("command:!", { detail: command }));
611
+ }
612
+ }
613
+ if (shouldParse) {
614
+ const parsed = this.mri(argv.slice(2));
615
+ this.setParsedInfo(parsed);
616
+ }
617
+ if (this.options.help && this.showHelpOnExit) {
618
+ this.outputHelp();
619
+ run = false;
620
+ this.unsetMatchedCommand();
621
+ }
622
+ if (this.options.version && this.showVersionOnExit && this.matchedCommandName == null) {
623
+ this.outputVersion();
624
+ run = false;
625
+ this.unsetMatchedCommand();
626
+ }
627
+ const parsedArgv = {
628
+ args: this.args,
629
+ options: this.options
630
+ };
631
+ if (run) this.runMatchedCommand();
632
+ if (!this.matchedCommand && this.args[0]) this.dispatchEvent(new CustomEvent("command:*", { detail: this.args[0] }));
633
+ return parsedArgv;
634
+ }
635
+ mri(argv, command) {
636
+ const cliOptions = [...this.globalCommand.options, ...command ? command.options : []];
637
+ const mriOptions = getMriOptions(cliOptions);
638
+ let argsAfterDoubleDashes = [];
639
+ const doubleDashesIndex = argv.indexOf("--");
640
+ if (doubleDashesIndex !== -1) {
641
+ argsAfterDoubleDashes = argv.slice(doubleDashesIndex + 1);
642
+ argv = argv.slice(0, doubleDashesIndex);
643
+ }
644
+ let parsed = lib_default(argv, mriOptions);
645
+ parsed = Object.keys(parsed).reduce((res, name) => {
646
+ return {
647
+ ...res,
648
+ [camelcaseOptionName(name)]: parsed[name]
649
+ };
650
+ }, { _: [] });
651
+ const args = parsed._;
652
+ const options = { "--": argsAfterDoubleDashes };
653
+ const ignoreDefault = command && command.config.ignoreOptionDefaultValue ? command.config.ignoreOptionDefaultValue : this.globalCommand.config.ignoreOptionDefaultValue;
654
+ const transforms = /* @__PURE__ */ Object.create(null);
655
+ for (const cliOption of cliOptions) {
656
+ if (!ignoreDefault && cliOption.config.default !== void 0) for (const name of cliOption.names) options[name] = cliOption.config.default;
657
+ if (Array.isArray(cliOption.config.type) && transforms[cliOption.name] === void 0) {
658
+ transforms[cliOption.name] = /* @__PURE__ */ Object.create(null);
659
+ transforms[cliOption.name].shouldTransform = true;
660
+ transforms[cliOption.name].transformFunction = cliOption.config.type[0];
661
+ }
662
+ }
663
+ for (const key of Object.keys(parsed)) if (key !== "_") {
664
+ setDotProp(options, key.split("."), parsed[key]);
665
+ setByType(options, transforms);
666
+ }
667
+ return {
668
+ args,
669
+ options
670
+ };
671
+ }
672
+ runMatchedCommand() {
673
+ const { args, options, matchedCommand: command } = this;
674
+ if (!command || !command.commandAction) return;
675
+ command.checkUnknownOptions();
676
+ command.checkOptionValue();
677
+ command.checkRequiredArgs();
678
+ command.checkUnusedArgs();
679
+ const actionArgs = [];
680
+ command.args.forEach((arg, index) => {
681
+ if (arg.variadic) actionArgs.push(args.slice(index));
682
+ else actionArgs.push(args[index]);
683
+ });
684
+ actionArgs.push(options);
685
+ return command.commandAction.apply(this, actionArgs);
686
+ }
687
+ };
688
+ var cac = (name = "") => new CAC(name);
689
+
690
+ // src/download.ts
691
+ var import_https = __toESM(require("https"));
692
+ var import_fs = __toESM(require("fs"));
693
+ var import_path = __toESM(require("path"));
694
+ var import_crypto = __toESM(require("crypto"));
695
+ var import_os = __toESM(require("os"));
696
+ var packageJson = require_package();
697
+ function parseGitHubRepo(repository) {
698
+ const rawUrl = typeof repository === "string" ? repository : repository?.url ?? "";
699
+ const match = rawUrl.match(/github\.com[:/](.+?)(?:\.git)?\/?$/);
700
+ if (!match) {
701
+ throw new Error(`Unsupported GitHub repository URL: ${rawUrl}`);
702
+ }
703
+ return match[1];
704
+ }
705
+ var BINARY_TAG = "v0.1.45-main.1.20260408132428";
706
+ var PACKAGE_NAME = packageJson.name;
707
+ var GITHUB_REPO = parseGitHubRepo(packageJson.repository);
708
+ var RELEASE_BASE_URL = `https://github.com/${GITHUB_REPO}/releases/download/${BINARY_TAG}`;
709
+ var CACHE_DIR = import_path.default.join(import_os.default.homedir(), ".vibe-kanban", "bin");
710
+ var BINARY_MANIFEST_NAME = "binary-manifest.json";
711
+ var DESKTOP_MANIFEST_NAME = "desktop-manifest.json";
712
+ var LOCAL_DIST_DIR = import_path.default.join(__dirname, "..", "dist");
713
+ var LOCAL_DEV_MODE = import_fs.default.existsSync(LOCAL_DIST_DIR) || process.env.VIBE_KANBAN_LOCAL === "1";
714
+ function assertReleaseConfigured() {
715
+ if (!BINARY_TAG.startsWith("__")) {
716
+ return;
717
+ }
718
+ throw new Error(
719
+ "This CLI package is missing release metadata. Rebuild and republish the npm package before using release downloads."
720
+ );
721
+ }
722
+ function getReleaseAssetUrl(fileName) {
723
+ assertReleaseConfigured();
724
+ return `${RELEASE_BASE_URL}/${fileName}`;
725
+ }
726
+ function fetchJson(url) {
727
+ return new Promise((resolve, reject) => {
728
+ import_https.default.get(url, (res) => {
729
+ if (res.statusCode === 301 || res.statusCode === 302) {
730
+ return fetchJson(res.headers.location).then(resolve).catch(reject);
731
+ }
732
+ if (res.statusCode !== 200) {
733
+ return reject(new Error(`HTTP ${res.statusCode} fetching ${url}`));
734
+ }
735
+ let data = "";
736
+ res.on("data", (chunk) => data += chunk);
737
+ res.on("end", () => {
738
+ try {
739
+ resolve(JSON.parse(data));
740
+ } catch {
741
+ reject(new Error(`Failed to parse JSON from ${url}`));
742
+ }
743
+ });
744
+ }).on("error", reject);
745
+ });
746
+ }
747
+ function downloadFile(url, destPath, expectedSha256, onProgress) {
748
+ const tempPath = destPath + ".tmp";
749
+ return new Promise((resolve, reject) => {
750
+ const file = import_fs.default.createWriteStream(tempPath);
751
+ const hash = import_crypto.default.createHash("sha256");
752
+ const cleanup = () => {
753
+ try {
754
+ import_fs.default.unlinkSync(tempPath);
755
+ } catch {
756
+ }
757
+ };
758
+ import_https.default.get(url, (res) => {
759
+ if (res.statusCode === 301 || res.statusCode === 302) {
760
+ file.close();
761
+ cleanup();
762
+ return downloadFile(
763
+ res.headers.location,
764
+ destPath,
765
+ expectedSha256,
766
+ onProgress
767
+ ).then(resolve).catch(reject);
768
+ }
769
+ if (res.statusCode !== 200) {
770
+ file.close();
771
+ cleanup();
772
+ return reject(
773
+ new Error(`HTTP ${res.statusCode} downloading ${url}`)
774
+ );
775
+ }
776
+ const totalSize = parseInt(
777
+ res.headers["content-length"] || "0",
778
+ 10
779
+ );
780
+ let downloadedSize = 0;
781
+ res.on("data", (chunk) => {
782
+ downloadedSize += chunk.length;
783
+ hash.update(chunk);
784
+ if (onProgress) onProgress(downloadedSize, totalSize);
785
+ });
786
+ res.pipe(file);
787
+ file.on("finish", () => {
788
+ file.close();
789
+ const actualSha256 = hash.digest("hex");
790
+ if (expectedSha256 && actualSha256 !== expectedSha256) {
791
+ cleanup();
792
+ reject(
793
+ new Error(
794
+ `Checksum mismatch: expected ${expectedSha256}, got ${actualSha256}`
795
+ )
796
+ );
797
+ } else {
798
+ try {
799
+ import_fs.default.renameSync(tempPath, destPath);
800
+ resolve(destPath);
801
+ } catch (err) {
802
+ cleanup();
803
+ reject(err);
804
+ }
805
+ }
806
+ });
807
+ }).on("error", (err) => {
808
+ file.close();
809
+ cleanup();
810
+ reject(err);
811
+ });
812
+ });
813
+ }
814
+ async function ensureBinary(platform2, binaryName, onProgress) {
815
+ if (LOCAL_DEV_MODE) {
816
+ const localZipPath = import_path.default.join(
817
+ LOCAL_DIST_DIR,
818
+ platform2,
819
+ `${binaryName}.zip`
820
+ );
821
+ if (import_fs.default.existsSync(localZipPath)) {
822
+ return localZipPath;
823
+ }
824
+ throw new Error(
825
+ `Local binary not found: ${localZipPath}
826
+ Run ./local-build.sh first to build the binaries.`
827
+ );
828
+ }
829
+ const cacheDir = import_path.default.join(CACHE_DIR, BINARY_TAG, platform2);
830
+ const zipPath = import_path.default.join(cacheDir, `${binaryName}.zip`);
831
+ if (import_fs.default.existsSync(zipPath)) return zipPath;
832
+ import_fs.default.mkdirSync(cacheDir, { recursive: true });
833
+ const manifest = await fetchJson(
834
+ getReleaseAssetUrl(BINARY_MANIFEST_NAME)
835
+ );
836
+ const binaryInfo = manifest.platforms?.[platform2]?.[binaryName];
837
+ if (!binaryInfo) {
838
+ throw new Error(
839
+ `Binary ${binaryName} not available for ${platform2}`
840
+ );
841
+ }
842
+ const fileName = binaryInfo.file ?? `${binaryName}-${platform2}.zip`;
843
+ const url = getReleaseAssetUrl(fileName);
844
+ await downloadFile(url, zipPath, binaryInfo.sha256, onProgress);
845
+ return zipPath;
846
+ }
847
+ var DESKTOP_CACHE_DIR = import_path.default.join(
848
+ import_os.default.homedir(),
849
+ ".vibe-kanban",
850
+ "desktop"
851
+ );
852
+ async function ensureDesktopBundle(tauriPlatform, onProgress) {
853
+ if (LOCAL_DEV_MODE) {
854
+ const localDir = import_path.default.join(LOCAL_DIST_DIR, "tauri", tauriPlatform);
855
+ if (import_fs.default.existsSync(localDir)) {
856
+ const files = import_fs.default.readdirSync(localDir);
857
+ const archive = files.find(
858
+ (f) => f.endsWith(".tar.gz") || f.endsWith("-setup.exe")
859
+ );
860
+ return {
861
+ dir: localDir,
862
+ archivePath: archive ? import_path.default.join(localDir, archive) : null,
863
+ type: null
864
+ };
865
+ }
866
+ throw new Error(
867
+ `Local desktop bundle not found: ${localDir}
868
+ Run './local-build.sh --desktop' first to build the Tauri app.`
869
+ );
870
+ }
871
+ const cacheDir = import_path.default.join(
872
+ DESKTOP_CACHE_DIR,
873
+ BINARY_TAG,
874
+ tauriPlatform
875
+ );
876
+ const sentinelPath = import_path.default.join(cacheDir, ".installed");
877
+ if (import_fs.default.existsSync(sentinelPath)) {
878
+ return { dir: cacheDir, archivePath: null, type: null };
879
+ }
880
+ import_fs.default.mkdirSync(cacheDir, { recursive: true });
881
+ const manifest = await fetchJson(
882
+ getReleaseAssetUrl(DESKTOP_MANIFEST_NAME)
883
+ );
884
+ const platformInfo = manifest.platforms?.[tauriPlatform];
885
+ if (!platformInfo) {
886
+ throw new Error(
887
+ `Desktop app not available for platform: ${tauriPlatform}`
888
+ );
889
+ }
890
+ const destPath = import_path.default.join(cacheDir, platformInfo.file);
891
+ if (!import_fs.default.existsSync(destPath)) {
892
+ const url = getReleaseAssetUrl(platformInfo.file);
893
+ await downloadFile(url, destPath, platformInfo.sha256, onProgress);
894
+ }
895
+ return {
896
+ archivePath: destPath,
897
+ dir: cacheDir,
898
+ type: platformInfo.type
899
+ };
900
+ }
901
+ async function getLatestVersion() {
902
+ const metadata = await fetchJson(
903
+ `https://registry.npmjs.org/${encodeURIComponent(PACKAGE_NAME)}/latest`
904
+ );
905
+ return metadata.version;
906
+ }
907
+
908
+ // src/desktop.ts
909
+ var import_child_process = require("child_process");
910
+ var import_path2 = __toESM(require("path"));
911
+ var import_fs2 = __toESM(require("fs"));
912
+ var import_os2 = __toESM(require("os"));
913
+ var PLATFORM_MAP = {
914
+ "macos-arm64": "darwin-aarch64",
915
+ "macos-x64": "darwin-x86_64",
916
+ "linux-x64": "linux-x86_64",
917
+ "linux-arm64": "linux-aarch64",
918
+ "windows-x64": "windows-x86_64",
919
+ "windows-arm64": "windows-aarch64"
920
+ };
921
+ function getTauriPlatform(npxPlatformDir) {
922
+ return PLATFORM_MAP[npxPlatformDir] || null;
923
+ }
924
+ function extractTarGz(archivePath, destDir) {
925
+ (0, import_child_process.execSync)(`tar -xzf "${archivePath}" -C "${destDir}"`, {
926
+ stdio: "pipe"
927
+ });
928
+ }
929
+ function writeSentinel(dir, meta) {
930
+ import_fs2.default.writeFileSync(
931
+ import_path2.default.join(dir, ".installed"),
932
+ JSON.stringify(meta)
933
+ );
934
+ }
935
+ function readSentinel(dir) {
936
+ const sentinelPath = import_path2.default.join(dir, ".installed");
937
+ if (!import_fs2.default.existsSync(sentinelPath)) return null;
938
+ try {
939
+ return JSON.parse(
940
+ import_fs2.default.readFileSync(sentinelPath, "utf-8")
941
+ );
942
+ } catch {
943
+ return null;
944
+ }
945
+ }
946
+ function tryCopyApp(srcAppPath, destDir) {
947
+ try {
948
+ const appName = import_path2.default.basename(srcAppPath);
949
+ const destAppPath = import_path2.default.join(destDir, appName);
950
+ import_fs2.default.mkdirSync(destDir, { recursive: true });
951
+ if (import_fs2.default.existsSync(destAppPath)) {
952
+ import_fs2.default.rmSync(destAppPath, { recursive: true, force: true });
953
+ }
954
+ (0, import_child_process.execSync)(`cp -R "${srcAppPath}" "${destAppPath}"`, {
955
+ stdio: "pipe"
956
+ });
957
+ return destAppPath;
958
+ } catch {
959
+ return null;
960
+ }
961
+ }
962
+ async function installAndLaunchMacOS(bundleInfo) {
963
+ const { archivePath, dir } = bundleInfo;
964
+ const sentinel = readSentinel(dir);
965
+ if (sentinel?.appPath && import_fs2.default.existsSync(sentinel.appPath)) {
966
+ return launchMacOSApp(sentinel.appPath);
967
+ }
968
+ if (!archivePath || !import_fs2.default.existsSync(archivePath)) {
969
+ throw new Error("No archive to extract for macOS desktop app");
970
+ }
971
+ extractTarGz(archivePath, dir);
972
+ const appName = import_fs2.default.readdirSync(dir).find((f) => f.endsWith(".app"));
973
+ if (!appName) {
974
+ throw new Error(
975
+ `No .app bundle found in ${dir} after extraction`
976
+ );
977
+ }
978
+ const extractedAppPath = import_path2.default.join(dir, appName);
979
+ const userApplications = import_path2.default.join(import_os2.default.homedir(), "Applications");
980
+ const finalAppPath = tryCopyApp(extractedAppPath, "/Applications") ?? tryCopyApp(extractedAppPath, userApplications) ?? extractedAppPath;
981
+ if (finalAppPath !== extractedAppPath) {
982
+ try {
983
+ import_fs2.default.rmSync(extractedAppPath, { recursive: true, force: true });
984
+ } catch {
985
+ }
986
+ }
987
+ try {
988
+ (0, import_child_process.execSync)(`xattr -rd com.apple.quarantine "${finalAppPath}"`, {
989
+ stdio: "pipe"
990
+ });
991
+ } catch {
992
+ }
993
+ writeSentinel(dir, { type: "app-tar-gz", appPath: finalAppPath });
994
+ return launchMacOSApp(finalAppPath);
995
+ }
996
+ function launchMacOSApp(appPath) {
997
+ const appName = import_path2.default.basename(appPath);
998
+ console.error(`Launching ${appName}...`);
999
+ const proc = (0, import_child_process.spawn)("open", ["--wait-apps", appPath], {
1000
+ stdio: "inherit"
1001
+ });
1002
+ return new Promise((resolve) => {
1003
+ proc.on("exit", (code) => resolve(code || 0));
1004
+ });
1005
+ }
1006
+ async function installAndLaunchLinux(bundleInfo) {
1007
+ const { archivePath, dir } = bundleInfo;
1008
+ const sentinel = readSentinel(dir);
1009
+ if (sentinel?.appPath && import_fs2.default.existsSync(sentinel.appPath)) {
1010
+ return launchLinuxAppImage(sentinel.appPath);
1011
+ }
1012
+ if (!archivePath || !import_fs2.default.existsSync(archivePath)) {
1013
+ throw new Error("No archive to extract for Linux desktop app");
1014
+ }
1015
+ extractTarGz(archivePath, dir);
1016
+ const appImage = import_fs2.default.readdirSync(dir).find((f) => f.endsWith(".AppImage"));
1017
+ if (!appImage) {
1018
+ throw new Error(`No .AppImage found in ${dir} after extraction`);
1019
+ }
1020
+ const appImagePath = import_path2.default.join(dir, appImage);
1021
+ import_fs2.default.chmodSync(appImagePath, 493);
1022
+ writeSentinel(dir, {
1023
+ type: "appimage-tar-gz",
1024
+ appPath: appImagePath
1025
+ });
1026
+ return launchLinuxAppImage(appImagePath);
1027
+ }
1028
+ function launchLinuxAppImage(appImagePath) {
1029
+ const appImage = import_path2.default.basename(appImagePath);
1030
+ console.error(`Launching ${appImage}...`);
1031
+ const proc = (0, import_child_process.spawn)(appImagePath, [], {
1032
+ stdio: "inherit",
1033
+ detached: false
1034
+ });
1035
+ return new Promise((resolve) => {
1036
+ proc.on("exit", (code) => resolve(code || 0));
1037
+ });
1038
+ }
1039
+ async function installAndLaunchWindows(bundleInfo) {
1040
+ const { dir } = bundleInfo;
1041
+ const sentinel = readSentinel(dir);
1042
+ if (sentinel?.appPath) {
1043
+ const appExe2 = import_path2.default.join(sentinel.appPath, "Vibe Kanban.exe");
1044
+ if (import_fs2.default.existsSync(appExe2)) {
1045
+ return launchWindowsApp(appExe2);
1046
+ }
1047
+ }
1048
+ const files = import_fs2.default.readdirSync(dir);
1049
+ const installer = files.find(
1050
+ (f) => f.endsWith("-setup.exe") || f.endsWith(".exe") && f !== ".installed"
1051
+ );
1052
+ if (!installer) {
1053
+ throw new Error(`No installer found in ${dir}`);
1054
+ }
1055
+ const installerPath = import_path2.default.join(dir, installer);
1056
+ const installDir = import_path2.default.join(dir, "app");
1057
+ console.error("Installing Vibe Kanban...");
1058
+ try {
1059
+ (0, import_child_process.execSync)(`"${installerPath}" /S /D="${installDir}"`, {
1060
+ stdio: "inherit",
1061
+ timeout: 12e4
1062
+ });
1063
+ } catch {
1064
+ console.error(
1065
+ "Silent install failed, launching interactive installer..."
1066
+ );
1067
+ (0, import_child_process.execSync)(`"${installerPath}"`, { stdio: "inherit" });
1068
+ const defaultDir = import_path2.default.join(
1069
+ process.env.LOCALAPPDATA || "",
1070
+ "vibe-kanban"
1071
+ );
1072
+ if (import_fs2.default.existsSync(import_path2.default.join(defaultDir, "Vibe Kanban.exe"))) {
1073
+ writeSentinel(dir, {
1074
+ type: "nsis-exe",
1075
+ appPath: defaultDir
1076
+ });
1077
+ return launchWindowsApp(
1078
+ import_path2.default.join(defaultDir, "Vibe Kanban.exe")
1079
+ );
1080
+ }
1081
+ console.error(
1082
+ "Installation complete. Please launch Vibe Kanban from your Start menu."
1083
+ );
1084
+ return 0;
1085
+ }
1086
+ writeSentinel(dir, { type: "nsis-exe", appPath: installDir });
1087
+ const appExe = import_path2.default.join(installDir, "Vibe Kanban.exe");
1088
+ if (import_fs2.default.existsSync(appExe)) {
1089
+ return launchWindowsApp(appExe);
1090
+ }
1091
+ console.error(
1092
+ "Installation complete. Please launch Vibe Kanban from your Start menu."
1093
+ );
1094
+ return 0;
1095
+ }
1096
+ function launchWindowsApp(appExe) {
1097
+ console.error("Launching Vibe Kanban...");
1098
+ (0, import_child_process.spawn)(appExe, [], { detached: true, stdio: "ignore" }).unref();
1099
+ return 0;
1100
+ }
1101
+ async function installAndLaunch(bundleInfo, osPlatform) {
1102
+ if (osPlatform === "darwin") {
1103
+ return installAndLaunchMacOS(bundleInfo);
1104
+ } else if (osPlatform === "linux") {
1105
+ return installAndLaunchLinux(bundleInfo);
1106
+ } else if (osPlatform === "win32") {
1107
+ return installAndLaunchWindows(bundleInfo);
1108
+ }
1109
+ throw new Error(
1110
+ `Desktop app not supported on platform: ${osPlatform}`
1111
+ );
1112
+ }
1113
+ function cleanOldDesktopVersions(desktopBaseDir, currentTag) {
1114
+ try {
1115
+ const entries = import_fs2.default.readdirSync(desktopBaseDir, {
1116
+ withFileTypes: true
1117
+ });
1118
+ for (const entry of entries) {
1119
+ if (entry.isDirectory() && entry.name !== currentTag) {
1120
+ const oldDir = import_path2.default.join(desktopBaseDir, entry.name);
1121
+ try {
1122
+ import_fs2.default.rmSync(oldDir, { recursive: true, force: true });
1123
+ } catch {
1124
+ }
1125
+ }
1126
+ }
1127
+ } catch {
1128
+ }
1129
+ }
1130
+
1131
+ // src/cli.ts
1132
+ var CLI_VERSION = require_package().version;
1133
+ function getEffectiveArch() {
1134
+ const platform2 = process.platform;
1135
+ const nodeArch = process.arch;
1136
+ if (platform2 === "darwin") {
1137
+ if (nodeArch === "arm64") return "arm64";
1138
+ try {
1139
+ const translated = (0, import_child_process2.execSync)("sysctl -in sysctl.proc_translated", {
1140
+ encoding: "utf8"
1141
+ }).trim();
1142
+ if (translated === "1") return "arm64";
1143
+ } catch {
1144
+ }
1145
+ return "x64";
1146
+ }
1147
+ if (/arm/i.test(nodeArch)) return "arm64";
1148
+ if (platform2 === "win32") {
1149
+ const pa = process.env.PROCESSOR_ARCHITECTURE || "";
1150
+ const paw = process.env.PROCESSOR_ARCHITEW6432 || "";
1151
+ if (/arm/i.test(pa) || /arm/i.test(paw)) return "arm64";
1152
+ }
1153
+ return "x64";
1154
+ }
1155
+ var platform = process.platform;
1156
+ var arch = getEffectiveArch();
1157
+ function getPlatformDir() {
1158
+ if (platform === "linux" && arch === "x64") return "linux-x64";
1159
+ if (platform === "linux" && arch === "arm64") return "linux-arm64";
1160
+ if (platform === "win32" && arch === "x64") return "windows-x64";
1161
+ if (platform === "win32" && arch === "arm64") return "windows-arm64";
1162
+ if (platform === "darwin" && arch === "x64") return "macos-x64";
1163
+ if (platform === "darwin" && arch === "arm64") return "macos-arm64";
1164
+ console.error(`Unsupported platform: ${platform}-${arch}`);
1165
+ console.error("Supported platforms:");
1166
+ console.error(" - Linux x64");
1167
+ console.error(" - Linux ARM64");
1168
+ console.error(" - Windows x64");
1169
+ console.error(" - Windows ARM64");
1170
+ console.error(" - macOS x64 (Intel)");
1171
+ console.error(" - macOS ARM64 (Apple Silicon)");
1172
+ process.exit(1);
1173
+ }
1174
+ function getBinaryName(base) {
1175
+ return platform === "win32" ? `${base}.exe` : base;
1176
+ }
1177
+ var platformDir = getPlatformDir();
1178
+ var versionCacheDir = LOCAL_DEV_MODE ? import_path3.default.join(LOCAL_DIST_DIR, platformDir) : import_path3.default.join(CACHE_DIR, BINARY_TAG, platformDir);
1179
+ function cleanOldVersions() {
1180
+ try {
1181
+ const entries = import_fs3.default.readdirSync(CACHE_DIR, {
1182
+ withFileTypes: true
1183
+ });
1184
+ for (const entry of entries) {
1185
+ if (entry.isDirectory() && entry.name !== BINARY_TAG) {
1186
+ const oldDir = import_path3.default.join(CACHE_DIR, entry.name);
1187
+ import_fs3.default.rmSync(oldDir, { recursive: true, force: true });
1188
+ }
1189
+ }
1190
+ } catch {
1191
+ }
1192
+ }
1193
+ function showProgress(downloaded, total) {
1194
+ const percent = total ? Math.round(downloaded / total * 100) : 0;
1195
+ const mb = (downloaded / (1024 * 1024)).toFixed(1);
1196
+ const totalMb = total ? (total / (1024 * 1024)).toFixed(1) : "?";
1197
+ process.stderr.write(
1198
+ `\r Downloading: ${mb}MB / ${totalMb}MB (${percent}%)`
1199
+ );
1200
+ }
1201
+ function buildMcpArgs(args) {
1202
+ return args.length > 0 ? args : ["--mode", "global"];
1203
+ }
1204
+ async function extractAndRun(baseName, launch) {
1205
+ const binName = getBinaryName(baseName);
1206
+ const binPath = import_path3.default.join(versionCacheDir, binName);
1207
+ const zipPath = import_path3.default.join(versionCacheDir, `${baseName}.zip`);
1208
+ try {
1209
+ if (import_fs3.default.existsSync(binPath)) {
1210
+ import_fs3.default.unlinkSync(binPath);
1211
+ }
1212
+ } catch (err) {
1213
+ if (process.env.VIBE_KANBAN_DEBUG) {
1214
+ const msg = err instanceof Error ? err.message : String(err);
1215
+ console.warn(`Warning: Could not delete existing binary: ${msg}`);
1216
+ }
1217
+ }
1218
+ if (!import_fs3.default.existsSync(zipPath)) {
1219
+ console.error(`Downloading ${baseName}...`);
1220
+ try {
1221
+ await ensureBinary(platformDir, baseName, showProgress);
1222
+ console.error("");
1223
+ } catch (err) {
1224
+ const msg = err instanceof Error ? err.message : String(err);
1225
+ console.error(`
1226
+ Download failed: ${msg}`);
1227
+ process.exit(1);
1228
+ }
1229
+ }
1230
+ if (!import_fs3.default.existsSync(binPath)) {
1231
+ try {
1232
+ const { default: AdmZip } = await import("adm-zip");
1233
+ const zip = new AdmZip(zipPath);
1234
+ zip.extractAllTo(versionCacheDir, true);
1235
+ } catch (err) {
1236
+ const msg = err instanceof Error ? err.message : String(err);
1237
+ console.error("Extraction failed:", msg);
1238
+ try {
1239
+ import_fs3.default.unlinkSync(zipPath);
1240
+ } catch {
1241
+ }
1242
+ process.exit(1);
1243
+ }
1244
+ }
1245
+ if (!import_fs3.default.existsSync(binPath)) {
1246
+ console.error(`Extracted binary not found at: ${binPath}`);
1247
+ console.error(
1248
+ "This usually indicates a corrupt download. Please try again."
1249
+ );
1250
+ process.exit(1);
1251
+ }
1252
+ if (!LOCAL_DEV_MODE) {
1253
+ cleanOldVersions();
1254
+ }
1255
+ if (platform !== "win32") {
1256
+ try {
1257
+ import_fs3.default.chmodSync(binPath, 493);
1258
+ } catch {
1259
+ }
1260
+ }
1261
+ return launch(binPath);
1262
+ }
1263
+ function checkForUpdates() {
1264
+ if (LOCAL_DEV_MODE) {
1265
+ return;
1266
+ }
1267
+ getLatestVersion().then((latest) => {
1268
+ if (latest && latest !== CLI_VERSION) {
1269
+ setTimeout(() => {
1270
+ console.log(`
1271
+ Update available: ${CLI_VERSION} -> ${latest}`);
1272
+ console.log(`Run: npx @raythunder/vibe-kanban@latest`);
1273
+ }, 2e3);
1274
+ }
1275
+ }).catch(() => {
1276
+ });
1277
+ }
1278
+ async function runMcp(args) {
1279
+ await extractAndRun("vibe-kanban-mcp", (bin) => {
1280
+ const proc = (0, import_child_process2.spawn)(bin, buildMcpArgs(args), {
1281
+ stdio: "inherit"
1282
+ });
1283
+ proc.on("exit", (c) => process.exit(c || 0));
1284
+ proc.on("error", (e) => {
1285
+ console.error("MCP server error:", e.message);
1286
+ process.exit(1);
1287
+ });
1288
+ process.on("SIGINT", () => {
1289
+ proc.kill("SIGINT");
1290
+ });
1291
+ process.on("SIGTERM", () => proc.kill("SIGTERM"));
1292
+ });
1293
+ }
1294
+ async function runReview(args) {
1295
+ await extractAndRun("vibe-kanban-review", (bin) => {
1296
+ const proc = (0, import_child_process2.spawn)(bin, args, { stdio: "inherit" });
1297
+ proc.on("exit", (c) => process.exit(c || 0));
1298
+ proc.on("error", (e) => {
1299
+ console.error("Review CLI error:", e.message);
1300
+ process.exit(1);
1301
+ });
1302
+ });
1303
+ }
1304
+ async function runMain(desktopMode) {
1305
+ checkForUpdates();
1306
+ const modeLabel = LOCAL_DEV_MODE ? " (local dev)" : "";
1307
+ const tauriPlatform = getTauriPlatform(platformDir);
1308
+ if (desktopMode && tauriPlatform) {
1309
+ try {
1310
+ console.log(
1311
+ `Starting vibe-kanban desktop v${CLI_VERSION}${modeLabel}...`
1312
+ );
1313
+ const bundleInfo = await ensureDesktopBundle(tauriPlatform, showProgress);
1314
+ console.error("");
1315
+ if (!LOCAL_DEV_MODE) {
1316
+ cleanOldDesktopVersions(DESKTOP_CACHE_DIR, BINARY_TAG);
1317
+ }
1318
+ const exitCode = await installAndLaunch(bundleInfo, platform);
1319
+ process.exit(exitCode);
1320
+ } catch (err) {
1321
+ const msg = err instanceof Error ? err.message : String(err);
1322
+ console.error(`Desktop app not available: ${msg}`);
1323
+ console.error("Falling back to browser mode...");
1324
+ }
1325
+ }
1326
+ console.log(`Starting vibe-kanban v${CLI_VERSION}${modeLabel}...`);
1327
+ await extractAndRun("vibe-kanban", (bin) => {
1328
+ (0, import_child_process2.execSync)(`"${bin}"`, { stdio: "inherit" });
1329
+ });
1330
+ }
1331
+ function normalizeArgv(argv) {
1332
+ const args = argv.slice(2);
1333
+ const mcpFlagIndex = args.indexOf("--mcp");
1334
+ if (mcpFlagIndex === -1) {
1335
+ return argv;
1336
+ }
1337
+ const normalizedArgs = [
1338
+ ...args.slice(0, mcpFlagIndex),
1339
+ "mcp",
1340
+ ...args.slice(mcpFlagIndex + 1)
1341
+ ];
1342
+ return [...argv.slice(0, 2), ...normalizedArgs];
1343
+ }
1344
+ function runOrExit(task) {
1345
+ void task.catch((err) => {
1346
+ const msg = err instanceof Error ? err.message : String(err);
1347
+ console.error("Fatal error:", msg);
1348
+ if (process.env.VIBE_KANBAN_DEBUG && err instanceof Error) {
1349
+ console.error(err.stack);
1350
+ }
1351
+ process.exit(1);
1352
+ });
1353
+ }
1354
+ async function main() {
1355
+ import_fs3.default.mkdirSync(versionCacheDir, { recursive: true });
1356
+ const cli = cac("vibe-kanban");
1357
+ cli.command("[...args]", "Launch the local vibe-kanban app").option("--desktop", "Launch the desktop app instead of browser mode").allowUnknownOptions().action((_args, options) => {
1358
+ runOrExit(runMain(Boolean(options.desktop)));
1359
+ });
1360
+ cli.command("review [...args]", "Run the review CLI").allowUnknownOptions().action((args) => {
1361
+ runOrExit(runReview(args));
1362
+ });
1363
+ cli.command("mcp [...args]", "Run the MCP server").allowUnknownOptions().action((args) => {
1364
+ runOrExit(runMcp(args));
1365
+ });
1366
+ cli.help();
1367
+ cli.version(CLI_VERSION);
1368
+ cli.parse(normalizeArgv(process.argv));
1369
+ }
1370
+ main().catch((err) => {
1371
+ const msg = err instanceof Error ? err.message : String(err);
1372
+ console.error("Fatal error:", msg);
1373
+ if (process.env.VIBE_KANBAN_DEBUG && err instanceof Error) {
1374
+ console.error(err.stack);
1375
+ }
1376
+ process.exit(1);
1377
+ });