@luxkit/cli 1.0.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +49 -208
  2. package/dist/index.js +228 -428
  3. package/package.json +2 -3
package/README.md CHANGED
@@ -4,45 +4,39 @@
4
4
 
5
5
  **One-click project formatting & VSCode config CLI**
6
6
 
7
- [![npm version](https://img.shields.io/badge/version-1.0.0-blue.svg)](https://github.com/TTT1231/lux)
7
+ [![npm version](https://img.shields.io/npm/v/@luxkit/cli.svg)](https://www.npmjs.com/package/@luxkit/cli)
8
8
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18-green.svg)](https://nodejs.org/)
9
9
  [![License: ISC](https://img.shields.io/badge/license-ISC-purple.svg)](https://opensource.org/licenses/ISC)
10
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-6.0-3178C6.svg)](https://www.typescriptlang.org/)
11
11
  [![ESM Only](https://img.shields.io/badge/ESM-only-F7DF1E.svg)](https://nodejs.org/api/esm.html)
12
12
 
13
- <br />
14
-
15
- <a href="#-english">English</a> | <a href="#-中文">中文</a>
13
+ **English** | [中文](./README_Zh.md)
16
14
 
17
15
  </div>
18
16
 
19
17
  ---
20
18
 
21
- <br />
22
-
23
- ## 🇬🇧 English
24
-
25
- <br />
26
-
27
19
  ### What is lux?
28
20
 
29
21
  `lux` is a CLI tool that initializes project formatting configs and VSCode workspace settings with a single command. It generates ESLint, Prettier, Stylelint, CSpell, EditorConfig files and VSCode settings from battle-tested presets — with smart merge and conflict resolution.
30
22
 
31
- <br />
23
+ <div align="center">
24
+ <img src="demo.gif" alt="lux demo" width="640" />
25
+ </div>
32
26
 
33
27
  ### ✨ Key Highlights
34
28
 
35
- | Feature | Description |
36
- |:--------|:------------|
37
- | 🎯 **One Command Setup** | `lux fmt init web` generates all linting & formatting configs instantly |
38
- | 🔧 **5 Fmt Presets** | `web` · `electron` · `uniapp` · `node` · `nest` — each with curated rules |
39
- | 🖥️ **6 VSCode Presets** | `web` · `electron` · `uniapp` · `node` · `nest` · `go` — settings + extensions |
40
- | 🔀 **Smart Merge** | Preset wins for linting keys; user wins for personal preferences |
41
- | 🛡️ **Conflict Resolution** | `neverOverwrite` / `forceOverwrite` lists + `--force` flag |
42
- | 📦 **Auto Install** | Detects bun / pnpm / yarn / npm and installs devDependencies |
43
- | 🔍 **Fuzzy Matching** | Typo a preset name? Levenshtein distance finds the closest match |
44
- | 🧪 **Dry Run** | Preview all changes with `--dry-run` before writing anything |
45
- | 🔗 **Script Injection** | Auto-injects `<pm> lint` / `<pm> format` scripts into package.json |
29
+ | Feature | Description |
30
+ | :------------------------- | :----------------------------------------------------------------------------- |
31
+ | 🎯 **One Command Setup** | `lux fmt web` generates all linting & formatting configs instantly |
32
+ | 🔧 **5 Fmt Presets** | `web` · `electron` · `uniapp` · `node` · `nest` — each with curated rules |
33
+ | 🖥️ **6 VSCode Presets** | `web` · `electron` · `uniapp` · `node` · `nest` · `go` — settings + extensions |
34
+ | 🔀 **Smart Merge** | Preset wins for linting keys; user wins for personal preferences |
35
+ | 🛡️ **Conflict Resolution** | `neverOverwrite` / `forceOverwrite` lists + `--force` flag |
36
+ | 📦 **Auto Install** | Detects bun / pnpm / yarn / npm and installs devDependencies |
37
+ | 🔍 **Fuzzy Matching** | Typo a preset name? Levenshtein distance finds the closest match |
38
+ | 🧪 **Dry Run** | Preview all changes with `--dry-run` before writing anything |
39
+ | 🔗 **Script Injection** | Auto-injects `<pm> lint` / `<pm> format` scripts into package.json |
46
40
 
47
41
  <br />
48
42
 
@@ -50,15 +44,15 @@
50
44
 
51
45
  ```bash
52
46
  # Install globally (pick your package manager)
53
- npm install -g lux
47
+ npm install -g @luxkit/cli
54
48
  # or
55
- bun add -g lux
49
+ bun add -g @luxkit/cli
56
50
 
57
51
  # Initialize formatting configs
58
- lux fmt init web # Generate ESLint, Prettier, Stylelint, CSpell, EditorConfig
52
+ lux fmt web # Generate ESLint, Prettier, Stylelint, CSpell, EditorConfig
59
53
 
60
54
  # Initialize VSCode settings
61
- lux vscode init web # Generate .vscode/settings.json + extensions.json
55
+ lux vscode web # Generate .vscode/settings.json + extensions.json
62
56
 
63
57
  # List available presets
64
58
  lux fmt list
@@ -69,34 +63,34 @@ lux vscode list
69
63
 
70
64
  ### CLI Commands
71
65
 
72
- | Command | Description |
73
- |:--------|:------------|
74
- | `lux fmt init <preset>` | Initialize formatting config files |
75
- | `lux fmt list` | List available fmt presets |
76
- | `lux vscode init <preset>` | Initialize VSCode workspace settings |
77
- | `lux vscode list` | List available VSCode presets |
78
- | `lux vpn cmd` | Copy CMD proxy env-vars to clipboard |
79
- | `lux vpn pw` | Copy PowerShell proxy env-vars to clipboard |
66
+ | Command | Description |
67
+ | :------------------------- | :------------------------------------------ |
68
+ | `lux fmt <preset>` | Initialize formatting config files |
69
+ | `lux fmt list` | List available fmt presets |
70
+ | `lux vscode <preset>` | Initialize VSCode workspace settings |
71
+ | `lux vscode list` | List available VSCode presets |
72
+ | `lux vpn cmd` | Copy CMD proxy env-vars to clipboard |
73
+ | `lux vpn pw` | Copy PowerShell proxy env-vars to clipboard |
80
74
 
81
75
  <br />
82
76
 
83
77
  ### Available Presets
84
78
 
85
- | Preset | Fmt | VSCode | Stack |
86
- |:-------|:---:|:------:|:------|
87
- | `web` | ✅ | | Vue / React / TS / CSS |
88
- | `electron` | ✅ | | Electron + Web stack |
89
- | `uniapp` | ✅ | | UniApp / WeChat Mini Program |
90
- | `node` | ✅ | | Node.js backend |
91
- | `nest` | ✅ | | NestJS backend |
92
- | `go` | | | Go backend |
79
+ | Preset | Fmt | VSCode | Stack |
80
+ | :--------- | :-: | :----: | :--------------------------- |
81
+ | `web` | ✅ | | Vue / React / TS / CSS |
82
+ | `electron` | ✅ | | Electron + Web stack |
83
+ | `uniapp` | ✅ | | UniApp / WeChat Mini Program |
84
+ | `node` | ✅ | | Node.js backend |
85
+ | `nest` | ✅ | | NestJS backend |
86
+ | `go` | | | Go backend |
93
87
 
94
88
  <br />
95
89
 
96
90
  ### Options
97
91
 
98
92
  ```bash
99
- lux fmt init <preset> [options]
93
+ lux fmt <preset> [options]
100
94
 
101
95
  --force Force overwrite existing files
102
96
  --no-install Skip dependency installation
@@ -108,7 +102,7 @@ lux fmt init <preset> [options]
108
102
  ### How It Works
109
103
 
110
104
  ```
111
- lux fmt init web
105
+ lux fmt web
112
106
 
113
107
 
114
108
  Parse CLI args ──► Resolve preset (fuzzy match on typo)
@@ -132,15 +126,15 @@ lux fmt init web
132
126
 
133
127
  ### Tech Stack
134
128
 
135
- | Category | Technology |
136
- |:---------|:-----------|
137
- | Language | TypeScript 6.0 (ESM-only) |
138
- | Runtime | Node.js 18+ |
139
- | Build | tsup |
140
- | Test | Vitest (unit + acceptance) |
141
- | CLI | Commander.js |
142
- | Output | Chalk |
143
- | Bundle | Zero runtime deps (chalk + commander only) |
129
+ | Category | Technology |
130
+ | :------- | :----------------------------------------- |
131
+ | Language | TypeScript 6.0 (ESM-only) |
132
+ | Runtime | Node.js 18+ |
133
+ | Build | tsup |
134
+ | Test | Vitest (unit + acceptance) |
135
+ | CLI | Commander.js |
136
+ | Output | Chalk |
137
+ | Bundle | Zero runtime deps (chalk + commander only) |
144
138
 
145
139
  <br />
146
140
 
@@ -152,7 +146,7 @@ cd lux
152
146
  bun install
153
147
 
154
148
  bun link # Register `lux` globally for testing
155
- lux fmt init web # Test it on any project
149
+ lux fmt web # Test it on any project
156
150
 
157
151
  bun test # Run tests
158
152
  bun build # Build to dist/
@@ -167,157 +161,4 @@ bun code:check:all # lint + format + spell check
167
161
 
168
162
  <br />
169
163
 
170
- <p align="right"><a href="#-中文">切换到中文 →</a></p>
171
-
172
- ---
173
-
174
- <br />
175
-
176
- ## 🇨🇳 中文
177
-
178
- <br />
179
-
180
- ### lux 是什么?
181
-
182
- `lux` 是一个 CLI 工具,只需一条命令即可初始化项目格式化配置和 VSCode 工作区设置。它从经过实战检验的预设中生成 ESLint、Prettier、Stylelint、CSpell、EditorConfig 配置文件以及 VSCode 设置 —— 并带有智能合并和冲突解决机制。
183
-
184
- <br />
185
-
186
- ### ✨ 核心亮点
187
-
188
- | 特性 | 说明 |
189
- |:-----|:-----|
190
- | 🎯 **一键配置** | `lux fmt init web` 即可生成所有 lint 与格式化配置 |
191
- | 🔧 **5 种格式化预设** | `web` · `electron` · `uniapp` · `node` · `nest` — 各配备精选规则 |
192
- | 🖥️ **6 种 VSCode 预设** | `web` · `electron` · `uniapp` · `node` · `nest` · `go` — 设置 + 扩展推荐 |
193
- | 🔀 **智能合并** | 预设优先覆盖 linting 相关键;用户配置优先保留个人偏好 |
194
- | 🛡️ **冲突解决** | `neverOverwrite` / `forceOverwrite` 列表 + `--force` 标志 |
195
- | 📦 **自动安装** | 自动检测 bun / pnpm / yarn / npm 并安装 devDependencies |
196
- | 🔍 **模糊匹配** | 预设名拼错了?Levenshtein 距离帮你找到最接近的匹配 |
197
- | 🧪 **Dry Run** | 使用 `--dry-run` 预览所有变更,不写入任何文件 |
198
- | 🔗 **脚本注入** | 自动将 `<pm> lint` / `<pm> format` 脚本注入 package.json |
199
-
200
- <br />
201
-
202
- ### 快速开始
203
-
204
- ```bash
205
- # 全局安装(选择你的包管理器)
206
- npm install -g lux
207
- # 或
208
- bun add -g lux
209
-
210
- # 初始化格式化配置
211
- lux fmt init web # 生成 ESLint、Prettier、Stylelint、CSpell、EditorConfig
212
-
213
- # 初始化 VSCode 设置
214
- lux vscode init web # 生成 .vscode/settings.json + extensions.json
215
-
216
- # 查看可用预设
217
- lux fmt list
218
- lux vscode list
219
- ```
220
-
221
- <br />
222
-
223
- ### CLI 命令
224
-
225
- | 命令 | 说明 |
226
- |:-----|:-----|
227
- | `lux fmt init <preset>` | 初始化格式化配置文件 |
228
- | `lux fmt list` | 列出可用的格式化预设 |
229
- | `lux vscode init <preset>` | 初始化 VSCode 工作区设置 |
230
- | `lux vscode list` | 列出可用的 VSCode 预设 |
231
- | `lux vpn cmd` | 复制 CMD 代理环境变量到剪贴板 |
232
- | `lux vpn pw` | 复制 PowerShell 代理环境变量到剪贴板 |
233
-
234
- <br />
235
-
236
- ### 可用预设
237
-
238
- | 预设 | 格式化 | VSCode | 技术栈 |
239
- |:-----|:------:|:------:|:-------|
240
- | `web` | ✅ | ✅ | Vue / React / TS / CSS |
241
- | `electron` | ✅ | ✅ | Electron + Web 技术栈 |
242
- | `uniapp` | ✅ | ✅ | UniApp / 微信小程序 |
243
- | `node` | ✅ | ✅ | Node.js 后端 |
244
- | `nest` | ✅ | ✅ | NestJS 后端 |
245
- | `go` | — | ✅ | Go 后端 |
246
-
247
- <br />
248
-
249
- ### 命令选项
250
-
251
- ```bash
252
- lux fmt init <preset> [options]
253
-
254
- --force 强制覆盖已有文件
255
- --no-install 跳过依赖安装
256
- --dry-run 预览模式,不写入文件
257
- ```
258
-
259
- <br />
260
-
261
- ### 工作原理
262
-
263
- ```
264
- lux fmt init web
265
-
266
-
267
- 解析 CLI 参数 ──► 解析预设(拼写错误时自动模糊匹配)
268
-
269
-
270
- 遍历每个配置文件:
271
-
272
- ├── 文件不存在? ──► 创建
273
- ├── 在 neverOverwrite 中? ──► 跳过
274
- ├── 在 forceOverwrite 中? ──► 覆盖
275
- └── 已存在 + --force? ──► 覆盖 / 跳过
276
-
277
-
278
- 注入脚本到 package.json(<pm> → bun run / pnpm run / ...)
279
-
280
-
281
- 自动安装 devDependencies(检测 lockfile 判断包管理器)
282
- ```
283
-
284
- <br />
285
-
286
- ### 技术栈
287
-
288
- | 分类 | 技术 |
289
- |:-----|:-----|
290
- | 语言 | TypeScript 6.0(纯 ESM) |
291
- | 运行时 | Node.js 18+ |
292
- | 构建 | tsup |
293
- | 测试 | Vitest(单元测试 + 验收测试) |
294
- | CLI | Commander.js |
295
- | 输出 | Chalk |
296
- | 依赖 | 零运行时依赖(仅 chalk + commander) |
297
-
298
- <br />
299
-
300
- ### 开发
301
-
302
- ```bash
303
- git clone git@github.com:TTT1231/lux.git
304
- cd lux
305
- bun install
306
-
307
- bun link # 全局注册 `lux` 用于测试
308
- lux fmt init web # 在任意项目上测试
309
-
310
- bun test # 运行测试
311
- bun build # 构建到 dist/
312
- bun code:check:all # lint + 格式化 + 拼写检查
313
- ```
314
-
315
- <br />
316
-
317
- ### 📄 许可证
318
-
319
- [ISC](https://opensource.org/licenses/ISC) — 可自由使用、修改和分发。
320
-
321
- <br />
322
-
323
- <p align="right"><a href="#-english">← Switch to English</a></p>
164
+ <p align="right"><a href="./README_Zh.md">切换到中文 →</a></p>
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  import { program } from "commander";
5
5
 
6
6
  // src/commands/fmt.ts
7
- import path3 from "path";
7
+ import path4 from "path";
8
8
 
9
9
  // src/presets/fmt/electron.ts
10
10
  var electronFmt = {
@@ -39,46 +39,12 @@ export default [
39
39
  null,
40
40
  2
41
41
  ) + "\n",
42
- prettierIgnore: () => `# Dependencies
43
- node_modules/
44
- pnpm-lock.yaml
45
- package-lock.json
46
-
47
- # Build outputs
42
+ prettierIgnore: () => `node_modules/
43
+ <lockfile>
48
44
  dist/
49
45
  release/
50
46
  out/
51
- *.tsbuildinfo
52
-
53
- # Logs
54
- *.log
55
- logs/
56
-
57
- # Environment files
58
- .env
59
- .env.local
60
- .env.*.local
61
-
62
- # IDE files
63
- .vscode/
64
- .idea/
65
- *.swp
66
- *.swo
67
-
68
- # Coverage
69
47
  coverage/
70
-
71
- # Static assets
72
- *.svg
73
- *.png
74
- *.jpg
75
- *.jpeg
76
- *.gif
77
- *.ico
78
- *.woff
79
- *.woff2
80
- *.ttf
81
- *.eot
82
48
  `,
83
49
  stylelint: () => `export default {
84
50
  plugins: ['stylelint-order', '@stylistic/stylelint-plugin'],
@@ -101,33 +67,10 @@ coverage/
101
67
  },
102
68
  }
103
69
  `,
104
- stylelintIgnore: () => `# Dependencies
105
- node_modules/
106
- pnpm-lock.yaml
107
-
108
- # Build outputs
70
+ stylelintIgnore: () => `node_modules/
109
71
  dist/
110
72
  release/
111
73
  out/
112
- *.tsbuildinfo
113
-
114
- # Logs
115
- *.log
116
-
117
- # Environment files
118
- .env
119
- .env.local
120
-
121
- # Coverage
122
- coverage/
123
-
124
- # Static assets
125
- *.svg
126
- *.png
127
- *.jpg
128
- *.jpeg
129
- *.gif
130
- *.ico
131
74
  `,
132
75
  cspell: () => JSON.stringify(
133
76
  {
@@ -135,8 +78,7 @@ coverage/
135
78
  version: "0.2",
136
79
  language: "en,en-US",
137
80
  allowCompoundWords: true,
138
- words: ["vite", "pinia", "vueuse", "unplugin", "trw", "electron", "electron-builder"],
139
- ignorePaths: ["pnpm-lock.yaml", "/coverage/", "*.svg", "*.png"]
81
+ words: ["vite", "pinia", "vueuse", "unplugin", "electron", "electron-builder"]
140
82
  },
141
83
  null,
142
84
  2
@@ -208,42 +150,11 @@ var nestFmt = {
208
150
  null,
209
151
  2
210
152
  ) + "\n",
211
- prettierIgnore: () => `# Dependencies
212
- node_modules/
213
- pnpm-lock.yaml
214
- package-lock.json
215
-
216
- # Build outputs
153
+ prettierIgnore: () => `node_modules/
154
+ <lockfile>
217
155
  dist/
218
156
  build/
219
- *.tsbuildinfo
220
-
221
- # Logs
222
- *.log
223
- logs/
224
-
225
- # Environment files
226
- .env
227
- .env.local
228
- .env.*.local
229
-
230
- # IDE files
231
- .vscode/
232
-
233
- # Coverage
234
157
  coverage/
235
-
236
- # Static assets
237
- *.svg
238
- *.png
239
- *.jpg
240
- *.jpeg
241
- *.gif
242
- *.ico
243
- *.woff
244
- *.woff2
245
- *.ttf
246
- *.eot
247
158
  `,
248
159
  // No stylelint for NestJS
249
160
  cspell: () => JSON.stringify(
@@ -252,8 +163,7 @@ coverage/
252
163
  version: "0.2",
253
164
  language: "en,en-US",
254
165
  allowCompoundWords: true,
255
- words: ["nestjs", "typeorm", "dtos"],
256
- ignorePaths: ["pnpm-lock.yaml", "*.svg", "*.png"]
166
+ words: ["nestjs", "typeorm", "dtos"]
257
167
  },
258
168
  null,
259
169
  2
@@ -295,7 +205,7 @@ import tseslint from 'typescript-eslint'
295
205
 
296
206
  export default defineConfig(
297
207
  {
298
- ignores: ['eslint.config.mjs', 'dist/', 'vitest.config.ts', 'tsup.config.ts'],
208
+ ignores: ['eslint.config.mjs', 'dist/'],
299
209
  },
300
210
  eslint.configs.recommended,
301
211
  ...tseslint.configs.recommended,
@@ -345,42 +255,11 @@ export default defineConfig(
345
255
  null,
346
256
  2
347
257
  ) + "\n",
348
- prettierIgnore: () => `# Dependencies
349
- node_modules/
350
- bun.lock
351
- package-lock.json
352
-
353
- # Build outputs
258
+ prettierIgnore: () => `node_modules/
259
+ <lockfile>
354
260
  dist/
355
261
  build/
356
- *.tsbuildinfo
357
-
358
- # Logs
359
- *.log
360
- logs/
361
-
362
- # Environment files
363
- .env
364
- .env.local
365
- .env.*.local
366
-
367
- # IDE files
368
- .vscode/
369
-
370
- # Coverage
371
262
  coverage/
372
-
373
- # Static assets
374
- *.svg
375
- *.png
376
- *.jpg
377
- *.jpeg
378
- *.gif
379
- *.ico
380
- *.woff
381
- *.woff2
382
- *.ttf
383
- *.eot
384
263
  `,
385
264
  // No stylelint for node projects
386
265
  cspell: () => JSON.stringify(
@@ -389,8 +268,7 @@ coverage/
389
268
  version: "0.2",
390
269
  language: "en,en-US",
391
270
  allowCompoundWords: true,
392
- words: [],
393
- ignorePaths: ["bun.lock", "*.svg", "*.png"]
271
+ words: []
394
272
  },
395
273
  null,
396
274
  2
@@ -466,44 +344,11 @@ export default [
466
344
  null,
467
345
  2
468
346
  ) + "\n",
469
- prettierIgnore: () => `# Dependencies
470
- node_modules/
471
- pnpm-lock.yaml
472
- package-lock.json
473
-
474
- # Build outputs (UniApp \u7F16\u8BD1\u8F93\u51FA)
347
+ prettierIgnore: () => `node_modules/
348
+ <lockfile>
475
349
  dist/
476
350
  unpackage/
477
-
478
- # Logs
479
- *.log
480
- logs/
481
-
482
- # Environment files
483
- .env
484
- .env.local
485
- .env.*.local
486
-
487
- # IDE files
488
- .vscode/
489
- .idea/
490
- *.swp
491
- *.swo
492
-
493
- # Coverage
494
351
  coverage/
495
-
496
- # Static assets
497
- *.svg
498
- *.png
499
- *.jpg
500
- *.jpeg
501
- *.gif
502
- *.ico
503
- *.woff
504
- *.woff2
505
- *.ttf
506
- *.eot
507
352
  `,
508
353
  stylelint: () => `export default {
509
354
  plugins: ['stylelint-order', '@stylistic/stylelint-plugin'],
@@ -526,31 +371,9 @@ coverage/
526
371
  },
527
372
  }
528
373
  `,
529
- stylelintIgnore: () => `# Dependencies
530
- node_modules/
531
- pnpm-lock.yaml
532
-
533
- # Build outputs (UniApp \u7F16\u8BD1\u8F93\u51FA)
374
+ stylelintIgnore: () => `node_modules/
534
375
  dist/
535
376
  unpackage/
536
-
537
- # Logs
538
- *.log
539
-
540
- # Environment files
541
- .env
542
- .env.local
543
-
544
- # Coverage
545
- coverage/
546
-
547
- # Static assets
548
- *.svg
549
- *.png
550
- *.jpg
551
- *.jpeg
552
- *.gif
553
- *.ico
554
377
  `,
555
378
  cspell: () => JSON.stringify(
556
379
  {
@@ -558,8 +381,7 @@ coverage/
558
381
  version: "0.2",
559
382
  language: "en,en-US",
560
383
  allowCompoundWords: true,
561
- words: ["vite", "pinia", "vueuse", "unplugin", "trw", "uniapp"],
562
- ignorePaths: ["pnpm-lock.yaml", "/coverage/", "*.svg", "*.png"]
384
+ words: ["vite", "pinia", "vueuse", "unplugin", "uniapp"]
563
385
  },
564
386
  null,
565
387
  2
@@ -643,44 +465,10 @@ export default [
643
465
  null,
644
466
  2
645
467
  ) + "\n",
646
- prettierIgnore: () => `# Dependencies
647
- node_modules/
648
- pnpm-lock.yaml
649
- package-lock.json
650
-
651
- # Build outputs
468
+ prettierIgnore: () => `node_modules/
469
+ <lockfile>
652
470
  dist/
653
- *.tsbuildinfo
654
-
655
- # Logs
656
- *.log
657
- logs/
658
-
659
- # Environment files
660
- .env
661
- .env.local
662
- .env.*.local
663
-
664
- # IDE files
665
- .vscode/
666
- .idea/
667
- *.swp
668
- *.swo
669
-
670
- # Coverage
671
471
  coverage/
672
-
673
- # Static assets
674
- *.svg
675
- *.png
676
- *.jpg
677
- *.jpeg
678
- *.gif
679
- *.ico
680
- *.woff
681
- *.woff2
682
- *.ttf
683
- *.eot
684
472
  `,
685
473
  stylelint: () => `export default {
686
474
  plugins: ['stylelint-order', '@stylistic/stylelint-plugin'],
@@ -703,31 +491,8 @@ coverage/
703
491
  },
704
492
  }
705
493
  `,
706
- stylelintIgnore: () => `# Dependencies
707
- node_modules/
708
- pnpm-lock.yaml
709
-
710
- # Build outputs
494
+ stylelintIgnore: () => `node_modules/
711
495
  dist/
712
- *.tsbuildinfo
713
-
714
- # Logs
715
- *.log
716
-
717
- # Environment files
718
- .env
719
- .env.local
720
-
721
- # Coverage
722
- coverage/
723
-
724
- # Static assets
725
- *.svg
726
- *.png
727
- *.jpg
728
- *.jpeg
729
- *.gif
730
- *.ico
731
496
  `,
732
497
  cspell: () => JSON.stringify(
733
498
  {
@@ -735,8 +500,7 @@ coverage/
735
500
  version: "0.2",
736
501
  language: "en,en-US",
737
502
  allowCompoundWords: true,
738
- words: ["vite", "pinia", "vueuse", "unplugin", "trw"],
739
- ignorePaths: ["pnpm-lock.yaml", "/coverage/", "*.svg", "*.png"]
503
+ words: ["vite", "pinia", "vueuse", "unplugin"]
740
504
  },
741
505
  null,
742
506
  2
@@ -793,26 +557,17 @@ var FMT_PRESETS = [webFmt, electronFmt, uniappFmt, nodeFmt, nestFmt];
793
557
  // src/utils/logger.ts
794
558
  import chalk from "chalk";
795
559
  var logger = {
796
- info(msg) {
797
- console.log(chalk.blue("\u2139"), msg);
560
+ log(msg) {
561
+ console.log(msg);
798
562
  },
799
563
  success(msg) {
800
- console.log(chalk.green("\u2714"), msg);
564
+ console.log(chalk.green(msg));
801
565
  },
802
566
  warn(msg) {
803
- console.log(chalk.yellow("\u26A0"), msg);
567
+ console.warn(chalk.yellow(msg));
804
568
  },
805
569
  error(msg) {
806
- console.error(chalk.red("\u2716"), msg);
807
- },
808
- skip(file, reason) {
809
- console.log(chalk.yellow("\u2298"), chalk.gray(file), chalk.gray(`(${reason})`));
810
- },
811
- create(file) {
812
- console.log(chalk.green("\u271A"), file);
813
- },
814
- overwrite(file) {
815
- console.log(chalk.cyan("\u21BB"), file);
570
+ console.error(chalk.red(msg));
816
571
  }
817
572
  };
818
573
 
@@ -868,7 +623,7 @@ function resolvePreset(presets, name) {
868
623
  name,
869
624
  presets.map((p) => p.name)
870
625
  );
871
- console.error(chalk2.red("\u2716"), err.message);
626
+ console.error(chalk2.red(err.message));
872
627
  process.exit(1);
873
628
  }
874
629
  return found;
@@ -936,89 +691,46 @@ function generateConfigFile(preset, filename, content, opts) {
936
691
  const filepath = path2.join(opts.cwd, filename);
937
692
  const exists = fileExists(filepath);
938
693
  const action = resolveConflict(filename, exists, preset, opts.force);
939
- if (action === "skip") {
940
- logger.skip(filename, "already exists");
941
- return false;
942
- }
943
- if (opts.dryRun) {
944
- logger.info(`[dry-run] Would ${exists ? "overwrite" : "create"} ${filename}`);
945
- return true;
946
- }
947
- writeFile(filepath, content);
948
- if (exists) {
949
- logger.overwrite(filename);
950
- } else {
951
- logger.create(filename);
952
- }
953
- return true;
694
+ if (action === "skip") return "skipped";
695
+ if (opts.dryRun) return exists ? "overwritten" : "created";
696
+ const resolved = opts.lockfile ? content.replace(/<lockfile>/g, opts.lockfile) : content.replace(/<lockfile>\n?/g, "");
697
+ writeFile(filepath, resolved);
698
+ return exists ? "overwritten" : "created";
954
699
  }
955
700
  function generateAllFmt(preset, opts) {
956
- const generated = [];
701
+ const result = { created: [], overwritten: [], skipped: [] };
957
702
  for (const { filename, getContent } of CONFIG_FILES) {
958
703
  const content = getContent(preset);
959
704
  if (content === void 0) continue;
960
- if (generateConfigFile(preset, filename, content, opts)) {
961
- generated.push(filename);
962
- }
963
- }
964
- return generated;
965
- }
966
-
967
- // src/presets/versions.ts
968
- var DEPS_VERSIONS = {
969
- eslint: "^10.2.0",
970
- "@eslint/js": "^10.0.1",
971
- "typescript-eslint": "^8.58.0",
972
- "eslint-plugin-prettier": "^5.5.5",
973
- "eslint-config-prettier": "^10.1.8",
974
- "@vue/eslint-config-typescript": "^14.7.0",
975
- "@vue/eslint-config-prettier": "^10.2.0",
976
- "eslint-plugin-vue": "^10.8.0",
977
- prettier: "^3.8.1",
978
- stylelint: "^17.6.0",
979
- "stylelint-config-standard-scss": "^17.0.0",
980
- "stylelint-order": "^8.1.1",
981
- "stylelint-scss": "^7.0.0",
982
- "@stylistic/stylelint-plugin": "^5.1.0",
983
- "postcss-html": "^1.8.1",
984
- "postcss-scss": "^4.0.9",
985
- cspell: "^10.0.0"
986
- };
987
- function resolveVersions(packages) {
988
- return packages.map((pkg) => {
989
- const version = DEPS_VERSIONS[pkg];
990
- return version ? `${pkg}@${version}` : pkg;
991
- });
992
- }
993
-
994
- // src/utils/execFileNoThrow.ts
995
- import { exec } from "child_process";
996
- import { promisify } from "util";
997
- var execAsync = promisify(exec);
998
- async function execFileNoThrow(command, args, options) {
999
- const cmdStr = args.length > 0 ? `${command} ${args.join(" ")}` : command;
1000
- try {
1001
- const { stdout, stderr } = await execAsync(cmdStr, {
1002
- cwd: options?.cwd
1003
- });
1004
- return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode: 0 };
1005
- } catch (err) {
1006
- const error = err;
1007
- return {
1008
- stdout: (error.stdout ?? "").trim(),
1009
- stderr: (error.stderr ?? "").trim(),
1010
- exitCode: error.code === "ENOENT" ? null : 1
1011
- };
705
+ const action = generateConfigFile(preset, filename, content, opts);
706
+ if (action === "created") result.created.push(filename);
707
+ else if (action === "overwritten") result.overwritten.push(filename);
708
+ else if (action === "skipped") result.skipped.push(filename);
1012
709
  }
710
+ return result;
1013
711
  }
1014
712
 
1015
713
  // src/utils/deps.ts
714
+ import path3 from "path";
715
+ import { spawn } from "child_process";
1016
716
  function detectPackageManager(cwd) {
1017
717
  if (fileExists(`${cwd}/bun.lockb`) || fileExists(`${cwd}/bun.lock`)) return "bun";
1018
718
  if (fileExists(`${cwd}/pnpm-lock.yaml`)) return "pnpm";
1019
719
  if (fileExists(`${cwd}/yarn.lock`)) return "yarn";
1020
720
  return "npm";
1021
721
  }
722
+ function getLockfileName(pm) {
723
+ switch (pm) {
724
+ case "bun":
725
+ return "bun.lock";
726
+ case "pnpm":
727
+ return "pnpm-lock.yaml";
728
+ case "yarn":
729
+ return "yarn.lock";
730
+ case "npm":
731
+ return "package-lock.json";
732
+ }
733
+ }
1022
734
  function getRunPrefix(pm) {
1023
735
  switch (pm) {
1024
736
  case "bun":
@@ -1031,85 +743,150 @@ function getRunPrefix(pm) {
1031
743
  return "npm run";
1032
744
  }
1033
745
  }
1034
- var INSTALL_CMDS = {
1035
- bun: ["bun", ["add"]],
1036
- pnpm: ["pnpm", ["add"]],
1037
- yarn: ["yarn", ["add"]],
1038
- npm: ["npm", ["install"]]
1039
- };
1040
746
  async function installDevDeps(packages, cwd, pm) {
1041
747
  const manager = pm ?? detectPackageManager(cwd);
1042
- const resolvedPackages = resolveVersions(packages);
1043
- logger.info(`Installing ${resolvedPackages.length} devDependencies via ${manager}...`);
1044
- const [command, subcommand] = INSTALL_CMDS[manager];
1045
- const args = [...subcommand, "-D", ...resolvedPackages];
1046
- const { stderr, exitCode } = await execFileNoThrow(command, args, { cwd });
748
+ const pkg = readJson(path3.join(cwd, "package.json"));
749
+ if (!pkg) {
750
+ throw new Error("package.json not found");
751
+ }
752
+ const devDeps = pkg.devDependencies ?? {};
753
+ const missing = packages.filter((pkg2) => !devDeps[pkg2]);
754
+ if (missing.length === 0) return;
755
+ const addCmd = manager === "npm" ? "npm install -D" : `${manager} add -D`;
756
+ const exitCode = await new Promise((resolve) => {
757
+ const child = spawn(`${addCmd} ${missing.join(" ")}`, {
758
+ cwd,
759
+ shell: true,
760
+ stdio: "inherit"
761
+ });
762
+ const timer = setTimeout(() => {
763
+ child.kill();
764
+ resolve(null);
765
+ }, 12e4);
766
+ child.on("close", (code) => {
767
+ clearTimeout(timer);
768
+ resolve(code);
769
+ });
770
+ child.on("error", () => {
771
+ clearTimeout(timer);
772
+ resolve(1);
773
+ });
774
+ });
775
+ if (exitCode === null) {
776
+ throw new Error("Dependency installation timed out (120s)");
777
+ }
1047
778
  if (exitCode !== 0) {
1048
- logger.error(`Failed to install dependencies: ${stderr}`);
1049
779
  throw new Error(`Dependency installation failed (exit code ${exitCode})`);
1050
780
  }
1051
- logger.success(`Installed ${resolvedPackages.length} packages`);
1052
781
  }
1053
782
 
1054
783
  // src/commands/fmt.ts
1055
784
  function registerFmtCommand(program2) {
1056
- const fmt = program2.command("fmt");
1057
- fmt.command("init <preset>").description("Initialize formatting config with preset").option("-F, --force", "Force overwrite existing files").option("--no-install", "Skip dependency installation").option("--dry-run", "Preview without writing files").action(
785
+ const fmt = program2.command("fmt").description("Initialize formatting config with preset");
786
+ fmt.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--no-install", "Skip dependency installation").option("--dry-run", "Preview without writing files").action(
1058
787
  async (presetName, options) => {
1059
788
  const preset = resolvePreset(FMT_PRESETS, presetName);
1060
789
  if (!preset) return;
1061
790
  const cwd = process.cwd();
791
+ const pm = fileExists(path4.join(cwd, "package.json")) ? detectPackageManager(cwd) : void 0;
1062
792
  const opts = {
1063
793
  cwd,
1064
794
  force: options.force ?? false,
1065
- dryRun: options.dryRun ?? false
795
+ dryRun: options.dryRun ?? false,
796
+ lockfile: pm ? getLockfileName(pm) : void 0
1066
797
  };
1067
- logger.info(`Initializing fmt preset: ${preset.name}`);
1068
- logger.info(`Description: ${preset.description}`);
1069
- const generated = generateAllFmt(preset, opts);
1070
- if (generated.length === 0) {
1071
- logger.warn("No files generated");
798
+ const result = generateAllFmt(preset, opts);
799
+ const allFiles = [...result.created, ...result.overwritten];
800
+ if (allFiles.length === 0 && result.skipped.length === 0) {
801
+ logger.warn("No files to generate for this preset");
802
+ return;
803
+ }
804
+ logGenerationResult(result, opts.dryRun);
805
+ if (!pm) {
806
+ warnMissingPackageJson(preset, options.install !== false);
1072
807
  return;
1073
808
  }
1074
- logger.success(`Generated ${generated.length} config file(s)`);
1075
809
  if (preset.scripts) {
1076
- await injectScripts(preset.scripts, opts);
810
+ await injectScripts(preset.scripts, opts, pm);
1077
811
  }
1078
- if (preset.dependencies?.dev && options.install !== false) {
1079
- if (opts.dryRun) {
1080
- logger.info(`[dry-run] Would install: ${preset.dependencies.dev.join(", ")}`);
1081
- } else {
1082
- try {
1083
- await installDevDeps(preset.dependencies.dev, cwd);
1084
- } catch {
1085
- logger.warn("Dependency installation failed. You can install manually.");
1086
- }
1087
- }
1088
- } else if (preset.dependencies?.dev && options.install === false) {
1089
- logger.info("Dependencies to install:");
1090
- for (const dep of preset.dependencies.dev) {
1091
- console.log(` - ${dep}`);
1092
- }
812
+ if (!preset.dependencies?.dev) return;
813
+ if (options.install === false) {
814
+ logger.log(`Dependencies: ${preset.dependencies.dev.join(", ")}`);
815
+ return;
816
+ }
817
+ if (opts.dryRun) {
818
+ logger.log(`[dry-run] Would install: ${preset.dependencies.dev.join(", ")}`);
819
+ return;
820
+ }
821
+ try {
822
+ logger.log(`Installing dependencies with ${pm}...`);
823
+ await installDevDeps(preset.dependencies.dev, cwd, pm);
824
+ logger.success("Dependencies installed successfully");
825
+ } catch {
826
+ logger.warn("Dependency installation failed. You can install manually.");
1093
827
  }
1094
- logger.success("fmt init complete!");
1095
828
  }
1096
829
  );
1097
830
  fmt.command("list").description("List available fmt presets").action(() => {
1098
- logger.info("Available fmt presets:");
1099
831
  for (const p of FMT_PRESETS) {
1100
- console.log(` ${p.name.padEnd(12)} ${p.description}`);
832
+ console.log(`${p.name.padEnd(12)} ${p.description}`);
1101
833
  }
1102
834
  });
1103
835
  }
1104
- async function injectScripts(scripts, opts) {
1105
- const pkgPath = path3.join(opts.cwd, "package.json");
836
+ function logGenerationResult(result, dryRun) {
837
+ const files = [...result.created, ...result.overwritten];
838
+ if (dryRun) {
839
+ if (files.length > 0) {
840
+ logger.log(`[dry-run] Would create ${files.join(", ")}`);
841
+ }
842
+ if (result.skipped.length > 0) {
843
+ logger.log(`[dry-run] Skipped ${result.skipped.join(", ")} (already exists)`);
844
+ }
845
+ return;
846
+ }
847
+ if (result.created.length > 0) {
848
+ logger.log(
849
+ `Created ${summarizeFiles(result.created)} config ${result.created.length} file${result.created.length > 1 ? "s" : ""}`
850
+ );
851
+ }
852
+ if (result.overwritten.length > 0) {
853
+ logger.log(
854
+ `Overwritten ${summarizeFiles(result.overwritten)} config ${result.overwritten.length} file${result.overwritten.length > 1 ? "s" : ""}`
855
+ );
856
+ }
857
+ if (result.skipped.length > 0) {
858
+ logger.log(
859
+ `Skipped ${result.skipped.length} file${result.skipped.length > 1 ? "s" : ""} (already exists)`
860
+ );
861
+ }
862
+ }
863
+ function warnMissingPackageJson(preset, installEnabled) {
864
+ const tasks = [];
865
+ if (preset.scripts) tasks.push("script injection");
866
+ if (preset.dependencies?.dev && installEnabled) tasks.push("dependency installation");
867
+ if (tasks.length > 0) {
868
+ logger.warn(`package.json not found, skipping ${tasks.join(" and ")}`);
869
+ }
870
+ }
871
+ function summarizeFiles(filenames) {
872
+ const categories = /* @__PURE__ */ new Set();
873
+ for (const name of filenames) {
874
+ if (name.includes("eslint")) categories.add("eslint");
875
+ else if (name.includes("prettier")) categories.add("prettier");
876
+ else if (name.includes("stylelint")) categories.add("stylelint");
877
+ else if (name.includes("cspell")) categories.add("cspell");
878
+ else if (name.includes("editorconfig")) categories.add("editorconfig");
879
+ }
880
+ return [...categories].join(", ");
881
+ }
882
+ async function injectScripts(scripts, opts, pm) {
883
+ const pkgPath = path4.join(opts.cwd, "package.json");
1106
884
  const pkg = readJson(pkgPath);
1107
885
  if (!pkg) {
1108
- logger.warn("package.json not found, skipping scripts injection");
886
+ logger.warn("package.json not found, skipping script injection");
1109
887
  return;
1110
888
  }
1111
889
  const existingScripts = pkg.scripts ?? {};
1112
- const pm = detectPackageManager(opts.cwd);
1113
890
  const prefix = getRunPrefix(pm);
1114
891
  const resolvedScripts = {};
1115
892
  for (const [key, value] of Object.entries(scripts)) {
@@ -1119,12 +896,10 @@ async function injectScripts(scripts, opts) {
1119
896
  let skipped = 0;
1120
897
  for (const [key, value] of Object.entries(resolvedScripts)) {
1121
898
  if (existingScripts[key] !== void 0 && !opts.force) {
1122
- logger.skip(`script:${key}`, "already exists");
1123
899
  skipped++;
1124
900
  continue;
1125
901
  }
1126
902
  if (opts.dryRun) {
1127
- logger.info(`[dry-run] Would add script: ${key}`);
1128
903
  added++;
1129
904
  continue;
1130
905
  }
@@ -1134,7 +909,30 @@ async function injectScripts(scripts, opts) {
1134
909
  if (added > 0 && !opts.dryRun) {
1135
910
  pkg.scripts = existingScripts;
1136
911
  writeJson(pkgPath, pkg);
1137
- logger.success(`Scripts: ${added} added, ${skipped} skipped`);
912
+ logger.log(
913
+ `Added ${added} script${added > 1 ? "s" : ""} to package.json${skipped > 0 ? ` (${skipped} skipped)` : ""}`
914
+ );
915
+ }
916
+ }
917
+
918
+ // src/utils/execFileNoThrow.ts
919
+ import { exec } from "child_process";
920
+ import { promisify } from "util";
921
+ var execAsync = promisify(exec);
922
+ async function execFileNoThrow(command, args, options) {
923
+ const cmdStr = args.length > 0 ? `${command} ${args.join(" ")}` : command;
924
+ try {
925
+ const { stdout, stderr } = await execAsync(cmdStr, {
926
+ cwd: options?.cwd
927
+ });
928
+ return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode: 0 };
929
+ } catch (err) {
930
+ const error = err;
931
+ return {
932
+ stdout: (error.stdout ?? "").trim(),
933
+ stderr: (error.stderr ?? "").trim(),
934
+ exitCode: error.code === "ENOENT" ? null : 1
935
+ };
1138
936
  }
1139
937
  }
1140
938
 
@@ -1187,22 +985,19 @@ async function performUpdate(pm) {
1187
985
  function registerUpdateCommand(program2) {
1188
986
  program2.command("update").description("Update @luxkit/cli to the latest version").option("--check", "Only check for updates without installing").action(async (options) => {
1189
987
  try {
1190
- logger.info("Checking for updates...");
1191
988
  const current = getCurrentVersion();
1192
989
  const latest = await fetchLatestVersion();
1193
990
  if (current === latest) {
1194
- logger.success(`Already up to date (v${current})`);
991
+ logger.log(`Already up to date (v${current})`);
1195
992
  return;
1196
993
  }
1197
994
  if (options.check) {
1198
- logger.info(`New version available: v${latest} (current: v${current})`);
1199
- logger.info(`Run \`lux update\` to update.`);
995
+ logger.log(`Update available: v${current} \u2192 v${latest}`);
1200
996
  return;
1201
997
  }
1202
998
  const pm = detectGlobalPackageManager();
1203
- logger.info(`Updating via ${pm}...`);
1204
999
  await performUpdate(pm);
1205
- logger.success(`Successfully updated to v${latest}`);
1000
+ logger.log(`Updated to v${latest}`);
1206
1001
  } catch (err) {
1207
1002
  const message = err instanceof Error ? err.message : String(err);
1208
1003
  logger.error(message);
@@ -1885,49 +1680,44 @@ function isPlainObject(val) {
1885
1680
  // src/generators/vscode.ts
1886
1681
  function generateVscodeSettings(preset, opts) {
1887
1682
  const settingsPath = `${opts.cwd}/.vscode/settings.json`;
1888
- const presetSettings = preset.settings();
1889
1683
  if (opts.dryRun) {
1890
- logger.info(`[dry-run] Would merge .vscode/settings.json`);
1891
- return true;
1684
+ const existingSettings2 = readJson(settingsPath);
1685
+ return existingSettings2 ? "overwritten" : "created";
1892
1686
  }
1687
+ const presetSettings = preset.settings();
1893
1688
  const existingSettings = readJson(settingsPath);
1894
1689
  if (existingSettings) {
1895
1690
  const backupPath = `${settingsPath}.bak`;
1896
1691
  if (!fileExists(backupPath)) {
1897
1692
  writeFile(backupPath, JSON.stringify(existingSettings, null, 2) + "\n");
1898
- logger.info(`Backed up .vscode/settings.json \u2192 settings.json.bak`);
1693
+ logger.log(`Backed up .vscode/settings.json \u2192 settings.json.bak`);
1899
1694
  }
1900
1695
  const merged = mergeVscodeSettings(presetSettings, existingSettings);
1901
1696
  writeJson(settingsPath, merged);
1902
- logger.overwrite(".vscode/settings.json");
1903
- } else {
1904
- writeJson(settingsPath, presetSettings);
1905
- logger.create(".vscode/settings.json");
1697
+ return "overwritten";
1906
1698
  }
1907
- return true;
1699
+ writeJson(settingsPath, presetSettings);
1700
+ return "created";
1908
1701
  }
1909
1702
  function generateVscodeExtensions(preset, opts) {
1910
- const extensionsPath = `${opts.cwd}/.vscode/extensions.json`;
1911
- const extensions = preset.extensions();
1912
- if (opts.dryRun) {
1913
- logger.info(`[dry-run] Would create .vscode/extensions.json`);
1914
- return true;
1915
- }
1916
- writeJson(extensionsPath, { recommendations: extensions });
1917
- logger.create(".vscode/extensions.json");
1918
- return true;
1703
+ if (opts.dryRun) return "created";
1704
+ writeJson(`${opts.cwd}/.vscode/extensions.json`, { recommendations: preset.extensions() });
1705
+ return "created";
1919
1706
  }
1920
1707
  function generateAllVscode(preset, opts) {
1921
- const generated = [];
1922
- if (generateVscodeSettings(preset, opts)) generated.push(".vscode/settings.json");
1923
- if (generateVscodeExtensions(preset, opts)) generated.push(".vscode/extensions.json");
1924
- return generated;
1708
+ const result = { created: [], overwritten: [], skipped: [] };
1709
+ const settingsAction = generateVscodeSettings(preset, opts);
1710
+ if (settingsAction === "created") result.created.push(".vscode/settings.json");
1711
+ else if (settingsAction === "overwritten") result.overwritten.push(".vscode/settings.json");
1712
+ const extAction = generateVscodeExtensions(preset, opts);
1713
+ if (extAction === "created") result.created.push(".vscode/extensions.json");
1714
+ return result;
1925
1715
  }
1926
1716
 
1927
1717
  // src/commands/vscode.ts
1928
1718
  function registerVscodeCommand(program2) {
1929
- const vscode = program2.command("vscode");
1930
- vscode.command("init <preset>").description("Initialize VSCode config with preset").option("-F, --force", "Force overwrite existing files").option("--dry-run", "Preview without writing files").action(async (presetName, options) => {
1719
+ const vscode = program2.command("vscode").description("Initialize VSCode config with preset");
1720
+ vscode.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--dry-run", "Preview without writing files").action(async (presetName, options) => {
1931
1721
  const preset = resolvePreset(VSCODE_PRESETS, presetName);
1932
1722
  if (!preset) return;
1933
1723
  const cwd = process.cwd();
@@ -1936,20 +1726,21 @@ function registerVscodeCommand(program2) {
1936
1726
  force: options.force ?? false,
1937
1727
  dryRun: options.dryRun ?? false
1938
1728
  };
1939
- logger.info(`Initializing vscode preset: ${preset.name}`);
1940
- logger.info(`Description: ${preset.description}`);
1941
- const generated = generateAllVscode(preset, opts);
1942
- if (generated.length === 0) {
1729
+ const result = generateAllVscode(preset, opts);
1730
+ const files = [...result.created, ...result.overwritten];
1731
+ if (files.length === 0) {
1943
1732
  logger.warn("No files generated");
1944
1733
  return;
1945
1734
  }
1946
- logger.success(`Generated ${generated.length} config file(s)`);
1947
- logger.success("vscode init complete!");
1735
+ if (opts.dryRun) {
1736
+ logger.log(`[dry-run] Would create ${files.join(", ")}`);
1737
+ return;
1738
+ }
1739
+ logger.log(`Created ${files.join(", ")}`);
1948
1740
  });
1949
1741
  vscode.command("list").description("List available vscode presets").action(() => {
1950
- logger.info("Available vscode presets:");
1951
1742
  for (const p of VSCODE_PRESETS) {
1952
- console.log(` ${p.name.padEnd(12)} ${p.description}`);
1743
+ console.log(`${p.name.padEnd(12)} ${p.description}`);
1953
1744
  }
1954
1745
  });
1955
1746
  }
@@ -1965,6 +1756,13 @@ function buildCommands(shell, httpProxy, socksProxy) {
1965
1756
  `set all_proxy=${socksProxy}`
1966
1757
  ].join("\r\n");
1967
1758
  }
1759
+ if (shell === "bash") {
1760
+ return [
1761
+ `export https_proxy=${httpProxy}`,
1762
+ `export http_proxy=${httpProxy}`,
1763
+ `export all_proxy=${socksProxy}`
1764
+ ].join("\n");
1765
+ }
1968
1766
  return [
1969
1767
  `$env:https_proxy="${httpProxy}"`,
1970
1768
  `$env:http_proxy="${httpProxy}"`,
@@ -1984,15 +1782,16 @@ function parseProxy(proxy) {
1984
1782
  }
1985
1783
  var SHELL_LABELS = {
1986
1784
  cmd: "CMD",
1987
- pw: "PowerShell"
1785
+ pw: "PowerShell",
1786
+ bash: "Bash"
1988
1787
  };
1989
1788
  function handleCopy(shell, proxy) {
1990
1789
  const { httpProxy, socksProxy } = parseProxy(proxy);
1991
1790
  const commands = buildCommands(shell, httpProxy, socksProxy);
1992
1791
  if (copyToClipboard(commands)) {
1993
- logger.success(`\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F\uFF0CCtrl+V \u7C98\u8D34\u5230 ${SHELL_LABELS[shell]} \u5373\u53EF`);
1792
+ logger.log(`Copied to clipboard \u2014 paste in ${SHELL_LABELS[shell]}`);
1994
1793
  } else {
1995
- logger.error("\u590D\u5236\u5230\u526A\u8D34\u677F\u5931\u8D25");
1794
+ logger.error("Failed to copy to clipboard");
1996
1795
  console.log(commands);
1997
1796
  }
1998
1797
  }
@@ -2000,6 +1799,7 @@ function registerVpnCommand(program2) {
2000
1799
  const vpn = program2.command("vpn");
2001
1800
  vpn.command("cmd").description("Copy CMD proxy commands to clipboard").option("--proxy <addr>", "Proxy address", DEFAULT_PROXY).action((options) => handleCopy("cmd", options.proxy));
2002
1801
  vpn.command("pw").description("Copy PowerShell proxy commands to clipboard").option("--proxy <addr>", "Proxy address", DEFAULT_PROXY).action((options) => handleCopy("pw", options.proxy));
1802
+ vpn.command("bash").description("Copy Bash proxy commands to clipboard").option("--proxy <addr>", "Proxy address", DEFAULT_PROXY).action((options) => handleCopy("bash", options.proxy));
2003
1803
  }
2004
1804
 
2005
1805
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luxkit/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "One-click project formatting & VSCode config CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,8 +28,7 @@
28
28
  "prepublishOnly": "cross-env NODE_ENV=production bun run build",
29
29
  "test": "vitest run",
30
30
  "test:watch": "vitest",
31
- "test:coverage": "vitest run --coverage",
32
- "bump:deps": "bunx tsx scripts/bump-deps.ts"
31
+ "test:coverage": "vitest run --coverage"
33
32
  },
34
33
  "keywords": [],
35
34
  "author": "",