statusbar-quick-actions 0.0.10 → 0.0.11

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,905 @@
1
+ #!/usr/bin/env bun
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.SettingsError = exports.TimeoutError = exports.ValidationError = exports.ConfigCLIError = void 0;
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const os_1 = require("os");
11
+ const console_clear_1 = __importDefault(require("console-clear"));
12
+ const package_json_1 = __importDefault(require("../package.json"));
13
+ const CONFIG = {
14
+ banner: "=".repeat(50),
15
+ appName: "Statusbar Quick Actions - Config CLI",
16
+ separator: "\n",
17
+ version: package_json_1.default.version,
18
+ maxRetries: 3,
19
+ timeoutMs: 30000,
20
+ colors: {
21
+ primary: "\x1b[36m",
22
+ success: "\x1b[32m",
23
+ error: "\x1b[31m",
24
+ warning: "\x1b[33m",
25
+ reset: "\x1b[0m",
26
+ bold: "\x1b[1m",
27
+ dim: "\x1b[2m",
28
+ },
29
+ };
30
+ const BUILTIN_PRESETS = {
31
+ "node-dev": {
32
+ name: "Node.js Development",
33
+ description: "Common Node.js development tasks",
34
+ buttons: [
35
+ {
36
+ id: "npm_start",
37
+ text: "▶️ Start",
38
+ tooltip: "Start the application",
39
+ command: { type: "npm", script: "start" },
40
+ enabled: true,
41
+ alignment: "left",
42
+ priority: 100,
43
+ },
44
+ {
45
+ id: "npm_test",
46
+ text: "🧪 Test",
47
+ tooltip: "Run tests",
48
+ command: { type: "npm", script: "test" },
49
+ enabled: true,
50
+ alignment: "left",
51
+ priority: 99,
52
+ },
53
+ {
54
+ id: "npm_build",
55
+ text: "🔨 Build",
56
+ tooltip: "Build the project",
57
+ command: { type: "npm", script: "build" },
58
+ enabled: true,
59
+ alignment: "left",
60
+ priority: 98,
61
+ },
62
+ ],
63
+ metadata: {
64
+ created: new Date(),
65
+ modified: new Date(),
66
+ author: "StatusBar Quick Actions",
67
+ },
68
+ },
69
+ "bun-dev": {
70
+ name: "Bun Development",
71
+ description: "Common Bun development tasks",
72
+ buttons: [
73
+ {
74
+ id: "bun_dev",
75
+ text: "⚡ Dev",
76
+ tooltip: "Start development server",
77
+ command: { type: "bun", script: "dev" },
78
+ enabled: true,
79
+ alignment: "left",
80
+ priority: 100,
81
+ },
82
+ {
83
+ id: "bun_test",
84
+ text: "🧪 Test",
85
+ tooltip: "Run tests with Bun",
86
+ command: { type: "bun", script: "test" },
87
+ enabled: true,
88
+ alignment: "left",
89
+ priority: 99,
90
+ },
91
+ {
92
+ id: "bun_build",
93
+ text: "🔨 Build",
94
+ tooltip: "Build with Bun",
95
+ command: { type: "bun", script: "build" },
96
+ enabled: true,
97
+ alignment: "left",
98
+ priority: 98,
99
+ },
100
+ ],
101
+ metadata: {
102
+ created: new Date(),
103
+ modified: new Date(),
104
+ author: "StatusBar Quick Actions",
105
+ },
106
+ },
107
+ "git-workflow": {
108
+ name: "Git Workflow",
109
+ description: "Common Git operations",
110
+ buttons: [
111
+ {
112
+ id: "git_status",
113
+ text: "📊 Status",
114
+ tooltip: "Show git status",
115
+ command: { type: "shell", command: "git status" },
116
+ enabled: true,
117
+ alignment: "right",
118
+ priority: 100,
119
+ },
120
+ {
121
+ id: "git_pull",
122
+ text: "⬇️ Pull",
123
+ tooltip: "Pull from remote",
124
+ command: { type: "shell", command: "git pull" },
125
+ enabled: true,
126
+ alignment: "right",
127
+ priority: 99,
128
+ },
129
+ {
130
+ id: "git_push",
131
+ text: "⬆️ Push",
132
+ tooltip: "Push to remote",
133
+ command: { type: "shell", command: "git push" },
134
+ enabled: true,
135
+ alignment: "right",
136
+ priority: 98,
137
+ },
138
+ ],
139
+ metadata: {
140
+ created: new Date(),
141
+ modified: new Date(),
142
+ author: "StatusBar Quick Actions",
143
+ },
144
+ },
145
+ };
146
+ const MENU_OPTIONS = [
147
+ {
148
+ key: "1",
149
+ label: "View Current Configuration",
150
+ description: "Display current button configurations",
151
+ action: viewCurrentConfiguration,
152
+ requiresConfirmation: false,
153
+ },
154
+ {
155
+ key: "2",
156
+ label: "Apply Preset",
157
+ description: "Apply a built-in preset configuration",
158
+ action: applyPreset,
159
+ requiresConfirmation: true,
160
+ },
161
+ {
162
+ key: "3",
163
+ label: "Add Button",
164
+ description: "Add a new button to the status bar",
165
+ action: addButton,
166
+ requiresConfirmation: false,
167
+ },
168
+ {
169
+ key: "4",
170
+ label: "Remove Button",
171
+ description: "Remove a button from the status bar",
172
+ action: removeButton,
173
+ requiresConfirmation: true,
174
+ },
175
+ {
176
+ key: "5",
177
+ label: "Toggle Debug Mode",
178
+ description: "Enable or disable debug logging",
179
+ action: toggleDebugMode,
180
+ requiresConfirmation: false,
181
+ },
182
+ {
183
+ key: "6",
184
+ label: "Export Configuration",
185
+ description: "Export current configuration to a file",
186
+ action: exportConfiguration,
187
+ requiresConfirmation: false,
188
+ },
189
+ {
190
+ key: "7",
191
+ label: "Import Configuration",
192
+ description: "Import configuration from a file",
193
+ action: importConfiguration,
194
+ requiresConfirmation: true,
195
+ },
196
+ {
197
+ key: "8",
198
+ label: "Reset to Defaults",
199
+ description: "Reset all settings to default values",
200
+ action: resetToDefaults,
201
+ requiresConfirmation: true,
202
+ },
203
+ {
204
+ key: "9",
205
+ label: "Help",
206
+ description: "Show help and usage information",
207
+ action: showHelp,
208
+ requiresConfirmation: false,
209
+ },
210
+ ];
211
+ class ConfigCLIError extends Error {
212
+ code;
213
+ suggestion;
214
+ constructor(message, code, suggestion) {
215
+ super(message);
216
+ this.code = code;
217
+ this.suggestion = suggestion;
218
+ this.name = "ConfigCLIError";
219
+ }
220
+ }
221
+ exports.ConfigCLIError = ConfigCLIError;
222
+ class ValidationError extends ConfigCLIError {
223
+ constructor(message, suggestion) {
224
+ super(message, "VALIDATION_ERROR", suggestion);
225
+ this.name = "ValidationError";
226
+ }
227
+ }
228
+ exports.ValidationError = ValidationError;
229
+ class TimeoutError extends ConfigCLIError {
230
+ constructor(operation, timeoutMs) {
231
+ super(`${operation} operation timed out after ${timeoutMs}ms`, "TIMEOUT_ERROR");
232
+ this.name = "TimeoutError";
233
+ }
234
+ }
235
+ exports.TimeoutError = TimeoutError;
236
+ class SettingsError extends ConfigCLIError {
237
+ constructor(message, suggestion) {
238
+ super(message, "SETTINGS_ERROR", suggestion);
239
+ this.name = "SettingsError";
240
+ }
241
+ }
242
+ exports.SettingsError = SettingsError;
243
+ class ConsoleUI {
244
+ static clear() {
245
+ (0, console_clear_1.default)(true);
246
+ }
247
+ static printColored(text, color) {
248
+ console.log(`${color}${text}${CONFIG.colors.reset}`);
249
+ }
250
+ static printBanner() {
251
+ const { colors } = CONFIG;
252
+ console.log(`${colors.primary}${CONFIG.banner}${CONFIG.colors.reset}`);
253
+ console.log(`${colors.bold}${colors.primary}${CONFIG.appName} v${CONFIG.version}${CONFIG.colors.reset}`);
254
+ console.log(`${colors.primary}${CONFIG.banner}${CONFIG.colors.reset}`);
255
+ console.log(CONFIG.separator);
256
+ }
257
+ static printMenu() {
258
+ console.log("What would you like to do?", CONFIG.separator);
259
+ for (const option of MENU_OPTIONS) {
260
+ console.log(` ${option.key}. ${option.label} - ${option.description}`);
261
+ }
262
+ console.log(CONFIG.banner);
263
+ console.log("Enter your choice (or 'q' to quit): ");
264
+ }
265
+ static printError(message, suggestion) {
266
+ this.printColored(`❌ ${message}`, CONFIG.colors.error);
267
+ if (suggestion) {
268
+ this.printColored(`💡 ${suggestion}`, CONFIG.colors.warning);
269
+ }
270
+ console.log(CONFIG.separator);
271
+ }
272
+ static printSuccess(message) {
273
+ this.printColored(`✅ ${message}`, CONFIG.colors.success);
274
+ console.log(CONFIG.separator);
275
+ }
276
+ static printWarning(message) {
277
+ this.printColored(`⚠️ ${message}`, CONFIG.colors.warning);
278
+ console.log(CONFIG.separator);
279
+ }
280
+ static printInfo(message) {
281
+ this.printColored(`ℹ️ ${message}`, CONFIG.colors.primary);
282
+ console.log(CONFIG.separator);
283
+ }
284
+ static printDivider(char = "─") {
285
+ console.log(char.repeat(50));
286
+ }
287
+ static printTable(headers, rows) {
288
+ const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] || "").length)));
289
+ const headerRow = headers.map((h, i) => h.padEnd(colWidths[i])).join(" | ");
290
+ console.log(headerRow);
291
+ console.log(colWidths.map((w) => "─".repeat(w)).join("─┼─"));
292
+ rows.forEach((row) => {
293
+ console.log(row.map((cell, i) => (cell || "").padEnd(colWidths[i])).join(" | "));
294
+ });
295
+ }
296
+ }
297
+ class SettingsManager {
298
+ static getUserSettingsPath() {
299
+ const platform = process.platform;
300
+ let settingsDir;
301
+ if (platform === "win32") {
302
+ settingsDir = (0, path_1.join)(process.env.APPDATA || (0, path_1.join)((0, os_1.homedir)(), "AppData", "Roaming"), "Code", "User");
303
+ }
304
+ else if (platform === "darwin") {
305
+ settingsDir = (0, path_1.join)((0, os_1.homedir)(), "Library", "Application Support", "Code", "User");
306
+ }
307
+ else {
308
+ settingsDir = (0, path_1.join)((0, os_1.homedir)(), ".config", "Code", "User");
309
+ }
310
+ return (0, path_1.join)(settingsDir, "settings.json");
311
+ }
312
+ static getWorkspaceSettingsPath() {
313
+ const workspaceRoot = process.cwd();
314
+ return (0, path_1.join)(workspaceRoot, ".vscode", "settings.json");
315
+ }
316
+ static getSettingsPath(location) {
317
+ return location === "user"
318
+ ? this.getUserSettingsPath()
319
+ : this.getWorkspaceSettingsPath();
320
+ }
321
+ static readSettings(location) {
322
+ const settingsPath = this.getSettingsPath(location);
323
+ if (!(0, fs_1.existsSync)(settingsPath)) {
324
+ const settingsDir = (0, path_1.dirname)(settingsPath);
325
+ if (!(0, fs_1.existsSync)(settingsDir)) {
326
+ (0, fs_1.mkdirSync)(settingsDir, { recursive: true });
327
+ }
328
+ (0, fs_1.writeFileSync)(settingsPath, "{}", "utf-8");
329
+ return {};
330
+ }
331
+ try {
332
+ const content = (0, fs_1.readFileSync)(settingsPath, "utf-8");
333
+ const cleanedContent = content
334
+ .replace(/\/\/.*$/gm, "")
335
+ .replace(/\/\*[\s\S]*?\*\//g, "")
336
+ .replace(/,(\s*[}\]])/g, "$1");
337
+ return JSON.parse(cleanedContent);
338
+ }
339
+ catch (error) {
340
+ throw new SettingsError(`Failed to read settings from ${settingsPath}: ${error instanceof Error ? error.message : String(error)}`, "Check if the file is valid JSON");
341
+ }
342
+ }
343
+ static writeSettings(location, settings) {
344
+ const settingsPath = this.getSettingsPath(location);
345
+ const settingsDir = (0, path_1.dirname)(settingsPath);
346
+ if (!(0, fs_1.existsSync)(settingsDir)) {
347
+ (0, fs_1.mkdirSync)(settingsDir, { recursive: true });
348
+ }
349
+ try {
350
+ const content = JSON.stringify(settings, null, 2);
351
+ (0, fs_1.writeFileSync)(settingsPath, content, "utf-8");
352
+ }
353
+ catch (error) {
354
+ throw new SettingsError(`Failed to write settings to ${settingsPath}: ${error instanceof Error ? error.message : String(error)}`, "Check if you have write permissions");
355
+ }
356
+ }
357
+ static getButtons(location) {
358
+ const settings = this.readSettings(location);
359
+ return settings["statusbarQuickActions.buttons"] || [];
360
+ }
361
+ static setButtons(location, buttons) {
362
+ const settings = this.readSettings(location);
363
+ settings["statusbarQuickActions.buttons"] = buttons;
364
+ this.writeSettings(location, settings);
365
+ }
366
+ static getDebugMode(location) {
367
+ const settings = this.readSettings(location);
368
+ return settings["statusbarQuickActions.settings.debug"] || false;
369
+ }
370
+ static setDebugMode(location, enabled) {
371
+ const settings = this.readSettings(location);
372
+ settings["statusbarQuickActions.settings.debug"] = enabled;
373
+ this.writeSettings(location, settings);
374
+ }
375
+ }
376
+ class InputValidator {
377
+ static validateChoice(input) {
378
+ const trimmedInput = input.trim().toLowerCase();
379
+ if (!trimmedInput) {
380
+ return {
381
+ isValid: false,
382
+ error: "Please enter a valid choice",
383
+ suggestion: `Type a number (1-${MENU_OPTIONS.length}) or 'q' to quit`,
384
+ };
385
+ }
386
+ if (this.isQuitCommand(trimmedInput)) {
387
+ return { isValid: false, value: "quit" };
388
+ }
389
+ const option = MENU_OPTIONS.find((opt) => opt.key === trimmedInput);
390
+ if (option) {
391
+ return { isValid: true, value: trimmedInput };
392
+ }
393
+ const suggestions = this.getSuggestions(trimmedInput);
394
+ return {
395
+ isValid: false,
396
+ error: `Invalid choice '${input}'`,
397
+ suggestion: suggestions.length > 0
398
+ ? `Did you mean: ${suggestions.join(", ")}?`
399
+ : `Please select a valid option (1-${MENU_OPTIONS.length}) or 'q' to quit`,
400
+ };
401
+ }
402
+ static isQuitCommand(input) {
403
+ return ["q", "quit", "exit", "x"].includes(input);
404
+ }
405
+ static getSuggestions(input) {
406
+ const suggestions = [];
407
+ if (/^\d+$/.test(input)) {
408
+ const num = parseInt(input, 10);
409
+ if (num > 0 && num <= MENU_OPTIONS.length + 5) {
410
+ suggestions.push(`${num}`);
411
+ }
412
+ }
413
+ for (const option of MENU_OPTIONS) {
414
+ if (option.label.toLowerCase().includes(input) || option.key === input) {
415
+ suggestions.push(option.key);
416
+ }
417
+ }
418
+ return suggestions.slice(0, 3);
419
+ }
420
+ }
421
+ class InputHandler {
422
+ static async getInput(timeoutMs = CONFIG.timeoutMs) {
423
+ try {
424
+ if (process.argv.length > 2) {
425
+ const arg = process.argv[2];
426
+ return arg;
427
+ }
428
+ return await Promise.race([
429
+ new Promise((resolve) => {
430
+ const stdin = process.stdin;
431
+ stdin.setEncoding("utf-8");
432
+ stdin.once("data", (data) => {
433
+ resolve(data.toString().trim());
434
+ });
435
+ }),
436
+ new Promise((_, reject) => {
437
+ setTimeout(() => {
438
+ reject(new TimeoutError("Input", timeoutMs));
439
+ }, timeoutMs);
440
+ }),
441
+ ]);
442
+ }
443
+ catch (error) {
444
+ if (error instanceof TimeoutError) {
445
+ ConsoleUI.printError("Input timeout", "Please try again");
446
+ }
447
+ else {
448
+ ConsoleUI.printError("Failed to read input", "Please check your terminal configuration");
449
+ }
450
+ return null;
451
+ }
452
+ }
453
+ static async getConfirmation(message) {
454
+ console.log(`${message} (y/N): `);
455
+ const input = await this.getInput(10000);
456
+ if (!input) {
457
+ return false;
458
+ }
459
+ return ["y", "yes", "yeah", "yep"].includes(input.toLowerCase());
460
+ }
461
+ static async promptLocation() {
462
+ console.log("\nSelect settings location:");
463
+ console.log(" 1. User settings (global)");
464
+ console.log(" 2. Workspace settings (project-specific)");
465
+ console.log("\nChoice (1 or 2): ");
466
+ const input = await this.getInput(10000);
467
+ if (input === "2") {
468
+ return "workspace";
469
+ }
470
+ return "user";
471
+ }
472
+ }
473
+ async function viewCurrentConfiguration() {
474
+ ConsoleUI.clear();
475
+ ConsoleUI.printBanner();
476
+ const location = await InputHandler.promptLocation();
477
+ const buttons = SettingsManager.getButtons(location);
478
+ const debugMode = SettingsManager.getDebugMode(location);
479
+ ConsoleUI.printDivider();
480
+ console.log(`Configuration (${location} settings)`);
481
+ ConsoleUI.printDivider();
482
+ console.log();
483
+ console.log(`Debug Mode: ${debugMode ? "✅ Enabled" : "❌ Disabled"}`);
484
+ console.log(`Total Buttons: ${buttons.length}`);
485
+ console.log();
486
+ if (buttons.length === 0) {
487
+ ConsoleUI.printWarning("No buttons configured");
488
+ return;
489
+ }
490
+ ConsoleUI.printDivider();
491
+ console.log("Buttons:");
492
+ ConsoleUI.printDivider();
493
+ const rows = buttons.map((btn, idx) => [
494
+ `${idx + 1}`,
495
+ btn.id,
496
+ btn.text,
497
+ btn.command.type,
498
+ btn.enabled === false ? "❌" : "✅",
499
+ ]);
500
+ ConsoleUI.printTable(["#", "ID", "Text", "Type", "Enabled"], rows);
501
+ }
502
+ async function applyPreset() {
503
+ ConsoleUI.clear();
504
+ ConsoleUI.printBanner();
505
+ console.log("Available Presets:");
506
+ ConsoleUI.printDivider();
507
+ const presetKeys = Object.keys(BUILTIN_PRESETS);
508
+ presetKeys.forEach((key, idx) => {
509
+ const preset = BUILTIN_PRESETS[key];
510
+ console.log(` ${idx + 1}. ${preset.name}`);
511
+ console.log(` ${CONFIG.colors.dim}${preset.description}${CONFIG.colors.reset}`);
512
+ console.log(` ${CONFIG.colors.dim}${preset.buttons.length} buttons${CONFIG.colors.reset}`);
513
+ console.log();
514
+ });
515
+ console.log("Select preset (1-" + presetKeys.length + "): ");
516
+ const input = await InputHandler.getInput(10000);
517
+ if (!input) {
518
+ ConsoleUI.printWarning("Operation cancelled");
519
+ return;
520
+ }
521
+ const index = parseInt(input, 10) - 1;
522
+ if (index < 0 || index >= presetKeys.length) {
523
+ ConsoleUI.printError("Invalid selection");
524
+ return;
525
+ }
526
+ const presetKey = presetKeys[index];
527
+ const preset = BUILTIN_PRESETS[presetKey];
528
+ ConsoleUI.printInfo(`Selected: ${preset.name}`);
529
+ const location = await InputHandler.promptLocation();
530
+ const existingButtons = SettingsManager.getButtons(location);
531
+ if (existingButtons.length > 0) {
532
+ console.log("\nMerge mode:");
533
+ console.log(" 1. Replace all (removes existing buttons)");
534
+ console.log(" 2. Append (adds to existing buttons)");
535
+ console.log(" 3. Merge (replaces buttons with same ID)");
536
+ console.log("\nChoice (1-3): ");
537
+ const mergeInput = await InputHandler.getInput(10000);
538
+ let buttons = [];
539
+ switch (mergeInput) {
540
+ case "1":
541
+ buttons = preset.buttons;
542
+ break;
543
+ case "2":
544
+ buttons = [...existingButtons, ...preset.buttons];
545
+ break;
546
+ case "3": {
547
+ const mergedMap = new Map(existingButtons.map((b) => [b.id, b]));
548
+ preset.buttons.forEach((b) => mergedMap.set(b.id, b));
549
+ buttons = Array.from(mergedMap.values());
550
+ break;
551
+ }
552
+ default:
553
+ ConsoleUI.printWarning("Invalid choice, operation cancelled");
554
+ return;
555
+ }
556
+ SettingsManager.setButtons(location, buttons);
557
+ }
558
+ else {
559
+ SettingsManager.setButtons(location, preset.buttons);
560
+ }
561
+ ConsoleUI.printSuccess(`Preset "${preset.name}" applied to ${location} settings`);
562
+ }
563
+ async function addButton() {
564
+ ConsoleUI.clear();
565
+ ConsoleUI.printBanner();
566
+ const location = await InputHandler.promptLocation();
567
+ console.log("Button ID (unique identifier): ");
568
+ const id = await InputHandler.getInput(10000);
569
+ if (!id) {
570
+ ConsoleUI.printWarning("Operation cancelled");
571
+ return;
572
+ }
573
+ console.log("Button text (what shows on status bar): ");
574
+ const text = await InputHandler.getInput(10000);
575
+ if (!text) {
576
+ ConsoleUI.printWarning("Operation cancelled");
577
+ return;
578
+ }
579
+ console.log("Tooltip (hover text): ");
580
+ const tooltip = (await InputHandler.getInput(10000)) || text;
581
+ console.log("\nCommand type:");
582
+ console.log(" 1. npm 2. yarn 3. pnpm 4. bun");
583
+ console.log(" 5. shell 6. vscode 7. task 8. detect");
584
+ console.log("\nChoice (1-8): ");
585
+ const typeInput = await InputHandler.getInput(10000);
586
+ const typeMap = {
587
+ "1": "npm",
588
+ "2": "yarn",
589
+ "3": "pnpm",
590
+ "4": "bun",
591
+ "5": "shell",
592
+ "6": "vscode",
593
+ "7": "task",
594
+ "8": "detect",
595
+ };
596
+ const commandType = typeMap[typeInput || ""] || "shell";
597
+ console.log(`\n${commandType === "shell" || commandType === "vscode" || commandType === "task" ? "Command" : "Script name"}: `);
598
+ const commandValue = await InputHandler.getInput(10000);
599
+ if (!commandValue) {
600
+ ConsoleUI.printWarning("Operation cancelled");
601
+ return;
602
+ }
603
+ const button = {
604
+ id,
605
+ text,
606
+ tooltip,
607
+ command: commandType === "shell" ||
608
+ commandType === "vscode" ||
609
+ commandType === "task"
610
+ ? {
611
+ type: commandType,
612
+ command: commandValue,
613
+ }
614
+ : {
615
+ type: commandType,
616
+ script: commandValue,
617
+ },
618
+ enabled: true,
619
+ alignment: "left",
620
+ priority: 100,
621
+ };
622
+ const buttons = SettingsManager.getButtons(location);
623
+ buttons.push(button);
624
+ SettingsManager.setButtons(location, buttons);
625
+ ConsoleUI.printSuccess(`Button "${text}" added to ${location} settings`);
626
+ }
627
+ async function removeButton() {
628
+ ConsoleUI.clear();
629
+ ConsoleUI.printBanner();
630
+ const location = await InputHandler.promptLocation();
631
+ const buttons = SettingsManager.getButtons(location);
632
+ if (buttons.length === 0) {
633
+ ConsoleUI.printWarning("No buttons to remove");
634
+ return;
635
+ }
636
+ console.log("Select button to remove:");
637
+ ConsoleUI.printDivider();
638
+ buttons.forEach((btn, idx) => {
639
+ console.log(` ${idx + 1}. ${btn.text} (${btn.id})`);
640
+ });
641
+ console.log(`\nChoice (1-${buttons.length}): `);
642
+ const input = await InputHandler.getInput(10000);
643
+ if (!input) {
644
+ ConsoleUI.printWarning("Operation cancelled");
645
+ return;
646
+ }
647
+ const index = parseInt(input, 10) - 1;
648
+ if (index < 0 || index >= buttons.length) {
649
+ ConsoleUI.printError("Invalid selection");
650
+ return;
651
+ }
652
+ const removedButton = buttons[index];
653
+ buttons.splice(index, 1);
654
+ SettingsManager.setButtons(location, buttons);
655
+ ConsoleUI.printSuccess(`Button "${removedButton.text}" removed from ${location} settings`);
656
+ }
657
+ async function toggleDebugMode() {
658
+ ConsoleUI.clear();
659
+ ConsoleUI.printBanner();
660
+ const location = await InputHandler.promptLocation();
661
+ const currentDebugMode = SettingsManager.getDebugMode(location);
662
+ ConsoleUI.printInfo(`Debug mode is currently ${currentDebugMode ? "enabled" : "disabled"}`);
663
+ const confirmed = await InputHandler.getConfirmation(`${currentDebugMode ? "Disable" : "Enable"} debug mode?`);
664
+ if (!confirmed) {
665
+ ConsoleUI.printWarning("Operation cancelled");
666
+ return;
667
+ }
668
+ SettingsManager.setDebugMode(location, !currentDebugMode);
669
+ ConsoleUI.printSuccess(`Debug mode ${!currentDebugMode ? "enabled" : "disabled"} in ${location} settings`);
670
+ }
671
+ async function exportConfiguration() {
672
+ ConsoleUI.clear();
673
+ ConsoleUI.printBanner();
674
+ const location = await InputHandler.promptLocation();
675
+ const buttons = SettingsManager.getButtons(location);
676
+ console.log("Export file path (e.g., config.json): ");
677
+ const filePath = await InputHandler.getInput(10000);
678
+ if (!filePath) {
679
+ ConsoleUI.printWarning("Operation cancelled");
680
+ return;
681
+ }
682
+ const exportData = {
683
+ buttons,
684
+ history: true,
685
+ autoDetect: false,
686
+ settings: {
687
+ debug: SettingsManager.getDebugMode(location),
688
+ },
689
+ };
690
+ try {
691
+ (0, fs_1.writeFileSync)(filePath, JSON.stringify(exportData, null, 2), "utf-8");
692
+ ConsoleUI.printSuccess(`Configuration exported to ${filePath}`);
693
+ }
694
+ catch (error) {
695
+ ConsoleUI.printError(`Failed to export: ${error instanceof Error ? error.message : String(error)}`);
696
+ }
697
+ }
698
+ async function importConfiguration() {
699
+ ConsoleUI.clear();
700
+ ConsoleUI.printBanner();
701
+ console.log("Import file path: ");
702
+ const filePath = await InputHandler.getInput(10000);
703
+ if (!filePath) {
704
+ ConsoleUI.printWarning("Operation cancelled");
705
+ return;
706
+ }
707
+ if (!(0, fs_1.existsSync)(filePath)) {
708
+ ConsoleUI.printError("File not found");
709
+ return;
710
+ }
711
+ try {
712
+ const content = (0, fs_1.readFileSync)(filePath, "utf-8");
713
+ const importedConfig = JSON.parse(content);
714
+ if (!importedConfig.buttons || !Array.isArray(importedConfig.buttons)) {
715
+ ConsoleUI.printError("Invalid configuration file");
716
+ return;
717
+ }
718
+ const location = await InputHandler.promptLocation();
719
+ const existingButtons = SettingsManager.getButtons(location);
720
+ if (existingButtons.length > 0) {
721
+ console.log("\nImport mode:");
722
+ console.log(" 1. Replace all");
723
+ console.log(" 2. Merge");
724
+ console.log("\nChoice (1-2): ");
725
+ const mergeInput = await InputHandler.getInput(10000);
726
+ if (mergeInput === "2") {
727
+ const mergedButtons = [...existingButtons, ...importedConfig.buttons];
728
+ SettingsManager.setButtons(location, mergedButtons);
729
+ }
730
+ else {
731
+ SettingsManager.setButtons(location, importedConfig.buttons);
732
+ }
733
+ }
734
+ else {
735
+ SettingsManager.setButtons(location, importedConfig.buttons);
736
+ }
737
+ ConsoleUI.printSuccess(`Configuration imported to ${location} settings (${importedConfig.buttons.length} buttons)`);
738
+ }
739
+ catch (error) {
740
+ ConsoleUI.printError(`Failed to import: ${error instanceof Error ? error.message : String(error)}`);
741
+ }
742
+ }
743
+ async function resetToDefaults() {
744
+ ConsoleUI.clear();
745
+ ConsoleUI.printBanner();
746
+ const location = await InputHandler.promptLocation();
747
+ const confirmed = await InputHandler.getConfirmation(`Reset ${location} settings to defaults? This will remove all buttons.`);
748
+ if (!confirmed) {
749
+ ConsoleUI.printWarning("Operation cancelled");
750
+ return;
751
+ }
752
+ SettingsManager.setButtons(location, []);
753
+ SettingsManager.setDebugMode(location, false);
754
+ ConsoleUI.printSuccess(`${location} settings reset to defaults`);
755
+ }
756
+ async function showHelp() {
757
+ ConsoleUI.clear();
758
+ ConsoleUI.printBanner();
759
+ ConsoleUI.printDivider();
760
+ console.log("Help & Usage Guide");
761
+ ConsoleUI.printDivider();
762
+ console.log();
763
+ console.log("Available Operations:");
764
+ ConsoleUI.printDivider();
765
+ for (const option of MENU_OPTIONS) {
766
+ console.log(` ${option.key}. ${option.label}`);
767
+ console.log(` ${option.description}`);
768
+ if (option.requiresConfirmation) {
769
+ ConsoleUI.printWarning(" Requires confirmation");
770
+ }
771
+ console.log();
772
+ }
773
+ ConsoleUI.printDivider();
774
+ console.log("Settings Locations:");
775
+ ConsoleUI.printDivider();
776
+ console.log(" • User settings: Global settings for all workspaces");
777
+ console.log(" • Workspace settings: Project-specific settings");
778
+ console.log();
779
+ ConsoleUI.printDivider();
780
+ console.log("Built-in Presets:");
781
+ ConsoleUI.printDivider();
782
+ Object.values(BUILTIN_PRESETS).forEach((preset) => {
783
+ console.log(` • ${preset.name}: ${preset.description}`);
784
+ });
785
+ console.log();
786
+ ConsoleUI.printDivider();
787
+ }
788
+ class ConfigCLI {
789
+ state = {
790
+ isRunning: true,
791
+ operationCount: 0,
792
+ startTime: Date.now(),
793
+ };
794
+ async run() {
795
+ ConsoleUI.clear();
796
+ ConsoleUI.printBanner();
797
+ try {
798
+ while (this.state.isRunning) {
799
+ this.displayMainMenu();
800
+ const input = await InputHandler.getInput();
801
+ if (!input) {
802
+ this.handleQuit("Input timeout");
803
+ break;
804
+ }
805
+ await this.processInput(input);
806
+ }
807
+ }
808
+ catch (error) {
809
+ this.handleError(error);
810
+ this.state.isRunning = false;
811
+ }
812
+ }
813
+ displayMainMenu() {
814
+ ConsoleUI.printMenu();
815
+ }
816
+ async processInput(input) {
817
+ const validation = InputValidator.validateChoice(input);
818
+ if (!validation.isValid) {
819
+ if (validation.value === "quit") {
820
+ this.handleQuit("User requested exit");
821
+ return;
822
+ }
823
+ ConsoleUI.printError(validation.error, validation.suggestion);
824
+ console.log("Press Enter to try again...");
825
+ await InputHandler.getInput(10000);
826
+ ConsoleUI.clear();
827
+ ConsoleUI.printBanner();
828
+ return;
829
+ }
830
+ await this.executeChoice(validation.value);
831
+ if (!["1", "9"].includes(validation.value)) {
832
+ console.log("\nPress Enter to return to menu...");
833
+ await InputHandler.getInput(15000);
834
+ }
835
+ ConsoleUI.clear();
836
+ ConsoleUI.printBanner();
837
+ }
838
+ async executeChoice(choice) {
839
+ const option = MENU_OPTIONS.find((opt) => opt.key === choice);
840
+ if (!option) {
841
+ ConsoleUI.printError("Invalid option selected");
842
+ return;
843
+ }
844
+ if (option.requiresConfirmation) {
845
+ const confirmed = await InputHandler.getConfirmation(`Are you sure you want to ${option.label.toLowerCase()}?`);
846
+ if (!confirmed) {
847
+ ConsoleUI.printWarning(`${option.label} cancelled`);
848
+ return;
849
+ }
850
+ }
851
+ try {
852
+ this.state.operationCount++;
853
+ await option.action();
854
+ }
855
+ catch (error) {
856
+ ConsoleUI.printError(`${option.label} operation failed`, error instanceof ConfigCLIError ? error.suggestion : "Please try again");
857
+ }
858
+ }
859
+ handleQuit(reason) {
860
+ const duration = Date.now() - this.state.startTime;
861
+ const message = reason ? `${reason}. ` : "";
862
+ ConsoleUI.clear();
863
+ ConsoleUI.printBanner();
864
+ ConsoleUI.printSuccess(`${message}Thank you for using Config CLI! Operations: ${this.state.operationCount}, Duration: ${Math.round(duration)}ms`);
865
+ this.state.isRunning = false;
866
+ }
867
+ handleError(error) {
868
+ ConsoleUI.clear();
869
+ ConsoleUI.printBanner();
870
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
871
+ const errorCode = error instanceof ConfigCLIError ? error.code : "UNKNOWN_ERROR";
872
+ ConsoleUI.printError(`An unexpected error occurred: ${errorMessage}`, "Please report this issue with the error code below");
873
+ console.log(`Error Code: ${errorCode}`);
874
+ console.log(`Timestamp: ${new Date().toISOString()}`);
875
+ ConsoleUI.printDivider();
876
+ }
877
+ }
878
+ async function main() {
879
+ try {
880
+ const cli = new ConfigCLI();
881
+ await cli.run();
882
+ }
883
+ catch (error) {
884
+ console.error("Failed to start application:", error);
885
+ process.exit(1);
886
+ }
887
+ }
888
+ process.on("unhandledRejection", (reason, promise) => {
889
+ console.error("Unhandled Rejection at:", promise, "reason:", reason);
890
+ console.error("This might be due to an unhandled async operation");
891
+ process.exit(1);
892
+ });
893
+ process.on("uncaughtException", (error) => {
894
+ console.error("Uncaught Exception:", error);
895
+ console.error("The application encountered a critical error and must exit");
896
+ process.exit(1);
897
+ });
898
+ process.on("SIGINT", () => {
899
+ ConsoleUI.printWarning("Received interrupt signal. Shutting down gracefully...");
900
+ process.exit(0);
901
+ });
902
+ main().catch((error) => {
903
+ console.error("Fatal error during application startup:", error);
904
+ process.exit(1);
905
+ });