@hasna/contacts 0.1.0

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.
@@ -0,0 +1,3992 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+ var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __toESM = (mod, isNodeMode, target) => {
9
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
10
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
+ for (let key of __getOwnPropNames(mod))
12
+ if (!__hasOwnProp.call(to, key))
13
+ __defProp(to, key, {
14
+ get: () => mod[key],
15
+ enumerable: true
16
+ });
17
+ return to;
18
+ };
19
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
20
+ var __export = (target, all) => {
21
+ for (var name in all)
22
+ __defProp(target, name, {
23
+ get: all[name],
24
+ enumerable: true,
25
+ configurable: true,
26
+ set: (newValue) => all[name] = () => newValue
27
+ });
28
+ };
29
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
30
+ var __require = import.meta.require;
31
+
32
+ // node_modules/commander/lib/error.js
33
+ var require_error = __commonJS((exports) => {
34
+ class CommanderError extends Error {
35
+ constructor(exitCode, code, message) {
36
+ super(message);
37
+ Error.captureStackTrace(this, this.constructor);
38
+ this.name = this.constructor.name;
39
+ this.code = code;
40
+ this.exitCode = exitCode;
41
+ this.nestedError = undefined;
42
+ }
43
+ }
44
+
45
+ class InvalidArgumentError extends CommanderError {
46
+ constructor(message) {
47
+ super(1, "commander.invalidArgument", message);
48
+ Error.captureStackTrace(this, this.constructor);
49
+ this.name = this.constructor.name;
50
+ }
51
+ }
52
+ exports.CommanderError = CommanderError;
53
+ exports.InvalidArgumentError = InvalidArgumentError;
54
+ });
55
+
56
+ // node_modules/commander/lib/argument.js
57
+ var require_argument = __commonJS((exports) => {
58
+ var { InvalidArgumentError } = require_error();
59
+
60
+ class Argument {
61
+ constructor(name, description) {
62
+ this.description = description || "";
63
+ this.variadic = false;
64
+ this.parseArg = undefined;
65
+ this.defaultValue = undefined;
66
+ this.defaultValueDescription = undefined;
67
+ this.argChoices = undefined;
68
+ switch (name[0]) {
69
+ case "<":
70
+ this.required = true;
71
+ this._name = name.slice(1, -1);
72
+ break;
73
+ case "[":
74
+ this.required = false;
75
+ this._name = name.slice(1, -1);
76
+ break;
77
+ default:
78
+ this.required = true;
79
+ this._name = name;
80
+ break;
81
+ }
82
+ if (this._name.length > 3 && this._name.slice(-3) === "...") {
83
+ this.variadic = true;
84
+ this._name = this._name.slice(0, -3);
85
+ }
86
+ }
87
+ name() {
88
+ return this._name;
89
+ }
90
+ _concatValue(value, previous) {
91
+ if (previous === this.defaultValue || !Array.isArray(previous)) {
92
+ return [value];
93
+ }
94
+ return previous.concat(value);
95
+ }
96
+ default(value, description) {
97
+ this.defaultValue = value;
98
+ this.defaultValueDescription = description;
99
+ return this;
100
+ }
101
+ argParser(fn) {
102
+ this.parseArg = fn;
103
+ return this;
104
+ }
105
+ choices(values) {
106
+ this.argChoices = values.slice();
107
+ this.parseArg = (arg, previous) => {
108
+ if (!this.argChoices.includes(arg)) {
109
+ throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
110
+ }
111
+ if (this.variadic) {
112
+ return this._concatValue(arg, previous);
113
+ }
114
+ return arg;
115
+ };
116
+ return this;
117
+ }
118
+ argRequired() {
119
+ this.required = true;
120
+ return this;
121
+ }
122
+ argOptional() {
123
+ this.required = false;
124
+ return this;
125
+ }
126
+ }
127
+ function humanReadableArgName(arg) {
128
+ const nameOutput = arg.name() + (arg.variadic === true ? "..." : "");
129
+ return arg.required ? "<" + nameOutput + ">" : "[" + nameOutput + "]";
130
+ }
131
+ exports.Argument = Argument;
132
+ exports.humanReadableArgName = humanReadableArgName;
133
+ });
134
+
135
+ // node_modules/commander/lib/help.js
136
+ var require_help = __commonJS((exports) => {
137
+ var { humanReadableArgName } = require_argument();
138
+
139
+ class Help {
140
+ constructor() {
141
+ this.helpWidth = undefined;
142
+ this.minWidthToWrap = 40;
143
+ this.sortSubcommands = false;
144
+ this.sortOptions = false;
145
+ this.showGlobalOptions = false;
146
+ }
147
+ prepareContext(contextOptions) {
148
+ this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
149
+ }
150
+ visibleCommands(cmd) {
151
+ const visibleCommands = cmd.commands.filter((cmd2) => !cmd2._hidden);
152
+ const helpCommand = cmd._getHelpCommand();
153
+ if (helpCommand && !helpCommand._hidden) {
154
+ visibleCommands.push(helpCommand);
155
+ }
156
+ if (this.sortSubcommands) {
157
+ visibleCommands.sort((a, b) => {
158
+ return a.name().localeCompare(b.name());
159
+ });
160
+ }
161
+ return visibleCommands;
162
+ }
163
+ compareOptions(a, b) {
164
+ const getSortKey = (option) => {
165
+ return option.short ? option.short.replace(/^-/, "") : option.long.replace(/^--/, "");
166
+ };
167
+ return getSortKey(a).localeCompare(getSortKey(b));
168
+ }
169
+ visibleOptions(cmd) {
170
+ const visibleOptions = cmd.options.filter((option) => !option.hidden);
171
+ const helpOption = cmd._getHelpOption();
172
+ if (helpOption && !helpOption.hidden) {
173
+ const removeShort = helpOption.short && cmd._findOption(helpOption.short);
174
+ const removeLong = helpOption.long && cmd._findOption(helpOption.long);
175
+ if (!removeShort && !removeLong) {
176
+ visibleOptions.push(helpOption);
177
+ } else if (helpOption.long && !removeLong) {
178
+ visibleOptions.push(cmd.createOption(helpOption.long, helpOption.description));
179
+ } else if (helpOption.short && !removeShort) {
180
+ visibleOptions.push(cmd.createOption(helpOption.short, helpOption.description));
181
+ }
182
+ }
183
+ if (this.sortOptions) {
184
+ visibleOptions.sort(this.compareOptions);
185
+ }
186
+ return visibleOptions;
187
+ }
188
+ visibleGlobalOptions(cmd) {
189
+ if (!this.showGlobalOptions)
190
+ return [];
191
+ const globalOptions = [];
192
+ for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
193
+ const visibleOptions = ancestorCmd.options.filter((option) => !option.hidden);
194
+ globalOptions.push(...visibleOptions);
195
+ }
196
+ if (this.sortOptions) {
197
+ globalOptions.sort(this.compareOptions);
198
+ }
199
+ return globalOptions;
200
+ }
201
+ visibleArguments(cmd) {
202
+ if (cmd._argsDescription) {
203
+ cmd.registeredArguments.forEach((argument) => {
204
+ argument.description = argument.description || cmd._argsDescription[argument.name()] || "";
205
+ });
206
+ }
207
+ if (cmd.registeredArguments.find((argument) => argument.description)) {
208
+ return cmd.registeredArguments;
209
+ }
210
+ return [];
211
+ }
212
+ subcommandTerm(cmd) {
213
+ const args = cmd.registeredArguments.map((arg) => humanReadableArgName(arg)).join(" ");
214
+ return cmd._name + (cmd._aliases[0] ? "|" + cmd._aliases[0] : "") + (cmd.options.length ? " [options]" : "") + (args ? " " + args : "");
215
+ }
216
+ optionTerm(option) {
217
+ return option.flags;
218
+ }
219
+ argumentTerm(argument) {
220
+ return argument.name();
221
+ }
222
+ longestSubcommandTermLength(cmd, helper) {
223
+ return helper.visibleCommands(cmd).reduce((max, command) => {
224
+ return Math.max(max, this.displayWidth(helper.styleSubcommandTerm(helper.subcommandTerm(command))));
225
+ }, 0);
226
+ }
227
+ longestOptionTermLength(cmd, helper) {
228
+ return helper.visibleOptions(cmd).reduce((max, option) => {
229
+ return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
230
+ }, 0);
231
+ }
232
+ longestGlobalOptionTermLength(cmd, helper) {
233
+ return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
234
+ return Math.max(max, this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))));
235
+ }, 0);
236
+ }
237
+ longestArgumentTermLength(cmd, helper) {
238
+ return helper.visibleArguments(cmd).reduce((max, argument) => {
239
+ return Math.max(max, this.displayWidth(helper.styleArgumentTerm(helper.argumentTerm(argument))));
240
+ }, 0);
241
+ }
242
+ commandUsage(cmd) {
243
+ let cmdName = cmd._name;
244
+ if (cmd._aliases[0]) {
245
+ cmdName = cmdName + "|" + cmd._aliases[0];
246
+ }
247
+ let ancestorCmdNames = "";
248
+ for (let ancestorCmd = cmd.parent;ancestorCmd; ancestorCmd = ancestorCmd.parent) {
249
+ ancestorCmdNames = ancestorCmd.name() + " " + ancestorCmdNames;
250
+ }
251
+ return ancestorCmdNames + cmdName + " " + cmd.usage();
252
+ }
253
+ commandDescription(cmd) {
254
+ return cmd.description();
255
+ }
256
+ subcommandDescription(cmd) {
257
+ return cmd.summary() || cmd.description();
258
+ }
259
+ optionDescription(option) {
260
+ const extraInfo = [];
261
+ if (option.argChoices) {
262
+ extraInfo.push(`choices: ${option.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`);
263
+ }
264
+ if (option.defaultValue !== undefined) {
265
+ const showDefault = option.required || option.optional || option.isBoolean() && typeof option.defaultValue === "boolean";
266
+ if (showDefault) {
267
+ extraInfo.push(`default: ${option.defaultValueDescription || JSON.stringify(option.defaultValue)}`);
268
+ }
269
+ }
270
+ if (option.presetArg !== undefined && option.optional) {
271
+ extraInfo.push(`preset: ${JSON.stringify(option.presetArg)}`);
272
+ }
273
+ if (option.envVar !== undefined) {
274
+ extraInfo.push(`env: ${option.envVar}`);
275
+ }
276
+ if (extraInfo.length > 0) {
277
+ return `${option.description} (${extraInfo.join(", ")})`;
278
+ }
279
+ return option.description;
280
+ }
281
+ argumentDescription(argument) {
282
+ const extraInfo = [];
283
+ if (argument.argChoices) {
284
+ extraInfo.push(`choices: ${argument.argChoices.map((choice) => JSON.stringify(choice)).join(", ")}`);
285
+ }
286
+ if (argument.defaultValue !== undefined) {
287
+ extraInfo.push(`default: ${argument.defaultValueDescription || JSON.stringify(argument.defaultValue)}`);
288
+ }
289
+ if (extraInfo.length > 0) {
290
+ const extraDescription = `(${extraInfo.join(", ")})`;
291
+ if (argument.description) {
292
+ return `${argument.description} ${extraDescription}`;
293
+ }
294
+ return extraDescription;
295
+ }
296
+ return argument.description;
297
+ }
298
+ formatHelp(cmd, helper) {
299
+ const termWidth = helper.padWidth(cmd, helper);
300
+ const helpWidth = helper.helpWidth ?? 80;
301
+ function callFormatItem(term, description) {
302
+ return helper.formatItem(term, termWidth, description, helper);
303
+ }
304
+ let output = [
305
+ `${helper.styleTitle("Usage:")} ${helper.styleUsage(helper.commandUsage(cmd))}`,
306
+ ""
307
+ ];
308
+ const commandDescription = helper.commandDescription(cmd);
309
+ if (commandDescription.length > 0) {
310
+ output = output.concat([
311
+ helper.boxWrap(helper.styleCommandDescription(commandDescription), helpWidth),
312
+ ""
313
+ ]);
314
+ }
315
+ const argumentList = helper.visibleArguments(cmd).map((argument) => {
316
+ return callFormatItem(helper.styleArgumentTerm(helper.argumentTerm(argument)), helper.styleArgumentDescription(helper.argumentDescription(argument)));
317
+ });
318
+ if (argumentList.length > 0) {
319
+ output = output.concat([
320
+ helper.styleTitle("Arguments:"),
321
+ ...argumentList,
322
+ ""
323
+ ]);
324
+ }
325
+ const optionList = helper.visibleOptions(cmd).map((option) => {
326
+ return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
327
+ });
328
+ if (optionList.length > 0) {
329
+ output = output.concat([
330
+ helper.styleTitle("Options:"),
331
+ ...optionList,
332
+ ""
333
+ ]);
334
+ }
335
+ if (helper.showGlobalOptions) {
336
+ const globalOptionList = helper.visibleGlobalOptions(cmd).map((option) => {
337
+ return callFormatItem(helper.styleOptionTerm(helper.optionTerm(option)), helper.styleOptionDescription(helper.optionDescription(option)));
338
+ });
339
+ if (globalOptionList.length > 0) {
340
+ output = output.concat([
341
+ helper.styleTitle("Global Options:"),
342
+ ...globalOptionList,
343
+ ""
344
+ ]);
345
+ }
346
+ }
347
+ const commandList = helper.visibleCommands(cmd).map((cmd2) => {
348
+ return callFormatItem(helper.styleSubcommandTerm(helper.subcommandTerm(cmd2)), helper.styleSubcommandDescription(helper.subcommandDescription(cmd2)));
349
+ });
350
+ if (commandList.length > 0) {
351
+ output = output.concat([
352
+ helper.styleTitle("Commands:"),
353
+ ...commandList,
354
+ ""
355
+ ]);
356
+ }
357
+ return output.join(`
358
+ `);
359
+ }
360
+ displayWidth(str) {
361
+ return stripColor(str).length;
362
+ }
363
+ styleTitle(str) {
364
+ return str;
365
+ }
366
+ styleUsage(str) {
367
+ return str.split(" ").map((word) => {
368
+ if (word === "[options]")
369
+ return this.styleOptionText(word);
370
+ if (word === "[command]")
371
+ return this.styleSubcommandText(word);
372
+ if (word[0] === "[" || word[0] === "<")
373
+ return this.styleArgumentText(word);
374
+ return this.styleCommandText(word);
375
+ }).join(" ");
376
+ }
377
+ styleCommandDescription(str) {
378
+ return this.styleDescriptionText(str);
379
+ }
380
+ styleOptionDescription(str) {
381
+ return this.styleDescriptionText(str);
382
+ }
383
+ styleSubcommandDescription(str) {
384
+ return this.styleDescriptionText(str);
385
+ }
386
+ styleArgumentDescription(str) {
387
+ return this.styleDescriptionText(str);
388
+ }
389
+ styleDescriptionText(str) {
390
+ return str;
391
+ }
392
+ styleOptionTerm(str) {
393
+ return this.styleOptionText(str);
394
+ }
395
+ styleSubcommandTerm(str) {
396
+ return str.split(" ").map((word) => {
397
+ if (word === "[options]")
398
+ return this.styleOptionText(word);
399
+ if (word[0] === "[" || word[0] === "<")
400
+ return this.styleArgumentText(word);
401
+ return this.styleSubcommandText(word);
402
+ }).join(" ");
403
+ }
404
+ styleArgumentTerm(str) {
405
+ return this.styleArgumentText(str);
406
+ }
407
+ styleOptionText(str) {
408
+ return str;
409
+ }
410
+ styleArgumentText(str) {
411
+ return str;
412
+ }
413
+ styleSubcommandText(str) {
414
+ return str;
415
+ }
416
+ styleCommandText(str) {
417
+ return str;
418
+ }
419
+ padWidth(cmd, helper) {
420
+ return Math.max(helper.longestOptionTermLength(cmd, helper), helper.longestGlobalOptionTermLength(cmd, helper), helper.longestSubcommandTermLength(cmd, helper), helper.longestArgumentTermLength(cmd, helper));
421
+ }
422
+ preformatted(str) {
423
+ return /\n[^\S\r\n]/.test(str);
424
+ }
425
+ formatItem(term, termWidth, description, helper) {
426
+ const itemIndent = 2;
427
+ const itemIndentStr = " ".repeat(itemIndent);
428
+ if (!description)
429
+ return itemIndentStr + term;
430
+ const paddedTerm = term.padEnd(termWidth + term.length - helper.displayWidth(term));
431
+ const spacerWidth = 2;
432
+ const helpWidth = this.helpWidth ?? 80;
433
+ const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
434
+ let formattedDescription;
435
+ if (remainingWidth < this.minWidthToWrap || helper.preformatted(description)) {
436
+ formattedDescription = description;
437
+ } else {
438
+ const wrappedDescription = helper.boxWrap(description, remainingWidth);
439
+ formattedDescription = wrappedDescription.replace(/\n/g, `
440
+ ` + " ".repeat(termWidth + spacerWidth));
441
+ }
442
+ return itemIndentStr + paddedTerm + " ".repeat(spacerWidth) + formattedDescription.replace(/\n/g, `
443
+ ${itemIndentStr}`);
444
+ }
445
+ boxWrap(str, width) {
446
+ if (width < this.minWidthToWrap)
447
+ return str;
448
+ const rawLines = str.split(/\r\n|\n/);
449
+ const chunkPattern = /[\s]*[^\s]+/g;
450
+ const wrappedLines = [];
451
+ rawLines.forEach((line) => {
452
+ const chunks = line.match(chunkPattern);
453
+ if (chunks === null) {
454
+ wrappedLines.push("");
455
+ return;
456
+ }
457
+ let sumChunks = [chunks.shift()];
458
+ let sumWidth = this.displayWidth(sumChunks[0]);
459
+ chunks.forEach((chunk) => {
460
+ const visibleWidth = this.displayWidth(chunk);
461
+ if (sumWidth + visibleWidth <= width) {
462
+ sumChunks.push(chunk);
463
+ sumWidth += visibleWidth;
464
+ return;
465
+ }
466
+ wrappedLines.push(sumChunks.join(""));
467
+ const nextChunk = chunk.trimStart();
468
+ sumChunks = [nextChunk];
469
+ sumWidth = this.displayWidth(nextChunk);
470
+ });
471
+ wrappedLines.push(sumChunks.join(""));
472
+ });
473
+ return wrappedLines.join(`
474
+ `);
475
+ }
476
+ }
477
+ function stripColor(str) {
478
+ const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
479
+ return str.replace(sgrPattern, "");
480
+ }
481
+ exports.Help = Help;
482
+ exports.stripColor = stripColor;
483
+ });
484
+
485
+ // node_modules/commander/lib/option.js
486
+ var require_option = __commonJS((exports) => {
487
+ var { InvalidArgumentError } = require_error();
488
+
489
+ class Option {
490
+ constructor(flags, description) {
491
+ this.flags = flags;
492
+ this.description = description || "";
493
+ this.required = flags.includes("<");
494
+ this.optional = flags.includes("[");
495
+ this.variadic = /\w\.\.\.[>\]]$/.test(flags);
496
+ this.mandatory = false;
497
+ const optionFlags = splitOptionFlags(flags);
498
+ this.short = optionFlags.shortFlag;
499
+ this.long = optionFlags.longFlag;
500
+ this.negate = false;
501
+ if (this.long) {
502
+ this.negate = this.long.startsWith("--no-");
503
+ }
504
+ this.defaultValue = undefined;
505
+ this.defaultValueDescription = undefined;
506
+ this.presetArg = undefined;
507
+ this.envVar = undefined;
508
+ this.parseArg = undefined;
509
+ this.hidden = false;
510
+ this.argChoices = undefined;
511
+ this.conflictsWith = [];
512
+ this.implied = undefined;
513
+ }
514
+ default(value, description) {
515
+ this.defaultValue = value;
516
+ this.defaultValueDescription = description;
517
+ return this;
518
+ }
519
+ preset(arg) {
520
+ this.presetArg = arg;
521
+ return this;
522
+ }
523
+ conflicts(names) {
524
+ this.conflictsWith = this.conflictsWith.concat(names);
525
+ return this;
526
+ }
527
+ implies(impliedOptionValues) {
528
+ let newImplied = impliedOptionValues;
529
+ if (typeof impliedOptionValues === "string") {
530
+ newImplied = { [impliedOptionValues]: true };
531
+ }
532
+ this.implied = Object.assign(this.implied || {}, newImplied);
533
+ return this;
534
+ }
535
+ env(name) {
536
+ this.envVar = name;
537
+ return this;
538
+ }
539
+ argParser(fn) {
540
+ this.parseArg = fn;
541
+ return this;
542
+ }
543
+ makeOptionMandatory(mandatory = true) {
544
+ this.mandatory = !!mandatory;
545
+ return this;
546
+ }
547
+ hideHelp(hide = true) {
548
+ this.hidden = !!hide;
549
+ return this;
550
+ }
551
+ _concatValue(value, previous) {
552
+ if (previous === this.defaultValue || !Array.isArray(previous)) {
553
+ return [value];
554
+ }
555
+ return previous.concat(value);
556
+ }
557
+ choices(values) {
558
+ this.argChoices = values.slice();
559
+ this.parseArg = (arg, previous) => {
560
+ if (!this.argChoices.includes(arg)) {
561
+ throw new InvalidArgumentError(`Allowed choices are ${this.argChoices.join(", ")}.`);
562
+ }
563
+ if (this.variadic) {
564
+ return this._concatValue(arg, previous);
565
+ }
566
+ return arg;
567
+ };
568
+ return this;
569
+ }
570
+ name() {
571
+ if (this.long) {
572
+ return this.long.replace(/^--/, "");
573
+ }
574
+ return this.short.replace(/^-/, "");
575
+ }
576
+ attributeName() {
577
+ if (this.negate) {
578
+ return camelcase(this.name().replace(/^no-/, ""));
579
+ }
580
+ return camelcase(this.name());
581
+ }
582
+ is(arg) {
583
+ return this.short === arg || this.long === arg;
584
+ }
585
+ isBoolean() {
586
+ return !this.required && !this.optional && !this.negate;
587
+ }
588
+ }
589
+
590
+ class DualOptions {
591
+ constructor(options) {
592
+ this.positiveOptions = new Map;
593
+ this.negativeOptions = new Map;
594
+ this.dualOptions = new Set;
595
+ options.forEach((option) => {
596
+ if (option.negate) {
597
+ this.negativeOptions.set(option.attributeName(), option);
598
+ } else {
599
+ this.positiveOptions.set(option.attributeName(), option);
600
+ }
601
+ });
602
+ this.negativeOptions.forEach((value, key) => {
603
+ if (this.positiveOptions.has(key)) {
604
+ this.dualOptions.add(key);
605
+ }
606
+ });
607
+ }
608
+ valueFromOption(value, option) {
609
+ const optionKey = option.attributeName();
610
+ if (!this.dualOptions.has(optionKey))
611
+ return true;
612
+ const preset = this.negativeOptions.get(optionKey).presetArg;
613
+ const negativeValue = preset !== undefined ? preset : false;
614
+ return option.negate === (negativeValue === value);
615
+ }
616
+ }
617
+ function camelcase(str) {
618
+ return str.split("-").reduce((str2, word) => {
619
+ return str2 + word[0].toUpperCase() + word.slice(1);
620
+ });
621
+ }
622
+ function splitOptionFlags(flags) {
623
+ let shortFlag;
624
+ let longFlag;
625
+ const shortFlagExp = /^-[^-]$/;
626
+ const longFlagExp = /^--[^-]/;
627
+ const flagParts = flags.split(/[ |,]+/).concat("guard");
628
+ if (shortFlagExp.test(flagParts[0]))
629
+ shortFlag = flagParts.shift();
630
+ if (longFlagExp.test(flagParts[0]))
631
+ longFlag = flagParts.shift();
632
+ if (!shortFlag && shortFlagExp.test(flagParts[0]))
633
+ shortFlag = flagParts.shift();
634
+ if (!shortFlag && longFlagExp.test(flagParts[0])) {
635
+ shortFlag = longFlag;
636
+ longFlag = flagParts.shift();
637
+ }
638
+ if (flagParts[0].startsWith("-")) {
639
+ const unsupportedFlag = flagParts[0];
640
+ const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
641
+ if (/^-[^-][^-]/.test(unsupportedFlag))
642
+ throw new Error(`${baseError}
643
+ - a short flag is a single dash and a single character
644
+ - either use a single dash and a single character (for a short flag)
645
+ - or use a double dash for a long option (and can have two, like '--ws, --workspace')`);
646
+ if (shortFlagExp.test(unsupportedFlag))
647
+ throw new Error(`${baseError}
648
+ - too many short flags`);
649
+ if (longFlagExp.test(unsupportedFlag))
650
+ throw new Error(`${baseError}
651
+ - too many long flags`);
652
+ throw new Error(`${baseError}
653
+ - unrecognised flag format`);
654
+ }
655
+ if (shortFlag === undefined && longFlag === undefined)
656
+ throw new Error(`option creation failed due to no flags found in '${flags}'.`);
657
+ return { shortFlag, longFlag };
658
+ }
659
+ exports.Option = Option;
660
+ exports.DualOptions = DualOptions;
661
+ });
662
+
663
+ // node_modules/commander/lib/suggestSimilar.js
664
+ var require_suggestSimilar = __commonJS((exports) => {
665
+ var maxDistance = 3;
666
+ function editDistance(a, b) {
667
+ if (Math.abs(a.length - b.length) > maxDistance)
668
+ return Math.max(a.length, b.length);
669
+ const d = [];
670
+ for (let i = 0;i <= a.length; i++) {
671
+ d[i] = [i];
672
+ }
673
+ for (let j = 0;j <= b.length; j++) {
674
+ d[0][j] = j;
675
+ }
676
+ for (let j = 1;j <= b.length; j++) {
677
+ for (let i = 1;i <= a.length; i++) {
678
+ let cost = 1;
679
+ if (a[i - 1] === b[j - 1]) {
680
+ cost = 0;
681
+ } else {
682
+ cost = 1;
683
+ }
684
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost);
685
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
686
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
687
+ }
688
+ }
689
+ }
690
+ return d[a.length][b.length];
691
+ }
692
+ function suggestSimilar(word, candidates) {
693
+ if (!candidates || candidates.length === 0)
694
+ return "";
695
+ candidates = Array.from(new Set(candidates));
696
+ const searchingOptions = word.startsWith("--");
697
+ if (searchingOptions) {
698
+ word = word.slice(2);
699
+ candidates = candidates.map((candidate) => candidate.slice(2));
700
+ }
701
+ let similar = [];
702
+ let bestDistance = maxDistance;
703
+ const minSimilarity = 0.4;
704
+ candidates.forEach((candidate) => {
705
+ if (candidate.length <= 1)
706
+ return;
707
+ const distance = editDistance(word, candidate);
708
+ const length = Math.max(word.length, candidate.length);
709
+ const similarity = (length - distance) / length;
710
+ if (similarity > minSimilarity) {
711
+ if (distance < bestDistance) {
712
+ bestDistance = distance;
713
+ similar = [candidate];
714
+ } else if (distance === bestDistance) {
715
+ similar.push(candidate);
716
+ }
717
+ }
718
+ });
719
+ similar.sort((a, b) => a.localeCompare(b));
720
+ if (searchingOptions) {
721
+ similar = similar.map((candidate) => `--${candidate}`);
722
+ }
723
+ if (similar.length > 1) {
724
+ return `
725
+ (Did you mean one of ${similar.join(", ")}?)`;
726
+ }
727
+ if (similar.length === 1) {
728
+ return `
729
+ (Did you mean ${similar[0]}?)`;
730
+ }
731
+ return "";
732
+ }
733
+ exports.suggestSimilar = suggestSimilar;
734
+ });
735
+
736
+ // node_modules/commander/lib/command.js
737
+ var require_command = __commonJS((exports) => {
738
+ var EventEmitter = __require("events").EventEmitter;
739
+ var childProcess = __require("child_process");
740
+ var path = __require("path");
741
+ var fs = __require("fs");
742
+ var process2 = __require("process");
743
+ var { Argument, humanReadableArgName } = require_argument();
744
+ var { CommanderError } = require_error();
745
+ var { Help, stripColor } = require_help();
746
+ var { Option, DualOptions } = require_option();
747
+ var { suggestSimilar } = require_suggestSimilar();
748
+
749
+ class Command extends EventEmitter {
750
+ constructor(name) {
751
+ super();
752
+ this.commands = [];
753
+ this.options = [];
754
+ this.parent = null;
755
+ this._allowUnknownOption = false;
756
+ this._allowExcessArguments = false;
757
+ this.registeredArguments = [];
758
+ this._args = this.registeredArguments;
759
+ this.args = [];
760
+ this.rawArgs = [];
761
+ this.processedArgs = [];
762
+ this._scriptPath = null;
763
+ this._name = name || "";
764
+ this._optionValues = {};
765
+ this._optionValueSources = {};
766
+ this._storeOptionsAsProperties = false;
767
+ this._actionHandler = null;
768
+ this._executableHandler = false;
769
+ this._executableFile = null;
770
+ this._executableDir = null;
771
+ this._defaultCommandName = null;
772
+ this._exitCallback = null;
773
+ this._aliases = [];
774
+ this._combineFlagAndOptionalValue = true;
775
+ this._description = "";
776
+ this._summary = "";
777
+ this._argsDescription = undefined;
778
+ this._enablePositionalOptions = false;
779
+ this._passThroughOptions = false;
780
+ this._lifeCycleHooks = {};
781
+ this._showHelpAfterError = false;
782
+ this._showSuggestionAfterError = true;
783
+ this._savedState = null;
784
+ this._outputConfiguration = {
785
+ writeOut: (str) => process2.stdout.write(str),
786
+ writeErr: (str) => process2.stderr.write(str),
787
+ outputError: (str, write) => write(str),
788
+ getOutHelpWidth: () => process2.stdout.isTTY ? process2.stdout.columns : undefined,
789
+ getErrHelpWidth: () => process2.stderr.isTTY ? process2.stderr.columns : undefined,
790
+ getOutHasColors: () => useColor() ?? (process2.stdout.isTTY && process2.stdout.hasColors?.()),
791
+ getErrHasColors: () => useColor() ?? (process2.stderr.isTTY && process2.stderr.hasColors?.()),
792
+ stripColor: (str) => stripColor(str)
793
+ };
794
+ this._hidden = false;
795
+ this._helpOption = undefined;
796
+ this._addImplicitHelpCommand = undefined;
797
+ this._helpCommand = undefined;
798
+ this._helpConfiguration = {};
799
+ }
800
+ copyInheritedSettings(sourceCommand) {
801
+ this._outputConfiguration = sourceCommand._outputConfiguration;
802
+ this._helpOption = sourceCommand._helpOption;
803
+ this._helpCommand = sourceCommand._helpCommand;
804
+ this._helpConfiguration = sourceCommand._helpConfiguration;
805
+ this._exitCallback = sourceCommand._exitCallback;
806
+ this._storeOptionsAsProperties = sourceCommand._storeOptionsAsProperties;
807
+ this._combineFlagAndOptionalValue = sourceCommand._combineFlagAndOptionalValue;
808
+ this._allowExcessArguments = sourceCommand._allowExcessArguments;
809
+ this._enablePositionalOptions = sourceCommand._enablePositionalOptions;
810
+ this._showHelpAfterError = sourceCommand._showHelpAfterError;
811
+ this._showSuggestionAfterError = sourceCommand._showSuggestionAfterError;
812
+ return this;
813
+ }
814
+ _getCommandAndAncestors() {
815
+ const result = [];
816
+ for (let command = this;command; command = command.parent) {
817
+ result.push(command);
818
+ }
819
+ return result;
820
+ }
821
+ command(nameAndArgs, actionOptsOrExecDesc, execOpts) {
822
+ let desc = actionOptsOrExecDesc;
823
+ let opts = execOpts;
824
+ if (typeof desc === "object" && desc !== null) {
825
+ opts = desc;
826
+ desc = null;
827
+ }
828
+ opts = opts || {};
829
+ const [, name, args] = nameAndArgs.match(/([^ ]+) *(.*)/);
830
+ const cmd = this.createCommand(name);
831
+ if (desc) {
832
+ cmd.description(desc);
833
+ cmd._executableHandler = true;
834
+ }
835
+ if (opts.isDefault)
836
+ this._defaultCommandName = cmd._name;
837
+ cmd._hidden = !!(opts.noHelp || opts.hidden);
838
+ cmd._executableFile = opts.executableFile || null;
839
+ if (args)
840
+ cmd.arguments(args);
841
+ this._registerCommand(cmd);
842
+ cmd.parent = this;
843
+ cmd.copyInheritedSettings(this);
844
+ if (desc)
845
+ return this;
846
+ return cmd;
847
+ }
848
+ createCommand(name) {
849
+ return new Command(name);
850
+ }
851
+ createHelp() {
852
+ return Object.assign(new Help, this.configureHelp());
853
+ }
854
+ configureHelp(configuration) {
855
+ if (configuration === undefined)
856
+ return this._helpConfiguration;
857
+ this._helpConfiguration = configuration;
858
+ return this;
859
+ }
860
+ configureOutput(configuration) {
861
+ if (configuration === undefined)
862
+ return this._outputConfiguration;
863
+ Object.assign(this._outputConfiguration, configuration);
864
+ return this;
865
+ }
866
+ showHelpAfterError(displayHelp = true) {
867
+ if (typeof displayHelp !== "string")
868
+ displayHelp = !!displayHelp;
869
+ this._showHelpAfterError = displayHelp;
870
+ return this;
871
+ }
872
+ showSuggestionAfterError(displaySuggestion = true) {
873
+ this._showSuggestionAfterError = !!displaySuggestion;
874
+ return this;
875
+ }
876
+ addCommand(cmd, opts) {
877
+ if (!cmd._name) {
878
+ throw new Error(`Command passed to .addCommand() must have a name
879
+ - specify the name in Command constructor or using .name()`);
880
+ }
881
+ opts = opts || {};
882
+ if (opts.isDefault)
883
+ this._defaultCommandName = cmd._name;
884
+ if (opts.noHelp || opts.hidden)
885
+ cmd._hidden = true;
886
+ this._registerCommand(cmd);
887
+ cmd.parent = this;
888
+ cmd._checkForBrokenPassThrough();
889
+ return this;
890
+ }
891
+ createArgument(name, description) {
892
+ return new Argument(name, description);
893
+ }
894
+ argument(name, description, fn, defaultValue) {
895
+ const argument = this.createArgument(name, description);
896
+ if (typeof fn === "function") {
897
+ argument.default(defaultValue).argParser(fn);
898
+ } else {
899
+ argument.default(fn);
900
+ }
901
+ this.addArgument(argument);
902
+ return this;
903
+ }
904
+ arguments(names) {
905
+ names.trim().split(/ +/).forEach((detail) => {
906
+ this.argument(detail);
907
+ });
908
+ return this;
909
+ }
910
+ addArgument(argument) {
911
+ const previousArgument = this.registeredArguments.slice(-1)[0];
912
+ if (previousArgument && previousArgument.variadic) {
913
+ throw new Error(`only the last argument can be variadic '${previousArgument.name()}'`);
914
+ }
915
+ if (argument.required && argument.defaultValue !== undefined && argument.parseArg === undefined) {
916
+ throw new Error(`a default value for a required argument is never used: '${argument.name()}'`);
917
+ }
918
+ this.registeredArguments.push(argument);
919
+ return this;
920
+ }
921
+ helpCommand(enableOrNameAndArgs, description) {
922
+ if (typeof enableOrNameAndArgs === "boolean") {
923
+ this._addImplicitHelpCommand = enableOrNameAndArgs;
924
+ return this;
925
+ }
926
+ enableOrNameAndArgs = enableOrNameAndArgs ?? "help [command]";
927
+ const [, helpName, helpArgs] = enableOrNameAndArgs.match(/([^ ]+) *(.*)/);
928
+ const helpDescription = description ?? "display help for command";
929
+ const helpCommand = this.createCommand(helpName);
930
+ helpCommand.helpOption(false);
931
+ if (helpArgs)
932
+ helpCommand.arguments(helpArgs);
933
+ if (helpDescription)
934
+ helpCommand.description(helpDescription);
935
+ this._addImplicitHelpCommand = true;
936
+ this._helpCommand = helpCommand;
937
+ return this;
938
+ }
939
+ addHelpCommand(helpCommand, deprecatedDescription) {
940
+ if (typeof helpCommand !== "object") {
941
+ this.helpCommand(helpCommand, deprecatedDescription);
942
+ return this;
943
+ }
944
+ this._addImplicitHelpCommand = true;
945
+ this._helpCommand = helpCommand;
946
+ return this;
947
+ }
948
+ _getHelpCommand() {
949
+ const hasImplicitHelpCommand = this._addImplicitHelpCommand ?? (this.commands.length && !this._actionHandler && !this._findCommand("help"));
950
+ if (hasImplicitHelpCommand) {
951
+ if (this._helpCommand === undefined) {
952
+ this.helpCommand(undefined, undefined);
953
+ }
954
+ return this._helpCommand;
955
+ }
956
+ return null;
957
+ }
958
+ hook(event, listener) {
959
+ const allowedValues = ["preSubcommand", "preAction", "postAction"];
960
+ if (!allowedValues.includes(event)) {
961
+ throw new Error(`Unexpected value for event passed to hook : '${event}'.
962
+ Expecting one of '${allowedValues.join("', '")}'`);
963
+ }
964
+ if (this._lifeCycleHooks[event]) {
965
+ this._lifeCycleHooks[event].push(listener);
966
+ } else {
967
+ this._lifeCycleHooks[event] = [listener];
968
+ }
969
+ return this;
970
+ }
971
+ exitOverride(fn) {
972
+ if (fn) {
973
+ this._exitCallback = fn;
974
+ } else {
975
+ this._exitCallback = (err) => {
976
+ if (err.code !== "commander.executeSubCommandAsync") {
977
+ throw err;
978
+ } else {}
979
+ };
980
+ }
981
+ return this;
982
+ }
983
+ _exit(exitCode, code, message) {
984
+ if (this._exitCallback) {
985
+ this._exitCallback(new CommanderError(exitCode, code, message));
986
+ }
987
+ process2.exit(exitCode);
988
+ }
989
+ action(fn) {
990
+ const listener = (args) => {
991
+ const expectedArgsCount = this.registeredArguments.length;
992
+ const actionArgs = args.slice(0, expectedArgsCount);
993
+ if (this._storeOptionsAsProperties) {
994
+ actionArgs[expectedArgsCount] = this;
995
+ } else {
996
+ actionArgs[expectedArgsCount] = this.opts();
997
+ }
998
+ actionArgs.push(this);
999
+ return fn.apply(this, actionArgs);
1000
+ };
1001
+ this._actionHandler = listener;
1002
+ return this;
1003
+ }
1004
+ createOption(flags, description) {
1005
+ return new Option(flags, description);
1006
+ }
1007
+ _callParseArg(target, value, previous, invalidArgumentMessage) {
1008
+ try {
1009
+ return target.parseArg(value, previous);
1010
+ } catch (err) {
1011
+ if (err.code === "commander.invalidArgument") {
1012
+ const message = `${invalidArgumentMessage} ${err.message}`;
1013
+ this.error(message, { exitCode: err.exitCode, code: err.code });
1014
+ }
1015
+ throw err;
1016
+ }
1017
+ }
1018
+ _registerOption(option) {
1019
+ const matchingOption = option.short && this._findOption(option.short) || option.long && this._findOption(option.long);
1020
+ if (matchingOption) {
1021
+ const matchingFlag = option.long && this._findOption(option.long) ? option.long : option.short;
1022
+ throw new Error(`Cannot add option '${option.flags}'${this._name && ` to command '${this._name}'`} due to conflicting flag '${matchingFlag}'
1023
+ - already used by option '${matchingOption.flags}'`);
1024
+ }
1025
+ this.options.push(option);
1026
+ }
1027
+ _registerCommand(command) {
1028
+ const knownBy = (cmd) => {
1029
+ return [cmd.name()].concat(cmd.aliases());
1030
+ };
1031
+ const alreadyUsed = knownBy(command).find((name) => this._findCommand(name));
1032
+ if (alreadyUsed) {
1033
+ const existingCmd = knownBy(this._findCommand(alreadyUsed)).join("|");
1034
+ const newCmd = knownBy(command).join("|");
1035
+ throw new Error(`cannot add command '${newCmd}' as already have command '${existingCmd}'`);
1036
+ }
1037
+ this.commands.push(command);
1038
+ }
1039
+ addOption(option) {
1040
+ this._registerOption(option);
1041
+ const oname = option.name();
1042
+ const name = option.attributeName();
1043
+ if (option.negate) {
1044
+ const positiveLongFlag = option.long.replace(/^--no-/, "--");
1045
+ if (!this._findOption(positiveLongFlag)) {
1046
+ this.setOptionValueWithSource(name, option.defaultValue === undefined ? true : option.defaultValue, "default");
1047
+ }
1048
+ } else if (option.defaultValue !== undefined) {
1049
+ this.setOptionValueWithSource(name, option.defaultValue, "default");
1050
+ }
1051
+ const handleOptionValue = (val, invalidValueMessage, valueSource) => {
1052
+ if (val == null && option.presetArg !== undefined) {
1053
+ val = option.presetArg;
1054
+ }
1055
+ const oldValue = this.getOptionValue(name);
1056
+ if (val !== null && option.parseArg) {
1057
+ val = this._callParseArg(option, val, oldValue, invalidValueMessage);
1058
+ } else if (val !== null && option.variadic) {
1059
+ val = option._concatValue(val, oldValue);
1060
+ }
1061
+ if (val == null) {
1062
+ if (option.negate) {
1063
+ val = false;
1064
+ } else if (option.isBoolean() || option.optional) {
1065
+ val = true;
1066
+ } else {
1067
+ val = "";
1068
+ }
1069
+ }
1070
+ this.setOptionValueWithSource(name, val, valueSource);
1071
+ };
1072
+ this.on("option:" + oname, (val) => {
1073
+ const invalidValueMessage = `error: option '${option.flags}' argument '${val}' is invalid.`;
1074
+ handleOptionValue(val, invalidValueMessage, "cli");
1075
+ });
1076
+ if (option.envVar) {
1077
+ this.on("optionEnv:" + oname, (val) => {
1078
+ const invalidValueMessage = `error: option '${option.flags}' value '${val}' from env '${option.envVar}' is invalid.`;
1079
+ handleOptionValue(val, invalidValueMessage, "env");
1080
+ });
1081
+ }
1082
+ return this;
1083
+ }
1084
+ _optionEx(config, flags, description, fn, defaultValue) {
1085
+ if (typeof flags === "object" && flags instanceof Option) {
1086
+ throw new Error("To add an Option object use addOption() instead of option() or requiredOption()");
1087
+ }
1088
+ const option = this.createOption(flags, description);
1089
+ option.makeOptionMandatory(!!config.mandatory);
1090
+ if (typeof fn === "function") {
1091
+ option.default(defaultValue).argParser(fn);
1092
+ } else if (fn instanceof RegExp) {
1093
+ const regex = fn;
1094
+ fn = (val, def) => {
1095
+ const m = regex.exec(val);
1096
+ return m ? m[0] : def;
1097
+ };
1098
+ option.default(defaultValue).argParser(fn);
1099
+ } else {
1100
+ option.default(fn);
1101
+ }
1102
+ return this.addOption(option);
1103
+ }
1104
+ option(flags, description, parseArg, defaultValue) {
1105
+ return this._optionEx({}, flags, description, parseArg, defaultValue);
1106
+ }
1107
+ requiredOption(flags, description, parseArg, defaultValue) {
1108
+ return this._optionEx({ mandatory: true }, flags, description, parseArg, defaultValue);
1109
+ }
1110
+ combineFlagAndOptionalValue(combine = true) {
1111
+ this._combineFlagAndOptionalValue = !!combine;
1112
+ return this;
1113
+ }
1114
+ allowUnknownOption(allowUnknown = true) {
1115
+ this._allowUnknownOption = !!allowUnknown;
1116
+ return this;
1117
+ }
1118
+ allowExcessArguments(allowExcess = true) {
1119
+ this._allowExcessArguments = !!allowExcess;
1120
+ return this;
1121
+ }
1122
+ enablePositionalOptions(positional = true) {
1123
+ this._enablePositionalOptions = !!positional;
1124
+ return this;
1125
+ }
1126
+ passThroughOptions(passThrough = true) {
1127
+ this._passThroughOptions = !!passThrough;
1128
+ this._checkForBrokenPassThrough();
1129
+ return this;
1130
+ }
1131
+ _checkForBrokenPassThrough() {
1132
+ if (this.parent && this._passThroughOptions && !this.parent._enablePositionalOptions) {
1133
+ throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`);
1134
+ }
1135
+ }
1136
+ storeOptionsAsProperties(storeAsProperties = true) {
1137
+ if (this.options.length) {
1138
+ throw new Error("call .storeOptionsAsProperties() before adding options");
1139
+ }
1140
+ if (Object.keys(this._optionValues).length) {
1141
+ throw new Error("call .storeOptionsAsProperties() before setting option values");
1142
+ }
1143
+ this._storeOptionsAsProperties = !!storeAsProperties;
1144
+ return this;
1145
+ }
1146
+ getOptionValue(key) {
1147
+ if (this._storeOptionsAsProperties) {
1148
+ return this[key];
1149
+ }
1150
+ return this._optionValues[key];
1151
+ }
1152
+ setOptionValue(key, value) {
1153
+ return this.setOptionValueWithSource(key, value, undefined);
1154
+ }
1155
+ setOptionValueWithSource(key, value, source) {
1156
+ if (this._storeOptionsAsProperties) {
1157
+ this[key] = value;
1158
+ } else {
1159
+ this._optionValues[key] = value;
1160
+ }
1161
+ this._optionValueSources[key] = source;
1162
+ return this;
1163
+ }
1164
+ getOptionValueSource(key) {
1165
+ return this._optionValueSources[key];
1166
+ }
1167
+ getOptionValueSourceWithGlobals(key) {
1168
+ let source;
1169
+ this._getCommandAndAncestors().forEach((cmd) => {
1170
+ if (cmd.getOptionValueSource(key) !== undefined) {
1171
+ source = cmd.getOptionValueSource(key);
1172
+ }
1173
+ });
1174
+ return source;
1175
+ }
1176
+ _prepareUserArgs(argv, parseOptions) {
1177
+ if (argv !== undefined && !Array.isArray(argv)) {
1178
+ throw new Error("first parameter to parse must be array or undefined");
1179
+ }
1180
+ parseOptions = parseOptions || {};
1181
+ if (argv === undefined && parseOptions.from === undefined) {
1182
+ if (process2.versions?.electron) {
1183
+ parseOptions.from = "electron";
1184
+ }
1185
+ const execArgv = process2.execArgv ?? [];
1186
+ if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
1187
+ parseOptions.from = "eval";
1188
+ }
1189
+ }
1190
+ if (argv === undefined) {
1191
+ argv = process2.argv;
1192
+ }
1193
+ this.rawArgs = argv.slice();
1194
+ let userArgs;
1195
+ switch (parseOptions.from) {
1196
+ case undefined:
1197
+ case "node":
1198
+ this._scriptPath = argv[1];
1199
+ userArgs = argv.slice(2);
1200
+ break;
1201
+ case "electron":
1202
+ if (process2.defaultApp) {
1203
+ this._scriptPath = argv[1];
1204
+ userArgs = argv.slice(2);
1205
+ } else {
1206
+ userArgs = argv.slice(1);
1207
+ }
1208
+ break;
1209
+ case "user":
1210
+ userArgs = argv.slice(0);
1211
+ break;
1212
+ case "eval":
1213
+ userArgs = argv.slice(1);
1214
+ break;
1215
+ default:
1216
+ throw new Error(`unexpected parse option { from: '${parseOptions.from}' }`);
1217
+ }
1218
+ if (!this._name && this._scriptPath)
1219
+ this.nameFromFilename(this._scriptPath);
1220
+ this._name = this._name || "program";
1221
+ return userArgs;
1222
+ }
1223
+ parse(argv, parseOptions) {
1224
+ this._prepareForParse();
1225
+ const userArgs = this._prepareUserArgs(argv, parseOptions);
1226
+ this._parseCommand([], userArgs);
1227
+ return this;
1228
+ }
1229
+ async parseAsync(argv, parseOptions) {
1230
+ this._prepareForParse();
1231
+ const userArgs = this._prepareUserArgs(argv, parseOptions);
1232
+ await this._parseCommand([], userArgs);
1233
+ return this;
1234
+ }
1235
+ _prepareForParse() {
1236
+ if (this._savedState === null) {
1237
+ this.saveStateBeforeParse();
1238
+ } else {
1239
+ this.restoreStateBeforeParse();
1240
+ }
1241
+ }
1242
+ saveStateBeforeParse() {
1243
+ this._savedState = {
1244
+ _name: this._name,
1245
+ _optionValues: { ...this._optionValues },
1246
+ _optionValueSources: { ...this._optionValueSources }
1247
+ };
1248
+ }
1249
+ restoreStateBeforeParse() {
1250
+ if (this._storeOptionsAsProperties)
1251
+ throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
1252
+ - either make a new Command for each call to parse, or stop storing options as properties`);
1253
+ this._name = this._savedState._name;
1254
+ this._scriptPath = null;
1255
+ this.rawArgs = [];
1256
+ this._optionValues = { ...this._savedState._optionValues };
1257
+ this._optionValueSources = { ...this._savedState._optionValueSources };
1258
+ this.args = [];
1259
+ this.processedArgs = [];
1260
+ }
1261
+ _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
1262
+ if (fs.existsSync(executableFile))
1263
+ return;
1264
+ const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
1265
+ const executableMissing = `'${executableFile}' does not exist
1266
+ - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
1267
+ - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
1268
+ - ${executableDirMessage}`;
1269
+ throw new Error(executableMissing);
1270
+ }
1271
+ _executeSubCommand(subcommand, args) {
1272
+ args = args.slice();
1273
+ let launchWithNode = false;
1274
+ const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1275
+ function findFile(baseDir, baseName) {
1276
+ const localBin = path.resolve(baseDir, baseName);
1277
+ if (fs.existsSync(localBin))
1278
+ return localBin;
1279
+ if (sourceExt.includes(path.extname(baseName)))
1280
+ return;
1281
+ const foundExt = sourceExt.find((ext) => fs.existsSync(`${localBin}${ext}`));
1282
+ if (foundExt)
1283
+ return `${localBin}${foundExt}`;
1284
+ return;
1285
+ }
1286
+ this._checkForMissingMandatoryOptions();
1287
+ this._checkForConflictingOptions();
1288
+ let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
1289
+ let executableDir = this._executableDir || "";
1290
+ if (this._scriptPath) {
1291
+ let resolvedScriptPath;
1292
+ try {
1293
+ resolvedScriptPath = fs.realpathSync(this._scriptPath);
1294
+ } catch {
1295
+ resolvedScriptPath = this._scriptPath;
1296
+ }
1297
+ executableDir = path.resolve(path.dirname(resolvedScriptPath), executableDir);
1298
+ }
1299
+ if (executableDir) {
1300
+ let localFile = findFile(executableDir, executableFile);
1301
+ if (!localFile && !subcommand._executableFile && this._scriptPath) {
1302
+ const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
1303
+ if (legacyName !== this._name) {
1304
+ localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
1305
+ }
1306
+ }
1307
+ executableFile = localFile || executableFile;
1308
+ }
1309
+ launchWithNode = sourceExt.includes(path.extname(executableFile));
1310
+ let proc;
1311
+ if (process2.platform !== "win32") {
1312
+ if (launchWithNode) {
1313
+ args.unshift(executableFile);
1314
+ args = incrementNodeInspectorPort(process2.execArgv).concat(args);
1315
+ proc = childProcess.spawn(process2.argv[0], args, { stdio: "inherit" });
1316
+ } else {
1317
+ proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
1318
+ }
1319
+ } else {
1320
+ this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
1321
+ args.unshift(executableFile);
1322
+ args = incrementNodeInspectorPort(process2.execArgv).concat(args);
1323
+ proc = childProcess.spawn(process2.execPath, args, { stdio: "inherit" });
1324
+ }
1325
+ if (!proc.killed) {
1326
+ const signals = ["SIGUSR1", "SIGUSR2", "SIGTERM", "SIGINT", "SIGHUP"];
1327
+ signals.forEach((signal) => {
1328
+ process2.on(signal, () => {
1329
+ if (proc.killed === false && proc.exitCode === null) {
1330
+ proc.kill(signal);
1331
+ }
1332
+ });
1333
+ });
1334
+ }
1335
+ const exitCallback = this._exitCallback;
1336
+ proc.on("close", (code) => {
1337
+ code = code ?? 1;
1338
+ if (!exitCallback) {
1339
+ process2.exit(code);
1340
+ } else {
1341
+ exitCallback(new CommanderError(code, "commander.executeSubCommandAsync", "(close)"));
1342
+ }
1343
+ });
1344
+ proc.on("error", (err) => {
1345
+ if (err.code === "ENOENT") {
1346
+ this._checkForMissingExecutable(executableFile, executableDir, subcommand._name);
1347
+ } else if (err.code === "EACCES") {
1348
+ throw new Error(`'${executableFile}' not executable`);
1349
+ }
1350
+ if (!exitCallback) {
1351
+ process2.exit(1);
1352
+ } else {
1353
+ const wrappedError = new CommanderError(1, "commander.executeSubCommandAsync", "(error)");
1354
+ wrappedError.nestedError = err;
1355
+ exitCallback(wrappedError);
1356
+ }
1357
+ });
1358
+ this.runningCommand = proc;
1359
+ }
1360
+ _dispatchSubcommand(commandName, operands, unknown) {
1361
+ const subCommand = this._findCommand(commandName);
1362
+ if (!subCommand)
1363
+ this.help({ error: true });
1364
+ subCommand._prepareForParse();
1365
+ let promiseChain;
1366
+ promiseChain = this._chainOrCallSubCommandHook(promiseChain, subCommand, "preSubcommand");
1367
+ promiseChain = this._chainOrCall(promiseChain, () => {
1368
+ if (subCommand._executableHandler) {
1369
+ this._executeSubCommand(subCommand, operands.concat(unknown));
1370
+ } else {
1371
+ return subCommand._parseCommand(operands, unknown);
1372
+ }
1373
+ });
1374
+ return promiseChain;
1375
+ }
1376
+ _dispatchHelpCommand(subcommandName) {
1377
+ if (!subcommandName) {
1378
+ this.help();
1379
+ }
1380
+ const subCommand = this._findCommand(subcommandName);
1381
+ if (subCommand && !subCommand._executableHandler) {
1382
+ subCommand.help();
1383
+ }
1384
+ return this._dispatchSubcommand(subcommandName, [], [this._getHelpOption()?.long ?? this._getHelpOption()?.short ?? "--help"]);
1385
+ }
1386
+ _checkNumberOfArguments() {
1387
+ this.registeredArguments.forEach((arg, i) => {
1388
+ if (arg.required && this.args[i] == null) {
1389
+ this.missingArgument(arg.name());
1390
+ }
1391
+ });
1392
+ if (this.registeredArguments.length > 0 && this.registeredArguments[this.registeredArguments.length - 1].variadic) {
1393
+ return;
1394
+ }
1395
+ if (this.args.length > this.registeredArguments.length) {
1396
+ this._excessArguments(this.args);
1397
+ }
1398
+ }
1399
+ _processArguments() {
1400
+ const myParseArg = (argument, value, previous) => {
1401
+ let parsedValue = value;
1402
+ if (value !== null && argument.parseArg) {
1403
+ const invalidValueMessage = `error: command-argument value '${value}' is invalid for argument '${argument.name()}'.`;
1404
+ parsedValue = this._callParseArg(argument, value, previous, invalidValueMessage);
1405
+ }
1406
+ return parsedValue;
1407
+ };
1408
+ this._checkNumberOfArguments();
1409
+ const processedArgs = [];
1410
+ this.registeredArguments.forEach((declaredArg, index) => {
1411
+ let value = declaredArg.defaultValue;
1412
+ if (declaredArg.variadic) {
1413
+ if (index < this.args.length) {
1414
+ value = this.args.slice(index);
1415
+ if (declaredArg.parseArg) {
1416
+ value = value.reduce((processed, v) => {
1417
+ return myParseArg(declaredArg, v, processed);
1418
+ }, declaredArg.defaultValue);
1419
+ }
1420
+ } else if (value === undefined) {
1421
+ value = [];
1422
+ }
1423
+ } else if (index < this.args.length) {
1424
+ value = this.args[index];
1425
+ if (declaredArg.parseArg) {
1426
+ value = myParseArg(declaredArg, value, declaredArg.defaultValue);
1427
+ }
1428
+ }
1429
+ processedArgs[index] = value;
1430
+ });
1431
+ this.processedArgs = processedArgs;
1432
+ }
1433
+ _chainOrCall(promise, fn) {
1434
+ if (promise && promise.then && typeof promise.then === "function") {
1435
+ return promise.then(() => fn());
1436
+ }
1437
+ return fn();
1438
+ }
1439
+ _chainOrCallHooks(promise, event) {
1440
+ let result = promise;
1441
+ const hooks = [];
1442
+ this._getCommandAndAncestors().reverse().filter((cmd) => cmd._lifeCycleHooks[event] !== undefined).forEach((hookedCommand) => {
1443
+ hookedCommand._lifeCycleHooks[event].forEach((callback) => {
1444
+ hooks.push({ hookedCommand, callback });
1445
+ });
1446
+ });
1447
+ if (event === "postAction") {
1448
+ hooks.reverse();
1449
+ }
1450
+ hooks.forEach((hookDetail) => {
1451
+ result = this._chainOrCall(result, () => {
1452
+ return hookDetail.callback(hookDetail.hookedCommand, this);
1453
+ });
1454
+ });
1455
+ return result;
1456
+ }
1457
+ _chainOrCallSubCommandHook(promise, subCommand, event) {
1458
+ let result = promise;
1459
+ if (this._lifeCycleHooks[event] !== undefined) {
1460
+ this._lifeCycleHooks[event].forEach((hook) => {
1461
+ result = this._chainOrCall(result, () => {
1462
+ return hook(this, subCommand);
1463
+ });
1464
+ });
1465
+ }
1466
+ return result;
1467
+ }
1468
+ _parseCommand(operands, unknown) {
1469
+ const parsed = this.parseOptions(unknown);
1470
+ this._parseOptionsEnv();
1471
+ this._parseOptionsImplied();
1472
+ operands = operands.concat(parsed.operands);
1473
+ unknown = parsed.unknown;
1474
+ this.args = operands.concat(unknown);
1475
+ if (operands && this._findCommand(operands[0])) {
1476
+ return this._dispatchSubcommand(operands[0], operands.slice(1), unknown);
1477
+ }
1478
+ if (this._getHelpCommand() && operands[0] === this._getHelpCommand().name()) {
1479
+ return this._dispatchHelpCommand(operands[1]);
1480
+ }
1481
+ if (this._defaultCommandName) {
1482
+ this._outputHelpIfRequested(unknown);
1483
+ return this._dispatchSubcommand(this._defaultCommandName, operands, unknown);
1484
+ }
1485
+ if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
1486
+ this.help({ error: true });
1487
+ }
1488
+ this._outputHelpIfRequested(parsed.unknown);
1489
+ this._checkForMissingMandatoryOptions();
1490
+ this._checkForConflictingOptions();
1491
+ const checkForUnknownOptions = () => {
1492
+ if (parsed.unknown.length > 0) {
1493
+ this.unknownOption(parsed.unknown[0]);
1494
+ }
1495
+ };
1496
+ const commandEvent = `command:${this.name()}`;
1497
+ if (this._actionHandler) {
1498
+ checkForUnknownOptions();
1499
+ this._processArguments();
1500
+ let promiseChain;
1501
+ promiseChain = this._chainOrCallHooks(promiseChain, "preAction");
1502
+ promiseChain = this._chainOrCall(promiseChain, () => this._actionHandler(this.processedArgs));
1503
+ if (this.parent) {
1504
+ promiseChain = this._chainOrCall(promiseChain, () => {
1505
+ this.parent.emit(commandEvent, operands, unknown);
1506
+ });
1507
+ }
1508
+ promiseChain = this._chainOrCallHooks(promiseChain, "postAction");
1509
+ return promiseChain;
1510
+ }
1511
+ if (this.parent && this.parent.listenerCount(commandEvent)) {
1512
+ checkForUnknownOptions();
1513
+ this._processArguments();
1514
+ this.parent.emit(commandEvent, operands, unknown);
1515
+ } else if (operands.length) {
1516
+ if (this._findCommand("*")) {
1517
+ return this._dispatchSubcommand("*", operands, unknown);
1518
+ }
1519
+ if (this.listenerCount("command:*")) {
1520
+ this.emit("command:*", operands, unknown);
1521
+ } else if (this.commands.length) {
1522
+ this.unknownCommand();
1523
+ } else {
1524
+ checkForUnknownOptions();
1525
+ this._processArguments();
1526
+ }
1527
+ } else if (this.commands.length) {
1528
+ checkForUnknownOptions();
1529
+ this.help({ error: true });
1530
+ } else {
1531
+ checkForUnknownOptions();
1532
+ this._processArguments();
1533
+ }
1534
+ }
1535
+ _findCommand(name) {
1536
+ if (!name)
1537
+ return;
1538
+ return this.commands.find((cmd) => cmd._name === name || cmd._aliases.includes(name));
1539
+ }
1540
+ _findOption(arg) {
1541
+ return this.options.find((option) => option.is(arg));
1542
+ }
1543
+ _checkForMissingMandatoryOptions() {
1544
+ this._getCommandAndAncestors().forEach((cmd) => {
1545
+ cmd.options.forEach((anOption) => {
1546
+ if (anOption.mandatory && cmd.getOptionValue(anOption.attributeName()) === undefined) {
1547
+ cmd.missingMandatoryOptionValue(anOption);
1548
+ }
1549
+ });
1550
+ });
1551
+ }
1552
+ _checkForConflictingLocalOptions() {
1553
+ const definedNonDefaultOptions = this.options.filter((option) => {
1554
+ const optionKey = option.attributeName();
1555
+ if (this.getOptionValue(optionKey) === undefined) {
1556
+ return false;
1557
+ }
1558
+ return this.getOptionValueSource(optionKey) !== "default";
1559
+ });
1560
+ const optionsWithConflicting = definedNonDefaultOptions.filter((option) => option.conflictsWith.length > 0);
1561
+ optionsWithConflicting.forEach((option) => {
1562
+ const conflictingAndDefined = definedNonDefaultOptions.find((defined) => option.conflictsWith.includes(defined.attributeName()));
1563
+ if (conflictingAndDefined) {
1564
+ this._conflictingOption(option, conflictingAndDefined);
1565
+ }
1566
+ });
1567
+ }
1568
+ _checkForConflictingOptions() {
1569
+ this._getCommandAndAncestors().forEach((cmd) => {
1570
+ cmd._checkForConflictingLocalOptions();
1571
+ });
1572
+ }
1573
+ parseOptions(argv) {
1574
+ const operands = [];
1575
+ const unknown = [];
1576
+ let dest = operands;
1577
+ const args = argv.slice();
1578
+ function maybeOption(arg) {
1579
+ return arg.length > 1 && arg[0] === "-";
1580
+ }
1581
+ let activeVariadicOption = null;
1582
+ while (args.length) {
1583
+ const arg = args.shift();
1584
+ if (arg === "--") {
1585
+ if (dest === unknown)
1586
+ dest.push(arg);
1587
+ dest.push(...args);
1588
+ break;
1589
+ }
1590
+ if (activeVariadicOption && !maybeOption(arg)) {
1591
+ this.emit(`option:${activeVariadicOption.name()}`, arg);
1592
+ continue;
1593
+ }
1594
+ activeVariadicOption = null;
1595
+ if (maybeOption(arg)) {
1596
+ const option = this._findOption(arg);
1597
+ if (option) {
1598
+ if (option.required) {
1599
+ const value = args.shift();
1600
+ if (value === undefined)
1601
+ this.optionMissingArgument(option);
1602
+ this.emit(`option:${option.name()}`, value);
1603
+ } else if (option.optional) {
1604
+ let value = null;
1605
+ if (args.length > 0 && !maybeOption(args[0])) {
1606
+ value = args.shift();
1607
+ }
1608
+ this.emit(`option:${option.name()}`, value);
1609
+ } else {
1610
+ this.emit(`option:${option.name()}`);
1611
+ }
1612
+ activeVariadicOption = option.variadic ? option : null;
1613
+ continue;
1614
+ }
1615
+ }
1616
+ if (arg.length > 2 && arg[0] === "-" && arg[1] !== "-") {
1617
+ const option = this._findOption(`-${arg[1]}`);
1618
+ if (option) {
1619
+ if (option.required || option.optional && this._combineFlagAndOptionalValue) {
1620
+ this.emit(`option:${option.name()}`, arg.slice(2));
1621
+ } else {
1622
+ this.emit(`option:${option.name()}`);
1623
+ args.unshift(`-${arg.slice(2)}`);
1624
+ }
1625
+ continue;
1626
+ }
1627
+ }
1628
+ if (/^--[^=]+=/.test(arg)) {
1629
+ const index = arg.indexOf("=");
1630
+ const option = this._findOption(arg.slice(0, index));
1631
+ if (option && (option.required || option.optional)) {
1632
+ this.emit(`option:${option.name()}`, arg.slice(index + 1));
1633
+ continue;
1634
+ }
1635
+ }
1636
+ if (maybeOption(arg)) {
1637
+ dest = unknown;
1638
+ }
1639
+ if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
1640
+ if (this._findCommand(arg)) {
1641
+ operands.push(arg);
1642
+ if (args.length > 0)
1643
+ unknown.push(...args);
1644
+ break;
1645
+ } else if (this._getHelpCommand() && arg === this._getHelpCommand().name()) {
1646
+ operands.push(arg);
1647
+ if (args.length > 0)
1648
+ operands.push(...args);
1649
+ break;
1650
+ } else if (this._defaultCommandName) {
1651
+ unknown.push(arg);
1652
+ if (args.length > 0)
1653
+ unknown.push(...args);
1654
+ break;
1655
+ }
1656
+ }
1657
+ if (this._passThroughOptions) {
1658
+ dest.push(arg);
1659
+ if (args.length > 0)
1660
+ dest.push(...args);
1661
+ break;
1662
+ }
1663
+ dest.push(arg);
1664
+ }
1665
+ return { operands, unknown };
1666
+ }
1667
+ opts() {
1668
+ if (this._storeOptionsAsProperties) {
1669
+ const result = {};
1670
+ const len = this.options.length;
1671
+ for (let i = 0;i < len; i++) {
1672
+ const key = this.options[i].attributeName();
1673
+ result[key] = key === this._versionOptionName ? this._version : this[key];
1674
+ }
1675
+ return result;
1676
+ }
1677
+ return this._optionValues;
1678
+ }
1679
+ optsWithGlobals() {
1680
+ return this._getCommandAndAncestors().reduce((combinedOptions, cmd) => Object.assign(combinedOptions, cmd.opts()), {});
1681
+ }
1682
+ error(message, errorOptions) {
1683
+ this._outputConfiguration.outputError(`${message}
1684
+ `, this._outputConfiguration.writeErr);
1685
+ if (typeof this._showHelpAfterError === "string") {
1686
+ this._outputConfiguration.writeErr(`${this._showHelpAfterError}
1687
+ `);
1688
+ } else if (this._showHelpAfterError) {
1689
+ this._outputConfiguration.writeErr(`
1690
+ `);
1691
+ this.outputHelp({ error: true });
1692
+ }
1693
+ const config = errorOptions || {};
1694
+ const exitCode = config.exitCode || 1;
1695
+ const code = config.code || "commander.error";
1696
+ this._exit(exitCode, code, message);
1697
+ }
1698
+ _parseOptionsEnv() {
1699
+ this.options.forEach((option) => {
1700
+ if (option.envVar && option.envVar in process2.env) {
1701
+ const optionKey = option.attributeName();
1702
+ if (this.getOptionValue(optionKey) === undefined || ["default", "config", "env"].includes(this.getOptionValueSource(optionKey))) {
1703
+ if (option.required || option.optional) {
1704
+ this.emit(`optionEnv:${option.name()}`, process2.env[option.envVar]);
1705
+ } else {
1706
+ this.emit(`optionEnv:${option.name()}`);
1707
+ }
1708
+ }
1709
+ }
1710
+ });
1711
+ }
1712
+ _parseOptionsImplied() {
1713
+ const dualHelper = new DualOptions(this.options);
1714
+ const hasCustomOptionValue = (optionKey) => {
1715
+ return this.getOptionValue(optionKey) !== undefined && !["default", "implied"].includes(this.getOptionValueSource(optionKey));
1716
+ };
1717
+ this.options.filter((option) => option.implied !== undefined && hasCustomOptionValue(option.attributeName()) && dualHelper.valueFromOption(this.getOptionValue(option.attributeName()), option)).forEach((option) => {
1718
+ Object.keys(option.implied).filter((impliedKey) => !hasCustomOptionValue(impliedKey)).forEach((impliedKey) => {
1719
+ this.setOptionValueWithSource(impliedKey, option.implied[impliedKey], "implied");
1720
+ });
1721
+ });
1722
+ }
1723
+ missingArgument(name) {
1724
+ const message = `error: missing required argument '${name}'`;
1725
+ this.error(message, { code: "commander.missingArgument" });
1726
+ }
1727
+ optionMissingArgument(option) {
1728
+ const message = `error: option '${option.flags}' argument missing`;
1729
+ this.error(message, { code: "commander.optionMissingArgument" });
1730
+ }
1731
+ missingMandatoryOptionValue(option) {
1732
+ const message = `error: required option '${option.flags}' not specified`;
1733
+ this.error(message, { code: "commander.missingMandatoryOptionValue" });
1734
+ }
1735
+ _conflictingOption(option, conflictingOption) {
1736
+ const findBestOptionFromValue = (option2) => {
1737
+ const optionKey = option2.attributeName();
1738
+ const optionValue = this.getOptionValue(optionKey);
1739
+ const negativeOption = this.options.find((target) => target.negate && optionKey === target.attributeName());
1740
+ const positiveOption = this.options.find((target) => !target.negate && optionKey === target.attributeName());
1741
+ if (negativeOption && (negativeOption.presetArg === undefined && optionValue === false || negativeOption.presetArg !== undefined && optionValue === negativeOption.presetArg)) {
1742
+ return negativeOption;
1743
+ }
1744
+ return positiveOption || option2;
1745
+ };
1746
+ const getErrorMessage = (option2) => {
1747
+ const bestOption = findBestOptionFromValue(option2);
1748
+ const optionKey = bestOption.attributeName();
1749
+ const source = this.getOptionValueSource(optionKey);
1750
+ if (source === "env") {
1751
+ return `environment variable '${bestOption.envVar}'`;
1752
+ }
1753
+ return `option '${bestOption.flags}'`;
1754
+ };
1755
+ const message = `error: ${getErrorMessage(option)} cannot be used with ${getErrorMessage(conflictingOption)}`;
1756
+ this.error(message, { code: "commander.conflictingOption" });
1757
+ }
1758
+ unknownOption(flag) {
1759
+ if (this._allowUnknownOption)
1760
+ return;
1761
+ let suggestion = "";
1762
+ if (flag.startsWith("--") && this._showSuggestionAfterError) {
1763
+ let candidateFlags = [];
1764
+ let command = this;
1765
+ do {
1766
+ const moreFlags = command.createHelp().visibleOptions(command).filter((option) => option.long).map((option) => option.long);
1767
+ candidateFlags = candidateFlags.concat(moreFlags);
1768
+ command = command.parent;
1769
+ } while (command && !command._enablePositionalOptions);
1770
+ suggestion = suggestSimilar(flag, candidateFlags);
1771
+ }
1772
+ const message = `error: unknown option '${flag}'${suggestion}`;
1773
+ this.error(message, { code: "commander.unknownOption" });
1774
+ }
1775
+ _excessArguments(receivedArgs) {
1776
+ if (this._allowExcessArguments)
1777
+ return;
1778
+ const expected = this.registeredArguments.length;
1779
+ const s = expected === 1 ? "" : "s";
1780
+ const forSubcommand = this.parent ? ` for '${this.name()}'` : "";
1781
+ const message = `error: too many arguments${forSubcommand}. Expected ${expected} argument${s} but got ${receivedArgs.length}.`;
1782
+ this.error(message, { code: "commander.excessArguments" });
1783
+ }
1784
+ unknownCommand() {
1785
+ const unknownName = this.args[0];
1786
+ let suggestion = "";
1787
+ if (this._showSuggestionAfterError) {
1788
+ const candidateNames = [];
1789
+ this.createHelp().visibleCommands(this).forEach((command) => {
1790
+ candidateNames.push(command.name());
1791
+ if (command.alias())
1792
+ candidateNames.push(command.alias());
1793
+ });
1794
+ suggestion = suggestSimilar(unknownName, candidateNames);
1795
+ }
1796
+ const message = `error: unknown command '${unknownName}'${suggestion}`;
1797
+ this.error(message, { code: "commander.unknownCommand" });
1798
+ }
1799
+ version(str, flags, description) {
1800
+ if (str === undefined)
1801
+ return this._version;
1802
+ this._version = str;
1803
+ flags = flags || "-V, --version";
1804
+ description = description || "output the version number";
1805
+ const versionOption = this.createOption(flags, description);
1806
+ this._versionOptionName = versionOption.attributeName();
1807
+ this._registerOption(versionOption);
1808
+ this.on("option:" + versionOption.name(), () => {
1809
+ this._outputConfiguration.writeOut(`${str}
1810
+ `);
1811
+ this._exit(0, "commander.version", str);
1812
+ });
1813
+ return this;
1814
+ }
1815
+ description(str, argsDescription) {
1816
+ if (str === undefined && argsDescription === undefined)
1817
+ return this._description;
1818
+ this._description = str;
1819
+ if (argsDescription) {
1820
+ this._argsDescription = argsDescription;
1821
+ }
1822
+ return this;
1823
+ }
1824
+ summary(str) {
1825
+ if (str === undefined)
1826
+ return this._summary;
1827
+ this._summary = str;
1828
+ return this;
1829
+ }
1830
+ alias(alias) {
1831
+ if (alias === undefined)
1832
+ return this._aliases[0];
1833
+ let command = this;
1834
+ if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) {
1835
+ command = this.commands[this.commands.length - 1];
1836
+ }
1837
+ if (alias === command._name)
1838
+ throw new Error("Command alias can't be the same as its name");
1839
+ const matchingCommand = this.parent?._findCommand(alias);
1840
+ if (matchingCommand) {
1841
+ const existingCmd = [matchingCommand.name()].concat(matchingCommand.aliases()).join("|");
1842
+ throw new Error(`cannot add alias '${alias}' to command '${this.name()}' as already have command '${existingCmd}'`);
1843
+ }
1844
+ command._aliases.push(alias);
1845
+ return this;
1846
+ }
1847
+ aliases(aliases) {
1848
+ if (aliases === undefined)
1849
+ return this._aliases;
1850
+ aliases.forEach((alias) => this.alias(alias));
1851
+ return this;
1852
+ }
1853
+ usage(str) {
1854
+ if (str === undefined) {
1855
+ if (this._usage)
1856
+ return this._usage;
1857
+ const args = this.registeredArguments.map((arg) => {
1858
+ return humanReadableArgName(arg);
1859
+ });
1860
+ return [].concat(this.options.length || this._helpOption !== null ? "[options]" : [], this.commands.length ? "[command]" : [], this.registeredArguments.length ? args : []).join(" ");
1861
+ }
1862
+ this._usage = str;
1863
+ return this;
1864
+ }
1865
+ name(str) {
1866
+ if (str === undefined)
1867
+ return this._name;
1868
+ this._name = str;
1869
+ return this;
1870
+ }
1871
+ nameFromFilename(filename) {
1872
+ this._name = path.basename(filename, path.extname(filename));
1873
+ return this;
1874
+ }
1875
+ executableDir(path2) {
1876
+ if (path2 === undefined)
1877
+ return this._executableDir;
1878
+ this._executableDir = path2;
1879
+ return this;
1880
+ }
1881
+ helpInformation(contextOptions) {
1882
+ const helper = this.createHelp();
1883
+ const context = this._getOutputContext(contextOptions);
1884
+ helper.prepareContext({
1885
+ error: context.error,
1886
+ helpWidth: context.helpWidth,
1887
+ outputHasColors: context.hasColors
1888
+ });
1889
+ const text = helper.formatHelp(this, helper);
1890
+ if (context.hasColors)
1891
+ return text;
1892
+ return this._outputConfiguration.stripColor(text);
1893
+ }
1894
+ _getOutputContext(contextOptions) {
1895
+ contextOptions = contextOptions || {};
1896
+ const error = !!contextOptions.error;
1897
+ let baseWrite;
1898
+ let hasColors;
1899
+ let helpWidth;
1900
+ if (error) {
1901
+ baseWrite = (str) => this._outputConfiguration.writeErr(str);
1902
+ hasColors = this._outputConfiguration.getErrHasColors();
1903
+ helpWidth = this._outputConfiguration.getErrHelpWidth();
1904
+ } else {
1905
+ baseWrite = (str) => this._outputConfiguration.writeOut(str);
1906
+ hasColors = this._outputConfiguration.getOutHasColors();
1907
+ helpWidth = this._outputConfiguration.getOutHelpWidth();
1908
+ }
1909
+ const write = (str) => {
1910
+ if (!hasColors)
1911
+ str = this._outputConfiguration.stripColor(str);
1912
+ return baseWrite(str);
1913
+ };
1914
+ return { error, write, hasColors, helpWidth };
1915
+ }
1916
+ outputHelp(contextOptions) {
1917
+ let deprecatedCallback;
1918
+ if (typeof contextOptions === "function") {
1919
+ deprecatedCallback = contextOptions;
1920
+ contextOptions = undefined;
1921
+ }
1922
+ const outputContext = this._getOutputContext(contextOptions);
1923
+ const eventContext = {
1924
+ error: outputContext.error,
1925
+ write: outputContext.write,
1926
+ command: this
1927
+ };
1928
+ this._getCommandAndAncestors().reverse().forEach((command) => command.emit("beforeAllHelp", eventContext));
1929
+ this.emit("beforeHelp", eventContext);
1930
+ let helpInformation = this.helpInformation({ error: outputContext.error });
1931
+ if (deprecatedCallback) {
1932
+ helpInformation = deprecatedCallback(helpInformation);
1933
+ if (typeof helpInformation !== "string" && !Buffer.isBuffer(helpInformation)) {
1934
+ throw new Error("outputHelp callback must return a string or a Buffer");
1935
+ }
1936
+ }
1937
+ outputContext.write(helpInformation);
1938
+ if (this._getHelpOption()?.long) {
1939
+ this.emit(this._getHelpOption().long);
1940
+ }
1941
+ this.emit("afterHelp", eventContext);
1942
+ this._getCommandAndAncestors().forEach((command) => command.emit("afterAllHelp", eventContext));
1943
+ }
1944
+ helpOption(flags, description) {
1945
+ if (typeof flags === "boolean") {
1946
+ if (flags) {
1947
+ this._helpOption = this._helpOption ?? undefined;
1948
+ } else {
1949
+ this._helpOption = null;
1950
+ }
1951
+ return this;
1952
+ }
1953
+ flags = flags ?? "-h, --help";
1954
+ description = description ?? "display help for command";
1955
+ this._helpOption = this.createOption(flags, description);
1956
+ return this;
1957
+ }
1958
+ _getHelpOption() {
1959
+ if (this._helpOption === undefined) {
1960
+ this.helpOption(undefined, undefined);
1961
+ }
1962
+ return this._helpOption;
1963
+ }
1964
+ addHelpOption(option) {
1965
+ this._helpOption = option;
1966
+ return this;
1967
+ }
1968
+ help(contextOptions) {
1969
+ this.outputHelp(contextOptions);
1970
+ let exitCode = Number(process2.exitCode ?? 0);
1971
+ if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
1972
+ exitCode = 1;
1973
+ }
1974
+ this._exit(exitCode, "commander.help", "(outputHelp)");
1975
+ }
1976
+ addHelpText(position, text) {
1977
+ const allowedValues = ["beforeAll", "before", "after", "afterAll"];
1978
+ if (!allowedValues.includes(position)) {
1979
+ throw new Error(`Unexpected value for position to addHelpText.
1980
+ Expecting one of '${allowedValues.join("', '")}'`);
1981
+ }
1982
+ const helpEvent = `${position}Help`;
1983
+ this.on(helpEvent, (context) => {
1984
+ let helpStr;
1985
+ if (typeof text === "function") {
1986
+ helpStr = text({ error: context.error, command: context.command });
1987
+ } else {
1988
+ helpStr = text;
1989
+ }
1990
+ if (helpStr) {
1991
+ context.write(`${helpStr}
1992
+ `);
1993
+ }
1994
+ });
1995
+ return this;
1996
+ }
1997
+ _outputHelpIfRequested(args) {
1998
+ const helpOption = this._getHelpOption();
1999
+ const helpRequested = helpOption && args.find((arg) => helpOption.is(arg));
2000
+ if (helpRequested) {
2001
+ this.outputHelp();
2002
+ this._exit(0, "commander.helpDisplayed", "(outputHelp)");
2003
+ }
2004
+ }
2005
+ }
2006
+ function incrementNodeInspectorPort(args) {
2007
+ return args.map((arg) => {
2008
+ if (!arg.startsWith("--inspect")) {
2009
+ return arg;
2010
+ }
2011
+ let debugOption;
2012
+ let debugHost = "127.0.0.1";
2013
+ let debugPort = "9229";
2014
+ let match;
2015
+ if ((match = arg.match(/^(--inspect(-brk)?)$/)) !== null) {
2016
+ debugOption = match[1];
2017
+ } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+)$/)) !== null) {
2018
+ debugOption = match[1];
2019
+ if (/^\d+$/.test(match[3])) {
2020
+ debugPort = match[3];
2021
+ } else {
2022
+ debugHost = match[3];
2023
+ }
2024
+ } else if ((match = arg.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/)) !== null) {
2025
+ debugOption = match[1];
2026
+ debugHost = match[3];
2027
+ debugPort = match[4];
2028
+ }
2029
+ if (debugOption && debugPort !== "0") {
2030
+ return `${debugOption}=${debugHost}:${parseInt(debugPort) + 1}`;
2031
+ }
2032
+ return arg;
2033
+ });
2034
+ }
2035
+ function useColor() {
2036
+ if (process2.env.NO_COLOR || process2.env.FORCE_COLOR === "0" || process2.env.FORCE_COLOR === "false")
2037
+ return false;
2038
+ if (process2.env.FORCE_COLOR || process2.env.CLICOLOR_FORCE !== undefined)
2039
+ return true;
2040
+ return;
2041
+ }
2042
+ exports.Command = Command;
2043
+ exports.useColor = useColor;
2044
+ });
2045
+
2046
+ // node_modules/commander/index.js
2047
+ var require_commander = __commonJS((exports) => {
2048
+ var { Argument } = require_argument();
2049
+ var { Command } = require_command();
2050
+ var { CommanderError, InvalidArgumentError } = require_error();
2051
+ var { Help } = require_help();
2052
+ var { Option } = require_option();
2053
+ exports.program = new Command;
2054
+ exports.createCommand = (name) => new Command(name);
2055
+ exports.createOption = (flags, description) => new Option(flags, description);
2056
+ exports.createArgument = (name, description) => new Argument(name, description);
2057
+ exports.Command = Command;
2058
+ exports.Option = Option;
2059
+ exports.Argument = Argument;
2060
+ exports.Help = Help;
2061
+ exports.CommanderError = CommanderError;
2062
+ exports.InvalidArgumentError = InvalidArgumentError;
2063
+ exports.InvalidOptionArgumentError = InvalidArgumentError;
2064
+ });
2065
+
2066
+ // src/types/index.ts
2067
+ var ContactNotFoundError, CompanyNotFoundError, TagNotFoundError, DuplicateTagNameError;
2068
+ var init_types = __esm(() => {
2069
+ ContactNotFoundError = class ContactNotFoundError extends Error {
2070
+ constructor(id) {
2071
+ super(`Contact not found: ${id}`);
2072
+ this.name = "ContactNotFoundError";
2073
+ }
2074
+ };
2075
+ CompanyNotFoundError = class CompanyNotFoundError extends Error {
2076
+ constructor(id) {
2077
+ super(`Company not found: ${id}`);
2078
+ this.name = "CompanyNotFoundError";
2079
+ }
2080
+ };
2081
+ TagNotFoundError = class TagNotFoundError extends Error {
2082
+ constructor(id) {
2083
+ super(`Tag not found: ${id}`);
2084
+ this.name = "TagNotFoundError";
2085
+ }
2086
+ };
2087
+ DuplicateTagNameError = class DuplicateTagNameError extends Error {
2088
+ constructor(name) {
2089
+ super(`Tag with name already exists: ${name}`);
2090
+ this.name = "DuplicateTagNameError";
2091
+ }
2092
+ };
2093
+ });
2094
+
2095
+ // src/db/database.ts
2096
+ import { Database } from "bun:sqlite";
2097
+ import { existsSync, mkdirSync } from "fs";
2098
+ import { dirname, join, resolve } from "path";
2099
+ function getDbPath() {
2100
+ if (process.env["CONTACTS_DB_PATH"])
2101
+ return process.env["CONTACTS_DB_PATH"];
2102
+ const home = process.env["HOME"] || "~";
2103
+ return join(home, ".contacts", "contacts.db");
2104
+ }
2105
+ function ensureDir(filePath) {
2106
+ if (filePath === ":memory:")
2107
+ return;
2108
+ const dir = dirname(resolve(filePath));
2109
+ if (!existsSync(dir))
2110
+ mkdirSync(dir, { recursive: true });
2111
+ }
2112
+ function getDatabase(path) {
2113
+ if (_db)
2114
+ return _db;
2115
+ const dbPath = path || getDbPath();
2116
+ ensureDir(dbPath);
2117
+ const db = new Database(dbPath, { create: true });
2118
+ db.exec("PRAGMA journal_mode=WAL");
2119
+ db.exec("PRAGMA foreign_keys=ON");
2120
+ runMigrations(db);
2121
+ _db = db;
2122
+ return db;
2123
+ }
2124
+ function uuid() {
2125
+ return crypto.randomUUID();
2126
+ }
2127
+ function now() {
2128
+ return new Date().toISOString();
2129
+ }
2130
+ function runMigrations(db) {
2131
+ try {
2132
+ const row = db.query("SELECT MAX(version) as v FROM _migrations").get();
2133
+ const current = row?.v ?? -1;
2134
+ for (let i = current + 1;i < MIGRATIONS.length; i++) {
2135
+ db.exec(MIGRATIONS[i]);
2136
+ db.exec(`INSERT OR REPLACE INTO _migrations(version) VALUES(${i})`);
2137
+ }
2138
+ } catch {
2139
+ for (const m of MIGRATIONS) {
2140
+ try {
2141
+ db.exec(m);
2142
+ } catch {}
2143
+ }
2144
+ try {
2145
+ db.exec(`INSERT OR REPLACE INTO _migrations(version) VALUES(${MIGRATIONS.length - 1})`);
2146
+ } catch {}
2147
+ }
2148
+ }
2149
+ var MIGRATIONS, _db = null;
2150
+ var init_database = __esm(() => {
2151
+ MIGRATIONS = [
2152
+ `
2153
+ CREATE TABLE IF NOT EXISTS companies (
2154
+ id TEXT PRIMARY KEY,
2155
+ name TEXT NOT NULL,
2156
+ domain TEXT,
2157
+ logo_url TEXT,
2158
+ description TEXT,
2159
+ industry TEXT,
2160
+ size TEXT,
2161
+ founded_year INTEGER,
2162
+ notes TEXT,
2163
+ custom_fields TEXT NOT NULL DEFAULT '{}',
2164
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2165
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2166
+ );
2167
+
2168
+ CREATE TABLE IF NOT EXISTS contacts (
2169
+ id TEXT PRIMARY KEY,
2170
+ first_name TEXT NOT NULL DEFAULT '',
2171
+ last_name TEXT NOT NULL DEFAULT '',
2172
+ display_name TEXT NOT NULL,
2173
+ nickname TEXT,
2174
+ avatar_url TEXT,
2175
+ notes TEXT,
2176
+ birthday TEXT,
2177
+ company_id TEXT REFERENCES companies(id) ON DELETE SET NULL,
2178
+ job_title TEXT,
2179
+ source TEXT NOT NULL DEFAULT 'manual',
2180
+ custom_fields TEXT NOT NULL DEFAULT '{}',
2181
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
2182
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
2183
+ );
2184
+
2185
+ CREATE TABLE IF NOT EXISTS tags (
2186
+ id TEXT PRIMARY KEY,
2187
+ name TEXT NOT NULL UNIQUE,
2188
+ color TEXT NOT NULL DEFAULT '#6366f1',
2189
+ description TEXT,
2190
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2191
+ );
2192
+
2193
+ CREATE TABLE IF NOT EXISTS contact_tags (
2194
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
2195
+ tag_id TEXT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
2196
+ PRIMARY KEY (contact_id, tag_id)
2197
+ );
2198
+
2199
+ CREATE TABLE IF NOT EXISTS company_tags (
2200
+ company_id TEXT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
2201
+ tag_id TEXT NOT NULL REFERENCES tags(id) ON DELETE CASCADE,
2202
+ PRIMARY KEY (company_id, tag_id)
2203
+ );
2204
+
2205
+ CREATE TABLE IF NOT EXISTS emails (
2206
+ id TEXT PRIMARY KEY,
2207
+ contact_id TEXT REFERENCES contacts(id) ON DELETE CASCADE,
2208
+ company_id TEXT REFERENCES companies(id) ON DELETE CASCADE,
2209
+ address TEXT NOT NULL,
2210
+ type TEXT NOT NULL DEFAULT 'work' CHECK(type IN ('work','personal','other')),
2211
+ is_primary INTEGER NOT NULL DEFAULT 0,
2212
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2213
+ );
2214
+
2215
+ CREATE TABLE IF NOT EXISTS phones (
2216
+ id TEXT PRIMARY KEY,
2217
+ contact_id TEXT REFERENCES contacts(id) ON DELETE CASCADE,
2218
+ company_id TEXT REFERENCES companies(id) ON DELETE CASCADE,
2219
+ number TEXT NOT NULL,
2220
+ country_code TEXT,
2221
+ type TEXT NOT NULL DEFAULT 'mobile' CHECK(type IN ('mobile','work','home','fax','whatsapp','other')),
2222
+ is_primary INTEGER NOT NULL DEFAULT 0,
2223
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2224
+ );
2225
+
2226
+ CREATE TABLE IF NOT EXISTS addresses (
2227
+ id TEXT PRIMARY KEY,
2228
+ contact_id TEXT REFERENCES contacts(id) ON DELETE CASCADE,
2229
+ company_id TEXT REFERENCES companies(id) ON DELETE CASCADE,
2230
+ type TEXT NOT NULL DEFAULT 'physical' CHECK(type IN ('physical','mailing','billing','virtual','other')),
2231
+ street TEXT,
2232
+ city TEXT,
2233
+ state TEXT,
2234
+ zip TEXT,
2235
+ country TEXT,
2236
+ is_primary INTEGER NOT NULL DEFAULT 0,
2237
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2238
+ );
2239
+
2240
+ CREATE TABLE IF NOT EXISTS social_profiles (
2241
+ id TEXT PRIMARY KEY,
2242
+ contact_id TEXT REFERENCES contacts(id) ON DELETE CASCADE,
2243
+ company_id TEXT REFERENCES companies(id) ON DELETE CASCADE,
2244
+ platform TEXT NOT NULL CHECK(platform IN ('twitter','linkedin','github','instagram','telegram','discord','youtube','tiktok','bluesky','facebook','whatsapp','snapchat','reddit','other')),
2245
+ handle TEXT,
2246
+ url TEXT,
2247
+ is_primary INTEGER NOT NULL DEFAULT 0,
2248
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2249
+ );
2250
+
2251
+ CREATE TABLE IF NOT EXISTS contact_relationships (
2252
+ id TEXT PRIMARY KEY,
2253
+ contact_a_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
2254
+ contact_b_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
2255
+ relationship_type TEXT NOT NULL CHECK(relationship_type IN ('colleague','friend','family','reports_to','mentor','investor','partner','client','vendor','other')),
2256
+ notes TEXT,
2257
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2258
+ );
2259
+
2260
+ CREATE TABLE IF NOT EXISTS activity_log (
2261
+ id TEXT PRIMARY KEY,
2262
+ contact_id TEXT REFERENCES contacts(id) ON DELETE CASCADE,
2263
+ company_id TEXT REFERENCES companies(id) ON DELETE CASCADE,
2264
+ action TEXT NOT NULL,
2265
+ details TEXT,
2266
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2267
+ );
2268
+
2269
+ CREATE TABLE IF NOT EXISTS webhooks (
2270
+ id TEXT PRIMARY KEY,
2271
+ url TEXT NOT NULL,
2272
+ events TEXT NOT NULL DEFAULT '["*"]',
2273
+ secret TEXT,
2274
+ active INTEGER NOT NULL DEFAULT 1,
2275
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
2276
+ );
2277
+
2278
+ CREATE VIRTUAL TABLE IF NOT EXISTS contacts_fts USING fts5(
2279
+ id UNINDEXED,
2280
+ display_name,
2281
+ first_name,
2282
+ last_name,
2283
+ nickname,
2284
+ notes,
2285
+ job_title,
2286
+ content='contacts',
2287
+ content_rowid='rowid'
2288
+ );
2289
+
2290
+ CREATE TRIGGER IF NOT EXISTS contacts_fts_insert AFTER INSERT ON contacts BEGIN
2291
+ INSERT INTO contacts_fts(rowid, id, display_name, first_name, last_name, nickname, notes, job_title)
2292
+ VALUES (new.rowid, new.id, new.display_name, new.first_name, new.last_name, new.nickname, new.notes, new.job_title);
2293
+ END;
2294
+
2295
+ CREATE TRIGGER IF NOT EXISTS contacts_fts_update AFTER UPDATE ON contacts BEGIN
2296
+ DELETE FROM contacts_fts WHERE rowid = old.rowid;
2297
+ INSERT INTO contacts_fts(rowid, id, display_name, first_name, last_name, nickname, notes, job_title)
2298
+ VALUES (new.rowid, new.id, new.display_name, new.first_name, new.last_name, new.nickname, new.notes, new.job_title);
2299
+ END;
2300
+
2301
+ CREATE TRIGGER IF NOT EXISTS contacts_fts_delete AFTER DELETE ON contacts BEGIN
2302
+ DELETE FROM contacts_fts WHERE rowid = old.rowid;
2303
+ END;
2304
+
2305
+ CREATE TABLE IF NOT EXISTS _migrations (version INTEGER PRIMARY KEY);
2306
+ `
2307
+ ];
2308
+ });
2309
+
2310
+ // src/db/activity.ts
2311
+ function logActivity(db, input) {
2312
+ const id = uuid();
2313
+ db.run(`INSERT INTO activity_log (id, contact_id, company_id, action, details) VALUES (?, ?, ?, ?, ?)`, [id, input.contact_id ?? null, input.company_id ?? null, input.action, input.details ?? null]);
2314
+ return db.query(`SELECT * FROM activity_log WHERE id = ?`).get(id);
2315
+ }
2316
+ var init_activity = __esm(() => {
2317
+ init_database();
2318
+ });
2319
+
2320
+ // src/db/contacts.ts
2321
+ function rowToContact(row) {
2322
+ return {
2323
+ ...row,
2324
+ source: row.source,
2325
+ custom_fields: JSON.parse(row.custom_fields || "{}")
2326
+ };
2327
+ }
2328
+ function rowToEmail(row) {
2329
+ return {
2330
+ ...row,
2331
+ type: row.type,
2332
+ is_primary: !!row.is_primary
2333
+ };
2334
+ }
2335
+ function rowToPhone(row) {
2336
+ return {
2337
+ ...row,
2338
+ type: row.type,
2339
+ is_primary: !!row.is_primary
2340
+ };
2341
+ }
2342
+ function rowToAddress(row) {
2343
+ return {
2344
+ ...row,
2345
+ type: row.type,
2346
+ is_primary: !!row.is_primary
2347
+ };
2348
+ }
2349
+ function rowToSocialProfile(row) {
2350
+ return {
2351
+ ...row,
2352
+ platform: row.platform,
2353
+ is_primary: !!row.is_primary
2354
+ };
2355
+ }
2356
+ function rowToTag(row) {
2357
+ return { ...row };
2358
+ }
2359
+ function rowToCompany(row) {
2360
+ return {
2361
+ ...row,
2362
+ custom_fields: JSON.parse(row.custom_fields || "{}")
2363
+ };
2364
+ }
2365
+ function insertEmails(db, contactId, companyId, emails) {
2366
+ for (const e of emails) {
2367
+ db.run(`INSERT INTO emails (id, contact_id, company_id, address, type, is_primary) VALUES (?, ?, ?, ?, ?, ?)`, [uuid(), contactId, companyId, e.address, e.type ?? "work", e.is_primary ? 1 : 0]);
2368
+ }
2369
+ }
2370
+ function insertPhones(db, contactId, companyId, phones) {
2371
+ for (const p of phones) {
2372
+ db.run(`INSERT INTO phones (id, contact_id, company_id, number, country_code, type, is_primary) VALUES (?, ?, ?, ?, ?, ?, ?)`, [uuid(), contactId, companyId, p.number, p.country_code ?? null, p.type ?? "mobile", p.is_primary ? 1 : 0]);
2373
+ }
2374
+ }
2375
+ function insertAddresses(db, contactId, companyId, addresses) {
2376
+ for (const a of addresses) {
2377
+ db.run(`INSERT INTO addresses (id, contact_id, company_id, type, street, city, state, zip, country, is_primary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [uuid(), contactId, companyId, a.type ?? "physical", a.street ?? null, a.city ?? null, a.state ?? null, a.zip ?? null, a.country ?? null, a.is_primary ? 1 : 0]);
2378
+ }
2379
+ }
2380
+ function insertSocialProfiles(db, contactId, companyId, profiles) {
2381
+ for (const s of profiles) {
2382
+ db.run(`INSERT INTO social_profiles (id, contact_id, company_id, platform, handle, url, is_primary) VALUES (?, ?, ?, ?, ?, ?, ?)`, [uuid(), contactId, companyId, s.platform, s.handle ?? null, s.url ?? null, s.is_primary ? 1 : 0]);
2383
+ }
2384
+ }
2385
+ function loadContactDetails(db, contact) {
2386
+ const emails = db.query(`SELECT * FROM emails WHERE contact_id = ?`).all(contact.id).map(rowToEmail);
2387
+ const phones = db.query(`SELECT * FROM phones WHERE contact_id = ?`).all(contact.id).map(rowToPhone);
2388
+ const addresses = db.query(`SELECT * FROM addresses WHERE contact_id = ?`).all(contact.id).map(rowToAddress);
2389
+ const social_profiles = db.query(`SELECT * FROM social_profiles WHERE contact_id = ?`).all(contact.id).map(rowToSocialProfile);
2390
+ const tags = db.query(`
2391
+ SELECT t.* FROM tags t
2392
+ JOIN contact_tags ct ON ct.tag_id = t.id
2393
+ WHERE ct.contact_id = ?
2394
+ `).all(contact.id).map(rowToTag);
2395
+ const companyRow = contact.company_id ? db.query(`SELECT * FROM companies WHERE id = ?`).get(contact.company_id) : null;
2396
+ const company = companyRow ? rowToCompany(companyRow) : null;
2397
+ return { ...contact, emails, phones, addresses, social_profiles, tags, company };
2398
+ }
2399
+ function createContact(input, db) {
2400
+ const d = db || getDatabase();
2401
+ const id = uuid();
2402
+ const timestamp = now();
2403
+ const firstName = input.first_name ?? "";
2404
+ const lastName = input.last_name ?? "";
2405
+ const displayName = input.display_name ?? (firstName || lastName ? `${firstName} ${lastName}`.trim() : "Unnamed Contact");
2406
+ d.run(`INSERT INTO contacts (id, first_name, last_name, display_name, nickname, avatar_url, notes, birthday, company_id, job_title, source, custom_fields, created_at, updated_at)
2407
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
2408
+ id,
2409
+ firstName,
2410
+ lastName,
2411
+ displayName,
2412
+ input.nickname ?? null,
2413
+ input.avatar_url ?? null,
2414
+ input.notes ?? null,
2415
+ input.birthday ?? null,
2416
+ input.company_id ?? null,
2417
+ input.job_title ?? null,
2418
+ input.source ?? "manual",
2419
+ JSON.stringify(input.custom_fields ?? {}),
2420
+ timestamp,
2421
+ timestamp
2422
+ ]);
2423
+ if (input.emails?.length)
2424
+ insertEmails(d, id, null, input.emails);
2425
+ if (input.phones?.length)
2426
+ insertPhones(d, id, null, input.phones);
2427
+ if (input.addresses?.length)
2428
+ insertAddresses(d, id, null, input.addresses);
2429
+ if (input.social_profiles?.length)
2430
+ insertSocialProfiles(d, id, null, input.social_profiles);
2431
+ if (input.tag_ids?.length) {
2432
+ for (const tagId of input.tag_ids) {
2433
+ d.run(`INSERT OR IGNORE INTO contact_tags (contact_id, tag_id) VALUES (?, ?)`, [id, tagId]);
2434
+ }
2435
+ }
2436
+ logActivity(d, { contact_id: id, action: "contact.created", details: `Created contact: ${displayName}` });
2437
+ const row = d.query(`SELECT * FROM contacts WHERE id = ?`).get(id);
2438
+ return loadContactDetails(d, rowToContact(row));
2439
+ }
2440
+ function getContact(id, db) {
2441
+ const d = db || getDatabase();
2442
+ const row = d.query(`SELECT * FROM contacts WHERE id = ?`).get(id);
2443
+ if (!row)
2444
+ throw new ContactNotFoundError(id);
2445
+ return loadContactDetails(d, rowToContact(row));
2446
+ }
2447
+ function listContacts(opts = {}, db) {
2448
+ const d = db || getDatabase();
2449
+ const {
2450
+ limit = 50,
2451
+ offset = 0,
2452
+ company_id,
2453
+ tag_id,
2454
+ source,
2455
+ order_by = "display_name",
2456
+ order_dir = "asc"
2457
+ } = opts;
2458
+ const conditions = [];
2459
+ const params = [];
2460
+ if (company_id) {
2461
+ conditions.push("c.company_id = ?");
2462
+ params.push(company_id);
2463
+ }
2464
+ if (source) {
2465
+ conditions.push("c.source = ?");
2466
+ params.push(source);
2467
+ }
2468
+ if (tag_id) {
2469
+ conditions.push("EXISTS (SELECT 1 FROM contact_tags ct WHERE ct.contact_id = c.id AND ct.tag_id = ?)");
2470
+ params.push(tag_id);
2471
+ }
2472
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2473
+ const validOrderBy = ["display_name", "created_at", "updated_at"].includes(order_by) ? order_by : "display_name";
2474
+ const validOrderDir = order_dir === "desc" ? "DESC" : "ASC";
2475
+ const totalRow = d.query(`SELECT COUNT(*) as total FROM contacts c ${where}`).get(...params);
2476
+ const rows = d.query(`SELECT c.* FROM contacts c ${where} ORDER BY c.${validOrderBy} ${validOrderDir} LIMIT ? OFFSET ?`).all(...params, limit, offset);
2477
+ const contacts = rows.map((row) => loadContactDetails(d, rowToContact(row)));
2478
+ return { contacts, total: totalRow.total };
2479
+ }
2480
+ function updateContact(id, input, db) {
2481
+ const d = db || getDatabase();
2482
+ const existing = d.query(`SELECT * FROM contacts WHERE id = ?`).get(id);
2483
+ if (!existing)
2484
+ throw new ContactNotFoundError(id);
2485
+ const setClauses = ["updated_at = ?"];
2486
+ const params = [now()];
2487
+ if (input.first_name !== undefined) {
2488
+ setClauses.push("first_name = ?");
2489
+ params.push(input.first_name);
2490
+ }
2491
+ if (input.last_name !== undefined) {
2492
+ setClauses.push("last_name = ?");
2493
+ params.push(input.last_name);
2494
+ }
2495
+ if (input.display_name !== undefined) {
2496
+ setClauses.push("display_name = ?");
2497
+ params.push(input.display_name);
2498
+ }
2499
+ if (input.nickname !== undefined) {
2500
+ setClauses.push("nickname = ?");
2501
+ params.push(input.nickname);
2502
+ }
2503
+ if (input.avatar_url !== undefined) {
2504
+ setClauses.push("avatar_url = ?");
2505
+ params.push(input.avatar_url);
2506
+ }
2507
+ if (input.notes !== undefined) {
2508
+ setClauses.push("notes = ?");
2509
+ params.push(input.notes);
2510
+ }
2511
+ if (input.birthday !== undefined) {
2512
+ setClauses.push("birthday = ?");
2513
+ params.push(input.birthday);
2514
+ }
2515
+ if (input.company_id !== undefined) {
2516
+ setClauses.push("company_id = ?");
2517
+ params.push(input.company_id);
2518
+ }
2519
+ if (input.job_title !== undefined) {
2520
+ setClauses.push("job_title = ?");
2521
+ params.push(input.job_title);
2522
+ }
2523
+ if (input.source !== undefined) {
2524
+ setClauses.push("source = ?");
2525
+ params.push(input.source);
2526
+ }
2527
+ if (input.custom_fields !== undefined) {
2528
+ setClauses.push("custom_fields = ?");
2529
+ params.push(JSON.stringify(input.custom_fields));
2530
+ }
2531
+ params.push(id);
2532
+ d.run(`UPDATE contacts SET ${setClauses.join(", ")} WHERE id = ?`, params);
2533
+ logActivity(d, { contact_id: id, action: "contact.updated", details: `Updated contact: ${existing.display_name}` });
2534
+ const row = d.query(`SELECT * FROM contacts WHERE id = ?`).get(id);
2535
+ return loadContactDetails(d, rowToContact(row));
2536
+ }
2537
+ function deleteContact(id, db) {
2538
+ const d = db || getDatabase();
2539
+ const row = d.query(`SELECT * FROM contacts WHERE id = ?`).get(id);
2540
+ if (!row)
2541
+ throw new ContactNotFoundError(id);
2542
+ logActivity(d, { contact_id: id, action: "contact.deleted", details: `Deleted contact: ${row.display_name}` });
2543
+ d.run(`DELETE FROM contacts WHERE id = ?`, [id]);
2544
+ }
2545
+ function searchContacts(query, db) {
2546
+ const d = db || getDatabase();
2547
+ const ftsRows = d.query(`
2548
+ SELECT c.* FROM contacts c
2549
+ JOIN contacts_fts fts ON fts.id = c.id
2550
+ WHERE contacts_fts MATCH ?
2551
+ ORDER BY rank
2552
+ LIMIT 50
2553
+ `).all(`"${query.replace(/"/g, '""')}"*`);
2554
+ const emailRows = d.query(`
2555
+ SELECT DISTINCT c.* FROM contacts c
2556
+ JOIN emails e ON e.contact_id = c.id
2557
+ WHERE e.address LIKE ?
2558
+ LIMIT 20
2559
+ `).all(`%${query}%`);
2560
+ const phoneRows = d.query(`
2561
+ SELECT DISTINCT c.* FROM contacts c
2562
+ JOIN phones p ON p.contact_id = c.id
2563
+ WHERE p.number LIKE ?
2564
+ LIMIT 20
2565
+ `).all(`%${query}%`);
2566
+ const seen = new Set;
2567
+ const allRows = [];
2568
+ for (const row of [...ftsRows, ...emailRows, ...phoneRows]) {
2569
+ if (!seen.has(row.id)) {
2570
+ seen.add(row.id);
2571
+ allRows.push(row);
2572
+ }
2573
+ }
2574
+ return allRows.map((row) => loadContactDetails(d, rowToContact(row)));
2575
+ }
2576
+ var init_contacts = __esm(() => {
2577
+ init_types();
2578
+ init_database();
2579
+ init_activity();
2580
+ });
2581
+
2582
+ // src/db/companies.ts
2583
+ function rowToCompany2(row) {
2584
+ return {
2585
+ ...row,
2586
+ custom_fields: JSON.parse(row.custom_fields || "{}")
2587
+ };
2588
+ }
2589
+ function insertEmails2(db, companyId, emails) {
2590
+ for (const e of emails) {
2591
+ db.run(`INSERT INTO emails (id, contact_id, company_id, address, type, is_primary) VALUES (?, ?, ?, ?, ?, ?)`, [uuid(), null, companyId, e.address, e.type ?? "work", e.is_primary ? 1 : 0]);
2592
+ }
2593
+ }
2594
+ function insertPhones2(db, companyId, phones) {
2595
+ for (const p of phones) {
2596
+ db.run(`INSERT INTO phones (id, contact_id, company_id, number, country_code, type, is_primary) VALUES (?, ?, ?, ?, ?, ?, ?)`, [uuid(), null, companyId, p.number, p.country_code ?? null, p.type ?? "work", p.is_primary ? 1 : 0]);
2597
+ }
2598
+ }
2599
+ function insertAddresses2(db, companyId, addresses) {
2600
+ for (const a of addresses) {
2601
+ db.run(`INSERT INTO addresses (id, contact_id, company_id, type, street, city, state, zip, country, is_primary) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [uuid(), null, companyId, a.type ?? "physical", a.street ?? null, a.city ?? null, a.state ?? null, a.zip ?? null, a.country ?? null, a.is_primary ? 1 : 0]);
2602
+ }
2603
+ }
2604
+ function insertSocialProfiles2(db, companyId, profiles) {
2605
+ for (const s of profiles) {
2606
+ db.run(`INSERT INTO social_profiles (id, contact_id, company_id, platform, handle, url, is_primary) VALUES (?, ?, ?, ?, ?, ?, ?)`, [uuid(), null, companyId, s.platform, s.handle ?? null, s.url ?? null, s.is_primary ? 1 : 0]);
2607
+ }
2608
+ }
2609
+ function loadCompanyDetails(db, company) {
2610
+ const emails = db.query(`SELECT * FROM emails WHERE company_id = ?`).all(company.id).map((row) => ({
2611
+ ...row,
2612
+ type: row.type,
2613
+ is_primary: !!row.is_primary
2614
+ }));
2615
+ const phones = db.query(`SELECT * FROM phones WHERE company_id = ?`).all(company.id).map((row) => ({
2616
+ ...row,
2617
+ type: row.type,
2618
+ is_primary: !!row.is_primary
2619
+ }));
2620
+ const addresses = db.query(`SELECT * FROM addresses WHERE company_id = ?`).all(company.id).map((row) => ({
2621
+ ...row,
2622
+ type: row.type,
2623
+ is_primary: !!row.is_primary
2624
+ }));
2625
+ const social_profiles = db.query(`SELECT * FROM social_profiles WHERE company_id = ?`).all(company.id).map((row) => ({
2626
+ ...row,
2627
+ platform: row.platform,
2628
+ is_primary: !!row.is_primary
2629
+ }));
2630
+ const tags = db.query(`
2631
+ SELECT t.* FROM tags t
2632
+ JOIN company_tags ct ON ct.tag_id = t.id
2633
+ WHERE ct.company_id = ?
2634
+ `).all(company.id);
2635
+ const empCount = db.query(`SELECT COUNT(*) as count FROM contacts WHERE company_id = ?`).get(company.id);
2636
+ return {
2637
+ ...company,
2638
+ emails,
2639
+ phones,
2640
+ addresses,
2641
+ social_profiles,
2642
+ tags,
2643
+ employee_count: empCount.count
2644
+ };
2645
+ }
2646
+ function createCompany(input, db) {
2647
+ const d = db || getDatabase();
2648
+ const id = uuid();
2649
+ const timestamp = now();
2650
+ d.run(`INSERT INTO companies (id, name, domain, logo_url, description, industry, size, founded_year, notes, custom_fields, created_at, updated_at)
2651
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
2652
+ id,
2653
+ input.name,
2654
+ input.domain ?? null,
2655
+ input.logo_url ?? null,
2656
+ input.description ?? null,
2657
+ input.industry ?? null,
2658
+ input.size ?? null,
2659
+ input.founded_year ?? null,
2660
+ input.notes ?? null,
2661
+ JSON.stringify(input.custom_fields ?? {}),
2662
+ timestamp,
2663
+ timestamp
2664
+ ]);
2665
+ if (input.emails?.length)
2666
+ insertEmails2(d, id, input.emails);
2667
+ if (input.phones?.length)
2668
+ insertPhones2(d, id, input.phones);
2669
+ if (input.addresses?.length)
2670
+ insertAddresses2(d, id, input.addresses);
2671
+ if (input.social_profiles?.length)
2672
+ insertSocialProfiles2(d, id, input.social_profiles);
2673
+ if (input.tag_ids?.length) {
2674
+ for (const tagId of input.tag_ids) {
2675
+ d.run(`INSERT OR IGNORE INTO company_tags (company_id, tag_id) VALUES (?, ?)`, [id, tagId]);
2676
+ }
2677
+ }
2678
+ logActivity(d, { company_id: id, action: "company.created", details: `Created company: ${input.name}` });
2679
+ const row = d.query(`SELECT * FROM companies WHERE id = ?`).get(id);
2680
+ return loadCompanyDetails(d, rowToCompany2(row));
2681
+ }
2682
+ function getCompany(id, db) {
2683
+ const d = db || getDatabase();
2684
+ const row = d.query(`SELECT * FROM companies WHERE id = ?`).get(id);
2685
+ if (!row)
2686
+ throw new CompanyNotFoundError(id);
2687
+ return loadCompanyDetails(d, rowToCompany2(row));
2688
+ }
2689
+ function listCompanies(opts = {}, db) {
2690
+ const d = db || getDatabase();
2691
+ const {
2692
+ limit = 50,
2693
+ offset = 0,
2694
+ industry,
2695
+ tag_id,
2696
+ order_by = "name",
2697
+ order_dir = "asc"
2698
+ } = opts;
2699
+ const conditions = [];
2700
+ const params = [];
2701
+ if (industry) {
2702
+ conditions.push("co.industry = ?");
2703
+ params.push(industry);
2704
+ }
2705
+ if (tag_id) {
2706
+ conditions.push("EXISTS (SELECT 1 FROM company_tags ct WHERE ct.company_id = co.id AND ct.tag_id = ?)");
2707
+ params.push(tag_id);
2708
+ }
2709
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2710
+ const validOrderBy = ["name", "created_at", "updated_at"].includes(order_by) ? order_by : "name";
2711
+ const validOrderDir = order_dir === "desc" ? "DESC" : "ASC";
2712
+ const totalRow = d.query(`SELECT COUNT(*) as total FROM companies co ${where}`).get(...params);
2713
+ const rows = d.query(`SELECT co.* FROM companies co ${where} ORDER BY co.${validOrderBy} ${validOrderDir} LIMIT ? OFFSET ?`).all(...params, limit, offset);
2714
+ const companies = rows.map((row) => loadCompanyDetails(d, rowToCompany2(row)));
2715
+ return { companies, total: totalRow.total };
2716
+ }
2717
+ function updateCompany(id, input, db) {
2718
+ const d = db || getDatabase();
2719
+ const existing = d.query(`SELECT * FROM companies WHERE id = ?`).get(id);
2720
+ if (!existing)
2721
+ throw new CompanyNotFoundError(id);
2722
+ const setClauses = ["updated_at = ?"];
2723
+ const params = [now()];
2724
+ if (input.name !== undefined) {
2725
+ setClauses.push("name = ?");
2726
+ params.push(input.name);
2727
+ }
2728
+ if (input.domain !== undefined) {
2729
+ setClauses.push("domain = ?");
2730
+ params.push(input.domain);
2731
+ }
2732
+ if (input.logo_url !== undefined) {
2733
+ setClauses.push("logo_url = ?");
2734
+ params.push(input.logo_url);
2735
+ }
2736
+ if (input.description !== undefined) {
2737
+ setClauses.push("description = ?");
2738
+ params.push(input.description);
2739
+ }
2740
+ if (input.industry !== undefined) {
2741
+ setClauses.push("industry = ?");
2742
+ params.push(input.industry);
2743
+ }
2744
+ if (input.size !== undefined) {
2745
+ setClauses.push("size = ?");
2746
+ params.push(input.size);
2747
+ }
2748
+ if (input.founded_year !== undefined) {
2749
+ setClauses.push("founded_year = ?");
2750
+ params.push(input.founded_year);
2751
+ }
2752
+ if (input.notes !== undefined) {
2753
+ setClauses.push("notes = ?");
2754
+ params.push(input.notes);
2755
+ }
2756
+ if (input.custom_fields !== undefined) {
2757
+ setClauses.push("custom_fields = ?");
2758
+ params.push(JSON.stringify(input.custom_fields));
2759
+ }
2760
+ params.push(id);
2761
+ d.run(`UPDATE companies SET ${setClauses.join(", ")} WHERE id = ?`, params);
2762
+ logActivity(d, { company_id: id, action: "company.updated", details: `Updated company: ${existing.name}` });
2763
+ const row = d.query(`SELECT * FROM companies WHERE id = ?`).get(id);
2764
+ return loadCompanyDetails(d, rowToCompany2(row));
2765
+ }
2766
+ function deleteCompany(id, db) {
2767
+ const d = db || getDatabase();
2768
+ const row = d.query(`SELECT * FROM companies WHERE id = ?`).get(id);
2769
+ if (!row)
2770
+ throw new CompanyNotFoundError(id);
2771
+ logActivity(d, { company_id: id, action: "company.deleted", details: `Deleted company: ${row.name}` });
2772
+ d.run(`DELETE FROM companies WHERE id = ?`, [id]);
2773
+ }
2774
+ var init_companies = __esm(() => {
2775
+ init_types();
2776
+ init_database();
2777
+ init_activity();
2778
+ });
2779
+
2780
+ // src/db/tags.ts
2781
+ function rowToTag2(row) {
2782
+ return { ...row };
2783
+ }
2784
+ function createTag(input, db) {
2785
+ const d = db || getDatabase();
2786
+ const existing = d.query(`SELECT id FROM tags WHERE name = ?`).get(input.name);
2787
+ if (existing)
2788
+ throw new DuplicateTagNameError(input.name);
2789
+ const id = uuid();
2790
+ d.run(`INSERT INTO tags (id, name, color, description) VALUES (?, ?, ?, ?)`, [id, input.name, input.color ?? "#6366f1", input.description ?? null]);
2791
+ return rowToTag2(d.query(`SELECT * FROM tags WHERE id = ?`).get(id));
2792
+ }
2793
+ function listTags(db) {
2794
+ const d = db || getDatabase();
2795
+ return d.query(`SELECT * FROM tags ORDER BY name ASC`).all().map(rowToTag2);
2796
+ }
2797
+ function deleteTag(id, db) {
2798
+ const d = db || getDatabase();
2799
+ const row = d.query(`SELECT id FROM tags WHERE id = ?`).get(id);
2800
+ if (!row)
2801
+ throw new TagNotFoundError(id);
2802
+ d.run(`DELETE FROM tags WHERE id = ?`, [id]);
2803
+ }
2804
+ var init_tags = __esm(() => {
2805
+ init_types();
2806
+ init_database();
2807
+ });
2808
+
2809
+ // src/lib/import.ts
2810
+ function parseCsv(text) {
2811
+ const lines = text.split(/\r?\n/);
2812
+ if (lines.length < 2)
2813
+ return [];
2814
+ const headers = parseCsvLine(lines[0]);
2815
+ const rows = [];
2816
+ for (let i = 1;i < lines.length; i++) {
2817
+ const line = lines[i].trim();
2818
+ if (!line)
2819
+ continue;
2820
+ const values = parseCsvLine(line);
2821
+ const row = {};
2822
+ headers.forEach((h, idx) => {
2823
+ row[h.trim()] = values[idx]?.trim() ?? "";
2824
+ });
2825
+ rows.push(row);
2826
+ }
2827
+ return rows;
2828
+ }
2829
+ function parseCsvLine(line) {
2830
+ const fields = [];
2831
+ let current = "";
2832
+ let inQuotes = false;
2833
+ for (let i = 0;i < line.length; i++) {
2834
+ const ch = line[i];
2835
+ if (ch === '"') {
2836
+ if (inQuotes && line[i + 1] === '"') {
2837
+ current += '"';
2838
+ i++;
2839
+ } else {
2840
+ inQuotes = !inQuotes;
2841
+ }
2842
+ } else if (ch === "," && !inQuotes) {
2843
+ fields.push(current);
2844
+ current = "";
2845
+ } else {
2846
+ current += ch;
2847
+ }
2848
+ }
2849
+ fields.push(current);
2850
+ return fields;
2851
+ }
2852
+ function csvRowToContact(row) {
2853
+ const firstName = row["First Name"] ?? row["first_name"] ?? row["Given Name"] ?? "";
2854
+ const lastName = row["Last Name"] ?? row["last_name"] ?? row["Family Name"] ?? "";
2855
+ const displayName = row["Name"] ?? row["display_name"] ?? row["Full Name"] ?? [firstName, lastName].filter(Boolean).join(" ") ?? "";
2856
+ if (!displayName && !firstName && !lastName)
2857
+ return null;
2858
+ const contact = {
2859
+ display_name: displayName || [firstName, lastName].filter(Boolean).join(" ") || "Unnamed",
2860
+ first_name: firstName || undefined,
2861
+ last_name: lastName || undefined,
2862
+ job_title: row["Job Title"] ?? row["job_title"] ?? row["Title"] ?? undefined,
2863
+ notes: row["Notes"] ?? row["notes"] ?? undefined,
2864
+ birthday: row["Birthday"] ?? row["birthday"] ?? undefined,
2865
+ source: "import"
2866
+ };
2867
+ const emails = [];
2868
+ for (let i = 1;i <= 5; i++) {
2869
+ const val = row[`Email ${i} - Value`] ?? row[`Email Address ${i}`] ?? (i === 1 ? row["Email"] ?? row["email"] ?? row["Email Address"] : undefined);
2870
+ const rawType = row[`Email ${i} - Type`] ?? (i === 1 ? "work" : "other");
2871
+ if (val) {
2872
+ const type = rawType?.toLowerCase() === "personal" ? "personal" : rawType?.toLowerCase() === "other" ? "other" : "work";
2873
+ emails.push({ address: val, type, is_primary: i === 1 });
2874
+ }
2875
+ }
2876
+ if (emails.length)
2877
+ contact.emails = emails;
2878
+ const phones = [];
2879
+ for (let i = 1;i <= 5; i++) {
2880
+ const val = row[`Phone ${i} - Value`] ?? row[`Phone ${i}`] ?? (i === 1 ? row["Phone"] ?? row["phone"] ?? row["Mobile"] : undefined);
2881
+ const rawType = row[`Phone ${i} - Type`] ?? (i === 1 ? "mobile" : "other");
2882
+ if (val) {
2883
+ const type = rawType?.toLowerCase().includes("mobile") || rawType?.toLowerCase().includes("cell") ? "mobile" : rawType?.toLowerCase().includes("work") ? "work" : rawType?.toLowerCase().includes("home") ? "home" : rawType?.toLowerCase().includes("fax") ? "fax" : "other";
2884
+ phones.push({ number: val, type, is_primary: i === 1 });
2885
+ }
2886
+ }
2887
+ if (phones.length)
2888
+ contact.phones = phones;
2889
+ return contact;
2890
+ }
2891
+ function importFromCsv(data) {
2892
+ const rows = parseCsv(data);
2893
+ return rows.map(csvRowToContact).filter(Boolean);
2894
+ }
2895
+ function parseVcf(data) {
2896
+ const contacts = [];
2897
+ const blocks = data.split(/BEGIN:VCARD/i).filter((b) => b.trim());
2898
+ for (const block of blocks) {
2899
+ try {
2900
+ const contact = parseVcfBlock(`BEGIN:VCARD
2901
+ ` + block);
2902
+ if (contact)
2903
+ contacts.push(contact);
2904
+ } catch {}
2905
+ }
2906
+ return contacts;
2907
+ }
2908
+ function parseVcfBlock(block) {
2909
+ const unfolded = block.replace(/\r?\n[ \t]/g, "");
2910
+ const lines = unfolded.split(/\r?\n/).filter((l) => l.trim());
2911
+ const contact = { source: "import" };
2912
+ const emails = [];
2913
+ const phones = [];
2914
+ const addresses = [];
2915
+ const socials = [];
2916
+ for (const line of lines) {
2917
+ if (/^BEGIN:VCARD$/i.test(line) || /^END:VCARD$/i.test(line) || /^VERSION:/i.test(line))
2918
+ continue;
2919
+ const colonIdx = line.indexOf(":");
2920
+ if (colonIdx === -1)
2921
+ continue;
2922
+ const propPart = line.slice(0, colonIdx);
2923
+ const value = line.slice(colonIdx + 1).trim();
2924
+ const semicolonIdx = propPart.indexOf(";");
2925
+ const propName = (semicolonIdx === -1 ? propPart : propPart.slice(0, semicolonIdx)).toUpperCase();
2926
+ const params = semicolonIdx !== -1 ? propPart.slice(semicolonIdx + 1) : "";
2927
+ switch (propName) {
2928
+ case "FN":
2929
+ contact.display_name = decodeVcfValue(value);
2930
+ break;
2931
+ case "N": {
2932
+ const parts = value.split(";");
2933
+ contact.last_name = decodeVcfValue(parts[0] ?? "") || undefined;
2934
+ contact.first_name = decodeVcfValue(parts[1] ?? "") || undefined;
2935
+ break;
2936
+ }
2937
+ case "NICKNAME":
2938
+ contact.nickname = decodeVcfValue(value) || undefined;
2939
+ break;
2940
+ case "TITLE":
2941
+ contact.job_title = decodeVcfValue(value) || undefined;
2942
+ break;
2943
+ case "NOTE":
2944
+ contact.notes = decodeVcfValue(value) || undefined;
2945
+ break;
2946
+ case "BDAY":
2947
+ contact.birthday = value.replace(/^(\d{4})(\d{2})(\d{2})$/, "$1-$2-$3");
2948
+ break;
2949
+ case "EMAIL": {
2950
+ const typeMatch = params.match(/TYPE=([^;]+)/i);
2951
+ const rawLabel = typeMatch ? typeMatch[1].toLowerCase() : "work";
2952
+ const type = rawLabel.includes("personal") ? "personal" : rawLabel.includes("other") ? "other" : "work";
2953
+ const isPrimary = params.includes("PREF") || emails.length === 0;
2954
+ emails.push({ address: decodeVcfValue(value), type, is_primary: isPrimary });
2955
+ break;
2956
+ }
2957
+ case "TEL": {
2958
+ const typeMatch = params.match(/TYPE=([^;]+)/i);
2959
+ const rawLabel = typeMatch ? typeMatch[1].toLowerCase() : "mobile";
2960
+ const type = rawLabel.includes("cell") || rawLabel.includes("mobile") ? "mobile" : rawLabel.includes("work") ? "work" : rawLabel.includes("home") ? "home" : rawLabel.includes("fax") ? "fax" : "other";
2961
+ const isPrimary = params.includes("PREF") || phones.length === 0;
2962
+ phones.push({ number: decodeVcfValue(value), type, is_primary: isPrimary });
2963
+ break;
2964
+ }
2965
+ case "ADR": {
2966
+ const parts = value.split(";");
2967
+ const typeMatch = params.match(/TYPE=([^;]+)/i);
2968
+ const rawLabel = typeMatch ? typeMatch[1].toLowerCase().split(",")[0] : "physical";
2969
+ const type = rawLabel.includes("home") || rawLabel.includes("physical") ? "physical" : rawLabel.includes("mail") ? "mailing" : rawLabel.includes("bill") ? "billing" : "other";
2970
+ addresses.push({
2971
+ type,
2972
+ street: decodeVcfValue(parts[2] ?? "") || undefined,
2973
+ city: decodeVcfValue(parts[3] ?? "") || undefined,
2974
+ state: decodeVcfValue(parts[4] ?? "") || undefined,
2975
+ zip: decodeVcfValue(parts[5] ?? "") || undefined,
2976
+ country: decodeVcfValue(parts[6] ?? "") || undefined,
2977
+ is_primary: addresses.length === 0
2978
+ });
2979
+ break;
2980
+ }
2981
+ case "URL": {
2982
+ const url = decodeVcfValue(value);
2983
+ const platform = detectPlatform(url);
2984
+ socials.push({ platform, url, handle: url });
2985
+ break;
2986
+ }
2987
+ case "X-SOCIALPROFILE": {
2988
+ const typeMatch = params.match(/TYPE=([^;]+)/i);
2989
+ const platform = normalizePlatform(typeMatch?.[1] ?? "other");
2990
+ socials.push({ platform, handle: decodeVcfValue(value), url: value });
2991
+ break;
2992
+ }
2993
+ }
2994
+ }
2995
+ if (!contact.display_name) {
2996
+ if (contact.first_name || contact.last_name) {
2997
+ contact.display_name = [contact.first_name, contact.last_name].filter(Boolean).join(" ");
2998
+ } else {
2999
+ return null;
3000
+ }
3001
+ }
3002
+ if (emails.length)
3003
+ contact.emails = emails;
3004
+ if (phones.length)
3005
+ contact.phones = phones;
3006
+ if (addresses.length)
3007
+ contact.addresses = addresses;
3008
+ if (socials.length)
3009
+ contact.social_profiles = socials;
3010
+ return contact;
3011
+ }
3012
+ function decodeVcfValue(val) {
3013
+ return val.replace(/\\n/g, `
3014
+ `).replace(/\\,/g, ",").replace(/\\;/g, ";").replace(/\\\\/g, "\\");
3015
+ }
3016
+ function detectPlatform(url) {
3017
+ const lower = url.toLowerCase();
3018
+ if (lower.includes("twitter.com") || lower.includes("x.com"))
3019
+ return "twitter";
3020
+ if (lower.includes("linkedin.com"))
3021
+ return "linkedin";
3022
+ if (lower.includes("github.com"))
3023
+ return "github";
3024
+ if (lower.includes("instagram.com"))
3025
+ return "instagram";
3026
+ if (lower.includes("facebook.com"))
3027
+ return "facebook";
3028
+ if (lower.includes("youtube.com"))
3029
+ return "youtube";
3030
+ if (lower.includes("telegram"))
3031
+ return "telegram";
3032
+ if (lower.includes("discord"))
3033
+ return "discord";
3034
+ if (lower.includes("tiktok"))
3035
+ return "tiktok";
3036
+ if (lower.includes("bluesky") || lower.includes("bsky"))
3037
+ return "bluesky";
3038
+ return "other";
3039
+ }
3040
+ function normalizePlatform(raw) {
3041
+ const lower = raw.toLowerCase();
3042
+ const platforms = [
3043
+ "twitter",
3044
+ "linkedin",
3045
+ "github",
3046
+ "instagram",
3047
+ "telegram",
3048
+ "discord",
3049
+ "youtube",
3050
+ "tiktok",
3051
+ "bluesky",
3052
+ "facebook",
3053
+ "whatsapp",
3054
+ "snapchat",
3055
+ "reddit"
3056
+ ];
3057
+ for (const p of platforms) {
3058
+ if (lower.includes(p))
3059
+ return p;
3060
+ }
3061
+ return "other";
3062
+ }
3063
+ function importFromJson(data) {
3064
+ let parsed;
3065
+ try {
3066
+ parsed = JSON.parse(data);
3067
+ } catch {
3068
+ throw new Error("Invalid JSON");
3069
+ }
3070
+ if (!Array.isArray(parsed)) {
3071
+ if (typeof parsed === "object" && parsed !== null) {
3072
+ parsed = [parsed];
3073
+ } else {
3074
+ throw new Error("JSON must be an array of contacts");
3075
+ }
3076
+ }
3077
+ return parsed.map((obj) => {
3078
+ const displayName = obj.display_name ?? obj.name ?? [obj.first_name ?? "", obj.last_name ?? ""].filter(Boolean).join(" ") ?? "Unnamed";
3079
+ return {
3080
+ ...obj,
3081
+ display_name: displayName,
3082
+ source: "import"
3083
+ };
3084
+ });
3085
+ }
3086
+ async function importContacts(format, data) {
3087
+ switch (format) {
3088
+ case "csv":
3089
+ return importFromCsv(data);
3090
+ case "vcf":
3091
+ return parseVcf(data);
3092
+ case "json":
3093
+ return importFromJson(data);
3094
+ default:
3095
+ throw new Error(`Unsupported import format: ${format}`);
3096
+ }
3097
+ }
3098
+
3099
+ // src/lib/export.ts
3100
+ function toJson(contacts) {
3101
+ return JSON.stringify(contacts, null, 2);
3102
+ }
3103
+ function escapeCsvField(val) {
3104
+ if (val == null)
3105
+ return "";
3106
+ const str = String(val);
3107
+ if (str.includes(",") || str.includes('"') || str.includes(`
3108
+ `)) {
3109
+ return `"${str.replace(/"/g, '""')}"`;
3110
+ }
3111
+ return str;
3112
+ }
3113
+ function toCsv(contacts) {
3114
+ const headers = [
3115
+ "First Name",
3116
+ "Last Name",
3117
+ "Name",
3118
+ "Nickname",
3119
+ "Job Title",
3120
+ "Company",
3121
+ "Email 1 - Value",
3122
+ "Email 1 - Type",
3123
+ "Email 2 - Value",
3124
+ "Email 2 - Type",
3125
+ "Phone 1 - Value",
3126
+ "Phone 1 - Type",
3127
+ "Phone 2 - Value",
3128
+ "Phone 2 - Type",
3129
+ "Address 1 - Street",
3130
+ "Address 1 - City",
3131
+ "Address 1 - State",
3132
+ "Address 1 - Postal Code",
3133
+ "Address 1 - Country",
3134
+ "Address 1 - Type",
3135
+ "Birthday",
3136
+ "Notes",
3137
+ "Tags"
3138
+ ];
3139
+ const rows = [headers.map(escapeCsvField).join(",")];
3140
+ for (const c of contacts) {
3141
+ const emails = c.emails ?? [];
3142
+ const phones = c.phones ?? [];
3143
+ const addrs = c.addresses ?? [];
3144
+ const tags = (c.tags ?? []).map((t) => t.name).join(";");
3145
+ const row = [
3146
+ c.first_name,
3147
+ c.last_name,
3148
+ c.display_name,
3149
+ c.nickname,
3150
+ c.job_title,
3151
+ c.company?.name,
3152
+ emails[0]?.address,
3153
+ emails[0]?.type,
3154
+ emails[1]?.address,
3155
+ emails[1]?.type,
3156
+ phones[0]?.number,
3157
+ phones[0]?.type,
3158
+ phones[1]?.number,
3159
+ phones[1]?.type,
3160
+ addrs[0]?.street,
3161
+ addrs[0]?.city,
3162
+ addrs[0]?.state,
3163
+ addrs[0]?.zip,
3164
+ addrs[0]?.country,
3165
+ addrs[0]?.type,
3166
+ c.birthday,
3167
+ c.notes,
3168
+ tags
3169
+ ];
3170
+ rows.push(row.map(escapeCsvField).join(","));
3171
+ }
3172
+ return rows.join(`
3173
+ `);
3174
+ }
3175
+ function escapeVcfValue(val) {
3176
+ if (!val)
3177
+ return "";
3178
+ return val.replace(/\\/g, "\\\\").replace(/,/g, "\\,").replace(/;/g, "\\;").replace(/\n/g, "\\n");
3179
+ }
3180
+ function foldVcfLine(line) {
3181
+ if (line.length <= 75)
3182
+ return line;
3183
+ const parts = [line.slice(0, 75)];
3184
+ let i = 75;
3185
+ while (i < line.length) {
3186
+ parts.push(" " + line.slice(i, i + 74));
3187
+ i += 74;
3188
+ }
3189
+ return parts.join(`\r
3190
+ `);
3191
+ }
3192
+ function toVcf(contacts) {
3193
+ const cards = [];
3194
+ for (const c of contacts) {
3195
+ const lines = ["BEGIN:VCARD", "VERSION:3.0"];
3196
+ lines.push(`FN:${escapeVcfValue(c.display_name)}`);
3197
+ lines.push(`N:${escapeVcfValue(c.last_name)};${escapeVcfValue(c.first_name)};;;`);
3198
+ if (c.nickname)
3199
+ lines.push(`NICKNAME:${escapeVcfValue(c.nickname)}`);
3200
+ if (c.job_title)
3201
+ lines.push(`TITLE:${escapeVcfValue(c.job_title)}`);
3202
+ if (c.company?.name)
3203
+ lines.push(`ORG:${escapeVcfValue(c.company.name)}`);
3204
+ if (c.birthday)
3205
+ lines.push(`BDAY:${c.birthday.replace(/-/g, "")}`);
3206
+ for (let i = 0;i < (c.emails ?? []).length; i++) {
3207
+ const e = c.emails[i];
3208
+ const pref = i === 0 || e.is_primary ? ";PREF" : "";
3209
+ lines.push(`EMAIL;TYPE=${e.type.toUpperCase()}${pref}:${escapeVcfValue(e.address)}`);
3210
+ }
3211
+ for (let i = 0;i < (c.phones ?? []).length; i++) {
3212
+ const p = c.phones[i];
3213
+ const pref = i === 0 || p.is_primary ? ";PREF" : "";
3214
+ const vcfType = p.type === "mobile" ? "CELL" : p.type.toUpperCase();
3215
+ lines.push(`TEL;TYPE=${vcfType}${pref}:${escapeVcfValue(p.number)}`);
3216
+ }
3217
+ for (let i = 0;i < (c.addresses ?? []).length; i++) {
3218
+ const a = c.addresses[i];
3219
+ const pref = i === 0 || a.is_primary ? ";PREF" : "";
3220
+ lines.push(`ADR;TYPE=${a.type.toUpperCase()}${pref}:;;${escapeVcfValue(a.street)};${escapeVcfValue(a.city)};${escapeVcfValue(a.state)};${escapeVcfValue(a.zip)};${escapeVcfValue(a.country)}`);
3221
+ }
3222
+ for (const sp of c.social_profiles ?? []) {
3223
+ if (sp.url)
3224
+ lines.push(`URL;TYPE=${sp.platform.toUpperCase()}:${escapeVcfValue(sp.url)}`);
3225
+ if (sp.handle)
3226
+ lines.push(`X-SOCIALPROFILE;TYPE=${sp.platform.toLowerCase()}:${escapeVcfValue(sp.handle)}`);
3227
+ }
3228
+ if (c.notes)
3229
+ lines.push(`NOTE:${escapeVcfValue(c.notes)}`);
3230
+ if (c.tags && c.tags.length > 0) {
3231
+ lines.push(`CATEGORIES:${c.tags.map((t) => escapeVcfValue(t.name)).join(",")}`);
3232
+ }
3233
+ lines.push(`UID:${c.id}`);
3234
+ lines.push("END:VCARD");
3235
+ cards.push(lines.map(foldVcfLine).join(`\r
3236
+ `));
3237
+ }
3238
+ return cards.join(`\r
3239
+ `);
3240
+ }
3241
+ async function exportContacts(format, contacts) {
3242
+ switch (format) {
3243
+ case "json":
3244
+ return toJson(contacts);
3245
+ case "csv":
3246
+ return toCsv(contacts);
3247
+ case "vcf":
3248
+ return toVcf(contacts);
3249
+ default:
3250
+ throw new Error(`Unsupported export format: ${format}`);
3251
+ }
3252
+ }
3253
+
3254
+ // src/server/serve.ts
3255
+ var exports_serve = {};
3256
+ __export(exports_serve, {
3257
+ startServer: () => startServer
3258
+ });
3259
+ import { existsSync as existsSync2 } from "fs";
3260
+ import { join as join2 } from "path";
3261
+ function json(data, status = 200) {
3262
+ return new Response(JSON.stringify(data), {
3263
+ status,
3264
+ headers: { "Content-Type": "application/json" }
3265
+ });
3266
+ }
3267
+ function apiError(message, status = 400) {
3268
+ return json({ error: message }, status);
3269
+ }
3270
+ async function parseJson(req) {
3271
+ try {
3272
+ return await req.json();
3273
+ } catch {
3274
+ return null;
3275
+ }
3276
+ }
3277
+ function getSegments(url) {
3278
+ return url.pathname.split("/").filter(Boolean);
3279
+ }
3280
+ async function handleContacts(req, url, segments) {
3281
+ const method = req.method;
3282
+ const id = segments[2];
3283
+ if (method === "GET" && !id) {
3284
+ const q = url.searchParams.get("q");
3285
+ if (q) {
3286
+ const contacts = searchContacts(q);
3287
+ return json(contacts);
3288
+ }
3289
+ const result = listContacts({
3290
+ tag_id: url.searchParams.get("tag_id") ?? url.searchParams.get("tag") ?? undefined,
3291
+ company_id: url.searchParams.get("company_id") ?? undefined,
3292
+ limit: parseInt(url.searchParams.get("limit") ?? "50", 10),
3293
+ offset: parseInt(url.searchParams.get("offset") ?? "0", 10)
3294
+ });
3295
+ return json(result);
3296
+ }
3297
+ if (method === "POST" && !id) {
3298
+ const body = await parseJson(req);
3299
+ if (!body || typeof body !== "object")
3300
+ return apiError("Invalid body");
3301
+ try {
3302
+ const contact = createContact(body);
3303
+ return json(contact, 201);
3304
+ } catch (err) {
3305
+ return apiError(err instanceof Error ? err.message : "Failed to create contact");
3306
+ }
3307
+ }
3308
+ if (method === "GET" && id) {
3309
+ try {
3310
+ const contact = getContact(id);
3311
+ return json(contact);
3312
+ } catch {
3313
+ return apiError("Contact not found", 404);
3314
+ }
3315
+ }
3316
+ if (method === "PATCH" && id) {
3317
+ const body = await parseJson(req);
3318
+ if (!body || typeof body !== "object")
3319
+ return apiError("Invalid body");
3320
+ try {
3321
+ const contact = updateContact(id, body);
3322
+ return json(contact);
3323
+ } catch {
3324
+ return apiError("Contact not found", 404);
3325
+ }
3326
+ }
3327
+ if (method === "DELETE" && id) {
3328
+ try {
3329
+ deleteContact(id);
3330
+ return json({ ok: true });
3331
+ } catch {
3332
+ return apiError("Contact not found", 404);
3333
+ }
3334
+ }
3335
+ return apiError("Method not allowed", 405);
3336
+ }
3337
+ async function handleCompanies(req, url, segments) {
3338
+ const method = req.method;
3339
+ const id = segments[2];
3340
+ if (method === "GET" && !id) {
3341
+ const result = listCompanies({
3342
+ tag_id: url.searchParams.get("tag_id") ?? undefined,
3343
+ industry: url.searchParams.get("industry") ?? undefined,
3344
+ limit: parseInt(url.searchParams.get("limit") ?? "50", 10),
3345
+ offset: parseInt(url.searchParams.get("offset") ?? "0", 10)
3346
+ });
3347
+ return json(result);
3348
+ }
3349
+ if (method === "POST" && !id) {
3350
+ const body = await parseJson(req);
3351
+ if (!body || typeof body !== "object")
3352
+ return apiError("Invalid body");
3353
+ try {
3354
+ const company = createCompany(body);
3355
+ return json(company, 201);
3356
+ } catch (err) {
3357
+ return apiError(err instanceof Error ? err.message : "Failed to create company");
3358
+ }
3359
+ }
3360
+ if (method === "GET" && id) {
3361
+ const company = getCompany(id);
3362
+ if (!company)
3363
+ return apiError("Company not found", 404);
3364
+ return json(company);
3365
+ }
3366
+ if (method === "PATCH" && id) {
3367
+ const body = await parseJson(req);
3368
+ if (!body || typeof body !== "object")
3369
+ return apiError("Invalid body");
3370
+ try {
3371
+ const company = updateCompany(id, body);
3372
+ return json(company);
3373
+ } catch {
3374
+ return apiError("Company not found", 404);
3375
+ }
3376
+ }
3377
+ if (method === "DELETE" && id) {
3378
+ try {
3379
+ deleteCompany(id);
3380
+ return json({ ok: true });
3381
+ } catch {
3382
+ return apiError("Company not found", 404);
3383
+ }
3384
+ }
3385
+ return apiError("Method not allowed", 405);
3386
+ }
3387
+ async function handleTags(req, _url, segments) {
3388
+ const method = req.method;
3389
+ const id = segments[2];
3390
+ if (method === "GET" && !id) {
3391
+ return json(listTags());
3392
+ }
3393
+ if (method === "POST" && !id) {
3394
+ const body = await parseJson(req);
3395
+ if (!body || typeof body !== "object")
3396
+ return apiError("Invalid body");
3397
+ const b = body;
3398
+ if (!b.name)
3399
+ return apiError("name is required");
3400
+ const tag = createTag({ name: b.name, color: b.color, description: b.description });
3401
+ return json(tag, 201);
3402
+ }
3403
+ if (method === "DELETE" && id) {
3404
+ try {
3405
+ deleteTag(id);
3406
+ return json({ ok: true });
3407
+ } catch {
3408
+ return apiError("Tag not found", 404);
3409
+ }
3410
+ }
3411
+ return apiError("Method not allowed", 405);
3412
+ }
3413
+ function handleStats() {
3414
+ const db = getDatabase();
3415
+ const contactCount = db.prepare("SELECT COUNT(*) as count FROM contacts").get().count;
3416
+ const companyCount = db.prepare("SELECT COUNT(*) as count FROM companies").get().count;
3417
+ const tagCount = db.prepare("SELECT COUNT(*) as count FROM tags").get().count;
3418
+ return json({ contacts: contactCount, companies: companyCount, tags: tagCount });
3419
+ }
3420
+ async function handleImport(req) {
3421
+ const body = await parseJson(req);
3422
+ if (!body || typeof body !== "object")
3423
+ return apiError("Invalid body");
3424
+ const { format, data } = body;
3425
+ if (!format || !data)
3426
+ return apiError("format and data are required");
3427
+ if (!["json", "csv", "vcf"].includes(format))
3428
+ return apiError("format must be json, csv, or vcf");
3429
+ try {
3430
+ const inputs = await importContacts(format, data);
3431
+ let importedCount = 0;
3432
+ const errors = [];
3433
+ for (const input of inputs) {
3434
+ try {
3435
+ createContact(input);
3436
+ importedCount++;
3437
+ } catch (err) {
3438
+ errors.push(err instanceof Error ? err.message : String(err));
3439
+ }
3440
+ }
3441
+ return json({ imported: importedCount, errors: errors.length, error_details: errors });
3442
+ } catch (err) {
3443
+ return apiError(err instanceof Error ? err.message : "Import failed");
3444
+ }
3445
+ }
3446
+ async function handleExport(req) {
3447
+ const url = new URL(req.url);
3448
+ const format = url.searchParams.get("format") ?? "json";
3449
+ if (!["json", "csv", "vcf"].includes(format))
3450
+ return apiError("format must be json, csv, or vcf");
3451
+ const { contacts } = listContacts({ limit: 1e5 });
3452
+ const output = await exportContacts(format, contacts);
3453
+ const contentTypes = {
3454
+ json: "application/json",
3455
+ csv: "text/csv",
3456
+ vcf: "text/vcard"
3457
+ };
3458
+ return new Response(output, {
3459
+ headers: {
3460
+ "Content-Type": contentTypes[format] ?? "text/plain",
3461
+ "Content-Disposition": `attachment; filename="contacts.${format}"`
3462
+ }
3463
+ });
3464
+ }
3465
+ function serveStaticFile(filePath) {
3466
+ if (!existsSync2(filePath))
3467
+ return null;
3468
+ return new Response(Bun.file(filePath));
3469
+ }
3470
+ function startServer(port) {
3471
+ Bun.serve({
3472
+ port,
3473
+ async fetch(req) {
3474
+ const url = new URL(req.url);
3475
+ const segments = getSegments(url);
3476
+ const corsHeaders = {
3477
+ "Access-Control-Allow-Origin": "*",
3478
+ "Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
3479
+ "Access-Control-Allow-Headers": "Content-Type"
3480
+ };
3481
+ if (req.method === "OPTIONS") {
3482
+ return new Response(null, { status: 204, headers: corsHeaders });
3483
+ }
3484
+ let response;
3485
+ try {
3486
+ if (segments[0] === "api") {
3487
+ switch (segments[1]) {
3488
+ case "contacts":
3489
+ response = await handleContacts(req, url, segments);
3490
+ break;
3491
+ case "companies":
3492
+ response = await handleCompanies(req, url, segments);
3493
+ break;
3494
+ case "tags":
3495
+ response = await handleTags(req, url, segments);
3496
+ break;
3497
+ case "stats":
3498
+ response = handleStats();
3499
+ break;
3500
+ case "import":
3501
+ response = req.method === "POST" ? await handleImport(req) : apiError("Method not allowed", 405);
3502
+ break;
3503
+ case "export":
3504
+ response = req.method === "GET" ? await handleExport(req) : apiError("Method not allowed", 405);
3505
+ break;
3506
+ default:
3507
+ response = apiError("Not found", 404);
3508
+ }
3509
+ } else {
3510
+ const filePath = join2(DASHBOARD_DIST, url.pathname === "/" ? "index.html" : url.pathname);
3511
+ response = serveStaticFile(filePath) ?? serveStaticFile(join2(DASHBOARD_DIST, "index.html")) ?? new Response("Not Found", { status: 404 });
3512
+ }
3513
+ } catch (err) {
3514
+ console.error("Request error:", err);
3515
+ response = apiError("Internal server error", 500);
3516
+ }
3517
+ const headers = new Headers(response.headers);
3518
+ for (const [k, v] of Object.entries(corsHeaders)) {
3519
+ headers.set(k, v);
3520
+ }
3521
+ return new Response(response.body, { status: response.status, headers });
3522
+ }
3523
+ });
3524
+ console.log(`Contacts server running at http://localhost:${port}`);
3525
+ }
3526
+ var DASHBOARD_DIST;
3527
+ var init_serve = __esm(() => {
3528
+ init_database();
3529
+ init_contacts();
3530
+ init_companies();
3531
+ init_tags();
3532
+ DASHBOARD_DIST = join2(import.meta.dir, "../../dashboard/dist");
3533
+ });
3534
+
3535
+ // node_modules/commander/esm.mjs
3536
+ var import__ = __toESM(require_commander(), 1);
3537
+ var {
3538
+ program,
3539
+ createCommand,
3540
+ createArgument,
3541
+ createOption,
3542
+ CommanderError,
3543
+ InvalidArgumentError,
3544
+ InvalidOptionArgumentError,
3545
+ Command,
3546
+ Argument,
3547
+ Option,
3548
+ Help
3549
+ } = import__.default;
3550
+
3551
+ // src/cli/index.tsx
3552
+ init_contacts();
3553
+ init_companies();
3554
+ init_tags();
3555
+ import chalk from "chalk";
3556
+ import { readFileSync, writeFileSync, existsSync as existsSync3 } from "fs";
3557
+ import { extname } from "path";
3558
+ function renderTable(headers, rows) {
3559
+ const colWidths = headers.map((h) => h.length);
3560
+ for (const row of rows) {
3561
+ headers.forEach((h, i) => {
3562
+ const val = String(row[h] ?? "");
3563
+ if (val.length > (colWidths[i] ?? 0))
3564
+ colWidths[i] = val.length;
3565
+ });
3566
+ }
3567
+ const cappedWidths = colWidths.map((w) => Math.min(w, 40));
3568
+ const topBorder = "\u250C" + cappedWidths.map((w) => "\u2500".repeat(w + 2)).join("\u252C") + "\u2510";
3569
+ const midBorder = "\u253C" + cappedWidths.map((w) => "\u2500".repeat(w + 2)).join("\u253C") + "\u253C";
3570
+ const bottomBorder = "\u2514" + cappedWidths.map((w) => "\u2500".repeat(w + 2)).join("\u2534") + "\u2518";
3571
+ console.log(chalk.gray(topBorder));
3572
+ console.log("\u2502" + headers.map((h, i) => " " + chalk.bold.cyan(h.padEnd(cappedWidths[i] ?? 0)) + " \u2502").join(""));
3573
+ console.log(chalk.gray(midBorder));
3574
+ for (const row of rows) {
3575
+ console.log("\u2502" + headers.map((h, i) => {
3576
+ let val = String(row[h] ?? "");
3577
+ const width = cappedWidths[i] ?? 0;
3578
+ if (val.length > width)
3579
+ val = val.slice(0, width - 1) + "\u2026";
3580
+ return " " + val.padEnd(width) + " \u2502";
3581
+ }).join(""));
3582
+ }
3583
+ console.log(chalk.gray(bottomBorder));
3584
+ }
3585
+ function formatContact(c) {
3586
+ console.log(`
3587
+ ` + chalk.bold.blue("\u2501\u2501\u2501 Contact: ") + chalk.bold(c.display_name) + chalk.bold.blue(" \u2501\u2501\u2501"));
3588
+ console.log();
3589
+ const name = [c.first_name, c.last_name].filter(Boolean).join(" ");
3590
+ if (name)
3591
+ console.log(chalk.gray(" Name: ") + name);
3592
+ if (c.nickname)
3593
+ console.log(chalk.gray(" Nickname: ") + c.nickname);
3594
+ if (c.job_title)
3595
+ console.log(chalk.gray(" Title: ") + c.job_title);
3596
+ if (c.company)
3597
+ console.log(chalk.gray(" Company: ") + chalk.cyan(c.company.name));
3598
+ if (c.birthday)
3599
+ console.log(chalk.gray(" Birthday: ") + c.birthday);
3600
+ if (c.emails?.length) {
3601
+ console.log();
3602
+ console.log(chalk.yellow(" Emails:"));
3603
+ for (const e of c.emails) {
3604
+ const star = e.is_primary ? chalk.green(" \u2605") : "";
3605
+ console.log(` ${chalk.gray(e.type.padEnd(10))} ${e.address}${star}`);
3606
+ }
3607
+ }
3608
+ if (c.phones?.length) {
3609
+ console.log();
3610
+ console.log(chalk.yellow(" Phones:"));
3611
+ for (const p of c.phones) {
3612
+ const star = p.is_primary ? chalk.green(" \u2605") : "";
3613
+ console.log(` ${chalk.gray(p.type.padEnd(10))} ${p.number}${star}`);
3614
+ }
3615
+ }
3616
+ if (c.addresses?.length) {
3617
+ console.log();
3618
+ console.log(chalk.yellow(" Addresses:"));
3619
+ for (const a of c.addresses) {
3620
+ const parts = [a.street, a.city, a.state, a.country].filter(Boolean);
3621
+ console.log(` ${chalk.gray(a.type.padEnd(10))} ${parts.join(", ")}`);
3622
+ }
3623
+ }
3624
+ if (c.social_profiles?.length) {
3625
+ console.log();
3626
+ console.log(chalk.yellow(" Social:"));
3627
+ for (const s of c.social_profiles) {
3628
+ console.log(` ${chalk.gray(s.platform.padEnd(12))} ${s.handle ?? s.url ?? ""}`);
3629
+ }
3630
+ }
3631
+ if (c.tags?.length) {
3632
+ console.log();
3633
+ console.log(chalk.yellow(" Tags: ") + c.tags.map((t) => chalk.magenta(`#${t.name}`)).join(" "));
3634
+ }
3635
+ if (c.notes) {
3636
+ console.log();
3637
+ console.log(chalk.yellow(" Notes:"));
3638
+ for (const line of c.notes.split(`
3639
+ `)) {
3640
+ console.log(" " + chalk.gray(line));
3641
+ }
3642
+ }
3643
+ console.log();
3644
+ console.log(chalk.gray(` ID: ${c.id} \u2022 Created: ${c.created_at.slice(0, 10)}`));
3645
+ console.log();
3646
+ }
3647
+ async function prompt(question) {
3648
+ process.stdout.write(chalk.cyan("? ") + question + " ");
3649
+ return new Promise((resolve2) => {
3650
+ process.stdin.setEncoding("utf8");
3651
+ process.stdin.resume();
3652
+ process.stdin.once("data", (data) => {
3653
+ process.stdin.pause();
3654
+ resolve2(data.toString().trim());
3655
+ });
3656
+ });
3657
+ }
3658
+ async function confirm(question) {
3659
+ const answer = await prompt(question + " [y/N]");
3660
+ return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
3661
+ }
3662
+ program.name("contacts").description("Open Contacts \u2014 contact management for AI coding agents").version("0.1.0");
3663
+ program.command("add").description("Add a new contact interactively").action(async () => {
3664
+ console.log(chalk.bold.blue(`
3665
+ Add New Contact
3666
+ `));
3667
+ const display_name = await prompt("Display name (required):");
3668
+ if (!display_name) {
3669
+ console.error(chalk.red("Display name is required."));
3670
+ process.exit(1);
3671
+ }
3672
+ const first_name = await prompt("First name:");
3673
+ const last_name = await prompt("Last name:");
3674
+ const job_title = await prompt("Job title:");
3675
+ const emailStr = await prompt("Email (e.g. alice@example.com):");
3676
+ const phoneStr = await prompt("Phone (e.g. +15551234):");
3677
+ const notes = await prompt("Notes:");
3678
+ const input = {
3679
+ display_name,
3680
+ first_name: first_name || undefined,
3681
+ last_name: last_name || undefined,
3682
+ job_title: job_title || undefined,
3683
+ notes: notes || undefined,
3684
+ emails: emailStr ? [{ address: emailStr, type: "work", is_primary: true }] : undefined,
3685
+ phones: phoneStr ? [{ number: phoneStr, type: "mobile", is_primary: true }] : undefined
3686
+ };
3687
+ const contact = createContact(input);
3688
+ console.log(chalk.green(`
3689
+ \u2713 Contact created: ${contact.display_name} (${contact.id})
3690
+ `));
3691
+ });
3692
+ program.command("list").description("List contacts").option("--tag <tag_id>", "Filter by tag ID").option("--company <id>", "Filter by company ID").option("--limit <n>", "Max results", "50").action(async (opts) => {
3693
+ const result = listContacts({
3694
+ tag_id: opts.tag,
3695
+ company_id: opts.company,
3696
+ limit: parseInt(opts.limit, 10)
3697
+ });
3698
+ if (result.contacts.length === 0) {
3699
+ console.log(chalk.gray(`
3700
+ No contacts found.
3701
+ `));
3702
+ return;
3703
+ }
3704
+ console.log();
3705
+ const rows = result.contacts.map((c) => ({
3706
+ Name: c.display_name,
3707
+ Company: c.company?.name ?? "",
3708
+ Email: c.emails?.[0]?.address ?? "",
3709
+ Phone: c.phones?.[0]?.number ?? "",
3710
+ Tags: c.tags?.map((t) => `#${t.name}`).join(" ") ?? ""
3711
+ }));
3712
+ renderTable(["Name", "Company", "Email", "Phone", "Tags"], rows);
3713
+ console.log(chalk.gray(`
3714
+ ${result.total} contact(s) total, showing ${result.contacts.length}
3715
+ `));
3716
+ });
3717
+ program.command("show <id>").description("Show full contact details").action((id) => {
3718
+ const contact = getContact(id);
3719
+ formatContact(contact);
3720
+ });
3721
+ program.command("edit <id>").description("Edit a contact interactively").action(async (id) => {
3722
+ const contact = getContact(id);
3723
+ console.log(chalk.bold.blue(`
3724
+ Editing: ${contact.display_name}
3725
+ `));
3726
+ console.log(chalk.gray(`Press Enter to keep the current value.
3727
+ `));
3728
+ const display_name = await prompt(`Display name [${contact.display_name}]:`);
3729
+ const first_name = await prompt(`First name [${contact.first_name}]:`);
3730
+ const last_name = await prompt(`Last name [${contact.last_name}]:`);
3731
+ const job_title = await prompt(`Job title [${contact.job_title ?? ""}]:`);
3732
+ const notes = await prompt(`Notes [${contact.notes ? contact.notes.slice(0, 30) + "..." : ""}]:`);
3733
+ const updates = {};
3734
+ if (display_name)
3735
+ updates.display_name = display_name;
3736
+ if (first_name)
3737
+ updates.first_name = first_name;
3738
+ if (last_name)
3739
+ updates.last_name = last_name;
3740
+ if (job_title)
3741
+ updates.job_title = job_title;
3742
+ if (notes)
3743
+ updates.notes = notes;
3744
+ if (Object.keys(updates).length === 0) {
3745
+ console.log(chalk.gray(`
3746
+ No changes made.
3747
+ `));
3748
+ return;
3749
+ }
3750
+ const updated = updateContact(id, updates);
3751
+ console.log(chalk.green(`
3752
+ \u2713 Contact updated: ${updated.display_name}
3753
+ `));
3754
+ });
3755
+ program.command("delete <id>").description("Delete a contact").option("-f, --force", "Skip confirmation").action(async (id, opts) => {
3756
+ const contact = getContact(id);
3757
+ if (!opts.force) {
3758
+ const ok = await confirm(`Delete ${chalk.bold(contact.display_name)}?`);
3759
+ if (!ok) {
3760
+ console.log(chalk.gray("Cancelled."));
3761
+ return;
3762
+ }
3763
+ }
3764
+ deleteContact(id);
3765
+ console.log(chalk.green(`
3766
+ \u2713 Contact deleted: ${contact.display_name}
3767
+ `));
3768
+ });
3769
+ program.command("search <query>").description("Search contacts").action((query) => {
3770
+ const contacts = searchContacts(query);
3771
+ if (contacts.length === 0) {
3772
+ console.log(chalk.gray(`
3773
+ No contacts found for: "${query}"
3774
+ `));
3775
+ return;
3776
+ }
3777
+ console.log();
3778
+ const rows = contacts.map((c) => ({
3779
+ Name: c.display_name,
3780
+ Company: c.company?.name ?? "",
3781
+ Email: c.emails?.[0]?.address ?? "",
3782
+ Phone: c.phones?.[0]?.number ?? "",
3783
+ Tags: c.tags?.map((t) => `#${t.name}`).join(" ") ?? ""
3784
+ }));
3785
+ renderTable(["Name", "Company", "Email", "Phone", "Tags"], rows);
3786
+ console.log(chalk.gray(`
3787
+ ${contacts.length} result(s) for "${query}"
3788
+ `));
3789
+ });
3790
+ var companiesCmd = program.command("companies").description("Manage companies").action(() => {
3791
+ const result = listCompanies({ limit: 50 });
3792
+ if (result.companies.length === 0) {
3793
+ console.log(chalk.gray(`
3794
+ No companies found.
3795
+ `));
3796
+ return;
3797
+ }
3798
+ console.log();
3799
+ const rows = result.companies.map((c) => ({
3800
+ Name: c.name,
3801
+ Domain: c.domain ?? "",
3802
+ Industry: c.industry ?? "",
3803
+ Size: c.size ?? "",
3804
+ Employees: String(c.employee_count)
3805
+ }));
3806
+ renderTable(["Name", "Domain", "Industry", "Size", "Employees"], rows);
3807
+ console.log(chalk.gray(`
3808
+ ${result.total} company/companies
3809
+ `));
3810
+ });
3811
+ companiesCmd.command("add").description("Add a new company").action(async () => {
3812
+ console.log(chalk.bold.blue(`
3813
+ Add New Company
3814
+ `));
3815
+ const name = await prompt("Company name (required):");
3816
+ if (!name) {
3817
+ console.error(chalk.red("Company name is required."));
3818
+ process.exit(1);
3819
+ }
3820
+ const domain = await prompt("Domain (e.g. acme.com):");
3821
+ const industry = await prompt("Industry:");
3822
+ const size = await prompt("Size (e.g. 1-10, 11-50):");
3823
+ const description = await prompt("Description:");
3824
+ const company = createCompany({
3825
+ name,
3826
+ domain: domain || undefined,
3827
+ industry: industry || undefined,
3828
+ size: size || undefined,
3829
+ description: description || undefined
3830
+ });
3831
+ console.log(chalk.green(`
3832
+ \u2713 Company created: ${company.name} (${company.id})
3833
+ `));
3834
+ });
3835
+ companiesCmd.command("show <id>").description("Show company details").action((id) => {
3836
+ const company = getCompany(id);
3837
+ if (!company) {
3838
+ console.error(chalk.red(`
3839
+ Company not found: ${id}
3840
+ `));
3841
+ process.exit(1);
3842
+ }
3843
+ console.log(`
3844
+ ` + chalk.bold.blue("\u2501\u2501\u2501 Company: ") + chalk.bold(company.name) + chalk.bold.blue(" \u2501\u2501\u2501"));
3845
+ console.log();
3846
+ if (company.domain)
3847
+ console.log(chalk.gray(" Domain: ") + company.domain);
3848
+ if (company.industry)
3849
+ console.log(chalk.gray(" Industry: ") + company.industry);
3850
+ if (company.size)
3851
+ console.log(chalk.gray(" Size: ") + company.size);
3852
+ if (company.description)
3853
+ console.log(chalk.gray(" About: ") + company.description);
3854
+ if (company.founded_year)
3855
+ console.log(chalk.gray(" Founded: ") + company.founded_year);
3856
+ console.log(chalk.gray(` Employees: ${company.employee_count}`));
3857
+ console.log(chalk.gray(`
3858
+ ID: ${company.id}
3859
+ `));
3860
+ });
3861
+ var tagsCmd = program.command("tags").description("Manage tags").action(() => {
3862
+ const tags = listTags();
3863
+ if (tags.length === 0) {
3864
+ console.log(chalk.gray(`
3865
+ No tags found.
3866
+ `));
3867
+ return;
3868
+ }
3869
+ console.log();
3870
+ for (const t of tags) {
3871
+ const swatch = t.color ? chalk.hex(t.color)("\u25A0") + " " : " ";
3872
+ console.log(` ${swatch}${chalk.magenta("#" + t.name)} ${chalk.gray(t.description ?? "")}`);
3873
+ }
3874
+ console.log();
3875
+ });
3876
+ tagsCmd.command("add").description("Create a new tag").action(async () => {
3877
+ console.log(chalk.bold.blue(`
3878
+ Add New Tag
3879
+ `));
3880
+ const name = await prompt("Tag name (required):");
3881
+ if (!name) {
3882
+ console.error(chalk.red("Tag name is required."));
3883
+ process.exit(1);
3884
+ }
3885
+ const color = await prompt("Color (hex, e.g. #FF5733 \u2014 optional):");
3886
+ const description = await prompt("Description (optional):");
3887
+ const tag = createTag({
3888
+ name,
3889
+ color: color || undefined,
3890
+ description: description || undefined
3891
+ });
3892
+ console.log(chalk.green(`
3893
+ \u2713 Tag created: #${tag.name} (${tag.id})
3894
+ `));
3895
+ });
3896
+ program.command("import <file>").description("Import contacts from CSV, vCard (.vcf), or JSON file").action(async (file) => {
3897
+ if (!existsSync3(file)) {
3898
+ console.error(chalk.red(`
3899
+ File not found: ${file}
3900
+ `));
3901
+ process.exit(1);
3902
+ }
3903
+ const ext = extname(file).toLowerCase();
3904
+ const formatMap = {
3905
+ ".csv": "csv",
3906
+ ".vcf": "vcf",
3907
+ ".vcard": "vcf",
3908
+ ".json": "json"
3909
+ };
3910
+ const format = formatMap[ext];
3911
+ if (!format) {
3912
+ console.error(chalk.red(`
3913
+ Unsupported file type: ${ext}. Use .csv, .vcf, or .json
3914
+ `));
3915
+ process.exit(1);
3916
+ }
3917
+ const data = readFileSync(file, "utf8");
3918
+ console.log(chalk.blue(`
3919
+ Importing ${format.toUpperCase()} from ${file}...
3920
+ `));
3921
+ const inputs = await importContacts(format, data);
3922
+ let created = 0;
3923
+ let errors = 0;
3924
+ for (const input of inputs) {
3925
+ try {
3926
+ createContact(input);
3927
+ created++;
3928
+ } catch (err) {
3929
+ errors++;
3930
+ console.log(chalk.red(` \u2717 ${input.display_name ?? "unknown"}: ${err instanceof Error ? err.message : String(err)}`));
3931
+ }
3932
+ }
3933
+ console.log(chalk.green(`
3934
+ \u2713 Imported ${created} contact(s)`) + (errors > 0 ? chalk.red(`, ${errors} error(s)`) : "") + `
3935
+ `);
3936
+ });
3937
+ program.command("export").description("Export contacts").option("--format <fmt>", "Export format: csv, vcf, json", "json").option("--output <file>", "Output file (default: stdout)").action(async (opts) => {
3938
+ const format = opts.format;
3939
+ if (!["csv", "vcf", "json"].includes(format)) {
3940
+ console.error(chalk.red(`
3941
+ Invalid format: ${format}. Use csv, vcf, or json
3942
+ `));
3943
+ process.exit(1);
3944
+ }
3945
+ const { contacts } = listContacts({ limit: 1e5 });
3946
+ const output = await exportContacts(format, contacts);
3947
+ if (opts.output) {
3948
+ writeFileSync(opts.output, output, "utf8");
3949
+ console.log(chalk.green(`
3950
+ \u2713 Exported ${contacts.length} contact(s) to ${opts.output}
3951
+ `));
3952
+ } else {
3953
+ process.stdout.write(output);
3954
+ }
3955
+ });
3956
+ program.command("serve").description("Start the HTTP server").option("--port <n>", "Port to listen on", "19428").action(async (opts) => {
3957
+ const { startServer: startServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
3958
+ const port = parseInt(opts.port, 10);
3959
+ console.log(chalk.blue(`
3960
+ Starting contacts server on port ${port}...
3961
+ `));
3962
+ startServer2(port);
3963
+ });
3964
+ program.command("mcp").description("Print MCP server setup instructions").action(() => {
3965
+ const config = JSON.stringify({ contacts: { command: "contacts-mcp", args: [], env: {} } }, null, 4);
3966
+ console.log(`
3967
+ ${chalk.bold.blue("\u2501\u2501\u2501 Contacts MCP Server Setup \u2501\u2501\u2501")}
3968
+
3969
+ ${chalk.bold("1. Install the package:")}
3970
+ ${chalk.cyan("npm install -g @hasna/contacts")}
3971
+ ${chalk.gray("or:")} ${chalk.cyan("bun add -g @hasna/contacts")}
3972
+
3973
+ ${chalk.bold("2. Add to Claude Code (recommended):")}
3974
+ ${chalk.cyan("claude mcp add --transport stdio --scope user contacts -- contacts-mcp")}
3975
+
3976
+ ${chalk.bold("3. Or add manually to ~/.claude.json:")}
3977
+ ${chalk.yellow(config)}
3978
+
3979
+ ${chalk.bold("4. Restart Claude Code and verify with")} ${chalk.cyan("/mcp")}
3980
+
3981
+ ${chalk.bold("Available tools (24 total):")}
3982
+ ${chalk.gray("Contacts: ")}${chalk.white("create_contact get_contact update_contact delete_contact")}
3983
+ ${chalk.gray(" ")}${chalk.white("list_contacts search_contacts merge_contacts")}
3984
+ ${chalk.gray("Companies:")}${chalk.white("create_company get_company update_company delete_company")}
3985
+ ${chalk.gray(" ")}${chalk.white("list_companies search_companies")}
3986
+ ${chalk.gray("Tags: ")}${chalk.white("create_tag list_tags delete_tag")}
3987
+ ${chalk.gray(" ")}${chalk.white("add_tag_to_contact remove_tag_from_contact")}
3988
+ ${chalk.gray("Rels: ")}${chalk.white("add_relationship list_relationships delete_relationship")}
3989
+ ${chalk.gray("I/O: ")}${chalk.white("import_contacts export_contacts get_stats")}
3990
+ `);
3991
+ });
3992
+ program.parse(process.argv);