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/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
- SKILL_SOURCE_PATH,
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-6E4LIZPL.js";
17
+ } from "./chunk-BEY36YDC.js";
17
18
 
18
19
  // src/createProgram.ts
19
- import { Command as Command3 } from "commander";
20
+ import { Command as Command4 } from "commander";
20
21
 
21
22
  // src/commands/ui/index.ts
22
- import { Command, Option } from "commander";
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, kind: "module" };
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
- const mod = await jiti.import(found.path);
106
- const cli = mod?.cli ?? mod;
107
- if (!cli || typeof cli !== "object") {
108
- throw new Error(t("config.cliSectionMissing", { filename: found.filename }));
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 validateCliConfig(cli) {
114
- const c = cli;
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
- if (typeof c.componentsDir !== "string" || !c.componentsDir) {
119
- throw new Error(t("config.invalidComponentsDir"));
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(cli) {
85
+ function writeFreshConfig(ui) {
126
86
  const filePath = getDefaultConfigPath();
127
- const body = renderConfigTemplate(cli);
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
- async function hasCliSection(filePath) {
132
- try {
133
- const jiti = createJiti(import.meta.url, { interopDefault: true });
134
- const mod = await jiti.import(filePath);
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
- ` cli: ${renderCliObject(cli, " ")},`,
225
- "};",
226
- ""
227
- ].join("\n");
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} componentsDir: ${JSON.stringify(cli.componentsDir)},`,
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/init.ts
404
- import { select, input } from "@inquirer/prompts";
405
- async function runInit(options = {}) {
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.init.promptFramework"),
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 componentsDir = options.componentsDir ?? await input({
414
- message: t("ui.init.promptComponentsDir"),
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
- if (configExists()) {
469
- config = await readConfig();
470
- } else {
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 runInit();
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 input2 of names) {
496
- const normalized = normalizeComponentName(input2);
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(input2);
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.componentsDir, componentDirName);
553
- const helperDir = path4.resolve(process.cwd(), config.helperDir);
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 Command("ui").description(t("cli.ui.description"));
644
- ui.command("init").description(t("cli.ui.init.description")).addOption(new Option("--framework <name>", t("cli.ui.init.opt.framework")).choices(["react", "astro"])).option("--components-dir <path>", t("cli.ui.init.opt.componentsDir")).option("--helper-dir <path>", t("cli.ui.init.opt.helperDir")).option("-f, --force", t("cli.ui.init.opt.force"), false).action(initCommand);
645
- 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).option("--ref <ref>", t("cli.ui.opt.ref")).action(addCommand);
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 Command2 } from "commander";
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/lism-css-guide",
661
- codex: ".agents/skills/lism-css-guide",
662
- cursor: ".cursor/skills/lism-css-guide",
663
- windsurf: ".windsurf/skills/lism-css-guide",
664
- cline: ".cline/skills/lism-css-guide",
665
- copilot: ".github/skills/lism-css-guide",
666
- gemini: ".gemini/skills/lism-css-guide",
667
- junie: ".junie/skills/lism-css-guide"
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}/${SKILL_SOURCE_PATH}#${ref}`, {
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
- async function skillAddCommand(options) {
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
- logger.info(t("skill.add.fetching", { ref }));
792
- const { dir: srcDir } = await fetchSkillSource(ref);
793
- try {
794
- for (const tool of targets) {
795
- await deploySkillTo(srcDir, tool, options);
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 destDir = path6.resolve(process.cwd(), SKILL_PATHS[tool]);
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} (${SKILL_PATHS[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: SKILL_PATHS[tool] }),
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 = ALL_SKILL_TOOLS.filter((tool) => fs6.existsSync(path7.join(cwd, SKILL_PATHS[tool], "SKILL.md")));
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
- logger.info(t("skill.check.fetching", { ref }));
846
- const { dir: remoteDir } = await fetchSkillSource(ref);
847
- try {
848
- let outdatedCount = 0;
849
- for (const tool of installed) {
850
- const localDir = path7.resolve(cwd, SKILL_PATHS[tool]);
851
- const diff = compareSkillDirs(localDir, remoteDir);
852
- const label = `${tool.padEnd(9)} ${SKILL_PATHS[tool]}`;
853
- if (!hasDiff(diff)) {
854
- logger.log(t("skill.check.upToDate", { label }));
855
- continue;
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
- outdatedCount += 1;
858
- logger.warn(` ! ${label}`);
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
- } finally {
869
- cleanupTempDir(remoteDir);
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 Command2("skill").description(t("cli.skill.description"));
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 Command3();
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.parse();
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
+ });