@reliverse/rempts 1.7.65 → 2.2.7

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.
Files changed (131) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1534 -1431
  3. package/cleanup.mjs +33 -0
  4. package/dist/cancel.d.ts +31 -0
  5. package/dist/cancel.js +28 -0
  6. package/dist/ffi.d.ts +1 -0
  7. package/dist/ffi.js +165 -0
  8. package/dist/group.d.ts +16 -0
  9. package/dist/group.js +22 -0
  10. package/dist/launcher/command.d.ts +8 -0
  11. package/dist/launcher/command.js +10 -0
  12. package/dist/launcher/discovery.d.ts +3 -0
  13. package/dist/launcher/discovery.js +207 -0
  14. package/dist/launcher/errors.d.ts +15 -0
  15. package/dist/launcher/errors.js +31 -0
  16. package/dist/launcher/help.d.ts +3 -0
  17. package/dist/launcher/help.js +145 -0
  18. package/dist/launcher/mod.d.ts +12 -0
  19. package/dist/launcher/mod.js +222 -0
  20. package/dist/launcher/parser.d.ts +14 -0
  21. package/dist/launcher/parser.js +255 -0
  22. package/dist/launcher/registry.d.ts +10 -0
  23. package/dist/launcher/registry.js +42 -0
  24. package/dist/launcher/types.d.ts +78 -0
  25. package/dist/launcher/validator.d.ts +3 -0
  26. package/dist/launcher/validator.js +39 -0
  27. package/dist/mod.d.ts +6 -0
  28. package/dist/mod.js +6 -0
  29. package/dist/prompt.d.ts +13 -0
  30. package/dist/prompt.js +53 -0
  31. package/dist/selection.d.ts +92 -0
  32. package/dist/selection.js +191 -0
  33. package/dist/spinner.d.ts +26 -0
  34. package/dist/spinner.js +141 -0
  35. package/dist/utils.d.ts +3 -0
  36. package/dist/utils.js +11 -0
  37. package/package.json +41 -47
  38. package/bin/libs/animate/animate-mod.ts.txt +0 -78
  39. package/bin/libs/anykey/anykey-mod.d.ts +0 -12
  40. package/bin/libs/anykey/anykey-mod.js +0 -125
  41. package/bin/libs/cancel/cancel.d.ts +0 -45
  42. package/bin/libs/cancel/cancel.js +0 -72
  43. package/bin/libs/confirm/confirm-alias.d.ts +0 -2
  44. package/bin/libs/confirm/confirm-alias.js +0 -2
  45. package/bin/libs/confirm/confirm-mod.d.ts +0 -5
  46. package/bin/libs/confirm/confirm-mod.js +0 -179
  47. package/bin/libs/date/date.d.ts +0 -2
  48. package/bin/libs/date/date.js +0 -254
  49. package/bin/libs/editor/editor-mod.d.ts +0 -25
  50. package/bin/libs/editor/editor-mod.js +0 -1133
  51. package/bin/libs/figures/figures-mod.d.ts +0 -461
  52. package/bin/libs/figures/figures-mod.js +0 -285
  53. package/bin/libs/group/group-mod.d.ts +0 -33
  54. package/bin/libs/group/group-mod.js +0 -89
  55. package/bin/libs/input/input-alias.d.ts +0 -5
  56. package/bin/libs/input/input-alias.js +0 -4
  57. package/bin/libs/input/input-mod.d.ts +0 -16
  58. package/bin/libs/input/input-mod.js +0 -370
  59. package/bin/libs/intro/intro-alias.d.ts +0 -3
  60. package/bin/libs/intro/intro-alias.js +0 -3
  61. package/bin/libs/intro/intro-mod.d.ts +0 -19
  62. package/bin/libs/intro/intro-mod.js +0 -71
  63. package/bin/libs/launcher/command-runner.d.ts +0 -31
  64. package/bin/libs/launcher/command-runner.js +0 -229
  65. package/bin/libs/launcher/launcher-alias.d.ts +0 -2
  66. package/bin/libs/launcher/launcher-alias.js +0 -2
  67. package/bin/libs/launcher/launcher-mod.d.ts +0 -66
  68. package/bin/libs/launcher/launcher-mod.js +0 -1037
  69. package/bin/libs/launcher/launcher-types.d.ts +0 -176
  70. package/bin/libs/launcher/launcher-types.js +0 -0
  71. package/bin/libs/log/log-alias.d.ts +0 -1
  72. package/bin/libs/log/log-alias.js +0 -2
  73. package/bin/libs/msg-fmt/colors.d.ts +0 -30
  74. package/bin/libs/msg-fmt/colors.js +0 -42
  75. package/bin/libs/msg-fmt/mapping.d.ts +0 -3
  76. package/bin/libs/msg-fmt/mapping.js +0 -41
  77. package/bin/libs/msg-fmt/messages.d.ts +0 -35
  78. package/bin/libs/msg-fmt/messages.js +0 -305
  79. package/bin/libs/msg-fmt/terminal.d.ts +0 -15
  80. package/bin/libs/msg-fmt/terminal.js +0 -60
  81. package/bin/libs/msg-fmt/variants.d.ts +0 -11
  82. package/bin/libs/msg-fmt/variants.js +0 -52
  83. package/bin/libs/multiselect/multiselect-alias.d.ts +0 -2
  84. package/bin/libs/multiselect/multiselect-alias.js +0 -2
  85. package/bin/libs/multiselect/multiselect-prompt.d.ts +0 -2
  86. package/bin/libs/multiselect/multiselect-prompt.js +0 -340
  87. package/bin/libs/next-steps/next-steps.d.ts +0 -13
  88. package/bin/libs/next-steps/next-steps.js +0 -24
  89. package/bin/libs/number/number-mod.d.ts +0 -28
  90. package/bin/libs/number/number-mod.js +0 -234
  91. package/bin/libs/outro/outro-alias.d.ts +0 -3
  92. package/bin/libs/outro/outro-alias.js +0 -3
  93. package/bin/libs/outro/outro-mod.d.ts +0 -7
  94. package/bin/libs/outro/outro-mod.js +0 -49
  95. package/bin/libs/reliarg/reliarg-mod.d.ts +0 -76
  96. package/bin/libs/reliarg/reliarg-mod.js +0 -276
  97. package/bin/libs/results/results.d.ts +0 -7
  98. package/bin/libs/results/results.js +0 -27
  99. package/bin/libs/select/nummultiselect-prompt.d.ts +0 -6
  100. package/bin/libs/select/nummultiselect-prompt.js +0 -141
  101. package/bin/libs/select/numselect-prompt.d.ts +0 -7
  102. package/bin/libs/select/numselect-prompt.js +0 -111
  103. package/bin/libs/select/select-alias.d.ts +0 -9
  104. package/bin/libs/select/select-alias.js +0 -9
  105. package/bin/libs/select/select-prompt.d.ts +0 -5
  106. package/bin/libs/select/select-prompt.js +0 -311
  107. package/bin/libs/select/toggle-prompt.d.ts +0 -5
  108. package/bin/libs/select/toggle-prompt.js +0 -207
  109. package/bin/libs/spinner/spinner-impl.d.ts +0 -70
  110. package/bin/libs/spinner/spinner-impl.js +0 -336
  111. package/bin/libs/spinner/spinner-mod.d.ts +0 -167
  112. package/bin/libs/spinner/spinner-mod.js +0 -447
  113. package/bin/libs/utils/colorize.d.ts +0 -2
  114. package/bin/libs/utils/colorize.js +0 -122
  115. package/bin/libs/utils/errors.d.ts +0 -1
  116. package/bin/libs/utils/errors.js +0 -17
  117. package/bin/libs/utils/prevent.d.ts +0 -8
  118. package/bin/libs/utils/prevent.js +0 -62
  119. package/bin/libs/utils/prompt-end.d.ts +0 -8
  120. package/bin/libs/utils/prompt-end.js +0 -36
  121. package/bin/libs/utils/stream-text.d.ts +0 -18
  122. package/bin/libs/utils/stream-text.js +0 -138
  123. package/bin/libs/utils/system.d.ts +0 -6
  124. package/bin/libs/utils/system.js +0 -7
  125. package/bin/libs/utils/validate.d.ts +0 -21
  126. package/bin/libs/utils/validate.js +0 -17
  127. package/bin/libs/visual/visual-mod.ts.txt +0 -19
  128. package/bin/mod.d.ts +0 -50
  129. package/bin/mod.js +0 -127
  130. package/bin/types.d.ts +0 -372
  131. /package/{bin → dist/launcher}/types.js +0 -0
@@ -1,1037 +0,0 @@
1
- import { readdir, stat } from "node:fs/promises";
2
- import process from "node:process";
3
- import { re } from "@reliverse/relico";
4
- import { relinka, relinkaConfig, relinkaShutdown } from "@reliverse/relinka";
5
- import path from "path";
6
- import { readPackageJSON } from "pkg-types";
7
- import { reliArgParser } from "../reliarg/reliarg-mod.js";
8
- const isBunRuntime = Boolean(
9
- // Prefer versions flag to avoid TS type needs
10
- process?.versions?.bun || typeof globalThis.Bun !== "undefined"
11
- );
12
- const _pathExistsCache = /* @__PURE__ */ new Map();
13
- async function pathExists(p) {
14
- const cached = _pathExistsCache.get(p);
15
- if (cached !== void 0) return cached;
16
- try {
17
- if (isBunRuntime) {
18
- const bun = globalThis.Bun;
19
- if (bun?.file) {
20
- const exists = await bun.file(p).exists();
21
- _pathExistsCache.set(p, exists);
22
- return exists;
23
- }
24
- }
25
- await stat(p);
26
- _pathExistsCache.set(p, true);
27
- return true;
28
- } catch (_error) {
29
- _pathExistsCache.set(p, false);
30
- return false;
31
- }
32
- }
33
- async function isDirectory(p) {
34
- try {
35
- const s = await stat(p);
36
- return s.isDirectory();
37
- } catch {
38
- return false;
39
- }
40
- }
41
- let _cachedPkgJson;
42
- async function readPkgJsonCached() {
43
- if (_cachedPkgJson) return _cachedPkgJson;
44
- try {
45
- if (isBunRuntime) {
46
- const bun = globalThis.Bun;
47
- if (bun?.file) {
48
- _cachedPkgJson = await bun.file("package.json").json();
49
- } else {
50
- _cachedPkgJson = await readPackageJSON();
51
- }
52
- } else {
53
- _cachedPkgJson = await readPackageJSON();
54
- }
55
- } catch (_e) {
56
- _cachedPkgJson = void 0;
57
- }
58
- return _cachedPkgJson;
59
- }
60
- function buildExampleArgs(args) {
61
- const parts = [];
62
- const positionalKeys = Object.keys(args || {}).filter((k) => args?.[k]?.type === "positional");
63
- positionalKeys.forEach((key) => {
64
- const def = args?.[key];
65
- if (def && (def.required || Math.random() > 0.5)) {
66
- parts.push(String(def.default ?? `<${key}>`));
67
- }
68
- });
69
- const otherKeys = Object.keys(args || {}).filter((k) => args?.[k]?.type !== "positional");
70
- for (const key of otherKeys) {
71
- const def = args?.[key];
72
- if (def && (def.required || Math.random() > 0.7)) {
73
- switch (def.type) {
74
- case "boolean":
75
- if (def.default === true) {
76
- if (Math.random() > 0.5) parts.push(`--no-${key}`);
77
- } else {
78
- parts.push(`--${key}`);
79
- }
80
- break;
81
- case "string":
82
- parts.push(`--${key}=${String(def.default ?? key)}`);
83
- break;
84
- case "number":
85
- parts.push(`--${key}=${String(def.default ?? 42)}`);
86
- break;
87
- case "array":
88
- parts.push(`--${key}=${String(def.default ?? key)}`);
89
- break;
90
- }
91
- }
92
- }
93
- return parts.join(" ");
94
- }
95
- const isDebugMode = process.argv.includes("--debug");
96
- let _relinkaConfigured = false;
97
- async function ensureRelinkaConfigured() {
98
- if (_relinkaConfigured) return;
99
- await relinkaConfig();
100
- _relinkaConfigured = true;
101
- }
102
- function debugLog(...args) {
103
- if (isDebugMode) {
104
- void ensureRelinkaConfigured().then(() => relinka("log", "[DEBUG]", ...args));
105
- }
106
- }
107
- function isFlag(str) {
108
- return str.startsWith("-");
109
- }
110
- export function defineCommand(options) {
111
- const onCmdInit = options.onCmdInit || options.setup;
112
- const onCmdExit = options.onCmdExit || options.cleanup;
113
- const onLauncherInit = options.onLauncherInit;
114
- const onLauncherExit = options.onLauncherExit;
115
- let commands = options.commands;
116
- if (!commands) {
117
- commands = options.subCommands;
118
- }
119
- const cmdObj = {
120
- ...options.meta !== void 0 && { meta: options.meta },
121
- args: options.args || {},
122
- ...options.run !== void 0 && { run: options.run },
123
- ...commands !== void 0 && { commands },
124
- ...onCmdInit !== void 0 && { onCmdInit },
125
- ...onCmdExit !== void 0 && { onCmdExit },
126
- ...onLauncherInit !== void 0 && { onLauncherInit },
127
- ...onLauncherExit !== void 0 && { onLauncherExit },
128
- // Backward-compatible aliases
129
- ...onCmdInit !== void 0 && { setup: onCmdInit },
130
- ...onCmdExit !== void 0 && { cleanup: onCmdExit }
131
- };
132
- Object.defineProperty(cmdObj, "subCommands", {
133
- get() {
134
- return this.commands;
135
- },
136
- enumerable: false,
137
- configurable: true
138
- });
139
- return cmdObj;
140
- }
141
- let _cachedDefaultCliName;
142
- let _cachedDefaultCliVersion;
143
- let _cachedDefaultCliDescription;
144
- async function getDefaultCliNameAndVersion() {
145
- if (_cachedDefaultCliName)
146
- return { name: _cachedDefaultCliName, version: _cachedDefaultCliVersion };
147
- try {
148
- const pkg = await readPkgJsonCached() ?? {};
149
- let name = pkg.name || "cli";
150
- if (name.startsWith("@")) {
151
- name = name.split("/").pop() || name;
152
- }
153
- _cachedDefaultCliName = name;
154
- _cachedDefaultCliVersion = pkg.version;
155
- if (pkg.description && !_cachedDefaultCliDescription) {
156
- _cachedDefaultCliDescription = pkg.description;
157
- }
158
- return { name, version: pkg.version };
159
- } catch (_e) {
160
- return { name: "cli", version: void 0 };
161
- }
162
- }
163
- async function findRecursiveFileBasedCommands(baseDir, currentPath = []) {
164
- const results = [];
165
- const items = await readdir(path.join(baseDir, ...currentPath), {
166
- withFileTypes: true
167
- });
168
- for (const dirent of items) {
169
- if (dirent.isDirectory()) {
170
- const newPath = [...currentPath, dirent.name];
171
- for (const fname of ["cmd.ts", "cmd.js"]) {
172
- const fpath = path.join(baseDir, ...newPath, fname);
173
- if (await pathExists(fpath)) {
174
- try {
175
- const imported = await import(path.resolve(fpath));
176
- if (imported.default && !imported.default.meta?.hidden) {
177
- results.push({
178
- name: dirent.name,
179
- def: imported.default,
180
- path: newPath
181
- });
182
- }
183
- } catch (err) {
184
- debugLog(`Skipping file-based command in ${fpath}:`, err);
185
- }
186
- break;
187
- }
188
- }
189
- const subResults = await findRecursiveFileBasedCommands(baseDir, newPath);
190
- results.push(...subResults);
191
- }
192
- }
193
- return results;
194
- }
195
- function calculatePadding(items, minPad = 2) {
196
- const maxLength = items.reduce((max, item) => Math.max(max, item.text.length), 0);
197
- return maxLength + minPad;
198
- }
199
- function formatTableRow(text, desc, padding) {
200
- const spaces = " ".repeat(Math.max(0, padding - text.length));
201
- return `${text}${spaces}| ${desc || ""}`;
202
- }
203
- export async function showUsage(command, parserOptions = {}, globalCliMeta) {
204
- const { name: fallbackName, version: fallbackVersion } = await getDefaultCliNameAndVersion();
205
- const cliName = globalCliMeta?.name || command.meta?.name || fallbackName;
206
- const cliVersion = globalCliMeta?.version || command.meta?.version || fallbackVersion;
207
- relinka("info", `${cliName}${cliVersion ? ` v${cliVersion}` : ""}`);
208
- if (parserOptions.metaSettings?.showDescriptionOnMain) {
209
- let description = globalCliMeta?.description || command.meta?.description;
210
- if (!description) {
211
- await getDefaultCliNameAndVersion();
212
- if (_cachedDefaultCliDescription) description = _cachedDefaultCliDescription;
213
- }
214
- if (description) {
215
- relinka("log", description);
216
- }
217
- }
218
- const { name: pkgName } = await getDefaultCliNameAndVersion();
219
- const fileCmds = parserOptions.fileBased;
220
- if (fileCmds?.enable) {
221
- const commandsDir = path.resolve(fileCmds.cmdsRootPath);
222
- const pathSegments = parserOptions._fileBasedPathSegments || [];
223
- let usageLine = [pkgName, ...pathSegments].join(" ");
224
- const allCommands = await findRecursiveFileBasedCommands(commandsDir, pathSegments);
225
- const directCommands = allCommands.filter((cmd) => cmd.path.length === pathSegments.length + 1);
226
- if (directCommands.length > 0) {
227
- usageLine += " <command> [command's options]";
228
- } else {
229
- usageLine += " [command's options]";
230
- const pos = renderPositional(command.args);
231
- if (pos) usageLine += ` ${pos}`;
232
- }
233
- relinka("log", re.cyan(`Usage: ${usageLine}`));
234
- if (directCommands.length > 0 && allCommands.length > 0) {
235
- const randomIdx = Math.floor(Math.random() * allCommands.length);
236
- const exampleCmd = allCommands[randomIdx];
237
- if (exampleCmd) {
238
- const { path: path2, def: exampleDef } = exampleCmd;
239
- const exampleArgs = buildExampleArgs(exampleDef.args || {});
240
- relinka(
241
- "log",
242
- re.cyan(`Example: ${pkgName} ${path2.join(" ")}${exampleArgs ? ` ${exampleArgs}` : ""}`)
243
- );
244
- }
245
- }
246
- if (allCommands.length > 0) {
247
- relinka("info", "Available commands (run with `help` to see more):");
248
- const commandsByPath = /* @__PURE__ */ new Map();
249
- for (const cmd of allCommands) {
250
- const parentPath = cmd.path.slice(0, -1).join("/") || "/";
251
- if (!commandsByPath.has(parentPath)) {
252
- commandsByPath.set(parentPath, []);
253
- }
254
- const group = commandsByPath.get(parentPath);
255
- if (group) {
256
- group.push(cmd);
257
- }
258
- }
259
- const groupPaddings = /* @__PURE__ */ new Map();
260
- for (const [parentPath, cmds] of commandsByPath) {
261
- const items = cmds.map(({ path: path2, def }) => ({
262
- text: `${parentPath === "/" ? "" : " "}\u2022 ${path2.join(" ")}`,
263
- desc: def?.meta?.description
264
- }));
265
- groupPaddings.set(parentPath, calculatePadding(items));
266
- }
267
- for (const [parentPath, cmds] of commandsByPath) {
268
- if (parentPath !== "/") {
269
- relinka("log", re.cyanBright(`Sub-commands in ${parentPath}:`));
270
- }
271
- const padding = groupPaddings.get(parentPath) || 0;
272
- for (const { def, path: path2 } of cmds) {
273
- const desc = def?.meta?.description ?? "";
274
- const indent = parentPath === "/" ? "" : " ";
275
- const text = `${indent}\u2022 ${path2.join(" ")}`;
276
- relinka("log", formatTableRow(text, desc, padding));
277
- }
278
- }
279
- }
280
- } else {
281
- const subCommandNames = [];
282
- const subCommandDefs = [];
283
- const objectCommands = command.commands;
284
- if (objectCommands) {
285
- for (const [name, spec] of Object.entries(objectCommands)) {
286
- try {
287
- const cmd = await loadSubCommand(spec);
288
- if (!cmd?.meta?.hidden) {
289
- const aliasDisplay = cmd.meta?.aliases ? ` (aliases: ${cmd.meta.aliases.join(", ")})` : "";
290
- subCommandNames.push(`${name}${aliasDisplay}`);
291
- subCommandDefs.push({ name, def: cmd });
292
- }
293
- } catch (err) {
294
- debugLog(`Error loading command ${name}:`, err);
295
- }
296
- }
297
- }
298
- let usageLine = pkgName;
299
- if (parserOptions._isSubcommand) {
300
- usageLine += ` ${cliName}`;
301
- }
302
- if (subCommandNames.length > 0) {
303
- usageLine += " <command> [command's options]";
304
- } else {
305
- usageLine += ` [command's options] ${renderPositional(command.args)}`;
306
- }
307
- relinka("log", re.cyan(`Usage: ${usageLine}`));
308
- if (subCommandDefs.length > 0) {
309
- const randomIdx = Math.floor(Math.random() * subCommandDefs.length);
310
- const exampleCmd = subCommandDefs[randomIdx];
311
- if (exampleCmd) {
312
- const { name: exampleCmdName, def: exampleDef } = exampleCmd;
313
- const exampleArgs = buildExampleArgs(exampleDef.args || {});
314
- relinka(
315
- "log",
316
- re.cyan(
317
- `Example: ${pkgName}${parserOptions._isSubcommand ? ` ${cliName}` : ""} ${exampleCmdName}${exampleArgs ? ` ${exampleArgs}` : ""}`
318
- )
319
- );
320
- }
321
- }
322
- if (subCommandNames.length > 0) {
323
- relinka("info", "Available commands (run with `help` to see more):");
324
- const commandItems = subCommandDefs.map(({ name, def }) => ({
325
- text: `\u2022 ${name}`,
326
- desc: def?.meta?.description
327
- }));
328
- const padding = calculatePadding(commandItems);
329
- for (const { text, desc } of commandItems) {
330
- relinka("log", formatTableRow(text, desc, padding));
331
- }
332
- }
333
- }
334
- relinka("info", "Available options:");
335
- const optionItems = [
336
- { text: "\u2022 -h, --help", desc: "Show help" },
337
- { text: "\u2022 -v, --version", desc: "Show version" },
338
- { text: "\u2022 --debug", desc: "Enable debug mode" }
339
- ];
340
- for (const [key, def] of Object.entries(command.args || {})) {
341
- if (def.type === "positional") {
342
- optionItems.push({
343
- text: `\u2022 <${key}>`,
344
- desc: `${def.description ?? ""}${def.required ? " | required" : ""}`
345
- });
346
- } else {
347
- const text = `\u2022 --${key}${"alias" in def && def.alias ? `, -${def.alias}` : ""}`;
348
- const parts = [
349
- def.description ?? "",
350
- `type=${def.type}`,
351
- def.default !== void 0 ? `default=${JSON.stringify(def.default)}` : null,
352
- def.required ? "required" : null,
353
- def.dependencies ? `depends on: ${def.dependencies.map((r) => `--${r}`).join(", ")}` : null
354
- ].filter(Boolean);
355
- optionItems.push({ text, desc: parts.join(" | ") });
356
- }
357
- }
358
- const optionsPadding = calculatePadding(optionItems);
359
- for (const { text, desc } of optionItems) {
360
- relinka("log", formatTableRow(text, desc, optionsPadding));
361
- }
362
- }
363
- export function createCli(options, legacyParserOptions) {
364
- let command;
365
- let parserOptions;
366
- let globalCliMeta = {};
367
- if (typeof options === "object" && !("run" in options) && !("meta" in options) && !("args" in options) && !("commands" in options)) {
368
- command = options;
369
- parserOptions = legacyParserOptions || {};
370
- } else if ("mainCommand" in options) {
371
- command = options.mainCommand;
372
- parserOptions = {
373
- ...options.fileBased !== void 0 && { fileBased: options.fileBased },
374
- ...options.autoExit !== void 0 && { autoExit: options.autoExit },
375
- ...options.metaSettings !== void 0 && { metaSettings: options.metaSettings }
376
- };
377
- globalCliMeta = {
378
- ...options.name !== void 0 && { name: options.name },
379
- ...options.version !== void 0 && { version: options.version },
380
- ...options.description !== void 0 && { description: options.description }
381
- };
382
- } else {
383
- const inlineOptions = options;
384
- const {
385
- name,
386
- version,
387
- description,
388
- fileBased,
389
- autoExit,
390
- metaSettings,
391
- mainCommand,
392
- ...commandOptions
393
- } = inlineOptions;
394
- command = {
395
- ...commandOptions.meta !== void 0 && { meta: commandOptions.meta },
396
- ...commandOptions.args !== void 0 && { args: commandOptions.args },
397
- ...commandOptions.run !== void 0 && { run: commandOptions.run },
398
- ...commandOptions.commands !== void 0 && { commands: commandOptions.commands },
399
- ...commandOptions.onCmdInit !== void 0 && { onCmdInit: commandOptions.onCmdInit },
400
- ...commandOptions.onCmdExit !== void 0 && { onCmdExit: commandOptions.onCmdExit },
401
- ...commandOptions.onLauncherInit !== void 0 && {
402
- onLauncherInit: commandOptions.onLauncherInit
403
- },
404
- ...commandOptions.onLauncherExit !== void 0 && {
405
- onLauncherExit: commandOptions.onLauncherExit
406
- }
407
- };
408
- parserOptions = {
409
- ...fileBased !== void 0 && { fileBased },
410
- ...autoExit !== void 0 && { autoExit },
411
- ...metaSettings !== void 0 && { metaSettings }
412
- };
413
- globalCliMeta = {
414
- ...name !== void 0 && { name },
415
- ...version !== void 0 && { version },
416
- ...description !== void 0 && { description }
417
- };
418
- }
419
- if (command.run && (globalCliMeta.name || globalCliMeta.version || globalCliMeta.description)) {
420
- const mergedMeta = { ...command.meta };
421
- if (globalCliMeta.name && !command.meta?.name) {
422
- mergedMeta.name = globalCliMeta.name;
423
- }
424
- if (globalCliMeta.version && !command.meta?.version) {
425
- mergedMeta.version = globalCliMeta.version;
426
- }
427
- if (globalCliMeta.description && !command.meta?.description) {
428
- mergedMeta.description = globalCliMeta.description;
429
- }
430
- command = {
431
- ...command,
432
- meta: mergedMeta
433
- };
434
- }
435
- const execute = async (_ctx) => {
436
- if (typeof command.onLauncherInit === "function") {
437
- try {
438
- await command.onLauncherInit();
439
- } catch (err) {
440
- relinka("error", "Error in onLauncherInit:", err);
441
- if (parserOptions.autoExit !== false) process.exit(1);
442
- throw err;
443
- }
444
- }
445
- try {
446
- if (!parserOptions.fileBased && !command.commands) {
447
- const mainEntry = process.argv[1] ? path.dirname(path.resolve(process.argv[1])) : process.cwd();
448
- const defaultCmdsRoot = path.join(mainEntry, "src", "app");
449
- const exists = await pathExists(defaultCmdsRoot);
450
- const finalCmdsRoot = exists ? defaultCmdsRoot : path.join(mainEntry, "app");
451
- parserOptions.fileBased = {
452
- enable: true,
453
- cmdsRootPath: finalCmdsRoot
454
- };
455
- }
456
- const rawArgv = process.argv.slice(2);
457
- const autoExit = parserOptions.autoExit !== false;
458
- if (!(parserOptions.fileBased?.enable || command.commands && Object.keys(command.commands).length > 0 || command.run)) {
459
- await ensureRelinkaConfigured();
460
- relinka(
461
- "error",
462
- "Invalid CLI configuration: No file-based commands, subCommands, or run() handler are defined. This CLI will not do anything.\n\u2502 To fix: add file-based commands (./app), or provide at least one subCommand, or a run() handler."
463
- );
464
- process.exit(1);
465
- }
466
- if (parserOptions.fileBased?.enable && rawArgv.length > 0) {
467
- const commandsDir = path.resolve(parserOptions.fileBased.cmdsRootPath);
468
- const resolved = await resolveFileBasedCommandPath(commandsDir, rawArgv);
469
- if (resolved) {
470
- const { def: subCommand, leftoverArgv, path: pathSegments } = resolved;
471
- const helpIdx = leftoverArgv.findIndex(
472
- (arg) => arg === "help" || arg === "--help" || arg === "-h"
473
- );
474
- if (helpIdx !== -1) {
475
- await showUsage(
476
- subCommand,
477
- {
478
- ...parserOptions,
479
- _fileBasedCurrentDir: pathSegments.length ? path.join(commandsDir, ...pathSegments) : commandsDir,
480
- _fileBasedPathSegments: pathSegments
481
- },
482
- globalCliMeta
483
- );
484
- if (autoExit) process.exit(0);
485
- return;
486
- }
487
- }
488
- }
489
- const fileBasedEnabled = parserOptions.fileBased?.enable;
490
- if (fileBasedEnabled && rawArgv.length > 0 && rawArgv[0] && !isFlag(rawArgv[0])) {
491
- const [subName, ...subCmdArgv] = rawArgv;
492
- try {
493
- const ctx = getParsedContext(command, rawArgv, parserOptions);
494
- if (typeof command.onCmdInit === "function") await command.onCmdInit(ctx);
495
- await runFileBasedSubCmd(
496
- subName,
497
- subCmdArgv,
498
- parserOptions.fileBased,
499
- parserOptions,
500
- command.onCmdExit ? async (_subCtx) => {
501
- await command.onCmdExit?.(ctx);
502
- } : void 0,
503
- globalCliMeta
504
- );
505
- if (autoExit) process.exit(0);
506
- return;
507
- } catch (err) {
508
- await ensureRelinkaConfigured();
509
- relinka("error", "Error loading file-based subcommand:", err.message);
510
- if (autoExit) process.exit(1);
511
- throw err;
512
- }
513
- }
514
- if (!fileBasedEnabled && command.commands && rawArgv.length > 0 && rawArgv[0] && !isFlag(rawArgv[0])) {
515
- const [maybeSub, ...subCmdArgv] = rawArgv;
516
- let subSpec;
517
- for (const [key, spec] of Object.entries(command.commands)) {
518
- if (key === maybeSub) {
519
- subSpec = spec;
520
- break;
521
- }
522
- try {
523
- const cmd = await loadSubCommand(spec);
524
- if (cmd.meta?.aliases?.includes(maybeSub)) {
525
- subSpec = spec;
526
- break;
527
- }
528
- } catch (err) {
529
- debugLog(`Error checking alias for command ${key}:`, err);
530
- }
531
- }
532
- if (subSpec) {
533
- const helpIdx = subCmdArgv.findIndex(
534
- (arg) => arg === "help" || arg === "--help" || arg === "-h"
535
- );
536
- if (helpIdx !== -1) {
537
- const subCommandDef = await loadSubCommand(subSpec);
538
- await showUsage(
539
- subCommandDef,
540
- {
541
- ...parserOptions,
542
- _isSubcommand: true
543
- },
544
- globalCliMeta
545
- );
546
- if (autoExit) process.exit(0);
547
- return;
548
- }
549
- try {
550
- const ctx = getParsedContext(command, rawArgv, parserOptions);
551
- if (typeof command.onCmdInit === "function") await command.onCmdInit(ctx);
552
- await runSubCommand(
553
- subSpec,
554
- subCmdArgv,
555
- { ...parserOptions, _isSubcommand: true },
556
- command.onCmdExit ? async (_subCtx) => {
557
- await command.onCmdExit?.(ctx);
558
- } : void 0,
559
- globalCliMeta
560
- );
561
- if (autoExit) process.exit(0);
562
- return;
563
- } catch (err) {
564
- await ensureRelinkaConfigured();
565
- relinka("error", "Error running subcommand:", err.message);
566
- if (autoExit) process.exit(1);
567
- throw err;
568
- }
569
- }
570
- }
571
- await ensureRelinkaConfigured();
572
- if (rawArgv[0] === "help" || checkHelp(rawArgv)) {
573
- await showUsage(command, parserOptions, globalCliMeta);
574
- if (autoExit) process.exit(0);
575
- return;
576
- }
577
- if (checkVersion(rawArgv)) {
578
- if (command.meta?.name) {
579
- relinka(
580
- "info",
581
- `${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
582
- );
583
- }
584
- if (autoExit) process.exit(0);
585
- return;
586
- }
587
- try {
588
- await runCommandWithArgs(command, rawArgv, parserOptions, globalCliMeta);
589
- } finally {
590
- }
591
- if (_relinkaConfigured) await relinkaShutdown();
592
- } finally {
593
- if (typeof command.onLauncherExit === "function") await command.onLauncherExit();
594
- }
595
- };
596
- const promise = execute();
597
- const cli = Object.assign(promise, {
598
- /**
599
- * @deprecated Use createCli() directly instead. This method will be removed in a future version.
600
- * @example
601
- * // Instead of:
602
- * createCli({...}).run()
603
- * // Use:
604
- * await createCli({...})
605
- */
606
- async run(_ctx) {
607
- relinka(
608
- "warn",
609
- "\u26A0\uFE0F Deprecated: .run() method is deprecated. Use createCli() directly instead."
610
- );
611
- relinka("warn", " Instead of: createCli({...}).run()");
612
- relinka("warn", " Use: await createCli({...})");
613
- return execute(_ctx);
614
- }
615
- });
616
- return cli;
617
- }
618
- function checkHelp(argv) {
619
- return argv.includes("--help") || argv.includes("-h");
620
- }
621
- function checkVersion(argv) {
622
- return argv.includes("--version") || argv.includes("-v");
623
- }
624
- async function loadSubCommand(spec) {
625
- if (typeof spec === "string") {
626
- const mod = await import(spec);
627
- if (!mod.default) {
628
- throw new Error(`Subcommand module "${spec}" has no default export`);
629
- }
630
- return mod.default;
631
- }
632
- const imported = await spec();
633
- if ("default" in imported && imported.default) {
634
- return imported.default;
635
- }
636
- if (imported) {
637
- return imported;
638
- }
639
- throw new Error("Subcommand import did not return a valid command");
640
- }
641
- async function runFileBasedSubCmd(subName, argv, fileCmdOpts, parserOptions, parentFinish, globalCliMeta) {
642
- async function resolveCmdPath(baseDir, args) {
643
- if (args.length === 0 || args[0] && isFlag(args[0])) {
644
- const possibleFiles2 = [path.join(baseDir, "cmd.js"), path.join(baseDir, "cmd.ts")];
645
- for (const file of possibleFiles2) {
646
- if (await pathExists(file)) {
647
- return { importPath: file, leftoverArgv: args };
648
- }
649
- }
650
- throw new Error(
651
- `
652
- Unknown command or arguments: ${args.join(" ")}
653
- Info for this CLI's developer: No valid command file found in ${baseDir}`
654
- );
655
- }
656
- const nextDir = path.join(baseDir, args[0] || "");
657
- if (await isDirectory(nextDir)) {
658
- return resolveCmdPath(nextDir, args.slice(1));
659
- }
660
- const possibleFiles = [path.join(baseDir, "cmd.js"), path.join(baseDir, "cmd.ts")];
661
- for (const file of possibleFiles) {
662
- if (await pathExists(file)) {
663
- return { importPath: file, leftoverArgv: args };
664
- }
665
- }
666
- throw new Error(
667
- `
668
- Unknown command or arguments: ${args.join(" ")}
669
- Info for this CLI's developer: No valid command file found in ${baseDir}`
670
- );
671
- }
672
- const startDir = path.join(fileCmdOpts.cmdsRootPath, subName);
673
- if (!await isDirectory(startDir)) {
674
- const attempted = [subName, ...argv].join(" ");
675
- const expectedPath = path.relative(
676
- process.cwd(),
677
- path.join(fileCmdOpts.cmdsRootPath, subName, "cmd.{ts,js}")
678
- );
679
- const allCommands = await findRecursiveFileBasedCommands(fileCmdOpts.cmdsRootPath);
680
- const commandNames = allCommands.map((cmd) => cmd.path.join(" "));
681
- let closestMatch = "";
682
- let minDistance = Number.POSITIVE_INFINITY;
683
- for (const cmd of commandNames) {
684
- const distance = levenshteinDistance(subName, cmd.split(" ")[0] || "");
685
- if (distance < minDistance) {
686
- minDistance = distance;
687
- closestMatch = cmd;
688
- }
689
- }
690
- const suggestion = minDistance <= 3 ? ` (Did you mean: \`${closestMatch}\`?)` : "";
691
- throw new Error(
692
- `
693
- Unknown command or arguments: ${attempted}${suggestion}
694
- Info for this CLI's developer: No valid command directory found, expected: ${expectedPath}`
695
- );
696
- }
697
- const { importPath, leftoverArgv } = await resolveCmdPath(startDir, argv);
698
- const imported = await import(path.resolve(importPath));
699
- const subCommand = imported.default;
700
- if (!subCommand) {
701
- throw new Error(`File-based subcommand has no default export or is invalid: ${importPath}`);
702
- }
703
- try {
704
- const subCtx = await runCommandWithArgs(subCommand, leftoverArgv, parserOptions, globalCliMeta);
705
- if (typeof parentFinish === "function" && subCtx) await parentFinish(subCtx);
706
- } finally {
707
- }
708
- }
709
- async function runSubCommand(spec, argv, parserOptions, parentFinish, globalCliMeta) {
710
- const subCommand = await loadSubCommand(spec);
711
- try {
712
- const helpIdx = argv.findIndex((arg) => arg === "help" || arg === "--help" || arg === "-h");
713
- if (helpIdx !== -1) {
714
- await showUsage(subCommand, { ...parserOptions, _isSubcommand: true }, globalCliMeta);
715
- if (parserOptions.autoExit !== false) process.exit(0);
716
- return;
717
- }
718
- const subCtx = await runCommandWithArgs(subCommand, argv, parserOptions, globalCliMeta, true);
719
- if (typeof parentFinish === "function" && subCtx) await parentFinish(subCtx);
720
- } finally {
721
- }
722
- }
723
- async function runCommandWithArgs(command, argv, parserOptions, globalCliMeta, returnCtx) {
724
- const autoExit = parserOptions.autoExit !== false;
725
- const booleanKeys = Object.keys(command.args || {}).filter(
726
- (k) => command.args?.[k]?.type === "boolean"
727
- );
728
- const defaultMap = {};
729
- for (const [argKey, def] of Object.entries(command.args || {})) {
730
- if (def.type === "boolean") {
731
- defaultMap[argKey] = def.default !== void 0 ? def.default : false;
732
- } else if (def.default !== void 0) {
733
- defaultMap[argKey] = def.type === "array" && typeof def.default === "string" ? [def.default] : def.default;
734
- }
735
- }
736
- const mergedParserOptions = {
737
- ...parserOptions,
738
- boolean: [...parserOptions.boolean || [], ...booleanKeys],
739
- defaults: { ...defaultMap, ...parserOptions.defaults || {} }
740
- };
741
- const parsed = reliArgParser(argv, mergedParserOptions);
742
- debugLog("Parsed arguments:", parsed);
743
- const finalArgs = {};
744
- const positionalKeys = Object.keys(command.args || {}).filter(
745
- (k) => command.args?.[k]?.type === "positional"
746
- );
747
- const leftoverPositionals = [...parsed._ || []];
748
- for (let i = 0; i < positionalKeys.length; i++) {
749
- const key = positionalKeys[i];
750
- if (!key || !command.args) continue;
751
- const def = command.args[key];
752
- const val = leftoverPositionals[i];
753
- finalArgs[key] = val != null && def ? castArgValue(def, val, key) : def?.default;
754
- }
755
- const otherKeys = Object.keys(command.args || {}).filter(
756
- (k) => command.args?.[k]?.type !== "positional"
757
- );
758
- for (const key of otherKeys) {
759
- const def = command.args?.[key];
760
- if (!def) continue;
761
- let rawVal = parsed[key];
762
- if (def.type === "array" && rawVal !== void 0 && !Array.isArray(rawVal)) {
763
- rawVal = [rawVal];
764
- }
765
- const casted = rawVal !== void 0 ? castArgValue(def, rawVal, key) : def.default;
766
- const argUsed = rawVal !== void 0 && (def.type === "boolean" ? casted === true : true);
767
- if (casted == null && def.required) {
768
- await showUsage(command, parserOptions, globalCliMeta);
769
- relinka("error", `Missing required argument: --${key}`);
770
- if (autoExit) process.exit(1);
771
- else throw new Error(`Missing required argument: --${key}`);
772
- }
773
- if (argUsed && def.dependencies?.length) {
774
- const missingDeps = def.dependencies.filter((d) => {
775
- const depVal = parsed[d] ?? defaultMap[d];
776
- return !depVal;
777
- });
778
- if (missingDeps.length > 0) {
779
- const depsList = missingDeps.map((d) => `--${d}`).join(", ");
780
- throw new Error(
781
- `Argument --${key} can only be used when ${depsList} ${missingDeps.length === 1 ? "is" : "are"} set`
782
- );
783
- }
784
- }
785
- finalArgs[key] = def.type === "boolean" ? Boolean(casted) : casted;
786
- }
787
- const ctx = {
788
- args: finalArgs,
789
- raw: argv
790
- };
791
- try {
792
- if (command.run) {
793
- await command.run(ctx);
794
- } else {
795
- const isDispatcher = parserOptions.fileBased?.enable || command.commands && Object.keys(command.commands).length > 0;
796
- const noSubcommandArgInCurrentCall = !argv.some((arg) => !isFlag(arg));
797
- if (isDispatcher && noSubcommandArgInCurrentCall) {
798
- relinka("warn", "Please specify a command");
799
- await showUsage(command, parserOptions, globalCliMeta);
800
- if (autoExit) process.exit(0);
801
- return;
802
- }
803
- const cmdName = command.meta?.name || "unknown";
804
- const attempted = argv.length > 0 ? argv.join(" ") : "(no arguments)";
805
- await showUsage(command, parserOptions, globalCliMeta);
806
- relinka("error", `Unknown command or arguments: ${attempted}`);
807
- if (autoExit) {
808
- process.exit(1);
809
- } else {
810
- throw new Error(`Command "${cmdName}" is not runnable.`);
811
- }
812
- }
813
- } catch (err) {
814
- relinka("error", `Error while executing command:
815
- ${String(err)}`);
816
- if (autoExit) process.exit(1);
817
- else throw err;
818
- }
819
- if (returnCtx) return ctx;
820
- return;
821
- }
822
- function castArgValue(def, rawVal, argName) {
823
- if (rawVal == null) {
824
- if (def.type === "array" && typeof def.default === "string") {
825
- return [def.default];
826
- }
827
- return def.default ?? void 0;
828
- }
829
- let castedValue;
830
- switch (def.type) {
831
- case "boolean":
832
- if (typeof rawVal === "string") {
833
- const lower = rawVal.toLowerCase();
834
- if (lower === "true") castedValue = true;
835
- else if (lower === "false") castedValue = false;
836
- else castedValue = Boolean(rawVal);
837
- } else {
838
- castedValue = Boolean(rawVal);
839
- }
840
- if (def.allowed && !def.allowed.includes(castedValue)) {
841
- throw new Error(
842
- `Invalid value for --${argName}: ${rawVal}. Allowed values are: ${def.allowed.join(", ")}`
843
- );
844
- }
845
- return castedValue;
846
- case "string":
847
- castedValue = typeof rawVal === "string" ? rawVal : String(rawVal);
848
- if (def.allowed && !def.allowed.includes(castedValue)) {
849
- throw new Error(
850
- `Invalid value for --${argName}: ${rawVal}. Allowed values are: ${def.allowed.join(", ")}`
851
- );
852
- }
853
- return castedValue;
854
- case "number": {
855
- const n = Number(rawVal);
856
- if (Number.isNaN(n)) {
857
- throw new Error(`Invalid number provided for --${argName}: ${rawVal}`);
858
- }
859
- if (def.allowed && !def.allowed.includes(n)) {
860
- throw new Error(
861
- `Invalid value for --${argName}: ${rawVal}. Allowed values are: ${def.allowed.join(", ")}`
862
- );
863
- }
864
- return n;
865
- }
866
- case "positional":
867
- castedValue = String(rawVal);
868
- if (def.allowed && !def.allowed.includes(castedValue)) {
869
- throw new Error(
870
- `Invalid value for <${argName}>: ${rawVal}. Allowed values are: ${def.allowed.join(", ")}`
871
- );
872
- }
873
- return castedValue;
874
- case "array": {
875
- const arrVal = Array.isArray(rawVal) ? rawVal : [String(rawVal)];
876
- const result = [];
877
- const arrValStr = arrVal.map(String);
878
- let warned = false;
879
- for (let v of arrValStr) {
880
- if (!warned && (v.startsWith("[") && !v.endsWith("]") || !v.startsWith("[") && v.endsWith("]"))) {
881
- relinka("error", `Don't use quotes around array elements.`);
882
- relinka("error", `Also \u2014 don't use spaces \u2014 unless you wrap the whole array in quotes.`);
883
- relinka(
884
- "warn",
885
- `Array argument --${argName}: Detected possible shell splitting of bracketed value ('${v}').`
886
- );
887
- relinka(
888
- "warn",
889
- `If you intended to pass a bracketed list, quote the whole value like: --${argName} "[a, b, c]"`
890
- );
891
- }
892
- warned = true;
893
- if (v.startsWith("[") && v.endsWith("]")) {
894
- v = v.slice(1, -1);
895
- }
896
- const parts = v.split(/\s*,\s*/).filter(Boolean);
897
- parts.forEach((p) => {
898
- if (p.startsWith('"') && p.endsWith('"') || p.startsWith("'") && p.endsWith("'")) {
899
- throw new Error(
900
- `Array argument --${argName}: Quoted values are not supported due to shell parsing limitations. Please avoid using single or double quotes around array elements.`
901
- );
902
- }
903
- if (def.allowed && !def.allowed.includes(p)) {
904
- throw new Error(
905
- `Invalid value in array --${argName}: ${p}. Allowed values are: ${def.allowed.join(", ")}`
906
- );
907
- }
908
- });
909
- result.push(...parts);
910
- }
911
- return result;
912
- }
913
- default:
914
- return rawVal;
915
- }
916
- }
917
- function renderPositional(args) {
918
- const positionalKeys = Object.keys(args || {}).filter((k) => args?.[k]?.type === "positional");
919
- return positionalKeys.map((k) => `<${k}>`).join(" ");
920
- }
921
- export function defineArgs(args) {
922
- return args;
923
- }
924
- function normalizeArgv(argv) {
925
- const normalized = [];
926
- for (const arg of argv) {
927
- const parts = arg.split(/\s+/).filter((part) => part.length > 0);
928
- normalized.push(...parts);
929
- }
930
- return normalized;
931
- }
932
- function getParsedContext(command, argv, parserOptions) {
933
- const normalizedArgv = normalizeArgv(argv);
934
- const booleanKeys = Object.keys(command.args || {}).filter(
935
- (k) => command.args?.[k]?.type === "boolean"
936
- );
937
- const defaultMap = {};
938
- for (const [argKey, def] of Object.entries(command.args || {})) {
939
- if (def.type === "boolean") {
940
- defaultMap[argKey] = def.default !== void 0 ? def.default : false;
941
- } else if (def.default !== void 0) {
942
- defaultMap[argKey] = def.type === "array" && typeof def.default === "string" ? [def.default] : def.default;
943
- }
944
- }
945
- const mergedParserOptions = {
946
- ...parserOptions,
947
- boolean: [...parserOptions.boolean || [], ...booleanKeys],
948
- defaults: { ...defaultMap, ...parserOptions.defaults || {} }
949
- };
950
- const parsed = reliArgParser(normalizedArgv, mergedParserOptions);
951
- const finalArgs = {};
952
- const positionalKeys = Object.keys(command.args || {}).filter(
953
- (k) => command.args?.[k]?.type === "positional"
954
- );
955
- const leftoverPositionals = [...parsed._ || []];
956
- for (let i = 0; i < positionalKeys.length; i++) {
957
- const key = positionalKeys[i];
958
- if (!key || !command.args) continue;
959
- const def = command.args[key];
960
- const val = leftoverPositionals[i];
961
- finalArgs[key] = val != null && def ? castArgValue(def, val, key) : def?.default;
962
- }
963
- const otherKeys = Object.keys(command.args || {}).filter(
964
- (k) => command.args?.[k]?.type !== "positional"
965
- );
966
- for (const key of otherKeys) {
967
- const def = command.args?.[key];
968
- if (!def) continue;
969
- let rawVal = parsed[key];
970
- if (def.type === "array" && rawVal !== void 0 && !Array.isArray(rawVal)) {
971
- rawVal = [rawVal];
972
- }
973
- if (def.type === "boolean") {
974
- finalArgs[key] = rawVal !== void 0 ? castArgValue(def, rawVal, key) : false;
975
- } else {
976
- finalArgs[key] = castArgValue(def, rawVal, key);
977
- }
978
- }
979
- return { args: finalArgs, raw: argv };
980
- }
981
- async function resolveFileBasedCommandPath(cmdsRoot, argv) {
982
- let currentDir = cmdsRoot;
983
- const pathSegments = [];
984
- let leftover = [...argv];
985
- while (leftover.length > 0 && leftover[0] && !isFlag(leftover[0])) {
986
- const nextDir = path.join(currentDir, leftover[0]);
987
- if (await isDirectory(nextDir)) {
988
- currentDir = nextDir;
989
- pathSegments.push(leftover[0]);
990
- leftover = leftover.slice(1);
991
- } else {
992
- break;
993
- }
994
- }
995
- for (const fname of ["cmd.ts", "cmd.js"]) {
996
- const fpath = path.join(currentDir, fname);
997
- if (await pathExists(fpath)) {
998
- const imported = await import(path.resolve(fpath));
999
- if (imported.default) {
1000
- return {
1001
- def: imported.default,
1002
- path: pathSegments,
1003
- leftoverArgv: leftover
1004
- };
1005
- }
1006
- }
1007
- }
1008
- return null;
1009
- }
1010
- function levenshteinDistance(a, b) {
1011
- if (a.length === 0) return b.length;
1012
- if (b.length === 0) return a.length;
1013
- const matrix = [];
1014
- for (let i = 0; i <= b.length; i++) {
1015
- matrix[i] = [i];
1016
- }
1017
- for (let j = 0; j <= a.length; j++) {
1018
- matrix[0][j] = j;
1019
- }
1020
- for (let i = 1; i <= b.length; i++) {
1021
- for (let j = 1; j <= a.length; j++) {
1022
- if (b.charAt(i - 1) === a.charAt(j - 1)) {
1023
- matrix[i][j] = matrix[i - 1][j - 1];
1024
- } else {
1025
- matrix[i][j] = Math.min(
1026
- matrix[i - 1][j - 1] + 1,
1027
- // substitution
1028
- matrix[i][j - 1] + 1,
1029
- // insertion
1030
- matrix[i - 1][j] + 1
1031
- // deletion
1032
- );
1033
- }
1034
- }
1035
- }
1036
- return matrix[b.length][a.length];
1037
- }