@luxkit/cli 1.1.1 → 1.1.3
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 +85 -91
- package/README_Zh.md +189 -0
- package/dist/index.js +420 -330
- package/dist/skills/lux/references/custom-preset-setting.md +125 -0
- package/dist/skills/lux/skill.md +18 -5
- package/package.json +7 -12
package/dist/index.js
CHANGED
|
@@ -6,19 +6,20 @@ import { program } from "commander";
|
|
|
6
6
|
// src/commands/fmt.ts
|
|
7
7
|
import fs3 from "fs";
|
|
8
8
|
import path5 from "path";
|
|
9
|
+
import chalk2 from "chalk";
|
|
9
10
|
|
|
10
11
|
// src/presets/fmt/electron-vue.ts
|
|
11
12
|
var electronVueFmt = {
|
|
12
13
|
name: "electron-vue",
|
|
13
14
|
description: "Vue 3 + Electron desktop app",
|
|
14
15
|
eslint: () => `import withVue from '@vue/eslint-config-typescript'
|
|
15
|
-
import
|
|
16
|
+
import prettierConfig from '@vue/eslint-config-prettier/skip-formatting'
|
|
16
17
|
import pluginVue from 'eslint-plugin-vue'
|
|
17
18
|
|
|
18
19
|
export default [
|
|
19
20
|
...pluginVue.configs['flat/recommended'],
|
|
20
21
|
...withVue(),
|
|
21
|
-
|
|
22
|
+
prettierConfig,
|
|
22
23
|
{
|
|
23
24
|
rules: {
|
|
24
25
|
'vue/multi-word-component-names': 'off',
|
|
@@ -79,7 +80,8 @@ out/
|
|
|
79
80
|
version: "0.2",
|
|
80
81
|
language: "en,en-US",
|
|
81
82
|
allowCompoundWords: true,
|
|
82
|
-
words: ["vite", "pinia", "vueuse", "unplugin", "electron", "electron-builder"]
|
|
83
|
+
words: ["vite", "pinia", "vueuse", "unplugin", "electron", "electron-builder"],
|
|
84
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
83
85
|
},
|
|
84
86
|
null,
|
|
85
87
|
2
|
|
@@ -115,18 +117,9 @@ trim_trailing_whitespace = false
|
|
|
115
117
|
]
|
|
116
118
|
},
|
|
117
119
|
scripts: {
|
|
118
|
-
lint:
|
|
119
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
120
|
-
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
121
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,vue,css,scss}"',
|
|
122
|
-
stylelint: 'stylelint "src/**/*.{css,scss,vue}"',
|
|
123
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss,vue}" --fix',
|
|
124
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
125
|
-
"type:check": "vue-tsc --noEmit",
|
|
126
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
127
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
128
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
129
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
120
|
+
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/',
|
|
121
|
+
"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/',
|
|
122
|
+
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
130
123
|
}
|
|
131
124
|
};
|
|
132
125
|
|
|
@@ -163,7 +156,8 @@ coverage/
|
|
|
163
156
|
version: "0.2",
|
|
164
157
|
language: "en,en-US",
|
|
165
158
|
allowCompoundWords: true,
|
|
166
|
-
words: ["nestjs", "typeorm", "dtos"]
|
|
159
|
+
words: ["nestjs", "typeorm", "dtos"],
|
|
160
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
167
161
|
},
|
|
168
162
|
null,
|
|
169
163
|
2
|
|
@@ -184,12 +178,10 @@ trim_trailing_whitespace = false
|
|
|
184
178
|
dependencies: {
|
|
185
179
|
dev: ["prettier", "cspell"]
|
|
186
180
|
},
|
|
187
|
-
// NestJS: only append new scripts, don't conflict with existing ones
|
|
188
181
|
scripts: {
|
|
189
|
-
|
|
190
|
-
"
|
|
191
|
-
|
|
192
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> cspell"
|
|
182
|
+
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',
|
|
183
|
+
"lint:fix": 'eslint "{src,apps,libs,test}/**/*.ts" --cache --cache-location node_modules/.cache/eslint --fix',
|
|
184
|
+
format: 'prettier --write "src/**/*.{ts,js,json}"'
|
|
193
185
|
}
|
|
194
186
|
};
|
|
195
187
|
|
|
@@ -268,7 +260,8 @@ coverage/
|
|
|
268
260
|
version: "0.2",
|
|
269
261
|
language: "en,en-US",
|
|
270
262
|
allowCompoundWords: true,
|
|
271
|
-
words: []
|
|
263
|
+
words: [],
|
|
264
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
272
265
|
},
|
|
273
266
|
null,
|
|
274
267
|
2
|
|
@@ -298,16 +291,9 @@ trim_trailing_whitespace = false
|
|
|
298
291
|
]
|
|
299
292
|
},
|
|
300
293
|
scripts: {
|
|
301
|
-
lint:
|
|
302
|
-
"lint:fix":
|
|
303
|
-
format: 'prettier --write "src/**/*.{ts,js,json}"'
|
|
304
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json}"',
|
|
305
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
306
|
-
"type:check": "tsc --noEmit",
|
|
307
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
308
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
309
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> cspell",
|
|
310
|
-
"code:fix:all": "<pm> lint:fix && <pm> format"
|
|
294
|
+
lint: 'eslint . --cache --cache-location node_modules/.cache/eslint && cspell --cache --cache-location node_modules/.cache/cspell --gitignore "src/**/*" && tsc --noEmit',
|
|
295
|
+
"lint:fix": "eslint . --cache --cache-location node_modules/.cache/eslint --fix",
|
|
296
|
+
format: 'prettier --write "src/**/*.{ts,js,json}"'
|
|
311
297
|
}
|
|
312
298
|
};
|
|
313
299
|
|
|
@@ -316,13 +302,13 @@ var uniappFmt = {
|
|
|
316
302
|
name: "uniapp",
|
|
317
303
|
description: "Vue 3 + UniApp WeChat mini program",
|
|
318
304
|
eslint: () => `import withVue from '@vue/eslint-config-typescript'
|
|
319
|
-
import
|
|
305
|
+
import prettierConfig from '@vue/eslint-config-prettier/skip-formatting'
|
|
320
306
|
import pluginVue from 'eslint-plugin-vue'
|
|
321
307
|
|
|
322
308
|
export default [
|
|
323
309
|
...pluginVue.configs['flat/recommended'],
|
|
324
310
|
...withVue(),
|
|
325
|
-
|
|
311
|
+
prettierConfig,
|
|
326
312
|
{
|
|
327
313
|
rules: {
|
|
328
314
|
'vue/multi-word-component-names': 'off',
|
|
@@ -381,7 +367,8 @@ unpackage/
|
|
|
381
367
|
version: "0.2",
|
|
382
368
|
language: "en,en-US",
|
|
383
369
|
allowCompoundWords: true,
|
|
384
|
-
words: ["vite", "pinia", "vueuse", "unplugin", "uniapp"]
|
|
370
|
+
words: ["vite", "pinia", "vueuse", "unplugin", "uniapp"],
|
|
371
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
385
372
|
},
|
|
386
373
|
null,
|
|
387
374
|
2
|
|
@@ -417,18 +404,9 @@ trim_trailing_whitespace = false
|
|
|
417
404
|
]
|
|
418
405
|
},
|
|
419
406
|
scripts: {
|
|
420
|
-
lint:
|
|
421
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
422
|
-
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
423
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,vue,css,scss}"',
|
|
424
|
-
stylelint: 'stylelint "src/**/*.{css,scss,vue}"',
|
|
425
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss,vue}" --fix',
|
|
426
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
427
|
-
"type:check": "vue-tsc --noEmit",
|
|
428
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
429
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
430
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
431
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
407
|
+
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/',
|
|
408
|
+
"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/',
|
|
409
|
+
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
432
410
|
}
|
|
433
411
|
};
|
|
434
412
|
|
|
@@ -507,7 +485,8 @@ dist/
|
|
|
507
485
|
version: "0.2",
|
|
508
486
|
language: "en,en-US",
|
|
509
487
|
allowCompoundWords: true,
|
|
510
|
-
words: ["vite", "react", "zustand", "tanstack"]
|
|
488
|
+
words: ["vite", "react", "zustand", "tanstack"],
|
|
489
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
511
490
|
},
|
|
512
491
|
null,
|
|
513
492
|
2
|
|
@@ -545,18 +524,9 @@ trim_trailing_whitespace = false
|
|
|
545
524
|
]
|
|
546
525
|
},
|
|
547
526
|
scripts: {
|
|
548
|
-
lint:
|
|
549
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
550
|
-
format: 'prettier --write "src/**/*.{ts,js,json,jsx,tsx,css,scss}"'
|
|
551
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,jsx,tsx,css,scss}"',
|
|
552
|
-
stylelint: 'stylelint "src/**/*.{css,scss}"',
|
|
553
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss}" --fix',
|
|
554
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
555
|
-
"type:check": "tsc --noEmit",
|
|
556
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
557
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
558
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
559
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
527
|
+
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/',
|
|
528
|
+
"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/',
|
|
529
|
+
format: 'prettier --write "src/**/*.{ts,js,json,jsx,tsx,css,scss}"'
|
|
560
530
|
}
|
|
561
531
|
};
|
|
562
532
|
|
|
@@ -565,13 +535,13 @@ var webVueFmt = {
|
|
|
565
535
|
name: "web-vue",
|
|
566
536
|
description: "Vue 3 Web frontend (Vite + Vue + TypeScript)",
|
|
567
537
|
eslint: () => `import withVue from '@vue/eslint-config-typescript'
|
|
568
|
-
import
|
|
538
|
+
import prettierConfig from '@vue/eslint-config-prettier/skip-formatting'
|
|
569
539
|
import pluginVue from 'eslint-plugin-vue'
|
|
570
540
|
|
|
571
541
|
export default [
|
|
572
542
|
...pluginVue.configs['flat/recommended'],
|
|
573
543
|
...withVue(),
|
|
574
|
-
|
|
544
|
+
prettierConfig,
|
|
575
545
|
{
|
|
576
546
|
rules: {
|
|
577
547
|
'vue/multi-word-component-names': 'off',
|
|
@@ -628,7 +598,8 @@ dist/
|
|
|
628
598
|
version: "0.2",
|
|
629
599
|
language: "en,en-US",
|
|
630
600
|
allowCompoundWords: true,
|
|
631
|
-
words: ["vite", "pinia", "vueuse", "unplugin"]
|
|
601
|
+
words: ["vite", "pinia", "vueuse", "unplugin"],
|
|
602
|
+
ignorePaths: ["**/*.svg", "**/*.png"]
|
|
632
603
|
},
|
|
633
604
|
null,
|
|
634
605
|
2
|
|
@@ -664,18 +635,9 @@ trim_trailing_whitespace = false
|
|
|
664
635
|
]
|
|
665
636
|
},
|
|
666
637
|
scripts: {
|
|
667
|
-
lint:
|
|
668
|
-
"lint:fix": 'eslint "src/**/*.{
|
|
669
|
-
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
670
|
-
"format:check": 'prettier --check "src/**/*.{ts,js,json,vue,css,scss}"',
|
|
671
|
-
stylelint: 'stylelint "src/**/*.{css,scss,vue}"',
|
|
672
|
-
"stylelint:fix": 'stylelint "src/**/*.{css,scss,vue}" --fix',
|
|
673
|
-
cspell: 'cspell --gitignore "src/**/*"',
|
|
674
|
-
"type:check": "vue-tsc --noEmit",
|
|
675
|
-
"code:check": "<pm> lint && <pm> format:check",
|
|
676
|
-
"code:fix": "<pm> lint:fix && <pm> format",
|
|
677
|
-
"code:check:all": "<pm> lint && <pm> format:check && <pm> stylelint && <pm> cspell",
|
|
678
|
-
"code:fix:all": "<pm> lint:fix && <pm> format && <pm> stylelint:fix"
|
|
638
|
+
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/',
|
|
639
|
+
"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/',
|
|
640
|
+
format: 'prettier --write "src/**/*.{ts,js,json,vue,css,scss}"'
|
|
679
641
|
}
|
|
680
642
|
};
|
|
681
643
|
|
|
@@ -707,7 +669,6 @@ var logger = {
|
|
|
707
669
|
};
|
|
708
670
|
|
|
709
671
|
// src/utils/errors.ts
|
|
710
|
-
import chalk2 from "chalk";
|
|
711
672
|
var CliError = class extends Error {
|
|
712
673
|
code;
|
|
713
674
|
suggestion;
|
|
@@ -753,14 +714,6 @@ function levenshtein(a, b) {
|
|
|
753
714
|
}
|
|
754
715
|
function resolvePreset(presets, name) {
|
|
755
716
|
const found = presets.find((p) => p.name === name);
|
|
756
|
-
if (!found) {
|
|
757
|
-
const err = new PresetNotFoundError(
|
|
758
|
-
name,
|
|
759
|
-
presets.map((p) => p.name)
|
|
760
|
-
);
|
|
761
|
-
console.error(chalk2.red(err.message));
|
|
762
|
-
process.exit(1);
|
|
763
|
-
}
|
|
764
717
|
return found;
|
|
765
718
|
}
|
|
766
719
|
|
|
@@ -1070,6 +1023,28 @@ function getLocalPresetDir(type, presetName) {
|
|
|
1070
1023
|
function isValidPresetName(name) {
|
|
1071
1024
|
return name.length > 0 && !name.includes("/") && !name.includes("\\") && !name.includes("..");
|
|
1072
1025
|
}
|
|
1026
|
+
function listCustomPresets() {
|
|
1027
|
+
const fmtDir = path4.join(getLuxDir(), "preset", "fmt");
|
|
1028
|
+
if (!fs2.existsSync(fmtDir)) return [];
|
|
1029
|
+
const entries = fs2.readdirSync(fmtDir, { withFileTypes: true });
|
|
1030
|
+
const result = [];
|
|
1031
|
+
for (const entry of entries) {
|
|
1032
|
+
if (!entry.isDirectory()) continue;
|
|
1033
|
+
if (!isValidPresetName(entry.name)) continue;
|
|
1034
|
+
const pkgPath = path4.join(fmtDir, entry.name, "package.json");
|
|
1035
|
+
if (fs2.existsSync(pkgPath)) {
|
|
1036
|
+
result.push(entry.name);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
return result;
|
|
1040
|
+
}
|
|
1041
|
+
function isValidCustomPreset(name) {
|
|
1042
|
+
if (!isValidPresetName(name)) return false;
|
|
1043
|
+
const presetDir = path4.join(getLuxDir(), "preset", "fmt", name);
|
|
1044
|
+
if (!fs2.existsSync(presetDir)) return false;
|
|
1045
|
+
const pkgPath = path4.join(presetDir, "package.json");
|
|
1046
|
+
return fs2.existsSync(pkgPath);
|
|
1047
|
+
}
|
|
1073
1048
|
function localPresetExists(type, presetName) {
|
|
1074
1049
|
const dir = getLocalPresetDir(type, presetName);
|
|
1075
1050
|
return fs2.existsSync(dir);
|
|
@@ -1113,6 +1088,15 @@ function materializeVscodePreset(cwd, presetName) {
|
|
|
1113
1088
|
}
|
|
1114
1089
|
logger.log(`Local preset created at ${presetDir}`);
|
|
1115
1090
|
}
|
|
1091
|
+
function materializeVscodePresetFromBuiltin(presetName, preset) {
|
|
1092
|
+
const presetDir = getLocalPresetDir("vscode", presetName);
|
|
1093
|
+
ensureDir(presetDir);
|
|
1094
|
+
const settings = preset.settings();
|
|
1095
|
+
writeJson(path4.join(presetDir, "settings.json"), settings);
|
|
1096
|
+
const extensions = preset.extensions();
|
|
1097
|
+
writeJson(path4.join(presetDir, "extensions.json"), { recommendations: extensions });
|
|
1098
|
+
logger.log(`Local preset created at ${presetDir}`);
|
|
1099
|
+
}
|
|
1116
1100
|
var InvalidPackageJsonError = class extends Error {
|
|
1117
1101
|
constructor(filePath) {
|
|
1118
1102
|
super(`package.json exists but is not valid JSON: ${filePath}`);
|
|
@@ -1220,7 +1204,9 @@ function applyLocalVscodePreset(cwd, presetName, opts) {
|
|
|
1220
1204
|
if (extensionsData) {
|
|
1221
1205
|
let presetRecommendations = extensionsData.recommendations ?? [];
|
|
1222
1206
|
if (opts.noStylelint) {
|
|
1223
|
-
presetRecommendations = presetRecommendations.filter(
|
|
1207
|
+
presetRecommendations = presetRecommendations.filter(
|
|
1208
|
+
(ext) => ext !== STYLELINT_EXTENSION
|
|
1209
|
+
);
|
|
1224
1210
|
}
|
|
1225
1211
|
if (opts.dryRun) {
|
|
1226
1212
|
result.created.push(".vscode/extensions.json");
|
|
@@ -1272,8 +1258,12 @@ function mergeTemplateIntoProject(templatePkg, projectPkg, pm, opts, result) {
|
|
|
1272
1258
|
if (templatePkg.scripts) {
|
|
1273
1259
|
const existingScripts = merged.scripts ?? {};
|
|
1274
1260
|
const newScripts = { ...existingScripts };
|
|
1275
|
-
|
|
1276
|
-
|
|
1261
|
+
const filteredScripts = filterScripts(
|
|
1262
|
+
templatePkg.scripts,
|
|
1263
|
+
opts.noStylelint,
|
|
1264
|
+
opts.noEditorconfig
|
|
1265
|
+
);
|
|
1266
|
+
for (const [key, value] of Object.entries(filteredScripts)) {
|
|
1277
1267
|
const resolved = value.replace(/<pm>/g, prefix);
|
|
1278
1268
|
if (existingScripts[key] !== void 0 && !opts.force) {
|
|
1279
1269
|
result.scriptsSkipped++;
|
|
@@ -1309,6 +1299,42 @@ function filterStylelintSettings(settings) {
|
|
|
1309
1299
|
}
|
|
1310
1300
|
return filtered;
|
|
1311
1301
|
}
|
|
1302
|
+
function filterScripts(scripts, noStylelint, noEditorconfig) {
|
|
1303
|
+
const filtered = {};
|
|
1304
|
+
for (const [key, value] of Object.entries(scripts)) {
|
|
1305
|
+
if (noStylelint && key.includes("stylelint")) continue;
|
|
1306
|
+
if (noEditorconfig && key.includes("editorconfig")) continue;
|
|
1307
|
+
let resolved = value;
|
|
1308
|
+
if (noStylelint) {
|
|
1309
|
+
resolved = resolved.replace(/\s*&&\s*stylelint\s+"[^"]*".*/g, "");
|
|
1310
|
+
}
|
|
1311
|
+
filtered[key] = resolved;
|
|
1312
|
+
}
|
|
1313
|
+
return filtered;
|
|
1314
|
+
}
|
|
1315
|
+
function detectPresetCapabilities(presetName) {
|
|
1316
|
+
const presetDir = path4.join(getLuxDir(), "preset", "fmt", presetName);
|
|
1317
|
+
const entries = fs2.readdirSync(presetDir);
|
|
1318
|
+
const hasStylelintFile = entries.some((f) => STYLELINT_FILES.has(f));
|
|
1319
|
+
const pkg = readJson(
|
|
1320
|
+
path4.join(presetDir, "package.json")
|
|
1321
|
+
);
|
|
1322
|
+
const hasStylelintDep = pkg?.devDependencies ? Object.keys(pkg.devDependencies).some((d) => isNotStylelintDep(d) === false) : false;
|
|
1323
|
+
const hasEditorconfigFile = entries.includes(EDITORCONFIG_FILE);
|
|
1324
|
+
const hasEditorconfigDep = pkg?.devDependencies ? Object.keys(pkg.devDependencies).some((d) => !isNotEditorconfigDep(d)) : false;
|
|
1325
|
+
return {
|
|
1326
|
+
hasStylelint: hasStylelintFile || hasStylelintDep,
|
|
1327
|
+
hasEditorconfig: hasEditorconfigFile || hasEditorconfigDep
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
function isNotStylelintDep(dep) {
|
|
1331
|
+
if (dep.includes("stylelint")) return false;
|
|
1332
|
+
if (dep === "postcss-html" || dep === "postcss-scss") return false;
|
|
1333
|
+
return true;
|
|
1334
|
+
}
|
|
1335
|
+
function isNotEditorconfigDep(dep) {
|
|
1336
|
+
return !dep.includes("editorconfig");
|
|
1337
|
+
}
|
|
1312
1338
|
function resolveLocalDeps(deps) {
|
|
1313
1339
|
const packages = [];
|
|
1314
1340
|
for (const [name, version] of Object.entries(deps)) {
|
|
@@ -1322,28 +1348,24 @@ function resolveLocalDeps(deps) {
|
|
|
1322
1348
|
}
|
|
1323
1349
|
|
|
1324
1350
|
// src/commands/fmt.ts
|
|
1325
|
-
function
|
|
1326
|
-
const filtered = {};
|
|
1327
|
-
for (const [key, value] of Object.entries(scripts)) {
|
|
1328
|
-
if (key.startsWith("stylelint")) continue;
|
|
1329
|
-
filtered[key] = value.replace(/\s*&&\s*<pm>\s+stylelint\S*/g, "");
|
|
1330
|
-
}
|
|
1331
|
-
return filtered;
|
|
1332
|
-
}
|
|
1333
|
-
function isNotStylelintDep(dep) {
|
|
1351
|
+
function isNotStylelintDep2(dep) {
|
|
1334
1352
|
if (dep.includes("stylelint")) return false;
|
|
1335
1353
|
if (dep === "postcss-html" || dep === "postcss-scss") return false;
|
|
1336
1354
|
return true;
|
|
1337
1355
|
}
|
|
1338
|
-
function
|
|
1356
|
+
function isNotEditorconfigDep2(dep) {
|
|
1339
1357
|
return !dep.includes("editorconfig");
|
|
1340
1358
|
}
|
|
1341
1359
|
function registerFmtCommand(program2) {
|
|
1342
1360
|
const fmt = program2.command("fmt").description("Initialize formatting config with preset");
|
|
1343
1361
|
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(
|
|
1344
1362
|
async (presetName, options) => {
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1363
|
+
const builtinPreset = FMT_PRESETS.find((p) => p.name === presetName);
|
|
1364
|
+
const isBuiltin = builtinPreset !== void 0;
|
|
1365
|
+
if (options.reset && !isBuiltin) {
|
|
1366
|
+
logger.warn(`"${presetName}" is a custom preset, --reset has no builtin to restore`);
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1347
1369
|
const cwd = process.cwd();
|
|
1348
1370
|
const pkgPath = path5.join(cwd, "package.json");
|
|
1349
1371
|
if (fileExists(pkgPath)) {
|
|
@@ -1356,25 +1378,52 @@ function registerFmtCommand(program2) {
|
|
|
1356
1378
|
return;
|
|
1357
1379
|
}
|
|
1358
1380
|
}
|
|
1359
|
-
if (
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1381
|
+
if (isBuiltin) {
|
|
1382
|
+
if (options.reset) {
|
|
1383
|
+
resetLocalPreset("fmt", presetName);
|
|
1384
|
+
}
|
|
1385
|
+
const useLocal = localPresetExists("fmt", presetName);
|
|
1386
|
+
if (useLocal) {
|
|
1387
|
+
await executeLocalPath(cwd, presetName, options);
|
|
1388
|
+
} else {
|
|
1389
|
+
await executeBuiltinPath(cwd, presetName, builtinPreset, options);
|
|
1390
|
+
}
|
|
1391
|
+
} else if (isValidCustomPreset(presetName)) {
|
|
1364
1392
|
await executeLocalPath(cwd, presetName, options);
|
|
1365
1393
|
} else {
|
|
1366
|
-
|
|
1394
|
+
const builtinNames = new Set(FMT_PRESETS.map((p) => p.name));
|
|
1395
|
+
const customNames = listCustomPresets().filter((n) => !builtinNames.has(n));
|
|
1396
|
+
const allNames = [...builtinNames, ...customNames];
|
|
1397
|
+
const err = new PresetNotFoundError(presetName, allNames);
|
|
1398
|
+
logger.error(err.message);
|
|
1399
|
+
process.exitCode = 1;
|
|
1367
1400
|
}
|
|
1368
1401
|
}
|
|
1369
1402
|
);
|
|
1370
1403
|
fmt.command("list").description("List available fmt presets").action(() => {
|
|
1404
|
+
const builtinNames = new Set(FMT_PRESETS.map((p) => p.name));
|
|
1371
1405
|
for (const p of FMT_PRESETS) {
|
|
1372
1406
|
console.log(`${p.name.padEnd(12)} ${p.description}`);
|
|
1373
1407
|
}
|
|
1408
|
+
const customs = listCustomPresets().filter((name) => !builtinNames.has(name));
|
|
1409
|
+
for (const name of customs) {
|
|
1410
|
+
console.log(`${name.padEnd(12)} ${chalk2.yellow("(custom)")}`);
|
|
1411
|
+
}
|
|
1374
1412
|
});
|
|
1375
1413
|
}
|
|
1376
1414
|
async function executeLocalPath(cwd, presetName, options) {
|
|
1377
1415
|
logger.log("Using local custom preset");
|
|
1416
|
+
const caps = detectPresetCapabilities(presetName);
|
|
1417
|
+
if (options.stylelint && !caps.hasStylelint) {
|
|
1418
|
+
logger.warn(
|
|
1419
|
+
"--stylelint has no effect: this custom preset has no stylelint config or dependencies"
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
if (options.editorconfig && !caps.hasEditorconfig) {
|
|
1423
|
+
logger.warn(
|
|
1424
|
+
"--editorconfig has no effect: this custom preset has no editorconfig config or dependencies"
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1378
1427
|
const opts = {
|
|
1379
1428
|
cwd,
|
|
1380
1429
|
force: options.force ?? false,
|
|
@@ -1419,18 +1468,26 @@ async function executeLocalPath(cwd, presetName, options) {
|
|
|
1419
1468
|
const existingDeps = projectPkg.devDependencies ?? {};
|
|
1420
1469
|
const missing = depsToInstall.filter((dep) => !existingDeps[dep]);
|
|
1421
1470
|
if (missing.length === 0) return;
|
|
1422
|
-
if (
|
|
1423
|
-
|
|
1424
|
-
const added = await addDepsToManifest(resolved, cwd);
|
|
1425
|
-
if (added.length > 0) {
|
|
1426
|
-
logger.success(`Added to package.json (skipped install): ${added.join(", ")}`);
|
|
1427
|
-
} else {
|
|
1428
|
-
logger.log("All dependencies already in package.json");
|
|
1429
|
-
}
|
|
1471
|
+
if (opts.dryRun) {
|
|
1472
|
+
logger.log(`[dry-run] Would add to package.json: ${missing.join(", ")}`);
|
|
1430
1473
|
return;
|
|
1431
1474
|
}
|
|
1432
|
-
if (
|
|
1433
|
-
|
|
1475
|
+
if (options.install === false) {
|
|
1476
|
+
try {
|
|
1477
|
+
const filteredTemplateDeps = Object.fromEntries(
|
|
1478
|
+
Object.entries(templatePkg.devDependencies).filter(([k]) => missing.includes(k))
|
|
1479
|
+
);
|
|
1480
|
+
const resolved = resolveLocalDeps(filteredTemplateDeps);
|
|
1481
|
+
const added = await addDepsToManifest(resolved, cwd);
|
|
1482
|
+
if (added.length > 0) {
|
|
1483
|
+
logger.success(`Added to package.json (skipped install): ${added.join(", ")}`);
|
|
1484
|
+
} else {
|
|
1485
|
+
logger.log("All dependencies already in package.json");
|
|
1486
|
+
}
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1489
|
+
logger.warn(`Failed to fetch versions: ${message}. You can add dependencies manually.`);
|
|
1490
|
+
}
|
|
1434
1491
|
return;
|
|
1435
1492
|
}
|
|
1436
1493
|
try {
|
|
@@ -1471,24 +1528,29 @@ async function executeBuiltinPath(cwd, presetName, preset, options) {
|
|
|
1471
1528
|
warnMissingPackageJson(preset, options.install !== false);
|
|
1472
1529
|
return;
|
|
1473
1530
|
}
|
|
1474
|
-
const scripts =
|
|
1531
|
+
const scripts = preset.scripts ? filterScripts(preset.scripts, opts.noStylelint, opts.noEditorconfig) : void 0;
|
|
1475
1532
|
if (scripts) {
|
|
1476
1533
|
await injectScripts(scripts, opts, pm);
|
|
1477
1534
|
}
|
|
1478
1535
|
if (!preset.dependencies?.dev) return;
|
|
1479
|
-
const devDeps = opts.noStylelint ? preset.dependencies.dev.filter(
|
|
1480
|
-
const finalDeps = opts.noEditorconfig ? devDeps.filter(
|
|
1481
|
-
if (
|
|
1482
|
-
|
|
1483
|
-
if (added.length > 0) {
|
|
1484
|
-
logger.success(`Added to package.json (skipped install): ${added.join(", ")}`);
|
|
1485
|
-
} else {
|
|
1486
|
-
logger.log("All dependencies already in package.json");
|
|
1487
|
-
}
|
|
1536
|
+
const devDeps = opts.noStylelint ? preset.dependencies.dev.filter(isNotStylelintDep2) : preset.dependencies.dev;
|
|
1537
|
+
const finalDeps = opts.noEditorconfig ? devDeps.filter(isNotEditorconfigDep2) : devDeps;
|
|
1538
|
+
if (opts.dryRun) {
|
|
1539
|
+
logger.log(`[dry-run] Would add to package.json: ${finalDeps.join(", ")}`);
|
|
1488
1540
|
return;
|
|
1489
1541
|
}
|
|
1490
|
-
if (
|
|
1491
|
-
|
|
1542
|
+
if (options.install === false) {
|
|
1543
|
+
try {
|
|
1544
|
+
const added = await addDepsToManifest(finalDeps, cwd);
|
|
1545
|
+
if (added.length > 0) {
|
|
1546
|
+
logger.success(`Added to package.json (skipped install): ${added.join(", ")}`);
|
|
1547
|
+
} else {
|
|
1548
|
+
logger.log("All dependencies already in package.json");
|
|
1549
|
+
}
|
|
1550
|
+
} catch (error) {
|
|
1551
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1552
|
+
logger.warn(`Failed to fetch versions: ${message}. You can add dependencies manually.`);
|
|
1553
|
+
}
|
|
1492
1554
|
return;
|
|
1493
1555
|
}
|
|
1494
1556
|
try {
|
|
@@ -1502,8 +1564,8 @@ async function executeBuiltinPath(cwd, presetName, preset, options) {
|
|
|
1502
1564
|
}
|
|
1503
1565
|
function filterDeps(deps, noStylelint, noEditorconfig) {
|
|
1504
1566
|
let filtered = deps;
|
|
1505
|
-
if (noStylelint) filtered = filtered.filter(
|
|
1506
|
-
if (noEditorconfig) filtered = filtered.filter(
|
|
1567
|
+
if (noStylelint) filtered = filtered.filter(isNotStylelintDep2);
|
|
1568
|
+
if (noEditorconfig) filtered = filtered.filter(isNotEditorconfigDep2);
|
|
1507
1569
|
return filtered;
|
|
1508
1570
|
}
|
|
1509
1571
|
function logGenerationResult(result, dryRun) {
|
|
@@ -1622,206 +1684,6 @@ var INIT_TOOLS = [
|
|
|
1622
1684
|
}
|
|
1623
1685
|
];
|
|
1624
1686
|
|
|
1625
|
-
// src/generators/init.ts
|
|
1626
|
-
import fs4 from "fs";
|
|
1627
|
-
import path6 from "path";
|
|
1628
|
-
function resolveSkillsDir() {
|
|
1629
|
-
const entryDir = path6.dirname(process.argv[1] ?? "");
|
|
1630
|
-
return path6.resolve(entryDir, "skills");
|
|
1631
|
-
}
|
|
1632
|
-
function listFilesRecursive(dir, base) {
|
|
1633
|
-
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
1634
|
-
const files = [];
|
|
1635
|
-
for (const entry of entries) {
|
|
1636
|
-
const childBase = `${base}/${entry.name}`;
|
|
1637
|
-
const fullPath = path6.join(dir, entry.name);
|
|
1638
|
-
if (entry.isDirectory()) {
|
|
1639
|
-
files.push(...listFilesRecursive(fullPath, childBase));
|
|
1640
|
-
} else {
|
|
1641
|
-
files.push(childBase);
|
|
1642
|
-
}
|
|
1643
|
-
}
|
|
1644
|
-
return files;
|
|
1645
|
-
}
|
|
1646
|
-
function generateInitSkills(targetBaseDir, cwd) {
|
|
1647
|
-
const skillsDir = resolveSkillsDir();
|
|
1648
|
-
if (!fs4.existsSync(skillsDir)) {
|
|
1649
|
-
logger.error(`Bundled skills directory not found: ${skillsDir}`);
|
|
1650
|
-
logger.error('Please run "lux build" or reinstall lux.');
|
|
1651
|
-
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
1652
|
-
}
|
|
1653
|
-
const targetPath = path6.resolve(cwd, targetBaseDir);
|
|
1654
|
-
try {
|
|
1655
|
-
fs4.cpSync(skillsDir, targetPath, { recursive: true, force: true });
|
|
1656
|
-
} catch (error) {
|
|
1657
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1658
|
-
logger.error(`Failed to copy skills to ${targetPath}: ${message}`);
|
|
1659
|
-
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
1660
|
-
}
|
|
1661
|
-
const copiedFiles = fs4.existsSync(targetPath) ? listFilesRecursive(targetPath, targetBaseDir) : [];
|
|
1662
|
-
return { copiedFiles, targetDir: targetBaseDir };
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
// src/commands/init.ts
|
|
1666
|
-
function registerInitCommand(program2) {
|
|
1667
|
-
program2.command("init").description("Initialize AI coding tool skills in current project").action(async () => {
|
|
1668
|
-
const toolOptions = INIT_TOOLS.map((tool2) => ({
|
|
1669
|
-
value: tool2.name,
|
|
1670
|
-
label: tool2.label
|
|
1671
|
-
}));
|
|
1672
|
-
const selected = await select({
|
|
1673
|
-
message: "Which AI coding tool do you use?",
|
|
1674
|
-
options: toolOptions
|
|
1675
|
-
});
|
|
1676
|
-
if (isCancel(selected)) {
|
|
1677
|
-
cancel("Operation cancelled.");
|
|
1678
|
-
return;
|
|
1679
|
-
}
|
|
1680
|
-
const tool = INIT_TOOLS.find((t) => t.name === selected);
|
|
1681
|
-
if (!tool) {
|
|
1682
|
-
logger.error(`Unknown tool: ${String(selected)}`);
|
|
1683
|
-
return;
|
|
1684
|
-
}
|
|
1685
|
-
const cwd = process.cwd();
|
|
1686
|
-
const result = generateInitSkills(tool.targetDir, cwd);
|
|
1687
|
-
if (result.copiedFiles.length === 0) {
|
|
1688
|
-
logger.warn("No skill files were copied.");
|
|
1689
|
-
return;
|
|
1690
|
-
}
|
|
1691
|
-
for (const file of result.copiedFiles) {
|
|
1692
|
-
logger.log(` ${file}`);
|
|
1693
|
-
}
|
|
1694
|
-
outro(`Skills installed to ${tool.targetDir}/`);
|
|
1695
|
-
});
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
// src/utils/config.ts
|
|
1699
|
-
import fs5 from "fs";
|
|
1700
|
-
import os2 from "os";
|
|
1701
|
-
import path7 from "path";
|
|
1702
|
-
var CONFIG_DIR = ".lux";
|
|
1703
|
-
var ENV_FILE = "env.txt";
|
|
1704
|
-
function getEnvConfigPath() {
|
|
1705
|
-
return path7.join(os2.homedir(), CONFIG_DIR, ENV_FILE);
|
|
1706
|
-
}
|
|
1707
|
-
function getEnvConfig() {
|
|
1708
|
-
let content;
|
|
1709
|
-
try {
|
|
1710
|
-
content = fs5.readFileSync(getEnvConfigPath(), "utf-8");
|
|
1711
|
-
} catch {
|
|
1712
|
-
return {};
|
|
1713
|
-
}
|
|
1714
|
-
const result = {};
|
|
1715
|
-
for (const line of content.split("\n")) {
|
|
1716
|
-
const trimmed = line.trim();
|
|
1717
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1718
|
-
const eqIndex = trimmed.indexOf("=");
|
|
1719
|
-
if (eqIndex === -1) continue;
|
|
1720
|
-
const key = trimmed.slice(0, eqIndex).trim();
|
|
1721
|
-
const raw = trimmed.slice(eqIndex + 1).trim();
|
|
1722
|
-
const value = raw.replace(/^["']|["']$/g, "");
|
|
1723
|
-
if (key) result[key] = value;
|
|
1724
|
-
}
|
|
1725
|
-
return result;
|
|
1726
|
-
}
|
|
1727
|
-
function setEnvConfig(data) {
|
|
1728
|
-
const lines = Object.entries(data).filter(([, v]) => v !== "").map(([k, v]) => `${k}="${v}"`);
|
|
1729
|
-
writeFile(getEnvConfigPath(), lines.join("\n") + "\n");
|
|
1730
|
-
}
|
|
1731
|
-
function clearEnvConfig() {
|
|
1732
|
-
try {
|
|
1733
|
-
fs5.unlinkSync(getEnvConfigPath());
|
|
1734
|
-
} catch {
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
// src/commands/show.ts
|
|
1739
|
-
function handleShowEnv() {
|
|
1740
|
-
const config = getEnvConfig();
|
|
1741
|
-
const entries = Object.entries(config);
|
|
1742
|
-
if (entries.length === 0) {
|
|
1743
|
-
logger.log("No env config.");
|
|
1744
|
-
return;
|
|
1745
|
-
}
|
|
1746
|
-
for (const [key, value] of entries) {
|
|
1747
|
-
logger.log(`${key}="${value}"`);
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
function registerShowCommand(program2) {
|
|
1751
|
-
const show = program2.command("show");
|
|
1752
|
-
show.command("env").description("Display stored proxy environment variables").action(() => handleShowEnv());
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
// src/utils/version.ts
|
|
1756
|
-
import { existsSync, readFileSync } from "fs";
|
|
1757
|
-
import { dirname, join } from "path";
|
|
1758
|
-
import { fileURLToPath } from "url";
|
|
1759
|
-
var PACKAGE_NAME = "@luxkit/cli";
|
|
1760
|
-
var cachedVersion;
|
|
1761
|
-
function getCurrentVersion() {
|
|
1762
|
-
if (cachedVersion) return cachedVersion;
|
|
1763
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
1764
|
-
const candidates = [
|
|
1765
|
-
join(__dirname, "..", "package.json"),
|
|
1766
|
-
join(__dirname, "..", "..", "package.json")
|
|
1767
|
-
];
|
|
1768
|
-
const pkgPath = candidates.find((p) => existsSync(p));
|
|
1769
|
-
if (!pkgPath) {
|
|
1770
|
-
throw new Error("Cannot locate package.json for version reading");
|
|
1771
|
-
}
|
|
1772
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1773
|
-
cachedVersion = pkg.version;
|
|
1774
|
-
return cachedVersion;
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
// src/commands/update.ts
|
|
1778
|
-
var GLOBAL_UPDATE_CMDS = {
|
|
1779
|
-
npm: ["npm", ["install", "-g", `${PACKAGE_NAME}@latest`]],
|
|
1780
|
-
bun: ["bun", ["install", "-g", `${PACKAGE_NAME}@latest`]]
|
|
1781
|
-
};
|
|
1782
|
-
function detectGlobalPackageManager() {
|
|
1783
|
-
return process.execPath.toLowerCase().includes("bun") ? "bun" : "npm";
|
|
1784
|
-
}
|
|
1785
|
-
async function fetchLatestVersion() {
|
|
1786
|
-
const { stdout, exitCode } = await execFileNoThrow("npm", ["view", PACKAGE_NAME, "version"]);
|
|
1787
|
-
if (exitCode !== 0 || !stdout) {
|
|
1788
|
-
throw new Error(`Failed to fetch latest version from npm registry.`);
|
|
1789
|
-
}
|
|
1790
|
-
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
1791
|
-
return lines[lines.length - 1].trim();
|
|
1792
|
-
}
|
|
1793
|
-
async function performUpdate(pm) {
|
|
1794
|
-
const [command, args] = GLOBAL_UPDATE_CMDS[pm];
|
|
1795
|
-
const { exitCode, stderr } = await execFileNoThrow(command, args);
|
|
1796
|
-
if (exitCode !== 0) {
|
|
1797
|
-
const hint = /EACCES|permission/i.test(stderr) ? "\nTry running with elevated privileges (e.g. sudo)." : "";
|
|
1798
|
-
throw new Error(`Update failed: ${stderr}${hint}`);
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
function registerUpdateCommand(program2) {
|
|
1802
|
-
program2.command("update").description("Update @luxkit/cli to the latest version").option("--check", "Only check for updates without installing").action(async (options) => {
|
|
1803
|
-
try {
|
|
1804
|
-
const current = getCurrentVersion();
|
|
1805
|
-
const latest = await fetchLatestVersion();
|
|
1806
|
-
if (current === latest) {
|
|
1807
|
-
logger.log(`Already up to date (v${current})`);
|
|
1808
|
-
return;
|
|
1809
|
-
}
|
|
1810
|
-
if (options.check) {
|
|
1811
|
-
logger.log(`Update available: v${current} \u2192 v${latest}`);
|
|
1812
|
-
return;
|
|
1813
|
-
}
|
|
1814
|
-
const pm = detectGlobalPackageManager();
|
|
1815
|
-
await performUpdate(pm);
|
|
1816
|
-
logger.log(`Updated to v${latest}`);
|
|
1817
|
-
} catch (err) {
|
|
1818
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1819
|
-
logger.error(message);
|
|
1820
|
-
logger.warn(`You can also update manually: npm install -g ${PACKAGE_NAME}@latest`);
|
|
1821
|
-
}
|
|
1822
|
-
});
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
1687
|
// src/presets/vscode/web-vue.ts
|
|
1826
1688
|
var webVueVscode = {
|
|
1827
1689
|
name: "web-vue",
|
|
@@ -2580,6 +2442,226 @@ var VSCODE_PRESETS = [
|
|
|
2580
2442
|
goVscode
|
|
2581
2443
|
];
|
|
2582
2444
|
|
|
2445
|
+
// src/generators/init.ts
|
|
2446
|
+
import fs4 from "fs";
|
|
2447
|
+
import path6 from "path";
|
|
2448
|
+
function resolveSkillsDir() {
|
|
2449
|
+
const entryDir = path6.dirname(process.argv[1] ?? "");
|
|
2450
|
+
return path6.resolve(entryDir, "skills");
|
|
2451
|
+
}
|
|
2452
|
+
function listFilesRecursive(dir, base) {
|
|
2453
|
+
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
2454
|
+
const files = [];
|
|
2455
|
+
for (const entry of entries) {
|
|
2456
|
+
const childBase = `${base}/${entry.name}`;
|
|
2457
|
+
const fullPath = path6.join(dir, entry.name);
|
|
2458
|
+
if (entry.isDirectory()) {
|
|
2459
|
+
files.push(...listFilesRecursive(fullPath, childBase));
|
|
2460
|
+
} else {
|
|
2461
|
+
files.push(childBase);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
return files;
|
|
2465
|
+
}
|
|
2466
|
+
function generateInitSkills(targetBaseDir, cwd) {
|
|
2467
|
+
const skillsDir = resolveSkillsDir();
|
|
2468
|
+
if (!fs4.existsSync(skillsDir)) {
|
|
2469
|
+
logger.error(`Bundled skills directory not found: ${skillsDir}`);
|
|
2470
|
+
logger.error('Please run "lux build" or reinstall lux.');
|
|
2471
|
+
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
2472
|
+
}
|
|
2473
|
+
const targetPath = path6.resolve(cwd, targetBaseDir);
|
|
2474
|
+
try {
|
|
2475
|
+
fs4.cpSync(skillsDir, targetPath, { recursive: true, force: true });
|
|
2476
|
+
} catch (error) {
|
|
2477
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2478
|
+
logger.error(`Failed to copy skills to ${targetPath}: ${message}`);
|
|
2479
|
+
return { copiedFiles: [], targetDir: targetBaseDir };
|
|
2480
|
+
}
|
|
2481
|
+
const copiedFiles = fs4.existsSync(targetPath) ? listFilesRecursive(targetPath, targetBaseDir) : [];
|
|
2482
|
+
return { copiedFiles, targetDir: targetBaseDir };
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// src/commands/init.ts
|
|
2486
|
+
function registerInitCommand(program2) {
|
|
2487
|
+
program2.command("init").description("Initialize skills or materialize presets").option("--preset", "Materialize all presets to ~/.lux/preset/ without writing to cwd").action(async (options) => {
|
|
2488
|
+
if (options.preset) {
|
|
2489
|
+
materializeAllPresets();
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
const toolOptions = INIT_TOOLS.map((tool2) => ({
|
|
2493
|
+
value: tool2.name,
|
|
2494
|
+
label: tool2.label
|
|
2495
|
+
}));
|
|
2496
|
+
const selected = await select({
|
|
2497
|
+
message: "Which AI coding tool do you use?",
|
|
2498
|
+
options: toolOptions
|
|
2499
|
+
});
|
|
2500
|
+
if (isCancel(selected)) {
|
|
2501
|
+
cancel("Operation cancelled.");
|
|
2502
|
+
return;
|
|
2503
|
+
}
|
|
2504
|
+
const tool = INIT_TOOLS.find((t) => t.name === selected);
|
|
2505
|
+
if (!tool) {
|
|
2506
|
+
logger.error(`Unknown tool: ${String(selected)}`);
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
const cwd = process.cwd();
|
|
2510
|
+
const result = generateInitSkills(tool.targetDir, cwd);
|
|
2511
|
+
if (result.copiedFiles.length === 0) {
|
|
2512
|
+
logger.warn("No skill files were copied.");
|
|
2513
|
+
return;
|
|
2514
|
+
}
|
|
2515
|
+
for (const file of result.copiedFiles) {
|
|
2516
|
+
logger.log(` ${file}`);
|
|
2517
|
+
}
|
|
2518
|
+
outro(`Skills installed to ${tool.targetDir}/`);
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
function materializeAllPresets() {
|
|
2522
|
+
const opts = {
|
|
2523
|
+
cwd: process.cwd(),
|
|
2524
|
+
force: false,
|
|
2525
|
+
dryRun: false,
|
|
2526
|
+
noStylelint: false,
|
|
2527
|
+
noEditorconfig: false
|
|
2528
|
+
};
|
|
2529
|
+
for (const preset of FMT_PRESETS) {
|
|
2530
|
+
materializeFmtPreset(preset.name, preset, opts);
|
|
2531
|
+
}
|
|
2532
|
+
for (const preset of VSCODE_PRESETS) {
|
|
2533
|
+
materializeVscodePresetFromBuiltin(preset.name, preset);
|
|
2534
|
+
}
|
|
2535
|
+
logger.success("All presets materialized to ~/.lux/preset/");
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
// src/utils/config.ts
|
|
2539
|
+
import fs5 from "fs";
|
|
2540
|
+
import os2 from "os";
|
|
2541
|
+
import path7 from "path";
|
|
2542
|
+
var CONFIG_DIR = ".lux";
|
|
2543
|
+
var ENV_FILE = "env.txt";
|
|
2544
|
+
function getEnvConfigPath() {
|
|
2545
|
+
return path7.join(os2.homedir(), CONFIG_DIR, ENV_FILE);
|
|
2546
|
+
}
|
|
2547
|
+
function getEnvConfig() {
|
|
2548
|
+
let content;
|
|
2549
|
+
try {
|
|
2550
|
+
content = fs5.readFileSync(getEnvConfigPath(), "utf-8");
|
|
2551
|
+
} catch {
|
|
2552
|
+
return {};
|
|
2553
|
+
}
|
|
2554
|
+
const result = {};
|
|
2555
|
+
for (const line of content.split("\n")) {
|
|
2556
|
+
const trimmed = line.trim();
|
|
2557
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
2558
|
+
const eqIndex = trimmed.indexOf("=");
|
|
2559
|
+
if (eqIndex === -1) continue;
|
|
2560
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
2561
|
+
const raw = trimmed.slice(eqIndex + 1).trim();
|
|
2562
|
+
const value = raw.replace(/^["']|["']$/g, "");
|
|
2563
|
+
if (key) result[key] = value;
|
|
2564
|
+
}
|
|
2565
|
+
return result;
|
|
2566
|
+
}
|
|
2567
|
+
function setEnvConfig(data) {
|
|
2568
|
+
const lines = Object.entries(data).filter(([, v]) => v !== "").map(([k, v]) => `${k}="${v}"`);
|
|
2569
|
+
writeFile(getEnvConfigPath(), lines.join("\n") + "\n");
|
|
2570
|
+
}
|
|
2571
|
+
function clearEnvConfig() {
|
|
2572
|
+
try {
|
|
2573
|
+
fs5.unlinkSync(getEnvConfigPath());
|
|
2574
|
+
} catch {
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
// src/commands/show.ts
|
|
2579
|
+
function handleShowEnv() {
|
|
2580
|
+
const config = getEnvConfig();
|
|
2581
|
+
const entries = Object.entries(config);
|
|
2582
|
+
if (entries.length === 0) {
|
|
2583
|
+
logger.log("No env config.");
|
|
2584
|
+
return;
|
|
2585
|
+
}
|
|
2586
|
+
for (const [key, value] of entries) {
|
|
2587
|
+
logger.log(`${key}="${value}"`);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
function registerShowCommand(program2) {
|
|
2591
|
+
const show = program2.command("show");
|
|
2592
|
+
show.command("env").description("Display stored proxy environment variables").action(() => handleShowEnv());
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
// src/utils/version.ts
|
|
2596
|
+
import { existsSync, readFileSync } from "fs";
|
|
2597
|
+
import { dirname, join } from "path";
|
|
2598
|
+
import { fileURLToPath } from "url";
|
|
2599
|
+
var PACKAGE_NAME = "@luxkit/cli";
|
|
2600
|
+
var cachedVersion;
|
|
2601
|
+
function getCurrentVersion() {
|
|
2602
|
+
if (cachedVersion) return cachedVersion;
|
|
2603
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
2604
|
+
const candidates = [
|
|
2605
|
+
join(__dirname, "..", "package.json"),
|
|
2606
|
+
join(__dirname, "..", "..", "package.json")
|
|
2607
|
+
];
|
|
2608
|
+
const pkgPath = candidates.find((p) => existsSync(p));
|
|
2609
|
+
if (!pkgPath) {
|
|
2610
|
+
throw new Error("Cannot locate package.json for version reading");
|
|
2611
|
+
}
|
|
2612
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
2613
|
+
cachedVersion = pkg.version;
|
|
2614
|
+
return cachedVersion;
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// src/commands/update.ts
|
|
2618
|
+
var GLOBAL_UPDATE_CMDS = {
|
|
2619
|
+
npm: ["npm", ["install", "-g", `${PACKAGE_NAME}@latest`]],
|
|
2620
|
+
bun: ["bun", ["install", "-g", `${PACKAGE_NAME}@latest`]]
|
|
2621
|
+
};
|
|
2622
|
+
function detectGlobalPackageManager() {
|
|
2623
|
+
return process.execPath.toLowerCase().includes("bun") ? "bun" : "npm";
|
|
2624
|
+
}
|
|
2625
|
+
async function fetchLatestVersion() {
|
|
2626
|
+
const { stdout, exitCode } = await execFileNoThrow("npm", ["view", PACKAGE_NAME, "version"]);
|
|
2627
|
+
if (exitCode !== 0 || !stdout) {
|
|
2628
|
+
throw new Error(`Failed to fetch latest version from npm registry.`);
|
|
2629
|
+
}
|
|
2630
|
+
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
2631
|
+
return lines[lines.length - 1].trim();
|
|
2632
|
+
}
|
|
2633
|
+
async function performUpdate(pm) {
|
|
2634
|
+
const [command, args] = GLOBAL_UPDATE_CMDS[pm];
|
|
2635
|
+
const { exitCode, stderr } = await execFileNoThrow(command, args);
|
|
2636
|
+
if (exitCode !== 0) {
|
|
2637
|
+
const hint = /EACCES|permission/i.test(stderr) ? "\nTry running with elevated privileges (e.g. sudo)." : "";
|
|
2638
|
+
throw new Error(`Update failed: ${stderr}${hint}`);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
function registerUpdateCommand(program2) {
|
|
2642
|
+
program2.command("update").description("Update @luxkit/cli to the latest version").option("--check", "Only check for updates without installing").action(async (options) => {
|
|
2643
|
+
try {
|
|
2644
|
+
const current = getCurrentVersion();
|
|
2645
|
+
const latest = await fetchLatestVersion();
|
|
2646
|
+
if (current === latest) {
|
|
2647
|
+
logger.log(`Already up to date (v${current})`);
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
if (options.check) {
|
|
2651
|
+
logger.log(`Update available: v${current} \u2192 v${latest}`);
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
const pm = detectGlobalPackageManager();
|
|
2655
|
+
await performUpdate(pm);
|
|
2656
|
+
logger.log(`Updated to v${latest}`);
|
|
2657
|
+
} catch (err) {
|
|
2658
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2659
|
+
logger.error(message);
|
|
2660
|
+
logger.warn(`You can also update manually: npm install -g ${PACKAGE_NAME}@latest`);
|
|
2661
|
+
}
|
|
2662
|
+
});
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2583
2665
|
// src/generators/vscode.ts
|
|
2584
2666
|
var STYLELINT_SETTINGS_PREFIXES2 = [
|
|
2585
2667
|
"stylelint.",
|
|
@@ -2670,7 +2752,15 @@ function registerVscodeCommand(program2) {
|
|
|
2670
2752
|
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(
|
|
2671
2753
|
async (presetName, options) => {
|
|
2672
2754
|
const preset = resolvePreset(VSCODE_PRESETS, presetName);
|
|
2673
|
-
if (!preset)
|
|
2755
|
+
if (!preset) {
|
|
2756
|
+
const err = new PresetNotFoundError(
|
|
2757
|
+
presetName,
|
|
2758
|
+
VSCODE_PRESETS.map((p) => p.name)
|
|
2759
|
+
);
|
|
2760
|
+
logger.error(err.message);
|
|
2761
|
+
process.exitCode = 1;
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2674
2764
|
const cwd = process.cwd();
|
|
2675
2765
|
if (options.reset) {
|
|
2676
2766
|
resetLocalPreset("vscode", presetName);
|