lism-cli 0.7.0 → 0.9.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 +87 -31
- package/dist/{chunk-6E4LIZPL.js → chunk-BEY36YDC.js} +111 -118
- package/dist/index.js +252 -291
- package/dist/lib.d.ts +77 -91
- package/dist/lib.js +1 -1
- package/package.json +12 -11
- /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-BEY36YDC.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
|
}
|
|
@@ -400,63 +283,18 @@ async function fetchHelper(name, options = {}) {
|
|
|
400
283
|
return { name, files };
|
|
401
284
|
}
|
|
402
285
|
|
|
403
|
-
// src/commands/ui/
|
|
404
|
-
import { select
|
|
405
|
-
async function
|
|
286
|
+
// src/commands/ui/promptUiConfig.ts
|
|
287
|
+
import { select } from "@inquirer/prompts";
|
|
288
|
+
async function promptUiConfig(options = {}) {
|
|
406
289
|
const framework = options.framework ?? await select({
|
|
407
|
-
message: t("ui.
|
|
290
|
+
message: t("ui.promptFramework"),
|
|
408
291
|
choices: [
|
|
409
292
|
{ name: "React", value: "react" },
|
|
410
293
|
{ name: "Astro", value: "astro" }
|
|
411
294
|
]
|
|
412
295
|
});
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
default: "src/components/ui"
|
|
416
|
-
});
|
|
417
|
-
const helperDir = options.helperDir ?? await input({
|
|
418
|
-
message: t("ui.init.promptHelperDir"),
|
|
419
|
-
default: `${componentsDir}/_helper`
|
|
420
|
-
});
|
|
421
|
-
const config = { framework, componentsDir, helperDir };
|
|
422
|
-
const found = findConfigFile();
|
|
423
|
-
if (found?.kind === "module") {
|
|
424
|
-
const { patched, path: outPath } = await patchConfigWithCli(config, found.path, {
|
|
425
|
-
force: options.force,
|
|
426
|
-
existingCli: options.existingCli
|
|
427
|
-
});
|
|
428
|
-
if (patched) {
|
|
429
|
-
logger.success(t(options.force ? "ui.init.patchedUpdate" : "ui.init.patchedAdd", { path: outPath }));
|
|
430
|
-
} else {
|
|
431
|
-
logger.warn(t("ui.init.notPatched", { path: outPath }));
|
|
432
|
-
}
|
|
433
|
-
} else {
|
|
434
|
-
const outPath = writeFreshConfig(config);
|
|
435
|
-
logger.success(t("ui.init.created", { path: outPath }));
|
|
436
|
-
}
|
|
437
|
-
return config;
|
|
438
|
-
}
|
|
439
|
-
async function initCommand(options) {
|
|
440
|
-
const found = findConfigFile();
|
|
441
|
-
let existingCli = false;
|
|
442
|
-
if (found?.kind === "legacy-json") {
|
|
443
|
-
logger.warn(t("ui.init.legacyDetected", { filename: found.filename }));
|
|
444
|
-
} else if (found?.kind === "module") {
|
|
445
|
-
try {
|
|
446
|
-
existingCli = await hasCliSection(found.path);
|
|
447
|
-
} catch (err) {
|
|
448
|
-
logger.error(err instanceof Error ? err.message : String(err));
|
|
449
|
-
process.exit(1);
|
|
450
|
-
}
|
|
451
|
-
if (existingCli) {
|
|
452
|
-
if (!options.force) {
|
|
453
|
-
logger.warn(t("ui.init.alreadyExists", { filename: found.filename }));
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
logger.warn(t("ui.init.willOverwrite", { filename: found.filename }));
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
await runInit({ ...options, existingCli });
|
|
296
|
+
const dir = options.dir ?? "src/components/ui";
|
|
297
|
+
return { framework, dir };
|
|
460
298
|
}
|
|
461
299
|
|
|
462
300
|
// src/commands/ui/normalize.ts
|
|
@@ -464,12 +302,12 @@ var normalizeComponentName = (s) => s.replace(/[-_]/g, "").toLowerCase();
|
|
|
464
302
|
|
|
465
303
|
// src/commands/ui/add.ts
|
|
466
304
|
async function addCommand(names, options) {
|
|
467
|
-
let config;
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
305
|
+
let config = await readConfig();
|
|
306
|
+
let needsGuidance = false;
|
|
307
|
+
if (!config) {
|
|
308
|
+
needsGuidance = true;
|
|
471
309
|
logger.info(t("ui.add.noConfig"));
|
|
472
|
-
config = await
|
|
310
|
+
config = await promptUiConfig({ framework: options.uiFramework, dir: options.uiDir });
|
|
473
311
|
console.log();
|
|
474
312
|
}
|
|
475
313
|
const fetchOpts = { ref: options.ref };
|
|
@@ -492,11 +330,11 @@ async function addCommand(names, options) {
|
|
|
492
330
|
}
|
|
493
331
|
const resolvedNames = [];
|
|
494
332
|
const notFound = [];
|
|
495
|
-
for (const
|
|
496
|
-
const normalized = normalizeComponentName(
|
|
333
|
+
for (const input of names) {
|
|
334
|
+
const normalized = normalizeComponentName(input);
|
|
497
335
|
const match = catalog.components.find((c) => normalizeComponentName(c.name) === normalized);
|
|
498
336
|
if (match) resolvedNames.push(match.name);
|
|
499
|
-
else notFound.push(
|
|
337
|
+
else notFound.push(input);
|
|
500
338
|
}
|
|
501
339
|
if (notFound.length > 0) {
|
|
502
340
|
logger.error(t("ui.add.notFound", { list: notFound.join(", ") }));
|
|
@@ -521,6 +359,10 @@ async function addCommand(names, options) {
|
|
|
521
359
|
const helperFailed = await writeComponent(result.value, config, overwriteAll, overwritePolicy, installedHelpers, fetchOpts);
|
|
522
360
|
if (helperFailed) hasFailure = true;
|
|
523
361
|
}
|
|
362
|
+
if (needsGuidance) {
|
|
363
|
+
const filename = findConfigFile()?.filename ?? DEFAULT_CONFIG_FILENAME;
|
|
364
|
+
logger.info(t("ui.add.snippetGuide", { filename, snippet: renderUiSnippet(config) }));
|
|
365
|
+
}
|
|
524
366
|
if (hasFailure) {
|
|
525
367
|
logger.error(t("ui.add.someFailed"));
|
|
526
368
|
process.exit(1);
|
|
@@ -549,8 +391,8 @@ async function writeComponent(component, config, overwriteAll, policy, installed
|
|
|
549
391
|
logger.info(t("ui.add.deploying", { name: component.name }));
|
|
550
392
|
const filesToWrite = [...component.files.shared, ...component.files[config.framework]];
|
|
551
393
|
const componentDirName = component.name;
|
|
552
|
-
const componentDir = path4.resolve(process.cwd(), config.
|
|
553
|
-
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");
|
|
554
396
|
let shouldWrite;
|
|
555
397
|
const hasExisting = hasExistingFiles(filesToWrite, componentDir);
|
|
556
398
|
if (overwriteAll) {
|
|
@@ -638,17 +480,23 @@ Lism UI v${catalog.version}
|
|
|
638
480
|
${t("ui.list.total", { count: catalog.components.length })}`);
|
|
639
481
|
}
|
|
640
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
|
+
|
|
641
489
|
// src/commands/ui/index.ts
|
|
642
490
|
function createUiCommand() {
|
|
643
|
-
const ui = new
|
|
644
|
-
ui.command("
|
|
645
|
-
|
|
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);
|
|
646
494
|
ui.command("list").description(t("cli.ui.list.description")).option("--ref <ref>", t("cli.ui.opt.ref")).action(listCommand);
|
|
647
495
|
return ui;
|
|
648
496
|
}
|
|
649
497
|
|
|
650
498
|
// src/commands/skill/index.ts
|
|
651
|
-
import { Command as
|
|
499
|
+
import { Command as Command3 } from "commander";
|
|
652
500
|
|
|
653
501
|
// src/commands/skill/add.ts
|
|
654
502
|
import fs5 from "fs";
|
|
@@ -657,15 +505,18 @@ import { checkbox, confirm as confirm2 } from "@inquirer/prompts";
|
|
|
657
505
|
|
|
658
506
|
// src/commands/skill/paths.ts
|
|
659
507
|
var SKILL_PATHS = {
|
|
660
|
-
claude: ".claude/skills
|
|
661
|
-
codex: ".agents/skills
|
|
662
|
-
cursor: ".cursor/skills
|
|
663
|
-
windsurf: ".windsurf/skills
|
|
664
|
-
cline: ".cline/skills
|
|
665
|
-
copilot: ".github/skills
|
|
666
|
-
gemini: ".gemini/skills
|
|
667
|
-
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"
|
|
668
516
|
};
|
|
517
|
+
function skillDestDir(tool, skill) {
|
|
518
|
+
return `${SKILL_PATHS[tool]}/${skill}`;
|
|
519
|
+
}
|
|
669
520
|
var ALL_SKILL_TOOLS = Object.keys(SKILL_PATHS);
|
|
670
521
|
var TOOL_MARKERS = {
|
|
671
522
|
claude: [".claude"],
|
|
@@ -684,9 +535,9 @@ import fs4 from "fs";
|
|
|
684
535
|
import os2 from "os";
|
|
685
536
|
import path5 from "path";
|
|
686
537
|
import { downloadTemplate as downloadTemplate2 } from "giget";
|
|
687
|
-
async function fetchSkillSource(ref = DEFAULT_SKILL_REF) {
|
|
538
|
+
async function fetchSkillSource(skillName, ref = DEFAULT_SKILL_REF) {
|
|
688
539
|
const tmpDir = fs4.mkdtempSync(path5.join(os2.tmpdir(), "lism-skill-"));
|
|
689
|
-
await downloadTemplate2(`github:${SOURCE_REPO}/${
|
|
540
|
+
await downloadTemplate2(`github:${SOURCE_REPO}/${SKILL_SOURCE_BASE}/${skillName}#${ref}`, {
|
|
690
541
|
dir: tmpDir,
|
|
691
542
|
force: true,
|
|
692
543
|
forceClean: true
|
|
@@ -766,8 +617,17 @@ function resolveExplicitTools(options) {
|
|
|
766
617
|
function autoDetectTools(cwd) {
|
|
767
618
|
return ALL_SKILL_TOOLS.filter((tool) => TOOL_MARKERS[tool].some((marker) => fs5.existsSync(path6.resolve(cwd, marker))));
|
|
768
619
|
}
|
|
769
|
-
|
|
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) {
|
|
770
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
|
+
}
|
|
771
631
|
let targets = resolveExplicitTools(options);
|
|
772
632
|
if (targets.length === 0) {
|
|
773
633
|
const detected = autoDetectTools(cwd);
|
|
@@ -788,23 +648,26 @@ async function skillAddCommand(options) {
|
|
|
788
648
|
return;
|
|
789
649
|
}
|
|
790
650
|
const ref = options.ref ?? DEFAULT_SKILL_REF;
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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);
|
|
796
660
|
}
|
|
797
|
-
logger.success(t("common.done"));
|
|
798
|
-
} finally {
|
|
799
|
-
cleanupTempDir(srcDir);
|
|
800
661
|
}
|
|
662
|
+
logger.success(t("common.done"));
|
|
801
663
|
}
|
|
802
|
-
async function deploySkillTo(srcDir, tool, options) {
|
|
803
|
-
const
|
|
664
|
+
async function deploySkillTo(srcDir, skill, tool, options) {
|
|
665
|
+
const relDest = skillDestDir(tool, skill);
|
|
666
|
+
const destDir = path6.resolve(process.cwd(), relDest);
|
|
804
667
|
const existing = fs5.existsSync(destDir);
|
|
805
668
|
if (existing && !options.overwrite) {
|
|
806
669
|
const diff = compareSkillDirs(destDir, srcDir);
|
|
807
|
-
const label = `${tool} (${
|
|
670
|
+
const label = `${tool} (${relDest})`;
|
|
808
671
|
if (!hasDiff(diff)) {
|
|
809
672
|
logger.log(t("skill.add.skippedSame", { label }));
|
|
810
673
|
return;
|
|
@@ -818,7 +681,7 @@ async function deploySkillTo(srcDir, tool, options) {
|
|
|
818
681
|
for (const rel of diff.localOnly) logger.log(` - ${rel}`);
|
|
819
682
|
}
|
|
820
683
|
const go = await confirm2({
|
|
821
|
-
message: t("skill.add.confirmOverwrite", { path:
|
|
684
|
+
message: t("skill.add.confirmOverwrite", { path: relDest }),
|
|
822
685
|
default: false
|
|
823
686
|
});
|
|
824
687
|
if (!go) {
|
|
@@ -834,39 +697,90 @@ async function deploySkillTo(srcDir, tool, options) {
|
|
|
834
697
|
// src/commands/skill/check.ts
|
|
835
698
|
import fs6 from "fs";
|
|
836
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
|
|
837
739
|
async function skillCheckCommand(options = {}) {
|
|
838
740
|
const cwd = process.cwd();
|
|
839
|
-
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
|
+
}
|
|
840
749
|
if (installed.length === 0) {
|
|
841
750
|
logger.info(t("skill.check.noneInstalled", { invoke: getInvokeCommand() }));
|
|
842
751
|
return;
|
|
843
752
|
}
|
|
844
753
|
const ref = options.ref ?? DEFAULT_SKILL_REF;
|
|
845
|
-
|
|
846
|
-
const
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
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));
|
|
856
774
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
logger.log(formatDiffSummary(diff));
|
|
860
|
-
if (options.verbose) logger.log(formatDiffDetails(diff));
|
|
861
|
-
}
|
|
862
|
-
logger.log("");
|
|
863
|
-
if (outdatedCount === 0) {
|
|
864
|
-
logger.success(t("skill.check.allLatest"));
|
|
865
|
-
} else {
|
|
866
|
-
logger.info(t("skill.check.outdated", { count: outdatedCount, invoke: getInvokeCommand() }));
|
|
775
|
+
} finally {
|
|
776
|
+
cleanupTempDir(remoteDir);
|
|
867
777
|
}
|
|
868
|
-
}
|
|
869
|
-
|
|
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() }));
|
|
870
784
|
}
|
|
871
785
|
}
|
|
872
786
|
function formatDiffSummary(diff) {
|
|
@@ -886,16 +800,16 @@ function formatDiffDetails(diff) {
|
|
|
886
800
|
|
|
887
801
|
// src/commands/skill/update.ts
|
|
888
802
|
async function skillUpdateCommand(options) {
|
|
889
|
-
await skillAddCommand({ ...options, overwrite: true });
|
|
803
|
+
await skillAddCommand(void 0, { ...options, overwrite: true });
|
|
890
804
|
}
|
|
891
805
|
|
|
892
806
|
// src/commands/skill/index.ts
|
|
893
807
|
function createSkillCommand() {
|
|
894
|
-
const skill = new
|
|
808
|
+
const skill = new Command3("skill").description(t("cli.skill.description"));
|
|
895
809
|
const toolOptDescription = (tool) => t("cli.skill.opt.toolPath", { path: SKILL_PATHS[tool] });
|
|
896
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"));
|
|
897
811
|
toolFlags(
|
|
898
|
-
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"))
|
|
899
813
|
).action(skillAddCommand);
|
|
900
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);
|
|
901
815
|
toolFlags(skill.command("update").description(t("cli.skill.update.description")).option("--ref <ref>", t("cli.skill.opt.ref"))).action(
|
|
@@ -904,22 +818,69 @@ function createSkillCommand() {
|
|
|
904
818
|
return skill;
|
|
905
819
|
}
|
|
906
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
|
+
|
|
856
|
+
// src/commands/mockup.ts
|
|
857
|
+
function mockupCommand() {
|
|
858
|
+
logger.log(t("mockup.guide"));
|
|
859
|
+
}
|
|
860
|
+
|
|
907
861
|
// src/createProgram.ts
|
|
908
862
|
function createLismProgram() {
|
|
909
|
-
const program2 = new
|
|
910
|
-
program2.name("lism").description(t("cli.description")).version(CLI_VERSION).option("--lang <code>", t("cli.opt.lang"));
|
|
863
|
+
const program2 = new Command4();
|
|
864
|
+
program2.name("lism-cli").description(t("cli.description")).version(CLI_VERSION).option("--lang <code>", t("cli.opt.lang"));
|
|
911
865
|
program2.hook("preAction", (thisCommand) => {
|
|
912
866
|
const opts = thisCommand.optsWithGlobals();
|
|
913
867
|
const lang = opts.lang;
|
|
914
868
|
if (typeof lang === "string") setLang(lang);
|
|
915
869
|
});
|
|
916
870
|
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);
|
|
871
|
+
const init = program2.command("init").description(t("cli.init.description"));
|
|
872
|
+
applyUiSectionOptions(init).action(initCommand);
|
|
917
873
|
program2.addCommand(createUiCommand());
|
|
918
874
|
program2.addCommand(createSkillCommand());
|
|
875
|
+
program2.command("mockup").description(t("cli.mockup.description")).allowExcessArguments().action(mockupCommand);
|
|
919
876
|
return program2;
|
|
920
877
|
}
|
|
921
878
|
|
|
922
879
|
// src/index.ts
|
|
923
880
|
preScanLang(process.argv.slice(2));
|
|
924
881
|
var program = createLismProgram();
|
|
925
|
-
program.
|
|
882
|
+
program.parseAsync().catch((err) => {
|
|
883
|
+
if (err instanceof Error && err.name === "ExitPromptError") process.exit(130);
|
|
884
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
885
|
+
process.exit(1);
|
|
886
|
+
});
|