lism-cli 0.6.0 → 0.8.0
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 +67 -31
- package/dist/{chunk-G4RBP3W6.js → chunk-RTEZODLP.js} +166 -129
- package/dist/index.js +248 -292
- package/dist/lib.d.ts +70 -93
- package/dist/lib.js +1 -1
- package/package.json +4 -3
- /package/bin/{lism.mjs → lism-cli.mjs} +0 -0
package/dist/index.js
CHANGED
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
DEFAULT_SKILL_REF,
|
|
4
4
|
DEFAULT_UI_REF,
|
|
5
5
|
RAW_GITHUB_BASE,
|
|
6
|
-
|
|
6
|
+
SKILL_NAMES,
|
|
7
|
+
SKILL_SOURCE_BASE,
|
|
7
8
|
SOURCE_REPO,
|
|
8
9
|
UI_COMPONENTS_PATH,
|
|
9
10
|
UI_HELPER_PATH,
|
|
@@ -13,13 +14,13 @@ import {
|
|
|
13
14
|
preScanLang,
|
|
14
15
|
setLang,
|
|
15
16
|
t
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-RTEZODLP.js";
|
|
17
18
|
|
|
18
19
|
// src/createProgram.ts
|
|
19
|
-
import { Command as
|
|
20
|
+
import { Command as Command4 } from "commander";
|
|
20
21
|
|
|
21
22
|
// src/commands/ui/index.ts
|
|
22
|
-
import { Command
|
|
23
|
+
import { Command as Command2 } from "commander";
|
|
23
24
|
|
|
24
25
|
// src/commands/ui/add.ts
|
|
25
26
|
import fs3 from "fs";
|
|
@@ -30,209 +31,91 @@ import { confirm, select as select2 } from "@inquirer/prompts";
|
|
|
30
31
|
import fs from "fs";
|
|
31
32
|
import path from "path";
|
|
32
33
|
import { createJiti } from "jiti";
|
|
33
|
-
|
|
34
|
-
// src/invokeCommand.ts
|
|
35
|
-
function getInvokeCommand() {
|
|
36
|
-
const scriptPath = process.argv[1] ?? "";
|
|
37
|
-
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
38
|
-
const normalizedScriptPath = normalizePath(scriptPath);
|
|
39
|
-
const inDlxCache = /\/_npx\//.test(normalizedScriptPath) || /\/pnpm(?:-cache)?\/(?:[^/]+\/)*dlx-[^/]+\//.test(normalizedScriptPath) || /\/\.yarn\/berry\/cache\//.test(normalizedScriptPath) || /\/\.bun\/install\/cache\//.test(normalizedScriptPath);
|
|
40
|
-
const inLocalDependency = isInProjectNodeModules(normalizedScriptPath) && (/\/node_modules\/\.bin\/lism(?:\.(?:cmd|ps1))?$/.test(normalizedScriptPath) || /\/node_modules\/lism-cli\/bin\/lism\.mjs$/.test(normalizedScriptPath));
|
|
41
|
-
if (inDlxCache) return getDlxInvokeCommand(userAgent);
|
|
42
|
-
if (inLocalDependency) return getLocalInvokeCommand(userAgent);
|
|
43
|
-
return "lism";
|
|
44
|
-
}
|
|
45
|
-
function getDlxInvokeCommand(userAgent) {
|
|
46
|
-
if (userAgent.startsWith("pnpm/")) return "pnpm dlx lism-cli";
|
|
47
|
-
if (userAgent.startsWith("yarn/")) return "yarn dlx lism-cli";
|
|
48
|
-
if (userAgent.startsWith("bun/")) return "bunx lism-cli";
|
|
49
|
-
return "npx lism-cli";
|
|
50
|
-
}
|
|
51
|
-
function getLocalInvokeCommand(userAgent) {
|
|
52
|
-
if (userAgent.startsWith("pnpm/")) return "pnpm exec lism";
|
|
53
|
-
if (userAgent.startsWith("yarn/")) return "yarn lism";
|
|
54
|
-
if (userAgent.startsWith("bun/")) return "bun run lism";
|
|
55
|
-
return "npx lism-cli";
|
|
56
|
-
}
|
|
57
|
-
function isInProjectNodeModules(scriptPath) {
|
|
58
|
-
let dir = normalizePath(process.cwd());
|
|
59
|
-
while (dir) {
|
|
60
|
-
if (scriptPath.startsWith(`${dir}/node_modules/`)) return true;
|
|
61
|
-
const parent = dir.slice(0, dir.lastIndexOf("/"));
|
|
62
|
-
if (parent === dir) break;
|
|
63
|
-
dir = parent;
|
|
64
|
-
}
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
function normalizePath(value) {
|
|
68
|
-
return value.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// src/config.ts
|
|
72
|
-
var LEGACY_CONFIG_FILE = "lism-ui.json";
|
|
73
|
-
var CONFIG_SEARCH = ["lism.config.js", "lism.config.mjs"];
|
|
34
|
+
var CONFIG_SEARCH = ["lism.config.ts", "lism.config.mjs", "lism.config.js"];
|
|
74
35
|
function resolvePath(filename) {
|
|
75
36
|
return path.resolve(process.cwd(), filename);
|
|
76
37
|
}
|
|
77
38
|
function findConfigFile() {
|
|
78
39
|
for (const name of CONFIG_SEARCH) {
|
|
79
40
|
const abs = resolvePath(name);
|
|
80
|
-
if (fs.existsSync(abs)) return { path: abs, filename: name
|
|
41
|
+
if (fs.existsSync(abs)) return { path: abs, filename: name };
|
|
81
42
|
}
|
|
82
|
-
const legacy = resolvePath(LEGACY_CONFIG_FILE);
|
|
83
|
-
if (fs.existsSync(legacy)) return { path: legacy, filename: LEGACY_CONFIG_FILE, kind: "legacy-json" };
|
|
84
43
|
return null;
|
|
85
44
|
}
|
|
86
|
-
function configExists() {
|
|
87
|
-
return findConfigFile() !== null;
|
|
88
|
-
}
|
|
89
45
|
var DEFAULT_CONFIG_FILENAME = "lism.config.js";
|
|
90
46
|
function getDefaultConfigPath() {
|
|
91
47
|
return resolvePath(DEFAULT_CONFIG_FILENAME);
|
|
92
48
|
}
|
|
93
49
|
async function readConfig() {
|
|
94
50
|
const found = findConfigFile();
|
|
95
|
-
if (!found)
|
|
96
|
-
throw new Error(t("config.notFound"));
|
|
97
|
-
}
|
|
98
|
-
if (found.kind === "legacy-json") {
|
|
99
|
-
logger.warn(t("config.legacyWarning", { filename: LEGACY_CONFIG_FILE, invoke: getInvokeCommand() }));
|
|
100
|
-
const raw = fs.readFileSync(found.path, "utf-8");
|
|
101
|
-
const parsed = JSON.parse(raw);
|
|
102
|
-
return parsed;
|
|
103
|
-
}
|
|
51
|
+
if (!found) return null;
|
|
104
52
|
const jiti = createJiti(import.meta.url, { interopDefault: true });
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
53
|
+
let mod;
|
|
54
|
+
try {
|
|
55
|
+
mod = await jiti.import(found.path);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
throw new Error(t("config.loadFailed", { path: found.path, reason: String(err) }));
|
|
58
|
+
}
|
|
59
|
+
const modObj = mod;
|
|
60
|
+
const hasUiKey = modObj?.ui !== void 0;
|
|
61
|
+
const hasCliKey = modObj?.cli !== void 0;
|
|
62
|
+
if (hasUiKey || hasCliKey) {
|
|
63
|
+
if (!hasUiKey && hasCliKey) {
|
|
64
|
+
logger.warn(t("config.cliKeyDeprecated", { filename: found.filename }));
|
|
65
|
+
}
|
|
66
|
+
return normalizeUiConfig(hasUiKey ? modObj.ui : modObj.cli);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return normalizeUiConfig(modObj);
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
109
72
|
}
|
|
110
|
-
validateCliConfig(cli);
|
|
111
|
-
return cli;
|
|
112
73
|
}
|
|
113
|
-
function
|
|
114
|
-
const c =
|
|
74
|
+
function normalizeUiConfig(raw) {
|
|
75
|
+
const c = raw ?? {};
|
|
115
76
|
if (c.framework !== "react" && c.framework !== "astro") {
|
|
116
77
|
throw new Error(t("config.invalidFramework"));
|
|
117
78
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (typeof c.helperDir !== "string" || !c.helperDir) {
|
|
122
|
-
throw new Error(t("config.invalidHelperDir"));
|
|
79
|
+
const dir = c.dir !== void 0 ? c.dir : c.componentsDir;
|
|
80
|
+
if (typeof dir !== "string" || !dir) {
|
|
81
|
+
throw new Error(t("config.invalidDir"));
|
|
123
82
|
}
|
|
83
|
+
return { framework: c.framework, dir };
|
|
124
84
|
}
|
|
125
|
-
function writeFreshConfig(
|
|
85
|
+
function writeFreshConfig(ui) {
|
|
126
86
|
const filePath = getDefaultConfigPath();
|
|
127
|
-
|
|
87
|
+
if (fs.existsSync(filePath)) {
|
|
88
|
+
throw new Error(t("config.freshConfigExists", { path: filePath }));
|
|
89
|
+
}
|
|
90
|
+
const body = renderConfigTemplate(ui);
|
|
128
91
|
fs.writeFileSync(filePath, body);
|
|
129
92
|
return filePath;
|
|
130
93
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return !!mod?.cli;
|
|
136
|
-
} catch (err) {
|
|
137
|
-
throw new Error(t("config.loadFailed", { path: filePath, reason: String(err) }));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
async function patchConfigWithCli(cli, targetPath, options = {}) {
|
|
141
|
-
const filePath = targetPath ?? getDefaultConfigPath();
|
|
142
|
-
let source = fs.readFileSync(filePath, "utf-8");
|
|
143
|
-
const hasExisting = options.existingCli ?? await hasCliSection(filePath);
|
|
144
|
-
if (hasExisting) {
|
|
145
|
-
if (!options.force) {
|
|
146
|
-
return { path: filePath, patched: false };
|
|
147
|
-
}
|
|
148
|
-
const removed = removeCliSection(source);
|
|
149
|
-
if (removed === null) {
|
|
150
|
-
return { path: filePath, patched: false };
|
|
151
|
-
}
|
|
152
|
-
source = removed;
|
|
153
|
-
}
|
|
154
|
-
const insertAt = findInsertPosition(source);
|
|
155
|
-
if (insertAt === -1) {
|
|
156
|
-
return { path: filePath, patched: false };
|
|
157
|
-
}
|
|
158
|
-
const insertion = `
|
|
159
|
-
cli: ${renderCliObject(cli, " ")},`;
|
|
160
|
-
const updated = source.slice(0, insertAt) + insertion + source.slice(insertAt);
|
|
161
|
-
fs.writeFileSync(filePath, updated);
|
|
162
|
-
return { path: filePath, patched: true };
|
|
163
|
-
}
|
|
164
|
-
function removeCliSection(source) {
|
|
165
|
-
const match = source.match(/(^|[\n,{])(\s*)cli\s*:\s*\{/);
|
|
166
|
-
if (!match) return null;
|
|
167
|
-
const sectionStart = match.index + match[1].length + match[2].length;
|
|
168
|
-
const openBrace = match.index + match[0].length - 1;
|
|
169
|
-
let i = openBrace + 1;
|
|
170
|
-
let depth = 1;
|
|
171
|
-
while (i < source.length && depth > 0) {
|
|
172
|
-
const c = source[i];
|
|
173
|
-
if (c === '"' || c === "'" || c === "`") {
|
|
174
|
-
const quote = c;
|
|
175
|
-
i++;
|
|
176
|
-
while (i < source.length && source[i] !== quote) {
|
|
177
|
-
if (source[i] === "\\") i++;
|
|
178
|
-
i++;
|
|
179
|
-
}
|
|
180
|
-
i++;
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (c === "/" && source[i + 1] === "/") {
|
|
184
|
-
while (i < source.length && source[i] !== "\n") i++;
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (c === "/" && source[i + 1] === "*") {
|
|
188
|
-
i += 2;
|
|
189
|
-
while (i < source.length - 1 && !(source[i] === "*" && source[i + 1] === "/")) i++;
|
|
190
|
-
i += 2;
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
if (c === "{") depth++;
|
|
194
|
-
else if (c === "}") depth--;
|
|
195
|
-
i++;
|
|
196
|
-
}
|
|
197
|
-
if (depth !== 0) return null;
|
|
198
|
-
let end = i;
|
|
199
|
-
if (source[end] === ",") end++;
|
|
200
|
-
const trailing = source.slice(end).match(/^[ \t]*\n/);
|
|
201
|
-
if (trailing) end += trailing[0].length;
|
|
202
|
-
return source.slice(0, sectionStart) + source.slice(end);
|
|
203
|
-
}
|
|
204
|
-
function findInsertPosition(source) {
|
|
205
|
-
const inline = source.match(/export default\s*(?:[A-Za-z_$][A-Za-z0-9_$]*\s*)?\(?\s*\{/);
|
|
206
|
-
if (inline) {
|
|
207
|
-
return inline.index + inline[0].length;
|
|
208
|
-
}
|
|
209
|
-
const varExport = source.match(/export default\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*;?/);
|
|
210
|
-
if (varExport) {
|
|
211
|
-
const varName = varExport[1];
|
|
212
|
-
const escaped = varName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
213
|
-
const decl = source.match(new RegExp(`(?:const|let|var)\\s+${escaped}\\s*=\\s*(?:[A-Za-z_$][A-Za-z0-9_$]*\\s*)?\\(?\\s*\\{`));
|
|
214
|
-
if (decl) {
|
|
215
|
-
return decl.index + decl[0].length;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return -1;
|
|
219
|
-
}
|
|
220
|
-
function renderConfigTemplate(cli) {
|
|
221
|
-
return [
|
|
94
|
+
function renderConfigTemplate(ui) {
|
|
95
|
+
const lines = [
|
|
96
|
+
// `.js` でもエディタ補完・typo 検出が効くように LismConfig 型(#449)を JSDoc で付与する
|
|
97
|
+
"/** @type {import('lism-css/config-types').LismConfig} */",
|
|
222
98
|
"export default {",
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
"}
|
|
226
|
-
""
|
|
227
|
-
]
|
|
99
|
+
" // tokens: {},",
|
|
100
|
+
" // props: {},",
|
|
101
|
+
" // traits: {},",
|
|
102
|
+
" // breakpoints: {},"
|
|
103
|
+
];
|
|
104
|
+
if (ui) {
|
|
105
|
+
lines.push(` ui: ${renderCliObject(ui, " ")},`);
|
|
106
|
+
}
|
|
107
|
+
lines.push("};", "");
|
|
108
|
+
return lines.join("\n");
|
|
109
|
+
}
|
|
110
|
+
function renderUiSnippet(cli) {
|
|
111
|
+
return `ui: ${renderCliObject(cli, "")},`;
|
|
228
112
|
}
|
|
229
113
|
function renderCliObject(cli, indent) {
|
|
230
114
|
return [
|
|
231
115
|
"{",
|
|
232
116
|
//
|
|
233
117
|
`${indent} framework: ${JSON.stringify(cli.framework)},`,
|
|
234
|
-
`${indent}
|
|
235
|
-
`${indent} helperDir: ${JSON.stringify(cli.helperDir)},`,
|
|
118
|
+
`${indent} dir: ${JSON.stringify(cli.dir)},`,
|
|
236
119
|
`${indent}}`
|
|
237
120
|
].join("\n");
|
|
238
121
|
}
|
|
@@ -322,10 +205,11 @@ function isExcludedComponentFile(rel, excludeRootFiles) {
|
|
|
322
205
|
if (/(^|\/)[^/]+\.test\.[a-z]+$/.test(rel)) return true;
|
|
323
206
|
return false;
|
|
324
207
|
}
|
|
208
|
+
var FETCH_TIMEOUT_MS = 5e3;
|
|
325
209
|
async function fetchCatalog(options = {}) {
|
|
326
210
|
const ref = options.ref ?? DEFAULT_UI_REF;
|
|
327
211
|
const url = `${RAW_GITHUB_BASE}/${SOURCE_REPO}/${ref}/${UI_REGISTRY_INDEX_PATH}`;
|
|
328
|
-
const res = await fetch(url);
|
|
212
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
329
213
|
if (!res.ok) {
|
|
330
214
|
throw new Error(`Failed to fetch registry-index.json (${res.status} ${res.statusText}): ${url}`);
|
|
331
215
|
}
|
|
@@ -399,63 +283,18 @@ async function fetchHelper(name, options = {}) {
|
|
|
399
283
|
return { name, files };
|
|
400
284
|
}
|
|
401
285
|
|
|
402
|
-
// src/commands/ui/
|
|
403
|
-
import { select
|
|
404
|
-
async function
|
|
286
|
+
// src/commands/ui/promptUiConfig.ts
|
|
287
|
+
import { select } from "@inquirer/prompts";
|
|
288
|
+
async function promptUiConfig(options = {}) {
|
|
405
289
|
const framework = options.framework ?? await select({
|
|
406
|
-
message: t("ui.
|
|
290
|
+
message: t("ui.promptFramework"),
|
|
407
291
|
choices: [
|
|
408
292
|
{ name: "React", value: "react" },
|
|
409
293
|
{ name: "Astro", value: "astro" }
|
|
410
294
|
]
|
|
411
295
|
});
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
default: "src/components/ui"
|
|
415
|
-
});
|
|
416
|
-
const helperDir = options.helperDir ?? await input({
|
|
417
|
-
message: t("ui.init.promptHelperDir"),
|
|
418
|
-
default: `${componentsDir}/_helper`
|
|
419
|
-
});
|
|
420
|
-
const config = { framework, componentsDir, helperDir };
|
|
421
|
-
const found = findConfigFile();
|
|
422
|
-
if (found?.kind === "module") {
|
|
423
|
-
const { patched, path: outPath } = await patchConfigWithCli(config, found.path, {
|
|
424
|
-
force: options.force,
|
|
425
|
-
existingCli: options.existingCli
|
|
426
|
-
});
|
|
427
|
-
if (patched) {
|
|
428
|
-
logger.success(t(options.force ? "ui.init.patchedUpdate" : "ui.init.patchedAdd", { path: outPath }));
|
|
429
|
-
} else {
|
|
430
|
-
logger.warn(t("ui.init.notPatched", { path: outPath }));
|
|
431
|
-
}
|
|
432
|
-
} else {
|
|
433
|
-
const outPath = writeFreshConfig(config);
|
|
434
|
-
logger.success(t("ui.init.created", { path: outPath }));
|
|
435
|
-
}
|
|
436
|
-
return config;
|
|
437
|
-
}
|
|
438
|
-
async function initCommand(options) {
|
|
439
|
-
const found = findConfigFile();
|
|
440
|
-
let existingCli = false;
|
|
441
|
-
if (found?.kind === "legacy-json") {
|
|
442
|
-
logger.warn(t("ui.init.legacyDetected", { filename: found.filename }));
|
|
443
|
-
} else if (found?.kind === "module") {
|
|
444
|
-
try {
|
|
445
|
-
existingCli = await hasCliSection(found.path);
|
|
446
|
-
} catch (err) {
|
|
447
|
-
logger.error(err instanceof Error ? err.message : String(err));
|
|
448
|
-
process.exit(1);
|
|
449
|
-
}
|
|
450
|
-
if (existingCli) {
|
|
451
|
-
if (!options.force) {
|
|
452
|
-
logger.warn(t("ui.init.alreadyExists", { filename: found.filename }));
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
logger.warn(t("ui.init.willOverwrite", { filename: found.filename }));
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
await runInit({ ...options, existingCli });
|
|
296
|
+
const dir = options.dir ?? "src/components/ui";
|
|
297
|
+
return { framework, dir };
|
|
459
298
|
}
|
|
460
299
|
|
|
461
300
|
// src/commands/ui/normalize.ts
|
|
@@ -463,12 +302,12 @@ var normalizeComponentName = (s) => s.replace(/[-_]/g, "").toLowerCase();
|
|
|
463
302
|
|
|
464
303
|
// src/commands/ui/add.ts
|
|
465
304
|
async function addCommand(names, options) {
|
|
466
|
-
let config;
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
305
|
+
let config = await readConfig();
|
|
306
|
+
let needsGuidance = false;
|
|
307
|
+
if (!config) {
|
|
308
|
+
needsGuidance = true;
|
|
470
309
|
logger.info(t("ui.add.noConfig"));
|
|
471
|
-
config = await
|
|
310
|
+
config = await promptUiConfig({ framework: options.uiFramework, dir: options.uiDir });
|
|
472
311
|
console.log();
|
|
473
312
|
}
|
|
474
313
|
const fetchOpts = { ref: options.ref };
|
|
@@ -491,11 +330,11 @@ async function addCommand(names, options) {
|
|
|
491
330
|
}
|
|
492
331
|
const resolvedNames = [];
|
|
493
332
|
const notFound = [];
|
|
494
|
-
for (const
|
|
495
|
-
const normalized = normalizeComponentName(
|
|
333
|
+
for (const input of names) {
|
|
334
|
+
const normalized = normalizeComponentName(input);
|
|
496
335
|
const match = catalog.components.find((c) => normalizeComponentName(c.name) === normalized);
|
|
497
336
|
if (match) resolvedNames.push(match.name);
|
|
498
|
-
else notFound.push(
|
|
337
|
+
else notFound.push(input);
|
|
499
338
|
}
|
|
500
339
|
if (notFound.length > 0) {
|
|
501
340
|
logger.error(t("ui.add.notFound", { list: notFound.join(", ") }));
|
|
@@ -520,6 +359,10 @@ async function addCommand(names, options) {
|
|
|
520
359
|
const helperFailed = await writeComponent(result.value, config, overwriteAll, overwritePolicy, installedHelpers, fetchOpts);
|
|
521
360
|
if (helperFailed) hasFailure = true;
|
|
522
361
|
}
|
|
362
|
+
if (needsGuidance) {
|
|
363
|
+
const filename = findConfigFile()?.filename ?? DEFAULT_CONFIG_FILENAME;
|
|
364
|
+
logger.info(t("ui.add.snippetGuide", { filename, snippet: renderUiSnippet(config) }));
|
|
365
|
+
}
|
|
523
366
|
if (hasFailure) {
|
|
524
367
|
logger.error(t("ui.add.someFailed"));
|
|
525
368
|
process.exit(1);
|
|
@@ -548,8 +391,8 @@ async function writeComponent(component, config, overwriteAll, policy, installed
|
|
|
548
391
|
logger.info(t("ui.add.deploying", { name: component.name }));
|
|
549
392
|
const filesToWrite = [...component.files.shared, ...component.files[config.framework]];
|
|
550
393
|
const componentDirName = component.name;
|
|
551
|
-
const componentDir = path4.resolve(process.cwd(), config.
|
|
552
|
-
const helperDir = path4.resolve(process.cwd(), config.
|
|
394
|
+
const componentDir = path4.resolve(process.cwd(), config.dir, componentDirName);
|
|
395
|
+
const helperDir = path4.resolve(process.cwd(), config.dir, "_helper");
|
|
553
396
|
let shouldWrite;
|
|
554
397
|
const hasExisting = hasExistingFiles(filesToWrite, componentDir);
|
|
555
398
|
if (overwriteAll) {
|
|
@@ -637,17 +480,23 @@ Lism UI v${catalog.version}
|
|
|
637
480
|
${t("ui.list.total", { count: catalog.components.length })}`);
|
|
638
481
|
}
|
|
639
482
|
|
|
483
|
+
// src/commands/ui/uiSectionOptions.ts
|
|
484
|
+
import { Option } from "commander";
|
|
485
|
+
function applyUiSectionOptions(command) {
|
|
486
|
+
return command.addOption(new Option("--ui-framework <name>", t("cli.init.opt.uiFramework")).choices(["react", "astro"])).option("--ui-dir <path>", t("cli.init.opt.uiDir"));
|
|
487
|
+
}
|
|
488
|
+
|
|
640
489
|
// src/commands/ui/index.ts
|
|
641
490
|
function createUiCommand() {
|
|
642
|
-
const ui = new
|
|
643
|
-
ui.command("
|
|
644
|
-
|
|
491
|
+
const ui = new Command2("ui").description(t("cli.ui.description"));
|
|
492
|
+
const add = ui.command("add").description(t("cli.ui.add.description")).argument("[names...]", t("cli.ui.add.arg.names")).option("-o, --overwrite", t("cli.ui.add.opt.overwrite"), false).option("-a, --all", t("cli.ui.add.opt.all"), false);
|
|
493
|
+
applyUiSectionOptions(add).option("--ref <ref>", t("cli.ui.opt.ref")).action(addCommand);
|
|
645
494
|
ui.command("list").description(t("cli.ui.list.description")).option("--ref <ref>", t("cli.ui.opt.ref")).action(listCommand);
|
|
646
495
|
return ui;
|
|
647
496
|
}
|
|
648
497
|
|
|
649
498
|
// src/commands/skill/index.ts
|
|
650
|
-
import { Command as
|
|
499
|
+
import { Command as Command3 } from "commander";
|
|
651
500
|
|
|
652
501
|
// src/commands/skill/add.ts
|
|
653
502
|
import fs5 from "fs";
|
|
@@ -656,15 +505,18 @@ import { checkbox, confirm as confirm2 } from "@inquirer/prompts";
|
|
|
656
505
|
|
|
657
506
|
// src/commands/skill/paths.ts
|
|
658
507
|
var SKILL_PATHS = {
|
|
659
|
-
claude: ".claude/skills
|
|
660
|
-
codex: ".agents/skills
|
|
661
|
-
cursor: ".cursor/skills
|
|
662
|
-
windsurf: ".windsurf/skills
|
|
663
|
-
cline: ".cline/skills
|
|
664
|
-
copilot: ".github/skills
|
|
665
|
-
gemini: ".gemini/skills
|
|
666
|
-
junie: ".junie/skills
|
|
508
|
+
claude: ".claude/skills",
|
|
509
|
+
codex: ".agents/skills",
|
|
510
|
+
cursor: ".cursor/skills",
|
|
511
|
+
windsurf: ".windsurf/skills",
|
|
512
|
+
cline: ".cline/skills",
|
|
513
|
+
copilot: ".github/skills",
|
|
514
|
+
gemini: ".gemini/skills",
|
|
515
|
+
junie: ".junie/skills"
|
|
667
516
|
};
|
|
517
|
+
function skillDestDir(tool, skill) {
|
|
518
|
+
return `${SKILL_PATHS[tool]}/${skill}`;
|
|
519
|
+
}
|
|
668
520
|
var ALL_SKILL_TOOLS = Object.keys(SKILL_PATHS);
|
|
669
521
|
var TOOL_MARKERS = {
|
|
670
522
|
claude: [".claude"],
|
|
@@ -683,9 +535,9 @@ import fs4 from "fs";
|
|
|
683
535
|
import os2 from "os";
|
|
684
536
|
import path5 from "path";
|
|
685
537
|
import { downloadTemplate as downloadTemplate2 } from "giget";
|
|
686
|
-
async function fetchSkillSource(ref = DEFAULT_SKILL_REF) {
|
|
538
|
+
async function fetchSkillSource(skillName, ref = DEFAULT_SKILL_REF) {
|
|
687
539
|
const tmpDir = fs4.mkdtempSync(path5.join(os2.tmpdir(), "lism-skill-"));
|
|
688
|
-
await downloadTemplate2(`github:${SOURCE_REPO}/${
|
|
540
|
+
await downloadTemplate2(`github:${SOURCE_REPO}/${SKILL_SOURCE_BASE}/${skillName}#${ref}`, {
|
|
689
541
|
dir: tmpDir,
|
|
690
542
|
force: true,
|
|
691
543
|
forceClean: true
|
|
@@ -765,8 +617,17 @@ function resolveExplicitTools(options) {
|
|
|
765
617
|
function autoDetectTools(cwd) {
|
|
766
618
|
return ALL_SKILL_TOOLS.filter((tool) => TOOL_MARKERS[tool].some((marker) => fs5.existsSync(path6.resolve(cwd, marker))));
|
|
767
619
|
}
|
|
768
|
-
|
|
620
|
+
function resolveSkills(skillArg) {
|
|
621
|
+
if (skillArg === void 0) return [...SKILL_NAMES];
|
|
622
|
+
return SKILL_NAMES.includes(skillArg) ? [skillArg] : null;
|
|
623
|
+
}
|
|
624
|
+
async function skillAddCommand(skillArg, options) {
|
|
769
625
|
const cwd = process.cwd();
|
|
626
|
+
const skills = resolveSkills(skillArg);
|
|
627
|
+
if (skills === null) {
|
|
628
|
+
logger.error(t("skill.unknownSkill", { name: skillArg, list: SKILL_NAMES.join(", ") }));
|
|
629
|
+
process.exit(1);
|
|
630
|
+
}
|
|
770
631
|
let targets = resolveExplicitTools(options);
|
|
771
632
|
if (targets.length === 0) {
|
|
772
633
|
const detected = autoDetectTools(cwd);
|
|
@@ -787,23 +648,26 @@ async function skillAddCommand(options) {
|
|
|
787
648
|
return;
|
|
788
649
|
}
|
|
789
650
|
const ref = options.ref ?? DEFAULT_SKILL_REF;
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
651
|
+
for (const skill of skills) {
|
|
652
|
+
logger.info(t("skill.add.fetching", { skill, ref }));
|
|
653
|
+
const { dir: srcDir } = await fetchSkillSource(skill, ref);
|
|
654
|
+
try {
|
|
655
|
+
for (const tool of targets) {
|
|
656
|
+
await deploySkillTo(srcDir, skill, tool, options);
|
|
657
|
+
}
|
|
658
|
+
} finally {
|
|
659
|
+
cleanupTempDir(srcDir);
|
|
795
660
|
}
|
|
796
|
-
logger.success(t("common.done"));
|
|
797
|
-
} finally {
|
|
798
|
-
cleanupTempDir(srcDir);
|
|
799
661
|
}
|
|
662
|
+
logger.success(t("common.done"));
|
|
800
663
|
}
|
|
801
|
-
async function deploySkillTo(srcDir, tool, options) {
|
|
802
|
-
const
|
|
664
|
+
async function deploySkillTo(srcDir, skill, tool, options) {
|
|
665
|
+
const relDest = skillDestDir(tool, skill);
|
|
666
|
+
const destDir = path6.resolve(process.cwd(), relDest);
|
|
803
667
|
const existing = fs5.existsSync(destDir);
|
|
804
668
|
if (existing && !options.overwrite) {
|
|
805
669
|
const diff = compareSkillDirs(destDir, srcDir);
|
|
806
|
-
const label = `${tool} (${
|
|
670
|
+
const label = `${tool} (${relDest})`;
|
|
807
671
|
if (!hasDiff(diff)) {
|
|
808
672
|
logger.log(t("skill.add.skippedSame", { label }));
|
|
809
673
|
return;
|
|
@@ -817,7 +681,7 @@ async function deploySkillTo(srcDir, tool, options) {
|
|
|
817
681
|
for (const rel of diff.localOnly) logger.log(` - ${rel}`);
|
|
818
682
|
}
|
|
819
683
|
const go = await confirm2({
|
|
820
|
-
message: t("skill.add.confirmOverwrite", { path:
|
|
684
|
+
message: t("skill.add.confirmOverwrite", { path: relDest }),
|
|
821
685
|
default: false
|
|
822
686
|
});
|
|
823
687
|
if (!go) {
|
|
@@ -833,39 +697,90 @@ async function deploySkillTo(srcDir, tool, options) {
|
|
|
833
697
|
// src/commands/skill/check.ts
|
|
834
698
|
import fs6 from "fs";
|
|
835
699
|
import path7 from "path";
|
|
700
|
+
|
|
701
|
+
// src/invokeCommand.ts
|
|
702
|
+
function getInvokeCommand() {
|
|
703
|
+
const scriptPath = process.argv[1] ?? "";
|
|
704
|
+
const userAgent = process.env.npm_config_user_agent ?? "";
|
|
705
|
+
const normalizedScriptPath = normalizePath(scriptPath);
|
|
706
|
+
const inDlxCache = /\/_npx\//.test(normalizedScriptPath) || /\/pnpm(?:-cache)?\/(?:[^/]+\/)*dlx-[^/]+\//.test(normalizedScriptPath) || /\/\.yarn\/berry\/cache\//.test(normalizedScriptPath) || /\/\.bun\/install\/cache\//.test(normalizedScriptPath);
|
|
707
|
+
const inLocalDependency = isInProjectNodeModules(normalizedScriptPath) && (/\/node_modules\/\.bin\/lism-cli(?:\.(?:cmd|ps1))?$/.test(normalizedScriptPath) || /\/node_modules\/lism-cli\/bin\/lism-cli\.mjs$/.test(normalizedScriptPath));
|
|
708
|
+
if (inDlxCache) return getDlxInvokeCommand(userAgent);
|
|
709
|
+
if (inLocalDependency) return getLocalInvokeCommand(userAgent);
|
|
710
|
+
return "lism-cli";
|
|
711
|
+
}
|
|
712
|
+
function getDlxInvokeCommand(userAgent) {
|
|
713
|
+
if (userAgent.startsWith("pnpm/")) return "pnpm dlx lism-cli";
|
|
714
|
+
if (userAgent.startsWith("yarn/")) return "yarn dlx lism-cli";
|
|
715
|
+
if (userAgent.startsWith("bun/")) return "bunx lism-cli";
|
|
716
|
+
return "npx lism-cli";
|
|
717
|
+
}
|
|
718
|
+
function getLocalInvokeCommand(userAgent) {
|
|
719
|
+
if (userAgent.startsWith("pnpm/")) return "pnpm exec lism-cli";
|
|
720
|
+
if (userAgent.startsWith("yarn/")) return "yarn lism-cli";
|
|
721
|
+
if (userAgent.startsWith("bun/")) return "bun run lism-cli";
|
|
722
|
+
return "npx lism-cli";
|
|
723
|
+
}
|
|
724
|
+
function isInProjectNodeModules(scriptPath) {
|
|
725
|
+
let dir = normalizePath(process.cwd());
|
|
726
|
+
while (dir) {
|
|
727
|
+
if (scriptPath.startsWith(`${dir}/node_modules/`)) return true;
|
|
728
|
+
const parent = dir.slice(0, dir.lastIndexOf("/"));
|
|
729
|
+
if (parent === dir) break;
|
|
730
|
+
dir = parent;
|
|
731
|
+
}
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
function normalizePath(value) {
|
|
735
|
+
return value.replaceAll("\\", "/").replace(/\/+$/, "");
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/commands/skill/check.ts
|
|
836
739
|
async function skillCheckCommand(options = {}) {
|
|
837
740
|
const cwd = process.cwd();
|
|
838
|
-
const installed =
|
|
741
|
+
const installed = [];
|
|
742
|
+
for (const skill of SKILL_NAMES) {
|
|
743
|
+
for (const tool of ALL_SKILL_TOOLS) {
|
|
744
|
+
if (fs6.existsSync(path7.join(cwd, skillDestDir(tool, skill), "SKILL.md"))) {
|
|
745
|
+
installed.push({ skill, tool });
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
839
749
|
if (installed.length === 0) {
|
|
840
750
|
logger.info(t("skill.check.noneInstalled", { invoke: getInvokeCommand() }));
|
|
841
751
|
return;
|
|
842
752
|
}
|
|
843
753
|
const ref = options.ref ?? DEFAULT_SKILL_REF;
|
|
844
|
-
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
const
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
754
|
+
let outdatedCount = 0;
|
|
755
|
+
for (const skill of SKILL_NAMES) {
|
|
756
|
+
const pairs = installed.filter((p) => p.skill === skill);
|
|
757
|
+
if (pairs.length === 0) continue;
|
|
758
|
+
logger.info(t("skill.check.fetching", { skill, ref }));
|
|
759
|
+
const { dir: remoteDir } = await fetchSkillSource(skill, ref);
|
|
760
|
+
try {
|
|
761
|
+
for (const { tool } of pairs) {
|
|
762
|
+
const relDest = skillDestDir(tool, skill);
|
|
763
|
+
const localDir = path7.resolve(cwd, relDest);
|
|
764
|
+
const diff = compareSkillDirs(localDir, remoteDir);
|
|
765
|
+
const label = `${tool.padEnd(9)} ${relDest}`;
|
|
766
|
+
if (!hasDiff(diff)) {
|
|
767
|
+
logger.log(t("skill.check.upToDate", { label }));
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
outdatedCount += 1;
|
|
771
|
+
logger.warn(` ! ${label}`);
|
|
772
|
+
logger.log(formatDiffSummary(diff));
|
|
773
|
+
if (options.verbose) logger.log(formatDiffDetails(diff));
|
|
855
774
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
logger.log(formatDiffSummary(diff));
|
|
859
|
-
if (options.verbose) logger.log(formatDiffDetails(diff));
|
|
860
|
-
}
|
|
861
|
-
logger.log("");
|
|
862
|
-
if (outdatedCount === 0) {
|
|
863
|
-
logger.success(t("skill.check.allLatest"));
|
|
864
|
-
} else {
|
|
865
|
-
logger.info(t("skill.check.outdated", { count: outdatedCount, invoke: getInvokeCommand() }));
|
|
775
|
+
} finally {
|
|
776
|
+
cleanupTempDir(remoteDir);
|
|
866
777
|
}
|
|
867
|
-
}
|
|
868
|
-
|
|
778
|
+
}
|
|
779
|
+
logger.log("");
|
|
780
|
+
if (outdatedCount === 0) {
|
|
781
|
+
logger.success(t("skill.check.allLatest"));
|
|
782
|
+
} else {
|
|
783
|
+
logger.info(t("skill.check.outdated", { count: outdatedCount, invoke: getInvokeCommand() }));
|
|
869
784
|
}
|
|
870
785
|
}
|
|
871
786
|
function formatDiffSummary(diff) {
|
|
@@ -885,16 +800,16 @@ function formatDiffDetails(diff) {
|
|
|
885
800
|
|
|
886
801
|
// src/commands/skill/update.ts
|
|
887
802
|
async function skillUpdateCommand(options) {
|
|
888
|
-
await skillAddCommand({ ...options, overwrite: true });
|
|
803
|
+
await skillAddCommand(void 0, { ...options, overwrite: true });
|
|
889
804
|
}
|
|
890
805
|
|
|
891
806
|
// src/commands/skill/index.ts
|
|
892
807
|
function createSkillCommand() {
|
|
893
|
-
const skill = new
|
|
808
|
+
const skill = new Command3("skill").description(t("cli.skill.description"));
|
|
894
809
|
const toolOptDescription = (tool) => t("cli.skill.opt.toolPath", { path: SKILL_PATHS[tool] });
|
|
895
810
|
const toolFlags = (cmd) => cmd.option("--all", t("cli.skill.opt.all")).option("--claude", toolOptDescription("claude")).option("--codex", toolOptDescription("codex")).option("--cursor", toolOptDescription("cursor")).option("--windsurf", toolOptDescription("windsurf")).option("--cline", toolOptDescription("cline")).option("--copilot", toolOptDescription("copilot")).option("--gemini", toolOptDescription("gemini")).option("--junie", toolOptDescription("junie"));
|
|
896
811
|
toolFlags(
|
|
897
|
-
skill.command("add").description(t("cli.skill.add.description")).option("-o, --overwrite", t("cli.skill.add.opt.overwrite"), false).option("--ref <ref>", t("cli.skill.opt.ref"))
|
|
812
|
+
skill.command("add").description(t("cli.skill.add.description")).argument("[skill]", t("cli.skill.add.arg.skill")).option("-o, --overwrite", t("cli.skill.add.opt.overwrite"), false).option("--ref <ref>", t("cli.skill.opt.ref"))
|
|
898
813
|
).action(skillAddCommand);
|
|
899
814
|
skill.command("check").description(t("cli.skill.check.description")).option("--ref <ref>", t("cli.skill.opt.ref")).option("-v, --verbose", t("cli.skill.check.opt.verbose")).action(skillCheckCommand);
|
|
900
815
|
toolFlags(skill.command("update").description(t("cli.skill.update.description")).option("--ref <ref>", t("cli.skill.opt.ref"))).action(
|
|
@@ -903,16 +818,53 @@ function createSkillCommand() {
|
|
|
903
818
|
return skill;
|
|
904
819
|
}
|
|
905
820
|
|
|
821
|
+
// src/commands/init.ts
|
|
822
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
823
|
+
async function initCommand(options = {}) {
|
|
824
|
+
const found = findConfigFile();
|
|
825
|
+
if (found) {
|
|
826
|
+
logger.warn(t("init.alreadyExists", { filename: found.filename }));
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
const ui = await resolveUiConfig(options);
|
|
830
|
+
const outPath = writeFreshConfig(ui);
|
|
831
|
+
logger.success(t("init.created", { path: outPath }));
|
|
832
|
+
}
|
|
833
|
+
async function resolveUiConfig(options) {
|
|
834
|
+
const hasFramework = options.uiFramework !== void 0;
|
|
835
|
+
const hasDir = options.uiDir !== void 0;
|
|
836
|
+
const uiOptions = { framework: options.uiFramework, dir: options.uiDir };
|
|
837
|
+
if (!process.stdin.isTTY) {
|
|
838
|
+
if (hasFramework) return promptUiConfig(uiOptions);
|
|
839
|
+
if (hasDir) {
|
|
840
|
+
logger.error(t("init.uiFrameworkRequired"));
|
|
841
|
+
process.exit(1);
|
|
842
|
+
}
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
if (hasFramework) {
|
|
846
|
+
const useUi2 = await confirm3({ message: t("init.promptUseUi"), default: true });
|
|
847
|
+
return useUi2 ? promptUiConfig(uiOptions) : null;
|
|
848
|
+
}
|
|
849
|
+
if (hasDir) {
|
|
850
|
+
return promptUiConfig(uiOptions);
|
|
851
|
+
}
|
|
852
|
+
const useUi = await confirm3({ message: t("init.promptUseUi"), default: false });
|
|
853
|
+
return useUi ? promptUiConfig(uiOptions) : null;
|
|
854
|
+
}
|
|
855
|
+
|
|
906
856
|
// src/createProgram.ts
|
|
907
857
|
function createLismProgram() {
|
|
908
|
-
const program2 = new
|
|
909
|
-
program2.name("lism").description(t("cli.description")).version(CLI_VERSION).option("--lang <code>", t("cli.opt.lang"));
|
|
858
|
+
const program2 = new Command4();
|
|
859
|
+
program2.name("lism-cli").description(t("cli.description")).version(CLI_VERSION).option("--lang <code>", t("cli.opt.lang"));
|
|
910
860
|
program2.hook("preAction", (thisCommand) => {
|
|
911
861
|
const opts = thisCommand.optsWithGlobals();
|
|
912
862
|
const lang = opts.lang;
|
|
913
863
|
if (typeof lang === "string") setLang(lang);
|
|
914
864
|
});
|
|
915
865
|
program2.command("create").description(t("cli.create.description")).argument("[targetDir]", t("cli.create.arg.targetDir")).option("-t, --template <name>", t("cli.create.opt.template")).option("-f, --force", t("cli.create.opt.force"), false).action(createCommand);
|
|
866
|
+
const init = program2.command("init").description(t("cli.init.description"));
|
|
867
|
+
applyUiSectionOptions(init).action(initCommand);
|
|
916
868
|
program2.addCommand(createUiCommand());
|
|
917
869
|
program2.addCommand(createSkillCommand());
|
|
918
870
|
return program2;
|
|
@@ -921,4 +873,8 @@ function createLismProgram() {
|
|
|
921
873
|
// src/index.ts
|
|
922
874
|
preScanLang(process.argv.slice(2));
|
|
923
875
|
var program = createLismProgram();
|
|
924
|
-
program.
|
|
876
|
+
program.parseAsync().catch((err) => {
|
|
877
|
+
if (err instanceof Error && err.name === "ExitPromptError") process.exit(130);
|
|
878
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
879
|
+
process.exit(1);
|
|
880
|
+
});
|