@luxkit/cli 1.1.0 → 1.1.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/README.md +31 -76
- package/dist/index.js +842 -384
- package/dist/skills/lux/references/custom-preset-setting.md +67 -0
- package/dist/skills/lux/skill.md +12 -2
- package/package.json +3 -10
package/dist/index.js
CHANGED
|
@@ -4,20 +4,21 @@
|
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/fmt.ts
|
|
7
|
-
import
|
|
7
|
+
import fs3 from "fs";
|
|
8
|
+
import path5 from "path";
|
|
8
9
|
|
|
9
10
|
// src/presets/fmt/electron-vue.ts
|
|
10
11
|
var electronVueFmt = {
|
|
11
12
|
name: "electron-vue",
|
|
12
13
|
description: "Vue 3 + Electron desktop app",
|
|
13
14
|
eslint: () => `import withVue from '@vue/eslint-config-typescript'
|
|
14
|
-
import
|
|
15
|
+
import prettierConfig from '@vue/eslint-config-prettier/skip-formatting'
|
|
15
16
|
import pluginVue from 'eslint-plugin-vue'
|
|
16
17
|
|
|
17
18
|
export default [
|
|
18
19
|
...pluginVue.configs['flat/recommended'],
|
|
19
20
|
...withVue(),
|
|
20
|
-
|
|
21
|
+
prettierConfig,
|
|
21
22
|
{
|
|
22
23
|
rules: {
|
|
23
24
|
'vue/multi-word-component-names': 'off',
|
|
@@ -78,7 +79,8 @@ out/
|
|
|
78
79
|
version: "0.2",
|
|
79
80
|
language: "en,en-US",
|
|
80
81
|
allowCompoundWords: true,
|
|
81
|
-
words: ["vite", "pinia", "vueuse", "unplugin", "electron", "electron-builder"]
|
|
82
|
+
words: ["vite", "pinia", "vueuse", "unplugin", "electron", "electron-builder"],
|
|
83
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
82
84
|
},
|
|
83
85
|
null,
|
|
84
86
|
2
|
|
@@ -114,18 +116,9 @@ trim_trailing_whitespace = false
|
|
|
114
116
|
]
|
|
115
117
|
},
|
|
116
118
|
scripts: {
|
|
117
|
-
lint:
|
|
118
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
119
|
-
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
120
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,vue,css,scss}"',
|
|
121
|
-
stylelint: 'stylelint "src/**/*.{css,scss,vue}"',
|
|
122
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss,vue}" --fix',
|
|
123
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
124
|
-
"type:check": "vue-tsc --noEmit",
|
|
125
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
126
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
127
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
128
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
119
|
+
lint: 'eslint . --cache --cache-location node_modules/.cache/eslint && cspell --cache --cache-location node_modules/.cache/cspell --gitignore "src/**/*" && vue-tsc --noEmit && stylelint "src/**/*.{css,scss,vue}" --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
120
|
+
"lint:fix": 'eslint . --cache --cache-location node_modules/.cache/eslint --fix && stylelint "src/**/*.{css,scss,vue}" --fix --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
121
|
+
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
129
122
|
}
|
|
130
123
|
};
|
|
131
124
|
|
|
@@ -162,7 +155,8 @@ coverage/
|
|
|
162
155
|
version: "0.2",
|
|
163
156
|
language: "en,en-US",
|
|
164
157
|
allowCompoundWords: true,
|
|
165
|
-
words: ["nestjs", "typeorm", "dtos"]
|
|
158
|
+
words: ["nestjs", "typeorm", "dtos"],
|
|
159
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
166
160
|
},
|
|
167
161
|
null,
|
|
168
162
|
2
|
|
@@ -183,12 +177,10 @@ trim_trailing_whitespace = false
|
|
|
183
177
|
dependencies: {
|
|
184
178
|
dev: ["prettier", "cspell"]
|
|
185
179
|
},
|
|
186
|
-
// NestJS: only append new scripts, don't conflict with existing ones
|
|
187
180
|
scripts: {
|
|
188
|
-
|
|
189
|
-
"
|
|
190
|
-
|
|
191
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> cspell"
|
|
181
|
+
lint: 'eslint "{src,apps,libs,test}/**/*.ts" --cache --cache-location node_modules/.cache/eslint && cspell --cache --cache-location node_modules/.cache/cspell --gitignore "src/**/*" && tsc --noEmit',
|
|
182
|
+
"lint:fix": 'eslint "{src,apps,libs,test}/**/*.ts" --cache --cache-location node_modules/.cache/eslint --fix',
|
|
183
|
+
format: 'prettier --write "src/**/*.{ts,js,json}"'
|
|
192
184
|
}
|
|
193
185
|
};
|
|
194
186
|
|
|
@@ -267,7 +259,8 @@ coverage/
|
|
|
267
259
|
version: "0.2",
|
|
268
260
|
language: "en,en-US",
|
|
269
261
|
allowCompoundWords: true,
|
|
270
|
-
words: []
|
|
262
|
+
words: [],
|
|
263
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
271
264
|
},
|
|
272
265
|
null,
|
|
273
266
|
2
|
|
@@ -297,16 +290,9 @@ trim_trailing_whitespace = false
|
|
|
297
290
|
]
|
|
298
291
|
},
|
|
299
292
|
scripts: {
|
|
300
|
-
lint:
|
|
301
|
-
"lint:fix":
|
|
302
|
-
format: 'prettier --write "src/**/*.{ts,js,json}"'
|
|
303
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json}"',
|
|
304
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
305
|
-
"type:check": "tsc --noEmit",
|
|
306
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
307
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
308
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> cspell",
|
|
309
|
-
"code:fix:all": "<pm> lint:fix && <pm> format"
|
|
293
|
+
lint: 'eslint . --cache --cache-location node_modules/.cache/eslint && cspell --cache --cache-location node_modules/.cache/cspell --gitignore "src/**/*" && tsc --noEmit',
|
|
294
|
+
"lint:fix": "eslint . --cache --cache-location node_modules/.cache/eslint --fix",
|
|
295
|
+
format: 'prettier --write "src/**/*.{ts,js,json}"'
|
|
310
296
|
}
|
|
311
297
|
};
|
|
312
298
|
|
|
@@ -315,13 +301,13 @@ var uniappFmt = {
|
|
|
315
301
|
name: "uniapp",
|
|
316
302
|
description: "Vue 3 + UniApp WeChat mini program",
|
|
317
303
|
eslint: () => `import withVue from '@vue/eslint-config-typescript'
|
|
318
|
-
import
|
|
304
|
+
import prettierConfig from '@vue/eslint-config-prettier/skip-formatting'
|
|
319
305
|
import pluginVue from 'eslint-plugin-vue'
|
|
320
306
|
|
|
321
307
|
export default [
|
|
322
308
|
...pluginVue.configs['flat/recommended'],
|
|
323
309
|
...withVue(),
|
|
324
|
-
|
|
310
|
+
prettierConfig,
|
|
325
311
|
{
|
|
326
312
|
rules: {
|
|
327
313
|
'vue/multi-word-component-names': 'off',
|
|
@@ -380,7 +366,8 @@ unpackage/
|
|
|
380
366
|
version: "0.2",
|
|
381
367
|
language: "en,en-US",
|
|
382
368
|
allowCompoundWords: true,
|
|
383
|
-
words: ["vite", "pinia", "vueuse", "unplugin", "uniapp"]
|
|
369
|
+
words: ["vite", "pinia", "vueuse", "unplugin", "uniapp"],
|
|
370
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
384
371
|
},
|
|
385
372
|
null,
|
|
386
373
|
2
|
|
@@ -416,18 +403,9 @@ trim_trailing_whitespace = false
|
|
|
416
403
|
]
|
|
417
404
|
},
|
|
418
405
|
scripts: {
|
|
419
|
-
lint:
|
|
420
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
421
|
-
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
422
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,vue,css,scss}"',
|
|
423
|
-
stylelint: 'stylelint "src/**/*.{css,scss,vue}"',
|
|
424
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss,vue}" --fix',
|
|
425
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
426
|
-
"type:check": "vue-tsc --noEmit",
|
|
427
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
428
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
429
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
430
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
406
|
+
lint: 'eslint . --cache --cache-location node_modules/.cache/eslint && cspell --cache --cache-location node_modules/.cache/cspell --gitignore "src/**/*" && vue-tsc --noEmit && stylelint "src/**/*.{css,scss,vue}" --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
407
|
+
"lint:fix": 'eslint . --cache --cache-location node_modules/.cache/eslint --fix && stylelint "src/**/*.{css,scss,vue}" --fix --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
408
|
+
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
431
409
|
}
|
|
432
410
|
};
|
|
433
411
|
|
|
@@ -506,7 +484,8 @@ dist/
|
|
|
506
484
|
version: "0.2",
|
|
507
485
|
language: "en,en-US",
|
|
508
486
|
allowCompoundWords: true,
|
|
509
|
-
words: ["vite", "react", "zustand", "tanstack"]
|
|
487
|
+
words: ["vite", "react", "zustand", "tanstack"],
|
|
488
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
510
489
|
},
|
|
511
490
|
null,
|
|
512
491
|
2
|
|
@@ -544,18 +523,9 @@ trim_trailing_whitespace = false
|
|
|
544
523
|
]
|
|
545
524
|
},
|
|
546
525
|
scripts: {
|
|
547
|
-
lint:
|
|
548
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
549
|
-
format: 'prettier --write "src/**/*.{ts,js,json,jsx,tsx,css,scss}"'
|
|
550
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,jsx,tsx,css,scss}"',
|
|
551
|
-
stylelint: 'stylelint "src/**/*.{css,scss}"',
|
|
552
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss}" --fix',
|
|
553
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
554
|
-
"type:check": "tsc --noEmit",
|
|
555
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
556
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
557
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
558
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
526
|
+
lint: 'eslint . --cache --cache-location node_modules/.cache/eslint && cspell --cache --cache-location node_modules/.cache/cspell --gitignore "src/**/*" && tsc --noEmit && stylelint "src/**/*.{css,scss}" --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
527
|
+
"lint:fix": 'eslint . --cache --cache-location node_modules/.cache/eslint --fix && stylelint "src/**/*.{css,scss}" --fix --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
528
|
+
format: 'prettier --write "src/**/*.{ts,js,json,jsx,tsx,css,scss}"'
|
|
559
529
|
}
|
|
560
530
|
};
|
|
561
531
|
|
|
@@ -564,13 +534,13 @@ var webVueFmt = {
|
|
|
564
534
|
name: "web-vue",
|
|
565
535
|
description: "Vue 3 Web frontend (Vite + Vue + TypeScript)",
|
|
566
536
|
eslint: () => `import withVue from '@vue/eslint-config-typescript'
|
|
567
|
-
import
|
|
537
|
+
import prettierConfig from '@vue/eslint-config-prettier/skip-formatting'
|
|
568
538
|
import pluginVue from 'eslint-plugin-vue'
|
|
569
539
|
|
|
570
540
|
export default [
|
|
571
541
|
...pluginVue.configs['flat/recommended'],
|
|
572
542
|
...withVue(),
|
|
573
|
-
|
|
543
|
+
prettierConfig,
|
|
574
544
|
{
|
|
575
545
|
rules: {
|
|
576
546
|
'vue/multi-word-component-names': 'off',
|
|
@@ -627,7 +597,8 @@ dist/
|
|
|
627
597
|
version: "0.2",
|
|
628
598
|
language: "en,en-US",
|
|
629
599
|
allowCompoundWords: true,
|
|
630
|
-
words: ["vite", "pinia", "vueuse", "unplugin"]
|
|
600
|
+
words: ["vite", "pinia", "vueuse", "unplugin"],
|
|
601
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
631
602
|
},
|
|
632
603
|
null,
|
|
633
604
|
2
|
|
@@ -663,18 +634,9 @@ trim_trailing_whitespace = false
|
|
|
663
634
|
]
|
|
664
635
|
},
|
|
665
636
|
scripts: {
|
|
666
|
-
lint:
|
|
667
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
668
|
-
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
669
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,vue,css,scss}"',
|
|
670
|
-
stylelint: 'stylelint "src/**/*.{css,scss,vue}"',
|
|
671
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss,vue}" --fix',
|
|
672
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
673
|
-
"type:check": "vue-tsc --noEmit",
|
|
674
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
675
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
676
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
677
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
637
|
+
lint: 'eslint . --cache --cache-location node_modules/.cache/eslint && cspell --cache --cache-location node_modules/.cache/cspell --gitignore "src/**/*" && vue-tsc --noEmit && stylelint "src/**/*.{css,scss,vue}" --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
638
|
+
"lint:fix": 'eslint . --cache --cache-location node_modules/.cache/eslint --fix && stylelint "src/**/*.{css,scss,vue}" --fix --cache --cache-strategy content --cache-location node_modules/.cache/stylelint/',
|
|
639
|
+
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
678
640
|
}
|
|
679
641
|
};
|
|
680
642
|
|
|
@@ -979,12 +941,365 @@ async function installDevDeps(packages, cwd, pm) {
|
|
|
979
941
|
}
|
|
980
942
|
}
|
|
981
943
|
|
|
944
|
+
// src/core/local-preset.ts
|
|
945
|
+
import fs2 from "fs";
|
|
946
|
+
import os from "os";
|
|
947
|
+
import path4 from "path";
|
|
948
|
+
|
|
949
|
+
// src/core/merge-settings.ts
|
|
950
|
+
var USER_PRIORITY_KEYS = /* @__PURE__ */ new Set([
|
|
951
|
+
// Cursor/animation
|
|
952
|
+
"editor.cursorBlinking",
|
|
953
|
+
"editor.cursorSmoothCaretAnimation",
|
|
954
|
+
"editor.renderWhitespace",
|
|
955
|
+
"editor.guides.indentation",
|
|
956
|
+
"editor.largeFileOptimizations",
|
|
957
|
+
// Theme/appearance
|
|
958
|
+
"workbench.iconTheme",
|
|
959
|
+
"workbench.colorTheme",
|
|
960
|
+
// Suggestions
|
|
961
|
+
"editor.inlineSuggest.enabled",
|
|
962
|
+
"editor.suggestSelection",
|
|
963
|
+
"editor.acceptSuggestionOnEnter",
|
|
964
|
+
"editor.bracketPairColorization.enabled",
|
|
965
|
+
"editor.autoClosingBrackets",
|
|
966
|
+
"editor.autoClosingOvertype"
|
|
967
|
+
]);
|
|
968
|
+
function mergeVscodeSettings(preset, existing) {
|
|
969
|
+
const result = { ...existing };
|
|
970
|
+
for (const [key, presetVal] of Object.entries(preset)) {
|
|
971
|
+
const existingVal = existing[key];
|
|
972
|
+
if (existingVal === void 0) {
|
|
973
|
+
result[key] = presetVal;
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
if (USER_PRIORITY_KEYS.has(key)) {
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (isPlainObject(presetVal) && isPlainObject(existingVal)) {
|
|
980
|
+
result[key] = mergeVscodeSettings(
|
|
981
|
+
presetVal,
|
|
982
|
+
existingVal
|
|
983
|
+
);
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
result[key] = presetVal;
|
|
987
|
+
}
|
|
988
|
+
return result;
|
|
989
|
+
}
|
|
990
|
+
function isPlainObject(val) {
|
|
991
|
+
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/core/local-preset.ts
|
|
995
|
+
var CONFIG_GETTERS = [
|
|
996
|
+
{ filename: "eslint.config.mjs", getContent: (p) => p.eslint?.() },
|
|
997
|
+
{ filename: ".prettierrc", getContent: (p) => p.prettier?.() },
|
|
998
|
+
{ filename: ".prettierignore", getContent: (p) => p.prettierIgnore?.() },
|
|
999
|
+
{ filename: "stylelint.config.mjs", getContent: (p) => p.stylelint?.() },
|
|
1000
|
+
{ filename: ".stylelintignore", getContent: (p) => p.stylelintIgnore?.() },
|
|
1001
|
+
{ filename: "cspell.json", getContent: (p) => p.cspell?.() },
|
|
1002
|
+
{ filename: ".editorconfig", getContent: (p) => p.editorconfig?.() }
|
|
1003
|
+
];
|
|
1004
|
+
var STYLELINT_FILES = /* @__PURE__ */ new Set(["stylelint.config.mjs", ".stylelintignore"]);
|
|
1005
|
+
var EDITORCONFIG_FILE = ".editorconfig";
|
|
1006
|
+
var STYLELINT_SETTINGS_PREFIXES = [
|
|
1007
|
+
"stylelint.",
|
|
1008
|
+
"css.validate",
|
|
1009
|
+
"less.validate",
|
|
1010
|
+
"scss.validate"
|
|
1011
|
+
];
|
|
1012
|
+
var STYLELINT_DEPS = /* @__PURE__ */ new Set([
|
|
1013
|
+
"stylelint",
|
|
1014
|
+
"stylelint-config-standard-scss",
|
|
1015
|
+
"stylelint-order",
|
|
1016
|
+
"stylelint-scss",
|
|
1017
|
+
"@stylistic/stylelint-plugin",
|
|
1018
|
+
"postcss-html",
|
|
1019
|
+
"postcss-scss"
|
|
1020
|
+
]);
|
|
1021
|
+
var STYLELINT_EXTENSION = "stylelint.vscode-stylelint";
|
|
1022
|
+
function getLuxDir() {
|
|
1023
|
+
return process.env.LUX_HOME || path4.join(os.homedir(), ".lux");
|
|
1024
|
+
}
|
|
1025
|
+
function getLocalPresetDir(type, presetName) {
|
|
1026
|
+
if (!isValidPresetName(presetName)) {
|
|
1027
|
+
throw new Error(`Invalid preset name: "${presetName}"`);
|
|
1028
|
+
}
|
|
1029
|
+
return path4.join(getLuxDir(), "preset", type, presetName);
|
|
1030
|
+
}
|
|
1031
|
+
function isValidPresetName(name) {
|
|
1032
|
+
return name.length > 0 && !name.includes("/") && !name.includes("\\") && !name.includes("..");
|
|
1033
|
+
}
|
|
1034
|
+
function localPresetExists(type, presetName) {
|
|
1035
|
+
const dir = getLocalPresetDir(type, presetName);
|
|
1036
|
+
return fs2.existsSync(dir);
|
|
1037
|
+
}
|
|
1038
|
+
function resetLocalPreset(type, presetName) {
|
|
1039
|
+
const dir = getLocalPresetDir(type, presetName);
|
|
1040
|
+
if (fs2.existsSync(dir)) {
|
|
1041
|
+
fs2.rmSync(dir, { recursive: true, force: true });
|
|
1042
|
+
logger.log(`Reset local preset: ${dir}`);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
function materializeFmtPreset(presetName, preset, opts) {
|
|
1046
|
+
if (opts.dryRun) {
|
|
1047
|
+
logger.log("[dry-run] Would materialize local preset to ~/.lux/preset/fmt/" + presetName);
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
const presetDir = getLocalPresetDir("fmt", presetName);
|
|
1051
|
+
ensureDir(presetDir);
|
|
1052
|
+
for (const { filename, getContent } of CONFIG_GETTERS) {
|
|
1053
|
+
const content = getContent(preset);
|
|
1054
|
+
if (content === void 0) continue;
|
|
1055
|
+
const resolved = opts.lockfile ? content.replace(/<lockfile>/g, opts.lockfile) : content.replace(/<lockfile>\n?/g, "");
|
|
1056
|
+
writeFile(path4.join(presetDir, filename), resolved);
|
|
1057
|
+
}
|
|
1058
|
+
const templatePkg = buildTemplatePackageJson(preset);
|
|
1059
|
+
writeJson(path4.join(presetDir, "package.json"), templatePkg);
|
|
1060
|
+
logger.log(`Local preset created at ${presetDir}`);
|
|
1061
|
+
}
|
|
1062
|
+
function materializeVscodePreset(cwd, presetName) {
|
|
1063
|
+
const presetDir = getLocalPresetDir("vscode", presetName);
|
|
1064
|
+
ensureDir(presetDir);
|
|
1065
|
+
const settingsSrc = path4.join(cwd, ".vscode", "settings.json");
|
|
1066
|
+
if (fileExists(settingsSrc)) {
|
|
1067
|
+
const content = fs2.readFileSync(settingsSrc, "utf-8");
|
|
1068
|
+
writeFile(path4.join(presetDir, "settings.json"), content);
|
|
1069
|
+
}
|
|
1070
|
+
const extensionsSrc = path4.join(cwd, ".vscode", "extensions.json");
|
|
1071
|
+
if (fileExists(extensionsSrc)) {
|
|
1072
|
+
const content = fs2.readFileSync(extensionsSrc, "utf-8");
|
|
1073
|
+
writeFile(path4.join(presetDir, "extensions.json"), content);
|
|
1074
|
+
}
|
|
1075
|
+
logger.log(`Local preset created at ${presetDir}`);
|
|
1076
|
+
}
|
|
1077
|
+
function materializeVscodePresetFromBuiltin(presetName, preset) {
|
|
1078
|
+
const presetDir = getLocalPresetDir("vscode", presetName);
|
|
1079
|
+
ensureDir(presetDir);
|
|
1080
|
+
const settings = preset.settings();
|
|
1081
|
+
writeJson(path4.join(presetDir, "settings.json"), settings);
|
|
1082
|
+
const extensions = preset.extensions();
|
|
1083
|
+
writeJson(path4.join(presetDir, "extensions.json"), { recommendations: extensions });
|
|
1084
|
+
logger.log(`Local preset created at ${presetDir}`);
|
|
1085
|
+
}
|
|
1086
|
+
var InvalidPackageJsonError = class extends Error {
|
|
1087
|
+
constructor(filePath) {
|
|
1088
|
+
super(`package.json exists but is not valid JSON: ${filePath}`);
|
|
1089
|
+
this.filePath = filePath;
|
|
1090
|
+
}
|
|
1091
|
+
filePath;
|
|
1092
|
+
};
|
|
1093
|
+
function applyLocalFmtPreset(cwd, presetName, opts) {
|
|
1094
|
+
const result = {
|
|
1095
|
+
created: [],
|
|
1096
|
+
overwritten: [],
|
|
1097
|
+
skipped: [],
|
|
1098
|
+
scriptsAdded: 0,
|
|
1099
|
+
scriptsSkipped: 0
|
|
1100
|
+
};
|
|
1101
|
+
const presetDir = getLocalPresetDir("fmt", presetName);
|
|
1102
|
+
if (!fs2.existsSync(presetDir)) {
|
|
1103
|
+
logger.warn(`Local preset not found at ${presetDir}`);
|
|
1104
|
+
return result;
|
|
1105
|
+
}
|
|
1106
|
+
const projectPkgPath = path4.join(cwd, "package.json");
|
|
1107
|
+
if (fileExists(projectPkgPath)) {
|
|
1108
|
+
try {
|
|
1109
|
+
JSON.parse(fs2.readFileSync(projectPkgPath, "utf-8"));
|
|
1110
|
+
} catch {
|
|
1111
|
+
throw new InvalidPackageJsonError(projectPkgPath);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
const entries = fs2.readdirSync(presetDir).filter((name) => name !== "package.json" && fs2.statSync(path4.join(presetDir, name)).isFile());
|
|
1115
|
+
for (const filename of entries) {
|
|
1116
|
+
if (opts.noStylelint && STYLELINT_FILES.has(filename)) continue;
|
|
1117
|
+
if (opts.noEditorconfig && filename === EDITORCONFIG_FILE) continue;
|
|
1118
|
+
const destPath = path4.join(cwd, filename);
|
|
1119
|
+
const exists = fileExists(destPath);
|
|
1120
|
+
if (exists && !opts.force) {
|
|
1121
|
+
result.skipped.push(filename);
|
|
1122
|
+
if (opts.dryRun) {
|
|
1123
|
+
logger.log(`[dry-run] Skipped ${filename} (already exists)`);
|
|
1124
|
+
}
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
if (opts.dryRun) {
|
|
1128
|
+
(exists ? result.overwritten : result.created).push(filename);
|
|
1129
|
+
logger.log(`[dry-run] Would copy ${filename} from local preset`);
|
|
1130
|
+
continue;
|
|
1131
|
+
}
|
|
1132
|
+
const content = fs2.readFileSync(path4.join(presetDir, filename), "utf-8");
|
|
1133
|
+
writeFile(destPath, content);
|
|
1134
|
+
(exists ? result.overwritten : result.created).push(filename);
|
|
1135
|
+
}
|
|
1136
|
+
const templatePkg = readJson(path4.join(presetDir, "package.json"));
|
|
1137
|
+
const projectPkg = readJson(projectPkgPath);
|
|
1138
|
+
if (templatePkg && projectPkg) {
|
|
1139
|
+
const pm = fileExists(path4.join(cwd, "package.json")) ? detectPackageManager(cwd) : void 0;
|
|
1140
|
+
const merged = mergeTemplateIntoProject(templatePkg, projectPkg, pm, opts, result);
|
|
1141
|
+
if (!opts.dryRun) {
|
|
1142
|
+
writeJson(projectPkgPath, merged);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return result;
|
|
1146
|
+
}
|
|
1147
|
+
function applyLocalVscodePreset(cwd, presetName, opts) {
|
|
1148
|
+
const result = {
|
|
1149
|
+
created: [],
|
|
1150
|
+
overwritten: [],
|
|
1151
|
+
skipped: [],
|
|
1152
|
+
scriptsAdded: 0,
|
|
1153
|
+
scriptsSkipped: 0
|
|
1154
|
+
};
|
|
1155
|
+
const presetDir = getLocalPresetDir("vscode", presetName);
|
|
1156
|
+
if (!fs2.existsSync(presetDir)) {
|
|
1157
|
+
logger.warn(`Local preset not found at ${presetDir}`);
|
|
1158
|
+
return result;
|
|
1159
|
+
}
|
|
1160
|
+
const settingsSrc = path4.join(presetDir, "settings.json");
|
|
1161
|
+
if (fileExists(settingsSrc)) {
|
|
1162
|
+
const presetSettings = readJson(settingsSrc);
|
|
1163
|
+
const filteredSettings = opts.noStylelint ? filterStylelintSettings(presetSettings ?? {}) : presetSettings;
|
|
1164
|
+
if (filteredSettings) {
|
|
1165
|
+
const settingsDest = path4.join(cwd, ".vscode", "settings.json");
|
|
1166
|
+
const existingSettings = readJson(settingsDest);
|
|
1167
|
+
if (existingSettings) {
|
|
1168
|
+
if (opts.dryRun) {
|
|
1169
|
+
result.overwritten.push(".vscode/settings.json");
|
|
1170
|
+
logger.log("[dry-run] Would merge .vscode/settings.json from local preset");
|
|
1171
|
+
} else {
|
|
1172
|
+
const merged = mergeVscodeSettings(filteredSettings, existingSettings);
|
|
1173
|
+
writeJson(settingsDest, merged);
|
|
1174
|
+
result.overwritten.push(".vscode/settings.json");
|
|
1175
|
+
}
|
|
1176
|
+
} else {
|
|
1177
|
+
if (opts.dryRun) {
|
|
1178
|
+
result.created.push(".vscode/settings.json");
|
|
1179
|
+
logger.log("[dry-run] Would create .vscode/settings.json from local preset");
|
|
1180
|
+
} else {
|
|
1181
|
+
writeJson(settingsDest, filteredSettings);
|
|
1182
|
+
result.created.push(".vscode/settings.json");
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
const extensionsSrc = path4.join(presetDir, "extensions.json");
|
|
1188
|
+
if (fileExists(extensionsSrc)) {
|
|
1189
|
+
const extensionsData = readJson(extensionsSrc);
|
|
1190
|
+
if (extensionsData) {
|
|
1191
|
+
let presetRecommendations = extensionsData.recommendations ?? [];
|
|
1192
|
+
if (opts.noStylelint) {
|
|
1193
|
+
presetRecommendations = presetRecommendations.filter(
|
|
1194
|
+
(ext) => ext !== STYLELINT_EXTENSION
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
if (opts.dryRun) {
|
|
1198
|
+
result.created.push(".vscode/extensions.json");
|
|
1199
|
+
logger.log("[dry-run] Would create .vscode/extensions.json from local preset");
|
|
1200
|
+
} else {
|
|
1201
|
+
const extensionsDest = path4.join(cwd, ".vscode", "extensions.json");
|
|
1202
|
+
const existingExtensions = readJson(extensionsDest);
|
|
1203
|
+
const existingRecommendations = existingExtensions?.recommendations ?? [];
|
|
1204
|
+
const merged = [.../* @__PURE__ */ new Set([...existingRecommendations, ...presetRecommendations])];
|
|
1205
|
+
writeJson(extensionsDest, { recommendations: merged });
|
|
1206
|
+
result.created.push(".vscode/extensions.json");
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return result;
|
|
1211
|
+
}
|
|
1212
|
+
function buildTemplatePackageJson(preset) {
|
|
1213
|
+
const deps = {};
|
|
1214
|
+
if (preset.dependencies?.dev) {
|
|
1215
|
+
for (const dep of preset.dependencies.dev) {
|
|
1216
|
+
deps[dep] = "<latest>";
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
const scripts = preset.scripts ? { ...preset.scripts } : void 0;
|
|
1220
|
+
const result = {};
|
|
1221
|
+
if (Object.keys(deps).length > 0) {
|
|
1222
|
+
result.devDependencies = deps;
|
|
1223
|
+
}
|
|
1224
|
+
if (scripts && Object.keys(scripts).length > 0) {
|
|
1225
|
+
result.scripts = scripts;
|
|
1226
|
+
}
|
|
1227
|
+
return result;
|
|
1228
|
+
}
|
|
1229
|
+
function mergeTemplateIntoProject(templatePkg, projectPkg, pm, opts, result) {
|
|
1230
|
+
const merged = { ...projectPkg };
|
|
1231
|
+
const prefix = pm ? getRunPrefix(pm) : "";
|
|
1232
|
+
if (templatePkg.devDependencies) {
|
|
1233
|
+
const existingDeps = merged.devDependencies ?? {};
|
|
1234
|
+
const newDeps = { ...existingDeps };
|
|
1235
|
+
for (const [dep, version] of Object.entries(templatePkg.devDependencies)) {
|
|
1236
|
+
if (opts.noStylelint && STYLELINT_DEPS.has(dep)) continue;
|
|
1237
|
+
if (opts.noEditorconfig && dep.includes("editorconfig")) continue;
|
|
1238
|
+
if (existingDeps[dep] === void 0 && version !== "<latest>") {
|
|
1239
|
+
newDeps[dep] = version;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
merged.devDependencies = newDeps;
|
|
1243
|
+
}
|
|
1244
|
+
if (templatePkg.scripts) {
|
|
1245
|
+
const existingScripts = merged.scripts ?? {};
|
|
1246
|
+
const newScripts = { ...existingScripts };
|
|
1247
|
+
for (const [key, value] of Object.entries(templatePkg.scripts)) {
|
|
1248
|
+
let resolved = value.replace(/<pm>/g, prefix);
|
|
1249
|
+
if (opts.noStylelint) {
|
|
1250
|
+
resolved = resolved.replace(/\s*&&\s*stylelint\s+"[^"]*".*/g, "");
|
|
1251
|
+
}
|
|
1252
|
+
if (existingScripts[key] !== void 0 && !opts.force) {
|
|
1253
|
+
result.scriptsSkipped++;
|
|
1254
|
+
if (opts.dryRun) {
|
|
1255
|
+
logger.log(`[dry-run] Skipped script "${key}" (already exists)`);
|
|
1256
|
+
} else {
|
|
1257
|
+
logger.log(`Skipped script "${key}" (already exists)`);
|
|
1258
|
+
}
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
if (opts.dryRun) {
|
|
1262
|
+
result.scriptsAdded++;
|
|
1263
|
+
logger.log(`[dry-run] Would add script "${key}"`);
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
newScripts[key] = resolved;
|
|
1267
|
+
result.scriptsAdded++;
|
|
1268
|
+
}
|
|
1269
|
+
merged.scripts = newScripts;
|
|
1270
|
+
}
|
|
1271
|
+
return merged;
|
|
1272
|
+
}
|
|
1273
|
+
function filterStylelintSettings(settings) {
|
|
1274
|
+
const filtered = Object.fromEntries(
|
|
1275
|
+
Object.entries(settings).filter(
|
|
1276
|
+
([key]) => !STYLELINT_SETTINGS_PREFIXES.some((prefix) => key.startsWith(prefix))
|
|
1277
|
+
)
|
|
1278
|
+
);
|
|
1279
|
+
if (typeof filtered["editor.codeActionsOnSave"] === "object" && filtered["editor.codeActionsOnSave"] !== null) {
|
|
1280
|
+
const actions = { ...filtered["editor.codeActionsOnSave"] };
|
|
1281
|
+
delete actions["source.fixAll.stylelint"];
|
|
1282
|
+
filtered["editor.codeActionsOnSave"] = actions;
|
|
1283
|
+
}
|
|
1284
|
+
return filtered;
|
|
1285
|
+
}
|
|
1286
|
+
function resolveLocalDeps(deps) {
|
|
1287
|
+
const packages = [];
|
|
1288
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
1289
|
+
if (version === "<latest>") {
|
|
1290
|
+
packages.push(name);
|
|
1291
|
+
} else {
|
|
1292
|
+
packages.push(`${name}@${version}`);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return packages;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
982
1298
|
// src/commands/fmt.ts
|
|
983
1299
|
function filterStylelintScripts(scripts) {
|
|
984
1300
|
const filtered = {};
|
|
985
1301
|
for (const [key, value] of Object.entries(scripts)) {
|
|
986
|
-
|
|
987
|
-
filtered[key] = value.replace(/\s*&&\s*<pm>\s+stylelint\S*/g, "");
|
|
1302
|
+
filtered[key] = value.replace(/\s*&&\s*stylelint\s+"[^"]*".*/g, "");
|
|
988
1303
|
}
|
|
989
1304
|
return filtered;
|
|
990
1305
|
}
|
|
@@ -998,58 +1313,30 @@ function isNotEditorconfigDep(dep) {
|
|
|
998
1313
|
}
|
|
999
1314
|
function registerFmtCommand(program2) {
|
|
1000
1315
|
const fmt = program2.command("fmt").description("Initialize formatting config with preset");
|
|
1001
|
-
fmt.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--no-install", "Skip dependency installation").option("--dry-run", "Preview without writing files").option("--stylelint", "Include Stylelint config generation").option("--editorconfig", "Include EditorConfig config generation").action(
|
|
1316
|
+
fmt.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--no-install", "Skip dependency installation").option("--dry-run", "Preview without writing files").option("--stylelint", "Include Stylelint config generation").option("--editorconfig", "Include EditorConfig config generation").option("--reset", "Reset local preset and re-materialize from built-in").action(
|
|
1002
1317
|
async (presetName, options) => {
|
|
1003
1318
|
const preset = resolvePreset(FMT_PRESETS, presetName);
|
|
1004
1319
|
if (!preset) return;
|
|
1005
1320
|
const cwd = process.cwd();
|
|
1006
|
-
const
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const result = generateAllFmt(preset, opts);
|
|
1016
|
-
const allFiles = [...result.created, ...result.overwritten];
|
|
1017
|
-
if (allFiles.length === 0 && result.skipped.length === 0) {
|
|
1018
|
-
logger.warn("No files to generate for this preset");
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
logGenerationResult(result, opts.dryRun);
|
|
1022
|
-
if (!pm) {
|
|
1023
|
-
warnMissingPackageJson(preset, options.install !== false);
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
const scripts = opts.noStylelint && preset.scripts ? filterStylelintScripts(preset.scripts) : preset.scripts;
|
|
1027
|
-
if (scripts) {
|
|
1028
|
-
await injectScripts(scripts, opts, pm);
|
|
1029
|
-
}
|
|
1030
|
-
if (!preset.dependencies?.dev) return;
|
|
1031
|
-
const devDeps = opts.noStylelint ? preset.dependencies.dev.filter(isNotStylelintDep) : preset.dependencies.dev;
|
|
1032
|
-
const finalDeps = opts.noEditorconfig ? devDeps.filter(isNotEditorconfigDep) : devDeps;
|
|
1033
|
-
if (options.install === false) {
|
|
1034
|
-
const added = await addDepsToManifest(finalDeps, cwd);
|
|
1035
|
-
if (added.length > 0) {
|
|
1036
|
-
logger.success(`Added to package.json (skipped install): ${added.join(", ")}`);
|
|
1037
|
-
} else {
|
|
1038
|
-
logger.log("All dependencies already in package.json");
|
|
1321
|
+
const pkgPath = path5.join(cwd, "package.json");
|
|
1322
|
+
if (fileExists(pkgPath)) {
|
|
1323
|
+
try {
|
|
1324
|
+
JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
1325
|
+
} catch {
|
|
1326
|
+
logger.error(
|
|
1327
|
+
"package.json exists but is not valid JSON. Fix it first, then re-run this command."
|
|
1328
|
+
);
|
|
1329
|
+
return;
|
|
1039
1330
|
}
|
|
1040
|
-
return;
|
|
1041
1331
|
}
|
|
1042
|
-
if (
|
|
1043
|
-
|
|
1044
|
-
return;
|
|
1332
|
+
if (options.reset) {
|
|
1333
|
+
resetLocalPreset("fmt", presetName);
|
|
1045
1334
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
await
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1052
|
-
logger.warn(`Dependency installation failed: ${message}. You can install manually.`);
|
|
1335
|
+
const useLocal = localPresetExists("fmt", presetName);
|
|
1336
|
+
if (useLocal) {
|
|
1337
|
+
await executeLocalPath(cwd, presetName, options);
|
|
1338
|
+
} else {
|
|
1339
|
+
await executeBuiltinPath(cwd, presetName, preset, options);
|
|
1053
1340
|
}
|
|
1054
1341
|
}
|
|
1055
1342
|
);
|
|
@@ -1059,6 +1346,152 @@ function registerFmtCommand(program2) {
|
|
|
1059
1346
|
}
|
|
1060
1347
|
});
|
|
1061
1348
|
}
|
|
1349
|
+
async function executeLocalPath(cwd, presetName, options) {
|
|
1350
|
+
logger.log("Using local custom preset");
|
|
1351
|
+
const opts = {
|
|
1352
|
+
cwd,
|
|
1353
|
+
force: options.force ?? false,
|
|
1354
|
+
dryRun: options.dryRun ?? false,
|
|
1355
|
+
noStylelint: options.stylelint !== true,
|
|
1356
|
+
noEditorconfig: options.editorconfig !== true
|
|
1357
|
+
};
|
|
1358
|
+
let result;
|
|
1359
|
+
try {
|
|
1360
|
+
result = applyLocalFmtPreset(cwd, presetName, opts);
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
if (error instanceof InvalidPackageJsonError) {
|
|
1363
|
+
logger.error(
|
|
1364
|
+
"package.json exists but is not valid JSON. Fix it first, then re-run this command."
|
|
1365
|
+
);
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
throw error;
|
|
1369
|
+
}
|
|
1370
|
+
const allFiles = [...result.created, ...result.overwritten];
|
|
1371
|
+
if (allFiles.length > 0 || result.skipped.length > 0) {
|
|
1372
|
+
logApplyResult(result);
|
|
1373
|
+
}
|
|
1374
|
+
if (result.scriptsAdded > 0 || result.scriptsSkipped > 0) {
|
|
1375
|
+
logger.log(
|
|
1376
|
+
`Added ${result.scriptsAdded} script${result.scriptsAdded > 1 ? "s" : ""} to package.json${result.scriptsSkipped > 0 ? ` (${result.scriptsSkipped} skipped)` : ""}`
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
const pm = fileExists(path5.join(cwd, "package.json")) ? detectPackageManager(cwd) : void 0;
|
|
1380
|
+
if (!pm) return;
|
|
1381
|
+
const templatePkgPath = path5.join(getLocalPresetDir("fmt", presetName), "package.json");
|
|
1382
|
+
const templatePkg = readJson(templatePkgPath);
|
|
1383
|
+
if (!templatePkg?.devDependencies) return;
|
|
1384
|
+
const depsToInstall = filterDeps(
|
|
1385
|
+
Object.keys(templatePkg.devDependencies),
|
|
1386
|
+
opts.noStylelint,
|
|
1387
|
+
opts.noEditorconfig
|
|
1388
|
+
);
|
|
1389
|
+
const projectPkgPath = path5.join(cwd, "package.json");
|
|
1390
|
+
const projectPkg = readJson(projectPkgPath);
|
|
1391
|
+
if (!projectPkg) return;
|
|
1392
|
+
const existingDeps = projectPkg.devDependencies ?? {};
|
|
1393
|
+
const missing = depsToInstall.filter((dep) => !existingDeps[dep]);
|
|
1394
|
+
if (missing.length === 0) return;
|
|
1395
|
+
if (opts.dryRun) {
|
|
1396
|
+
logger.log(`[dry-run] Would add to package.json: ${missing.join(", ")}`);
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
if (options.install === false) {
|
|
1400
|
+
try {
|
|
1401
|
+
const filteredTemplateDeps = Object.fromEntries(
|
|
1402
|
+
Object.entries(templatePkg.devDependencies).filter(([k]) => missing.includes(k))
|
|
1403
|
+
);
|
|
1404
|
+
const resolved = resolveLocalDeps(filteredTemplateDeps);
|
|
1405
|
+
const added = await addDepsToManifest(resolved, cwd);
|
|
1406
|
+
if (added.length > 0) {
|
|
1407
|
+
logger.success(`Added to package.json (skipped install): ${added.join(", ")}`);
|
|
1408
|
+
} else {
|
|
1409
|
+
logger.log("All dependencies already in package.json");
|
|
1410
|
+
}
|
|
1411
|
+
} catch (error) {
|
|
1412
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1413
|
+
logger.warn(`Failed to fetch versions: ${message}. You can add dependencies manually.`);
|
|
1414
|
+
}
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
try {
|
|
1418
|
+
logger.log(`Installing dependencies with ${pm}...`);
|
|
1419
|
+
const resolved = resolveLocalDeps(
|
|
1420
|
+
Object.fromEntries(
|
|
1421
|
+
Object.entries(templatePkg.devDependencies).filter(([k]) => missing.includes(k))
|
|
1422
|
+
)
|
|
1423
|
+
);
|
|
1424
|
+
await installDevDeps(resolved, cwd, pm);
|
|
1425
|
+
logger.success("Dependencies installed successfully");
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1428
|
+
logger.warn(`Dependency installation failed: ${message}. You can install manually.`);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
async function executeBuiltinPath(cwd, presetName, preset, options) {
|
|
1432
|
+
const pm = fileExists(path5.join(cwd, "package.json")) ? detectPackageManager(cwd) : void 0;
|
|
1433
|
+
const opts = {
|
|
1434
|
+
cwd,
|
|
1435
|
+
force: options.force ?? false,
|
|
1436
|
+
dryRun: options.dryRun ?? false,
|
|
1437
|
+
noStylelint: options.stylelint !== true,
|
|
1438
|
+
noEditorconfig: options.editorconfig !== true,
|
|
1439
|
+
lockfile: pm ? getLockfileName(pm) : void 0
|
|
1440
|
+
};
|
|
1441
|
+
const result = generateAllFmt(preset, opts);
|
|
1442
|
+
const allFiles = [...result.created, ...result.overwritten];
|
|
1443
|
+
if (allFiles.length === 0 && result.skipped.length === 0) {
|
|
1444
|
+
logger.warn("No files to generate for this preset");
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
logGenerationResult(result, opts.dryRun);
|
|
1448
|
+
if (!opts.dryRun) {
|
|
1449
|
+
materializeFmtPreset(presetName, preset, opts);
|
|
1450
|
+
}
|
|
1451
|
+
if (!pm) {
|
|
1452
|
+
warnMissingPackageJson(preset, options.install !== false);
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const scripts = opts.noStylelint && preset.scripts ? filterStylelintScripts(preset.scripts) : preset.scripts;
|
|
1456
|
+
if (scripts) {
|
|
1457
|
+
await injectScripts(scripts, opts, pm);
|
|
1458
|
+
}
|
|
1459
|
+
if (!preset.dependencies?.dev) return;
|
|
1460
|
+
const devDeps = opts.noStylelint ? preset.dependencies.dev.filter(isNotStylelintDep) : preset.dependencies.dev;
|
|
1461
|
+
const finalDeps = opts.noEditorconfig ? devDeps.filter(isNotEditorconfigDep) : devDeps;
|
|
1462
|
+
if (opts.dryRun) {
|
|
1463
|
+
logger.log(`[dry-run] Would add to package.json: ${finalDeps.join(", ")}`);
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
if (options.install === false) {
|
|
1467
|
+
try {
|
|
1468
|
+
const added = await addDepsToManifest(finalDeps, cwd);
|
|
1469
|
+
if (added.length > 0) {
|
|
1470
|
+
logger.success(`Added to package.json (skipped install): ${added.join(", ")}`);
|
|
1471
|
+
} else {
|
|
1472
|
+
logger.log("All dependencies already in package.json");
|
|
1473
|
+
}
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1476
|
+
logger.warn(`Failed to fetch versions: ${message}. You can add dependencies manually.`);
|
|
1477
|
+
}
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
try {
|
|
1481
|
+
logger.log(`Installing dependencies with ${pm}...`);
|
|
1482
|
+
await installDevDeps(finalDeps, cwd, pm);
|
|
1483
|
+
logger.success("Dependencies installed successfully");
|
|
1484
|
+
} catch (error) {
|
|
1485
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1486
|
+
logger.warn(`Dependency installation failed: ${message}. You can install manually.`);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
function filterDeps(deps, noStylelint, noEditorconfig) {
|
|
1490
|
+
let filtered = deps;
|
|
1491
|
+
if (noStylelint) filtered = filtered.filter(isNotStylelintDep);
|
|
1492
|
+
if (noEditorconfig) filtered = filtered.filter(isNotEditorconfigDep);
|
|
1493
|
+
return filtered;
|
|
1494
|
+
}
|
|
1062
1495
|
function logGenerationResult(result, dryRun) {
|
|
1063
1496
|
const files = [...result.created, ...result.overwritten];
|
|
1064
1497
|
if (dryRun) {
|
|
@@ -1086,6 +1519,23 @@ function logGenerationResult(result, dryRun) {
|
|
|
1086
1519
|
);
|
|
1087
1520
|
}
|
|
1088
1521
|
}
|
|
1522
|
+
function logApplyResult(result) {
|
|
1523
|
+
if (result.created.length > 0) {
|
|
1524
|
+
logger.log(
|
|
1525
|
+
`Created ${summarizeFiles(result.created)} config ${result.created.length} file${result.created.length > 1 ? "s" : ""} from local preset`
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
if (result.overwritten.length > 0) {
|
|
1529
|
+
logger.log(
|
|
1530
|
+
`Overwritten ${summarizeFiles(result.overwritten)} config ${result.overwritten.length} file${result.overwritten.length > 1 ? "s" : ""} from local preset`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
if (result.skipped.length > 0) {
|
|
1534
|
+
logger.log(
|
|
1535
|
+
`Skipped ${result.skipped.length} file${result.skipped.length > 1 ? "s" : ""} (already exists)`
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1089
1539
|
function warnMissingPackageJson(preset, installEnabled) {
|
|
1090
1540
|
const tasks = [];
|
|
1091
1541
|
if (preset.scripts) tasks.push("script injection");
|
|
@@ -1106,7 +1556,7 @@ function summarizeFiles(filenames) {
|
|
|
1106
1556
|
return [...categories].join(", ");
|
|
1107
1557
|
}
|
|
1108
1558
|
async function injectScripts(scripts, opts, pm) {
|
|
1109
|
-
const pkgPath =
|
|
1559
|
+
const pkgPath = path5.join(opts.cwd, "package.json");
|
|
1110
1560
|
const pkg = readJson(pkgPath);
|
|
1111
1561
|
if (!pkg) {
|
|
1112
1562
|
logger.warn("package.json not found, skipping script injection");
|
|
@@ -1158,206 +1608,6 @@ var INIT_TOOLS = [
|
|
|
1158
1608
|
}
|
|
1159
1609
|
];
|
|
1160
1610
|
|
|
1161
|
-
// src/generators/init.ts
|
|
1162
|
-
import fs2 from "fs";
|
|
1163
|
-
import path5 from "path";
|
|
1164
|
-
function resolveSkillsDir() {
|
|
1165
|
-
const entryDir = path5.dirname(process.argv[1] ?? "");
|
|
1166
|
-
return path5.resolve(entryDir, "skills");
|
|
1167
|
-
}
|
|
1168
|
-
function listFilesRecursive(dir, base) {
|
|
1169
|
-
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
1170
|
-
const files = [];
|
|
1171
|
-
for (const entry of entries) {
|
|
1172
|
-
const childBase = `${base}/${entry.name}`;
|
|
1173
|
-
const fullPath = path5.join(dir, entry.name);
|
|
1174
|
-
if (entry.isDirectory()) {
|
|
1175
|
-
files.push(...listFilesRecursive(fullPath, childBase));
|
|
1176
|
-
} else {
|
|
1177
|
-
files.push(childBase);
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
return files;
|
|
1181
|
-
}
|
|
1182
|
-
function generateInitSkills(targetBaseDir, cwd) {
|
|
1183
|
-
const skillsDir = resolveSkillsDir();
|
|
1184
|
-
if (!fs2.existsSync(skillsDir)) {
|
|
1185
|
-
logger.error(`Bundled skills directory not found: ${skillsDir}`);
|
|
1186
|
-
logger.error('Please run "lux build" or reinstall lux.');
|
|
1187
|
-
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
1188
|
-
}
|
|
1189
|
-
const targetPath = path5.resolve(cwd, targetBaseDir);
|
|
1190
|
-
try {
|
|
1191
|
-
fs2.cpSync(skillsDir, targetPath, { recursive: true, force: true });
|
|
1192
|
-
} catch (error) {
|
|
1193
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1194
|
-
logger.error(`Failed to copy skills to ${targetPath}: ${message}`);
|
|
1195
|
-
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
1196
|
-
}
|
|
1197
|
-
const copiedFiles = fs2.existsSync(targetPath) ? listFilesRecursive(targetPath, targetBaseDir) : [];
|
|
1198
|
-
return { copiedFiles, targetDir: targetBaseDir };
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
// src/commands/init.ts
|
|
1202
|
-
function registerInitCommand(program2) {
|
|
1203
|
-
program2.command("init").description("Initialize AI coding tool skills in current project").action(async () => {
|
|
1204
|
-
const toolOptions = INIT_TOOLS.map((tool2) => ({
|
|
1205
|
-
value: tool2.name,
|
|
1206
|
-
label: tool2.label
|
|
1207
|
-
}));
|
|
1208
|
-
const selected = await select({
|
|
1209
|
-
message: "Which AI coding tool do you use?",
|
|
1210
|
-
options: toolOptions
|
|
1211
|
-
});
|
|
1212
|
-
if (isCancel(selected)) {
|
|
1213
|
-
cancel("Operation cancelled.");
|
|
1214
|
-
return;
|
|
1215
|
-
}
|
|
1216
|
-
const tool = INIT_TOOLS.find((t) => t.name === selected);
|
|
1217
|
-
if (!tool) {
|
|
1218
|
-
logger.error(`Unknown tool: ${String(selected)}`);
|
|
1219
|
-
return;
|
|
1220
|
-
}
|
|
1221
|
-
const cwd = process.cwd();
|
|
1222
|
-
const result = generateInitSkills(tool.targetDir, cwd);
|
|
1223
|
-
if (result.copiedFiles.length === 0) {
|
|
1224
|
-
logger.warn("No skill files were copied.");
|
|
1225
|
-
return;
|
|
1226
|
-
}
|
|
1227
|
-
for (const file of result.copiedFiles) {
|
|
1228
|
-
logger.log(` ${file}`);
|
|
1229
|
-
}
|
|
1230
|
-
outro(`Skills installed to ${tool.targetDir}/`);
|
|
1231
|
-
});
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
// src/utils/config.ts
|
|
1235
|
-
import fs3 from "fs";
|
|
1236
|
-
import os from "os";
|
|
1237
|
-
import path6 from "path";
|
|
1238
|
-
var CONFIG_DIR = ".lux";
|
|
1239
|
-
var ENV_FILE = "env.txt";
|
|
1240
|
-
function getEnvConfigPath() {
|
|
1241
|
-
return path6.join(os.homedir(), CONFIG_DIR, ENV_FILE);
|
|
1242
|
-
}
|
|
1243
|
-
function getEnvConfig() {
|
|
1244
|
-
let content;
|
|
1245
|
-
try {
|
|
1246
|
-
content = fs3.readFileSync(getEnvConfigPath(), "utf-8");
|
|
1247
|
-
} catch {
|
|
1248
|
-
return {};
|
|
1249
|
-
}
|
|
1250
|
-
const result = {};
|
|
1251
|
-
for (const line of content.split("\n")) {
|
|
1252
|
-
const trimmed = line.trim();
|
|
1253
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1254
|
-
const eqIndex = trimmed.indexOf("=");
|
|
1255
|
-
if (eqIndex === -1) continue;
|
|
1256
|
-
const key = trimmed.slice(0, eqIndex).trim();
|
|
1257
|
-
const raw = trimmed.slice(eqIndex + 1).trim();
|
|
1258
|
-
const value = raw.replace(/^["']|["']$/g, "");
|
|
1259
|
-
if (key) result[key] = value;
|
|
1260
|
-
}
|
|
1261
|
-
return result;
|
|
1262
|
-
}
|
|
1263
|
-
function setEnvConfig(data) {
|
|
1264
|
-
const lines = Object.entries(data).filter(([, v]) => v !== "").map(([k, v]) => `${k}="${v}"`);
|
|
1265
|
-
writeFile(getEnvConfigPath(), lines.join("\n") + "\n");
|
|
1266
|
-
}
|
|
1267
|
-
function clearEnvConfig() {
|
|
1268
|
-
try {
|
|
1269
|
-
fs3.unlinkSync(getEnvConfigPath());
|
|
1270
|
-
} catch {
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
// src/commands/show.ts
|
|
1275
|
-
function handleShowEnv() {
|
|
1276
|
-
const config = getEnvConfig();
|
|
1277
|
-
const entries = Object.entries(config);
|
|
1278
|
-
if (entries.length === 0) {
|
|
1279
|
-
logger.log("No env config.");
|
|
1280
|
-
return;
|
|
1281
|
-
}
|
|
1282
|
-
for (const [key, value] of entries) {
|
|
1283
|
-
logger.log(`${key}="${value}"`);
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
function registerShowCommand(program2) {
|
|
1287
|
-
const show = program2.command("show");
|
|
1288
|
-
show.command("env").description("Display stored proxy environment variables").action(() => handleShowEnv());
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
// src/utils/version.ts
|
|
1292
|
-
import { existsSync, readFileSync } from "fs";
|
|
1293
|
-
import { dirname, join } from "path";
|
|
1294
|
-
import { fileURLToPath } from "url";
|
|
1295
|
-
var PACKAGE_NAME = "@luxkit/cli";
|
|
1296
|
-
var cachedVersion;
|
|
1297
|
-
function getCurrentVersion() {
|
|
1298
|
-
if (cachedVersion) return cachedVersion;
|
|
1299
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1300
|
-
const candidates = [
|
|
1301
|
-
join(__dirname, "..", "package.json"),
|
|
1302
|
-
join(__dirname, "..", "..", "package.json")
|
|
1303
|
-
];
|
|
1304
|
-
const pkgPath = candidates.find((p) => existsSync(p));
|
|
1305
|
-
if (!pkgPath) {
|
|
1306
|
-
throw new Error("Cannot locate package.json for version reading");
|
|
1307
|
-
}
|
|
1308
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1309
|
-
cachedVersion = pkg.version;
|
|
1310
|
-
return cachedVersion;
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
// src/commands/update.ts
|
|
1314
|
-
var GLOBAL_UPDATE_CMDS = {
|
|
1315
|
-
npm: ["npm", ["install", "-g", `${PACKAGE_NAME}@latest`]],
|
|
1316
|
-
bun: ["bun", ["install", "-g", `${PACKAGE_NAME}@latest`]]
|
|
1317
|
-
};
|
|
1318
|
-
function detectGlobalPackageManager() {
|
|
1319
|
-
return process.execPath.toLowerCase().includes("bun") ? "bun" : "npm";
|
|
1320
|
-
}
|
|
1321
|
-
async function fetchLatestVersion() {
|
|
1322
|
-
const { stdout, exitCode } = await execFileNoThrow("npm", ["view", PACKAGE_NAME, "version"]);
|
|
1323
|
-
if (exitCode !== 0 || !stdout) {
|
|
1324
|
-
throw new Error(`Failed to fetch latest version from npm registry.`);
|
|
1325
|
-
}
|
|
1326
|
-
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
1327
|
-
return lines[lines.length - 1].trim();
|
|
1328
|
-
}
|
|
1329
|
-
async function performUpdate(pm) {
|
|
1330
|
-
const [command, args] = GLOBAL_UPDATE_CMDS[pm];
|
|
1331
|
-
const { exitCode, stderr } = await execFileNoThrow(command, args);
|
|
1332
|
-
if (exitCode !== 0) {
|
|
1333
|
-
const hint = /EACCES|permission/i.test(stderr) ? "\nTry running with elevated privileges (e.g. sudo)." : "";
|
|
1334
|
-
throw new Error(`Update failed: ${stderr}${hint}`);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
function registerUpdateCommand(program2) {
|
|
1338
|
-
program2.command("update").description("Update @luxkit/cli to the latest version").option("--check", "Only check for updates without installing").action(async (options) => {
|
|
1339
|
-
try {
|
|
1340
|
-
const current = getCurrentVersion();
|
|
1341
|
-
const latest = await fetchLatestVersion();
|
|
1342
|
-
if (current === latest) {
|
|
1343
|
-
logger.log(`Already up to date (v${current})`);
|
|
1344
|
-
return;
|
|
1345
|
-
}
|
|
1346
|
-
if (options.check) {
|
|
1347
|
-
logger.log(`Update available: v${current} \u2192 v${latest}`);
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
const pm = detectGlobalPackageManager();
|
|
1351
|
-
await performUpdate(pm);
|
|
1352
|
-
logger.log(`Updated to v${latest}`);
|
|
1353
|
-
} catch (err) {
|
|
1354
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1355
|
-
logger.error(message);
|
|
1356
|
-
logger.warn(`You can also update manually: npm install -g ${PACKAGE_NAME}@latest`);
|
|
1357
|
-
}
|
|
1358
|
-
});
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
1611
|
// src/presets/vscode/web-vue.ts
|
|
1362
1612
|
var webVueVscode = {
|
|
1363
1613
|
name: "web-vue",
|
|
@@ -2116,59 +2366,234 @@ var VSCODE_PRESETS = [
|
|
|
2116
2366
|
goVscode
|
|
2117
2367
|
];
|
|
2118
2368
|
|
|
2119
|
-
// src/
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
"editor.autoClosingOvertype"
|
|
2137
|
-
]);
|
|
2138
|
-
function mergeVscodeSettings(preset, existing) {
|
|
2139
|
-
const result = { ...existing };
|
|
2140
|
-
for (const [key, presetVal] of Object.entries(preset)) {
|
|
2141
|
-
const existingVal = existing[key];
|
|
2142
|
-
if (existingVal === void 0) {
|
|
2143
|
-
result[key] = presetVal;
|
|
2144
|
-
continue;
|
|
2369
|
+
// src/generators/init.ts
|
|
2370
|
+
import fs4 from "fs";
|
|
2371
|
+
import path6 from "path";
|
|
2372
|
+
function resolveSkillsDir() {
|
|
2373
|
+
const entryDir = path6.dirname(process.argv[1] ?? "");
|
|
2374
|
+
return path6.resolve(entryDir, "skills");
|
|
2375
|
+
}
|
|
2376
|
+
function listFilesRecursive(dir, base) {
|
|
2377
|
+
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
2378
|
+
const files = [];
|
|
2379
|
+
for (const entry of entries) {
|
|
2380
|
+
const childBase = `${base}/${entry.name}`;
|
|
2381
|
+
const fullPath = path6.join(dir, entry.name);
|
|
2382
|
+
if (entry.isDirectory()) {
|
|
2383
|
+
files.push(...listFilesRecursive(fullPath, childBase));
|
|
2384
|
+
} else {
|
|
2385
|
+
files.push(childBase);
|
|
2145
2386
|
}
|
|
2146
|
-
|
|
2147
|
-
|
|
2387
|
+
}
|
|
2388
|
+
return files;
|
|
2389
|
+
}
|
|
2390
|
+
function generateInitSkills(targetBaseDir, cwd) {
|
|
2391
|
+
const skillsDir = resolveSkillsDir();
|
|
2392
|
+
if (!fs4.existsSync(skillsDir)) {
|
|
2393
|
+
logger.error(`Bundled skills directory not found: ${skillsDir}`);
|
|
2394
|
+
logger.error('Please run "lux build" or reinstall lux.');
|
|
2395
|
+
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
2396
|
+
}
|
|
2397
|
+
const targetPath = path6.resolve(cwd, targetBaseDir);
|
|
2398
|
+
try {
|
|
2399
|
+
fs4.cpSync(skillsDir, targetPath, { recursive: true, force: true });
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2402
|
+
logger.error(`Failed to copy skills to ${targetPath}: ${message}`);
|
|
2403
|
+
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
2404
|
+
}
|
|
2405
|
+
const copiedFiles = fs4.existsSync(targetPath) ? listFilesRecursive(targetPath, targetBaseDir) : [];
|
|
2406
|
+
return { copiedFiles, targetDir: targetBaseDir };
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
// src/commands/init.ts
|
|
2410
|
+
function registerInitCommand(program2) {
|
|
2411
|
+
program2.command("init").description("Initialize skills or materialize presets").option("--preset", "Materialize all presets to ~/.lux/preset/ without writing to cwd").action(async (options) => {
|
|
2412
|
+
if (options.preset) {
|
|
2413
|
+
materializeAllPresets();
|
|
2414
|
+
return;
|
|
2148
2415
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2416
|
+
const toolOptions = INIT_TOOLS.map((tool2) => ({
|
|
2417
|
+
value: tool2.name,
|
|
2418
|
+
label: tool2.label
|
|
2419
|
+
}));
|
|
2420
|
+
const selected = await select({
|
|
2421
|
+
message: "Which AI coding tool do you use?",
|
|
2422
|
+
options: toolOptions
|
|
2423
|
+
});
|
|
2424
|
+
if (isCancel(selected)) {
|
|
2425
|
+
cancel("Operation cancelled.");
|
|
2426
|
+
return;
|
|
2155
2427
|
}
|
|
2156
|
-
|
|
2428
|
+
const tool = INIT_TOOLS.find((t) => t.name === selected);
|
|
2429
|
+
if (!tool) {
|
|
2430
|
+
logger.error(`Unknown tool: ${String(selected)}`);
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
const cwd = process.cwd();
|
|
2434
|
+
const result = generateInitSkills(tool.targetDir, cwd);
|
|
2435
|
+
if (result.copiedFiles.length === 0) {
|
|
2436
|
+
logger.warn("No skill files were copied.");
|
|
2437
|
+
return;
|
|
2438
|
+
}
|
|
2439
|
+
for (const file of result.copiedFiles) {
|
|
2440
|
+
logger.log(` ${file}`);
|
|
2441
|
+
}
|
|
2442
|
+
outro(`Skills installed to ${tool.targetDir}/`);
|
|
2443
|
+
});
|
|
2444
|
+
}
|
|
2445
|
+
function materializeAllPresets() {
|
|
2446
|
+
const opts = {
|
|
2447
|
+
cwd: process.cwd(),
|
|
2448
|
+
force: false,
|
|
2449
|
+
dryRun: false,
|
|
2450
|
+
noStylelint: false,
|
|
2451
|
+
noEditorconfig: false
|
|
2452
|
+
};
|
|
2453
|
+
for (const preset of FMT_PRESETS) {
|
|
2454
|
+
materializeFmtPreset(preset.name, preset, opts);
|
|
2455
|
+
}
|
|
2456
|
+
for (const preset of VSCODE_PRESETS) {
|
|
2457
|
+
materializeVscodePresetFromBuiltin(preset.name, preset);
|
|
2458
|
+
}
|
|
2459
|
+
logger.success("All presets materialized to ~/.lux/preset/");
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// src/utils/config.ts
|
|
2463
|
+
import fs5 from "fs";
|
|
2464
|
+
import os2 from "os";
|
|
2465
|
+
import path7 from "path";
|
|
2466
|
+
var CONFIG_DIR = ".lux";
|
|
2467
|
+
var ENV_FILE = "env.txt";
|
|
2468
|
+
function getEnvConfigPath() {
|
|
2469
|
+
return path7.join(os2.homedir(), CONFIG_DIR, ENV_FILE);
|
|
2470
|
+
}
|
|
2471
|
+
function getEnvConfig() {
|
|
2472
|
+
let content;
|
|
2473
|
+
try {
|
|
2474
|
+
content = fs5.readFileSync(getEnvConfigPath(), "utf-8");
|
|
2475
|
+
} catch {
|
|
2476
|
+
return {};
|
|
2477
|
+
}
|
|
2478
|
+
const result = {};
|
|
2479
|
+
for (const line of content.split("\n")) {
|
|
2480
|
+
const trimmed = line.trim();
|
|
2481
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
2482
|
+
const eqIndex = trimmed.indexOf("=");
|
|
2483
|
+
if (eqIndex === -1) continue;
|
|
2484
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
2485
|
+
const raw = trimmed.slice(eqIndex + 1).trim();
|
|
2486
|
+
const value = raw.replace(/^["']|["']$/g, "");
|
|
2487
|
+
if (key) result[key] = value;
|
|
2157
2488
|
}
|
|
2158
2489
|
return result;
|
|
2159
2490
|
}
|
|
2160
|
-
function
|
|
2161
|
-
|
|
2491
|
+
function setEnvConfig(data) {
|
|
2492
|
+
const lines = Object.entries(data).filter(([, v]) => v !== "").map(([k, v]) => `${k}="${v}"`);
|
|
2493
|
+
writeFile(getEnvConfigPath(), lines.join("\n") + "\n");
|
|
2494
|
+
}
|
|
2495
|
+
function clearEnvConfig() {
|
|
2496
|
+
try {
|
|
2497
|
+
fs5.unlinkSync(getEnvConfigPath());
|
|
2498
|
+
} catch {
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
// src/commands/show.ts
|
|
2503
|
+
function handleShowEnv() {
|
|
2504
|
+
const config = getEnvConfig();
|
|
2505
|
+
const entries = Object.entries(config);
|
|
2506
|
+
if (entries.length === 0) {
|
|
2507
|
+
logger.log("No env config.");
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
for (const [key, value] of entries) {
|
|
2511
|
+
logger.log(`${key}="${value}"`);
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
function registerShowCommand(program2) {
|
|
2515
|
+
const show = program2.command("show");
|
|
2516
|
+
show.command("env").description("Display stored proxy environment variables").action(() => handleShowEnv());
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
// src/utils/version.ts
|
|
2520
|
+
import { existsSync, readFileSync } from "fs";
|
|
2521
|
+
import { dirname, join } from "path";
|
|
2522
|
+
import { fileURLToPath } from "url";
|
|
2523
|
+
var PACKAGE_NAME = "@luxkit/cli";
|
|
2524
|
+
var cachedVersion;
|
|
2525
|
+
function getCurrentVersion() {
|
|
2526
|
+
if (cachedVersion) return cachedVersion;
|
|
2527
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
2528
|
+
const candidates = [
|
|
2529
|
+
join(__dirname, "..", "package.json"),
|
|
2530
|
+
join(__dirname, "..", "..", "package.json")
|
|
2531
|
+
];
|
|
2532
|
+
const pkgPath = candidates.find((p) => existsSync(p));
|
|
2533
|
+
if (!pkgPath) {
|
|
2534
|
+
throw new Error("Cannot locate package.json for version reading");
|
|
2535
|
+
}
|
|
2536
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
2537
|
+
cachedVersion = pkg.version;
|
|
2538
|
+
return cachedVersion;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
// src/commands/update.ts
|
|
2542
|
+
var GLOBAL_UPDATE_CMDS = {
|
|
2543
|
+
npm: ["npm", ["install", "-g", `${PACKAGE_NAME}@latest`]],
|
|
2544
|
+
bun: ["bun", ["install", "-g", `${PACKAGE_NAME}@latest`]]
|
|
2545
|
+
};
|
|
2546
|
+
function detectGlobalPackageManager() {
|
|
2547
|
+
return process.execPath.toLowerCase().includes("bun") ? "bun" : "npm";
|
|
2548
|
+
}
|
|
2549
|
+
async function fetchLatestVersion() {
|
|
2550
|
+
const { stdout, exitCode } = await execFileNoThrow("npm", ["view", PACKAGE_NAME, "version"]);
|
|
2551
|
+
if (exitCode !== 0 || !stdout) {
|
|
2552
|
+
throw new Error(`Failed to fetch latest version from npm registry.`);
|
|
2553
|
+
}
|
|
2554
|
+
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
2555
|
+
return lines[lines.length - 1].trim();
|
|
2556
|
+
}
|
|
2557
|
+
async function performUpdate(pm) {
|
|
2558
|
+
const [command, args] = GLOBAL_UPDATE_CMDS[pm];
|
|
2559
|
+
const { exitCode, stderr } = await execFileNoThrow(command, args);
|
|
2560
|
+
if (exitCode !== 0) {
|
|
2561
|
+
const hint = /EACCES|permission/i.test(stderr) ? "\nTry running with elevated privileges (e.g. sudo)." : "";
|
|
2562
|
+
throw new Error(`Update failed: ${stderr}${hint}`);
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
function registerUpdateCommand(program2) {
|
|
2566
|
+
program2.command("update").description("Update @luxkit/cli to the latest version").option("--check", "Only check for updates without installing").action(async (options) => {
|
|
2567
|
+
try {
|
|
2568
|
+
const current = getCurrentVersion();
|
|
2569
|
+
const latest = await fetchLatestVersion();
|
|
2570
|
+
if (current === latest) {
|
|
2571
|
+
logger.log(`Already up to date (v${current})`);
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
if (options.check) {
|
|
2575
|
+
logger.log(`Update available: v${current} \u2192 v${latest}`);
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
const pm = detectGlobalPackageManager();
|
|
2579
|
+
await performUpdate(pm);
|
|
2580
|
+
logger.log(`Updated to v${latest}`);
|
|
2581
|
+
} catch (err) {
|
|
2582
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2583
|
+
logger.error(message);
|
|
2584
|
+
logger.warn(`You can also update manually: npm install -g ${PACKAGE_NAME}@latest`);
|
|
2585
|
+
}
|
|
2586
|
+
});
|
|
2162
2587
|
}
|
|
2163
2588
|
|
|
2164
2589
|
// src/generators/vscode.ts
|
|
2165
|
-
var
|
|
2590
|
+
var STYLELINT_SETTINGS_PREFIXES2 = [
|
|
2166
2591
|
"stylelint.",
|
|
2167
2592
|
"css.validate",
|
|
2168
2593
|
"less.validate",
|
|
2169
2594
|
"scss.validate"
|
|
2170
2595
|
];
|
|
2171
|
-
var
|
|
2596
|
+
var STYLELINT_EXTENSION2 = "stylelint.vscode-stylelint";
|
|
2172
2597
|
function generateVscodeSettings(preset, opts) {
|
|
2173
2598
|
const settingsPath = `${opts.cwd}/.vscode/settings.json`;
|
|
2174
2599
|
if (opts.dryRun) {
|
|
@@ -2176,7 +2601,7 @@ function generateVscodeSettings(preset, opts) {
|
|
|
2176
2601
|
return existingSettings2 ? "overwritten" : "created";
|
|
2177
2602
|
}
|
|
2178
2603
|
const rawSettings = preset.settings();
|
|
2179
|
-
const presetSettings = opts.noStylelint ?
|
|
2604
|
+
const presetSettings = opts.noStylelint ? filterStylelintSettings2(rawSettings) : rawSettings;
|
|
2180
2605
|
const existingSettings = readJson(settingsPath);
|
|
2181
2606
|
if (existingSettings) {
|
|
2182
2607
|
const backupPath = `${settingsPath}.bak`;
|
|
@@ -2212,7 +2637,7 @@ function generateVscodeSettings(preset, opts) {
|
|
|
2212
2637
|
}
|
|
2213
2638
|
function generateVscodeExtensions(preset, opts) {
|
|
2214
2639
|
if (opts.dryRun) return "created";
|
|
2215
|
-
const extensions = opts.noStylelint ? preset.extensions().filter((ext) => ext !==
|
|
2640
|
+
const extensions = opts.noStylelint ? preset.extensions().filter((ext) => ext !== STYLELINT_EXTENSION2) : preset.extensions();
|
|
2216
2641
|
try {
|
|
2217
2642
|
writeJson(`${opts.cwd}/.vscode/extensions.json`, { recommendations: extensions });
|
|
2218
2643
|
} catch (error) {
|
|
@@ -2231,10 +2656,10 @@ function generateAllVscode(preset, opts) {
|
|
|
2231
2656
|
if (extAction === "created") result.created.push(".vscode/extensions.json");
|
|
2232
2657
|
return result;
|
|
2233
2658
|
}
|
|
2234
|
-
function
|
|
2659
|
+
function filterStylelintSettings2(settings) {
|
|
2235
2660
|
const filtered = Object.fromEntries(
|
|
2236
2661
|
Object.entries(settings).filter(
|
|
2237
|
-
([key]) => !
|
|
2662
|
+
([key]) => !STYLELINT_SETTINGS_PREFIXES2.some((prefix) => key.startsWith(prefix))
|
|
2238
2663
|
)
|
|
2239
2664
|
);
|
|
2240
2665
|
if (typeof filtered["editor.codeActionsOnSave"] === "object" && filtered["editor.codeActionsOnSave"] !== null) {
|
|
@@ -2248,29 +2673,20 @@ function filterStylelintSettings(settings) {
|
|
|
2248
2673
|
// src/commands/vscode.ts
|
|
2249
2674
|
function registerVscodeCommand(program2) {
|
|
2250
2675
|
const vscode = program2.command("vscode").description("Initialize VSCode config with preset");
|
|
2251
|
-
vscode.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--dry-run", "Preview without writing files").option("--stylelint", "Include Stylelint settings and extension").action(
|
|
2676
|
+
vscode.argument("<preset>").option("-F, --force", "Force overwrite existing files").option("--dry-run", "Preview without writing files").option("--stylelint", "Include Stylelint settings and extension").option("--reset", "Reset local preset and re-materialize from built-in").action(
|
|
2252
2677
|
async (presetName, options) => {
|
|
2253
2678
|
const preset = resolvePreset(VSCODE_PRESETS, presetName);
|
|
2254
2679
|
if (!preset) return;
|
|
2255
2680
|
const cwd = process.cwd();
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
force: options.force ?? false,
|
|
2259
|
-
dryRun: options.dryRun ?? false,
|
|
2260
|
-
noStylelint: options.stylelint !== true,
|
|
2261
|
-
noEditorconfig: false
|
|
2262
|
-
};
|
|
2263
|
-
const result = generateAllVscode(preset, opts);
|
|
2264
|
-
const files = [...result.created, ...result.overwritten];
|
|
2265
|
-
if (files.length === 0) {
|
|
2266
|
-
logger.warn("No files generated");
|
|
2267
|
-
return;
|
|
2681
|
+
if (options.reset) {
|
|
2682
|
+
resetLocalPreset("vscode", presetName);
|
|
2268
2683
|
}
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2684
|
+
const useLocal = localPresetExists("vscode", presetName);
|
|
2685
|
+
if (useLocal) {
|
|
2686
|
+
executeVscodeLocalPath(cwd, presetName, options);
|
|
2687
|
+
} else {
|
|
2688
|
+
executeVscodeBuiltinPath(cwd, presetName, preset, options);
|
|
2272
2689
|
}
|
|
2273
|
-
logger.log(`Created ${files.join(", ")}`);
|
|
2274
2690
|
}
|
|
2275
2691
|
);
|
|
2276
2692
|
vscode.command("list").description("List available vscode presets").action(() => {
|
|
@@ -2279,6 +2695,48 @@ function registerVscodeCommand(program2) {
|
|
|
2279
2695
|
}
|
|
2280
2696
|
});
|
|
2281
2697
|
}
|
|
2698
|
+
function executeVscodeLocalPath(cwd, presetName, options) {
|
|
2699
|
+
logger.log("Using local custom preset");
|
|
2700
|
+
const opts = {
|
|
2701
|
+
cwd,
|
|
2702
|
+
force: options.force ?? false,
|
|
2703
|
+
dryRun: options.dryRun ?? false,
|
|
2704
|
+
noStylelint: options.stylelint !== true,
|
|
2705
|
+
noEditorconfig: false
|
|
2706
|
+
};
|
|
2707
|
+
const result = applyLocalVscodePreset(cwd, presetName, opts);
|
|
2708
|
+
const files = [...result.created, ...result.overwritten];
|
|
2709
|
+
if (files.length === 0) {
|
|
2710
|
+
logger.warn("No files generated");
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
if (opts.dryRun) {
|
|
2714
|
+
logger.log(`[dry-run] Would create ${files.join(", ")} from local preset`);
|
|
2715
|
+
return;
|
|
2716
|
+
}
|
|
2717
|
+
logger.log(`Created ${files.join(", ")} from local preset`);
|
|
2718
|
+
}
|
|
2719
|
+
function executeVscodeBuiltinPath(cwd, presetName, preset, options) {
|
|
2720
|
+
const opts = {
|
|
2721
|
+
cwd,
|
|
2722
|
+
force: options.force ?? false,
|
|
2723
|
+
dryRun: options.dryRun ?? false,
|
|
2724
|
+
noStylelint: options.stylelint !== true,
|
|
2725
|
+
noEditorconfig: false
|
|
2726
|
+
};
|
|
2727
|
+
const result = generateAllVscode(preset, opts);
|
|
2728
|
+
const files = [...result.created, ...result.overwritten];
|
|
2729
|
+
if (files.length === 0) {
|
|
2730
|
+
logger.warn("No files generated");
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
if (opts.dryRun) {
|
|
2734
|
+
logger.log(`[dry-run] Would create ${files.join(", ")}`);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
logger.log(`Created ${files.join(", ")}`);
|
|
2738
|
+
materializeVscodePreset(cwd, presetName);
|
|
2739
|
+
}
|
|
2282
2740
|
|
|
2283
2741
|
// src/commands/vpn.ts
|
|
2284
2742
|
import { spawnSync } from "child_process";
|