@x-all-in-one/coding-helper 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
File without changes
@@ -20,7 +20,7 @@ export interface ClaudeCodeSettingsConfig {
20
20
  ANTHROPIC_DEFAULT_SONNET_MODEL?: string;
21
21
  ANTHROPIC_DEFAULT_OPUS_MODEL?: string;
22
22
  API_TIMEOUT_MS?: string;
23
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC?: number;
23
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC?: string;
24
24
  [key: string]: any;
25
25
  };
26
26
  [key: string]: any;
@@ -154,7 +154,7 @@ export class ClaudeCodeManager {
154
154
  ANTHROPIC_DEFAULT_SONNET_MODEL: config.sonnetModel,
155
155
  ANTHROPIC_DEFAULT_OPUS_MODEL: config.opusModel,
156
156
  API_TIMEOUT_MS: '3000000',
157
- CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1
157
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
158
158
  }
159
159
  };
160
160
  this.saveSettings(newSettings);
@@ -38,7 +38,12 @@ export class ConfigManager {
38
38
  catch (error) {
39
39
  console.warn('Failed to load config, using defaults:', error);
40
40
  }
41
- return { lang: 'en_US' };
41
+ return {
42
+ lang: 'en_US',
43
+ haikuModel: 'Qwen3-Coder-30B-A3B-Instruct',
44
+ sonnetModel: 'MiniMax-M2',
45
+ opusModel: 'Qwen3-Coder-480B-A35B-Instruct'
46
+ };
42
47
  }
43
48
  saveConfig(config) {
44
49
  try {
@@ -30,12 +30,13 @@ export declare class Wizard {
30
30
  */
31
31
  configModels(): Promise<void>;
32
32
  /**
33
- * Step-by-step model selection
33
+ * Step-by-step model selection with back navigation
34
34
  */
35
35
  selectModels(): Promise<void>;
36
36
  selectAndConfigureTool(): Promise<void>;
37
37
  configureTool(toolName: any): Promise<void>;
38
38
  showMainMenu(): Promise<void>;
39
+ openOfficialWebsite(): Promise<void>;
39
40
  showToolMenu(toolName: any): Promise<void>;
40
41
  startTool(toolName: any): Promise<void>;
41
42
  loadModelConfig(toolName: any): Promise<void>;
@@ -1,13 +1,19 @@
1
1
  import inquirer from 'inquirer';
2
2
  import chalk from 'chalk';
3
3
  import ora from 'ora';
4
+ import open from 'open';
5
+ import terminalLink from 'terminal-link';
4
6
  import { configManager } from './config.js';
5
7
  import { toolManager, SUPPORTED_TOOLS } from './tool-manager.js';
6
8
  import { claudeCodeManager, DEFAULT_CONFIG } from './claude-code-manager.js';
7
9
  import { i18n } from './i18n.js';
8
10
  import { createBorderLine, createContentLine } from '../utils/string-width.js';
9
11
  import { execSync } from 'child_process';
12
+ import { existsSync } from 'fs';
10
13
  import { validateApiKey } from './api-validator.js';
14
+ // 常量定义
15
+ const OFFICIAL_WEBSITE = 'https://code.x-aio.com';
16
+ const API_KEYS_URL = 'https://code.x-aio.com/dashboard/keys';
11
17
  export class Wizard {
12
18
  static instance;
13
19
  BOX_WIDTH = 63; // Default box width for UI elements
@@ -51,6 +57,9 @@ export class Wizard {
51
57
  const subtitleLine = createContentLine(subtitle, '║', '║', BANNER_WIDTH, 'center');
52
58
  const emptyLine = createContentLine('', '║', '║', BANNER_WIDTH, 'center');
53
59
  const titleLine = createContentLine('Coding Helper v0.0.1', '║', '║', BANNER_WIDTH, 'center');
60
+ // 官网链接行
61
+ const websiteText = i18n.t('wizard.official_website') + ': ' + OFFICIAL_WEBSITE;
62
+ const websiteLine = createContentLine(websiteText, '║', '║', BANNER_WIDTH, 'center');
54
63
  const asciiLines = [
55
64
  ' █ █ ▄▀▄ █ ▄▀▄ ',
56
65
  ' █ ▄▄ █▀█ █ █ █ ',
@@ -63,6 +72,7 @@ export class Wizard {
63
72
  emptyLine,
64
73
  titleLine,
65
74
  subtitleLine,
75
+ websiteLine,
66
76
  createBorderLine('╚', '╝', '═', BANNER_WIDTH)
67
77
  ];
68
78
  console.log(chalk.cyan.bold('\n' + bannerLines.join('\n')));
@@ -80,10 +90,8 @@ export class Wizard {
80
90
  await this.configLanguage();
81
91
  // Step 2: Input API key
82
92
  await this.configApiKey();
83
- // Step 3: Configure models
84
- await this.configModels();
85
- // Step 4: Select and configure tool
86
- await this.selectAndConfigureTool();
93
+ // Step 3: 进入主菜单(模型配置已移到工具配置中)
94
+ await this.showMainMenu();
87
95
  }
88
96
  async configLanguage() {
89
97
  while (true) {
@@ -121,6 +129,10 @@ export class Wizard {
121
129
  while (true) {
122
130
  this.resetScreen();
123
131
  this.createBox(i18n.t('wizard.config_api_key'));
132
+ // 显示获取 API Key 的引导链接
133
+ const apiKeyLink = terminalLink(i18n.t('wizard.get_api_key_link_text'), API_KEYS_URL, { fallback: () => API_KEYS_URL });
134
+ console.log(chalk.blue(' ' + i18n.t('wizard.get_api_key_hint') + ' ' + apiKeyLink));
135
+ console.log('');
124
136
  const currentConfig = configManager.getConfig();
125
137
  if (currentConfig.api_key) {
126
138
  console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ' ') + chalk.gray(i18n.t('wizard.api_key_set') + ' (' + currentConfig.api_key.slice(0, 4) + '****)'));
@@ -185,8 +197,8 @@ export class Wizard {
185
197
  this.cachedModels = [];
186
198
  spinner.succeed("✅ " + i18n.t('wizard.set_success'));
187
199
  await new Promise(resolve => setTimeout(resolve, 600));
188
- // After setting API key, go to model configuration
189
- await this.configModels();
200
+ // 直接返回,不再跳转到模型选择
201
+ return;
190
202
  }
191
203
  }
192
204
  }
@@ -246,7 +258,7 @@ export class Wizard {
246
258
  }
247
259
  }
248
260
  /**
249
- * Step-by-step model selection
261
+ * Step-by-step model selection with back navigation
250
262
  */
251
263
  async selectModels() {
252
264
  const apiKey = configManager.getApiKey();
@@ -269,76 +281,70 @@ export class Wizard {
269
281
  spinner.succeed(chalk.green(i18n.t('wizard.api_key_valid')));
270
282
  await new Promise(resolve => setTimeout(resolve, 300));
271
283
  const currentConfig = configManager.getConfig();
272
- // Step 1: Select Haiku model
273
- this.resetScreen();
274
- this.createBox(i18n.t('wizard.select_haiku_model'));
275
- const haikuChoices = availableModels.map(model => ({
276
- name: model + (currentConfig.haikuModel === model ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''),
277
- value: model
278
- }));
279
- haikuChoices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
280
- const { haikuModel } = await this.promptWithHints([
281
- {
282
- type: 'list',
283
- name: 'haikuModel',
284
- message: i18n.t('wizard.select_haiku_model'),
285
- choices: haikuChoices,
286
- default: currentConfig.haikuModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_HAIKU_MODEL
284
+ const modelTypes = ['haiku', 'sonnet', 'opus'];
285
+ const titleKeys = [
286
+ 'wizard.select_haiku_model',
287
+ 'wizard.select_sonnet_model',
288
+ 'wizard.select_opus_model'
289
+ ];
290
+ const defaultModels = [
291
+ DEFAULT_CONFIG.ANTHROPIC_DEFAULT_HAIKU_MODEL,
292
+ DEFAULT_CONFIG.ANTHROPIC_DEFAULT_SONNET_MODEL,
293
+ DEFAULT_CONFIG.ANTHROPIC_DEFAULT_OPUS_MODEL
294
+ ];
295
+ const currentModels = [
296
+ currentConfig.haikuModel,
297
+ currentConfig.sonnetModel,
298
+ currentConfig.opusModel
299
+ ];
300
+ // Selected models (initialize with current config)
301
+ const selectedModels = [...currentModels];
302
+ let step = 0;
303
+ while (step >= 0 && step < 3) {
304
+ this.resetScreen();
305
+ this.createBox(i18n.t(titleKeys[step]));
306
+ // Build choices with back option at top
307
+ const choices = [
308
+ { name: '<- ' + i18n.t('wizard.nav_go_back'), value: 'back' },
309
+ new inquirer.Separator()
310
+ ];
311
+ // Add model choices
312
+ availableModels.forEach(model => {
313
+ const isCurrentActive = currentModels[step] === model;
314
+ const isSelected = selectedModels[step] === model;
315
+ let label = model;
316
+ if (isCurrentActive) {
317
+ label += chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')');
318
+ }
319
+ else if (isSelected && !isCurrentActive) {
320
+ label += chalk.cyan(' ✓');
321
+ }
322
+ choices.push({ name: label, value: model });
323
+ });
324
+ try {
325
+ const { model } = await this.promptWithHints([
326
+ {
327
+ type: 'list',
328
+ name: 'model',
329
+ message: i18n.t(titleKeys[step]),
330
+ choices,
331
+ default: selectedModels[step] || defaultModels[step]
332
+ }
333
+ ]);
334
+ if (model === 'back') {
335
+ step--; // Go back to previous step
336
+ }
337
+ else {
338
+ selectedModels[step] = model;
339
+ step++; // Move to next step
340
+ }
287
341
  }
288
- ]);
289
- if (haikuModel === 'exit') {
290
- console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
291
- process.exit(0);
292
- }
293
- else if (haikuModel === 'back') {
294
- return;
295
- }
296
- // Step 2: Select Sonnet model
297
- this.resetScreen();
298
- this.createBox(i18n.t('wizard.select_sonnet_model'));
299
- const sonnetChoices = availableModels.map(model => ({
300
- name: model + (currentConfig.sonnetModel === model ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''),
301
- value: model
302
- }));
303
- sonnetChoices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
304
- const { sonnetModel } = await this.promptWithHints([
305
- {
306
- type: 'list',
307
- name: 'sonnetModel',
308
- message: i18n.t('wizard.select_sonnet_model'),
309
- choices: sonnetChoices,
310
- default: currentConfig.sonnetModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_SONNET_MODEL
342
+ catch {
343
+ return; // User pressed Ctrl+C
311
344
  }
312
- ]);
313
- if (sonnetModel === 'exit') {
314
- console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
315
- process.exit(0);
316
345
  }
317
- else if (sonnetModel === 'back') {
318
- return;
319
- }
320
- // Step 3: Select Opus model
321
- this.resetScreen();
322
- this.createBox(i18n.t('wizard.select_opus_model'));
323
- const opusChoices = availableModels.map(model => ({
324
- name: model + (currentConfig.opusModel === model ? chalk.green(' ✓ (' + i18n.t('wizard.current_active') + ')') : ''),
325
- value: model
326
- }));
327
- opusChoices.push(new inquirer.Separator(), { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }, { name: 'x ' + i18n.t('wizard.nav_exit'), value: 'exit' });
328
- const { opusModel } = await this.promptWithHints([
329
- {
330
- type: 'list',
331
- name: 'opusModel',
332
- message: i18n.t('wizard.select_opus_model'),
333
- choices: opusChoices,
334
- default: currentConfig.opusModel || DEFAULT_CONFIG.ANTHROPIC_DEFAULT_OPUS_MODEL
335
- }
336
- ]);
337
- if (opusModel === 'exit') {
338
- console.log(chalk.green('\n👋 ' + i18n.t('wizard.goodbye_message')));
339
- process.exit(0);
340
- }
341
- else if (opusModel === 'back') {
346
+ // If step < 0, user went back from first step, exit without saving
347
+ if (step < 0) {
342
348
  return;
343
349
  }
344
350
  // Save the configuration
@@ -348,9 +354,9 @@ export class Wizard {
348
354
  }).start();
349
355
  try {
350
356
  configManager.setModels({
351
- haikuModel,
352
- sonnetModel,
353
- opusModel
357
+ haikuModel: selectedModels[0],
358
+ sonnetModel: selectedModels[1],
359
+ opusModel: selectedModels[2]
354
360
  });
355
361
  await new Promise(resolve => setTimeout(resolve, 600));
356
362
  saveSpinner.succeed(chalk.green(i18n.t('wizard.model_config_saved')));
@@ -445,18 +451,15 @@ export class Wizard {
445
451
  this.resetScreen();
446
452
  const currentCfg = configManager.getConfig();
447
453
  this.createBox(i18n.t('wizard.main_menu_title'));
448
- // 显示当前配置状态
454
+ // 显示当前配置状态(仅 API Key)
449
455
  console.log(chalk.gray(' ' + i18n.t('wizard.config_api_key') + ': ') + (currentCfg.api_key ? chalk.gray(i18n.t('wizard.api_key_set') + ' (' + currentCfg.api_key.slice(0, 4) + '****)') : chalk.red(i18n.t('wizard.not_set'))));
450
- console.log(chalk.gray(' ' + i18n.t('wizard.current_haiku_model') + ': ') + (currentCfg.haikuModel ? chalk.green(currentCfg.haikuModel) : chalk.red(i18n.t('wizard.not_set'))));
451
- console.log(chalk.gray(' ' + i18n.t('wizard.current_sonnet_model') + ': ') + (currentCfg.sonnetModel ? chalk.green(currentCfg.sonnetModel) : chalk.red(i18n.t('wizard.not_set'))));
452
- console.log(chalk.gray(' ' + i18n.t('wizard.current_opus_model') + ': ') + (currentCfg.opusModel ? chalk.green(currentCfg.opusModel) : chalk.red(i18n.t('wizard.not_set'))));
453
456
  console.log('');
454
457
  const choices = [
455
458
  { name: '> ' + i18n.t('wizard.menu_config_language'), value: 'lang' },
456
459
  { name: '> ' + i18n.t('wizard.menu_config_api_key'), value: 'apikey' },
457
- { name: '> ' + i18n.t('wizard.menu_config_models'), value: 'models' },
458
460
  { name: '> ' + i18n.t('wizard.menu_config_tool'), value: 'tool' },
459
461
  new inquirer.Separator(),
462
+ { name: '> ' + i18n.t('wizard.menu_open_website'), value: 'website' },
460
463
  { name: 'x ' + i18n.t('wizard.menu_exit'), value: 'exit' }
461
464
  ];
462
465
  const { action } = await this.promptWithHints([
@@ -477,13 +480,32 @@ export class Wizard {
477
480
  else if (action === 'apikey') {
478
481
  await this.configApiKey();
479
482
  }
480
- else if (action === 'models') {
481
- await this.configModels();
482
- }
483
483
  else if (action === 'tool') {
484
484
  await this.selectAndConfigureTool();
485
485
  }
486
+ else if (action === 'website') {
487
+ await this.openOfficialWebsite();
488
+ }
489
+ }
490
+ }
491
+ async openOfficialWebsite() {
492
+ const spinner = ora({
493
+ text: i18n.t('wizard.opening_website'),
494
+ spinner: 'star2'
495
+ }).start();
496
+ try {
497
+ await open(OFFICIAL_WEBSITE);
498
+ spinner.succeed(chalk.green(i18n.t('wizard.website_opened')));
499
+ }
500
+ catch (error) {
501
+ spinner.fail(chalk.red(i18n.t('wizard.website_open_failed')));
502
+ // 显示可点击链接作为备选
503
+ const link = terminalLink(OFFICIAL_WEBSITE, OFFICIAL_WEBSITE, {
504
+ fallback: () => OFFICIAL_WEBSITE
505
+ });
506
+ console.log(chalk.blue(' ' + i18n.t('wizard.click_to_open') + ': ' + link));
486
507
  }
508
+ await new Promise(resolve => setTimeout(resolve, 1500));
487
509
  }
488
510
  async showToolMenu(toolName) {
489
511
  while (true) {
@@ -545,6 +567,8 @@ export class Wizard {
545
567
  }
546
568
  console.log('');
547
569
  const choices = [];
570
+ // 配置模型选项(移到工具菜单)
571
+ choices.push({ name: '> ' + i18n.t('wizard.action_config_models'), value: 'config_models' });
548
572
  choices.push({ name: '> ' + actionText, value: 'load_config' });
549
573
  // 如果已经配置了,显示卸载选项
550
574
  if (detectedConfig) {
@@ -570,6 +594,9 @@ export class Wizard {
570
594
  else if (action === 'back') {
571
595
  return;
572
596
  }
597
+ else if (action === 'config_models') {
598
+ await this.selectModels();
599
+ }
573
600
  else if (action === 'load_config') {
574
601
  await this.loadModelConfig(toolName);
575
602
  }
@@ -586,13 +613,51 @@ export class Wizard {
586
613
  if (!tool) {
587
614
  throw new Error(`Unknown tool: ${toolName}`);
588
615
  }
616
+ this.resetScreen();
617
+ this.createBox(i18n.t('wizard.select_start_directory'));
618
+ // 询问启动目录
619
+ const { startOption } = await this.promptWithHints([
620
+ {
621
+ type: 'list',
622
+ name: 'startOption',
623
+ message: i18n.t('wizard.select_start_directory'),
624
+ choices: [
625
+ { name: '> ' + i18n.t('wizard.start_from_current_dir') + ' (' + process.cwd() + ')', value: 'current' },
626
+ { name: '> ' + i18n.t('wizard.start_from_custom_dir'), value: 'custom' },
627
+ new inquirer.Separator(),
628
+ { name: '<- ' + i18n.t('wizard.nav_return'), value: 'back' }
629
+ ]
630
+ }
631
+ ]);
632
+ if (startOption === 'back') {
633
+ return;
634
+ }
635
+ let workingDir = process.cwd();
636
+ if (startOption === 'custom') {
637
+ const { customDir } = await inquirer.prompt([
638
+ {
639
+ type: 'input',
640
+ name: 'customDir',
641
+ message: i18n.t('wizard.input_custom_directory'),
642
+ validate: (input) => {
643
+ if (!input || input.trim().length === 0) {
644
+ return i18n.t('wizard.directory_required');
645
+ }
646
+ if (!existsSync(input.trim())) {
647
+ return i18n.t('wizard.directory_not_exist');
648
+ }
649
+ return true;
650
+ }
651
+ }
652
+ ]);
653
+ workingDir = customDir.trim();
654
+ }
589
655
  const spinner = ora({
590
656
  text: i18n.t('wizard.starting_tool'),
591
657
  spinner: 'star2'
592
658
  }).start();
593
659
  try {
594
- // 开启新窗口 执行 tool.command
595
- execSync(tool.command, { stdio: 'inherit' });
660
+ execSync(tool.command, { stdio: 'inherit', cwd: workingDir });
596
661
  spinner.succeed(i18n.t('wizard.tool_started'));
597
662
  }
598
663
  catch (error) {
@@ -132,7 +132,24 @@
132
132
  "model_config_saved": "Model configuration saved successfully",
133
133
  "model_config_failed": "Failed to save model configuration",
134
134
  "no_models_available": "No models available. Please configure API Key first.",
135
- "models_config_title": "Models Configuration"
135
+ "models_config_title": "Models Configuration",
136
+ "official_website": "Website",
137
+ "menu_open_website": "Open Official Website",
138
+ "opening_website": "Opening official website...",
139
+ "website_opened": "Official website opened in browser",
140
+ "website_open_failed": "Failed to open browser automatically",
141
+ "click_to_open": "Please click the link to open",
142
+ "get_api_key_hint": "To get API Key, visit:",
143
+ "get_api_key_link_text": "Click here to get API Key",
144
+ "action_config_models": "Configure Models - (Select Haiku/Sonnet/Opus models)",
145
+ "select_start_directory": "Select start directory",
146
+ "start_from_current_dir": "Start from current directory",
147
+ "start_from_custom_dir": "Enter custom directory",
148
+ "input_custom_directory": "Enter working directory path:",
149
+ "directory_required": "Directory path cannot be empty",
150
+ "directory_not_exist": "Directory does not exist",
151
+ "press_ctrl_c_to_exit": "Press Ctrl+C to go back",
152
+ "nav_go_back": "Go back"
136
153
  },
137
154
  "doctor": {
138
155
  "checking": "Running health check...",
@@ -132,7 +132,24 @@
132
132
  "model_config_saved": "模型配置保存成功",
133
133
  "model_config_failed": "模型配置保存失败",
134
134
  "no_models_available": "没有可用的模型,请先配置 API Key。",
135
- "models_config_title": "模型配置"
135
+ "models_config_title": "模型配置",
136
+ "official_website": "官网",
137
+ "menu_open_website": "打开官网",
138
+ "opening_website": "正在打开官网...",
139
+ "website_opened": "官网已在浏览器中打开",
140
+ "website_open_failed": "无法自动打开浏览器",
141
+ "click_to_open": "请点击链接打开",
142
+ "get_api_key_hint": "获取 API Key 请访问:",
143
+ "get_api_key_link_text": "点击这里获取 API Key",
144
+ "action_config_models": "配置模型 - (选择 Haiku/Sonnet/Opus 模型)",
145
+ "select_start_directory": "选择启动目录",
146
+ "start_from_current_dir": "从当前目录启动",
147
+ "start_from_custom_dir": "输入自定义目录",
148
+ "input_custom_directory": "请输入工作目录路径:",
149
+ "directory_required": "目录路径不能为空",
150
+ "directory_not_exist": "目录不存在",
151
+ "press_ctrl_c_to_exit": "按 Ctrl+C 可退出返回",
152
+ "nav_go_back": "返回上一步"
136
153
  },
137
154
  "doctor": {
138
155
  "checking": "正在进行健康检查...",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-all-in-one/coding-helper",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "X All In One Coding Helper",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,17 +23,6 @@
23
23
  "publishConfig": {
24
24
  "access": "public"
25
25
  },
26
- "scripts": {
27
- "build": "tsc --project tsconfig.json && node scripts/postbuild.mjs",
28
- "package": "pnpm run build && pnpm pack",
29
- "dev": "tsx watch src/cli.ts",
30
- "start": "node dist/cli.js",
31
- "clean": "rm -rf dist",
32
- "prepublishOnly": "pnpm run clean && pnpm run build",
33
- "test": "echo \"Error: no test specified\" && exit 1",
34
- "lint": "eslint .",
35
- "lint:fix": "eslint . --fix"
36
- },
37
26
  "keywords": [
38
27
  "cli",
39
28
  "coding",
@@ -41,12 +30,12 @@
41
30
  "tools"
42
31
  ],
43
32
  "author": "X.AIO",
44
- "packageManager": "pnpm@10.15.0",
45
33
  "dependencies": {
46
34
  "chalk": "^5.3.0",
47
35
  "commander": "^12.1.0",
48
36
  "inquirer": "^9.2.12",
49
37
  "js-yaml": "^4.1.0",
38
+ "open": "^11.0.0",
50
39
  "ora": "^8.0.1",
51
40
  "terminal-link": "^5.0.0"
52
41
  },
@@ -62,5 +51,15 @@
62
51
  "tsx": "^4.20.6",
63
52
  "typescript": "^5.9.3",
64
53
  "typescript-eslint": "^8.46.4"
54
+ },
55
+ "scripts": {
56
+ "build": "tsc --project tsconfig.json && node scripts/postbuild.mjs",
57
+ "package": "pnpm run build",
58
+ "dev": "tsx watch src/cli.ts",
59
+ "start": "node dist/cli.js",
60
+ "test": "echo \"Error: no test specified\" && exit 1",
61
+ "lint": "eslint .",
62
+ "lint:fix": "eslint . --fix",
63
+ "release": "pnpm run build && pnpm publish"
65
64
  }
66
- }
65
+ }