@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
package/cleanup.mjs ADDED
@@ -0,0 +1,33 @@
1
+ import { suffix } from "bun:ffi";
2
+ import fs from "node:fs";
3
+
4
+ const releasePath = new URL("./release", import.meta.url).pathname;
5
+
6
+ // Check if release directory exists
7
+ if (!fs.existsSync(releasePath)) {
8
+ console.log("Release directory does not exist, nothing to clean up.");
9
+ process.exit(0);
10
+ }
11
+
12
+ const files = fs.readdirSync(releasePath);
13
+
14
+ const { platform, arch } = process;
15
+ let filename;
16
+
17
+ if (arch === "x64") {
18
+ filename = `rempts-${platform}-amd64.${suffix}`;
19
+ } else {
20
+ filename = `rempts-${platform}-${arch}.${suffix}`;
21
+ }
22
+
23
+ files.forEach((file) => {
24
+ if (file !== filename) {
25
+ const filePath = `./release/${file}`;
26
+ try {
27
+ fs.unlinkSync(filePath);
28
+ console.log(`Removed: ${file}`);
29
+ } catch (error) {
30
+ console.warn(`Failed to remove ${file}:`, error.message);
31
+ }
32
+ }
33
+ });
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Custom error class for prompt cancellations
3
+ */
4
+ export declare class PromptCancelledError extends Error {
5
+ constructor(message?: string);
6
+ }
7
+ /**
8
+ * Throws a PromptCancelledError to signal that user cancelled the prompt
9
+ * @param message - Optional custom cancellation message
10
+ */
11
+ export declare function cancel(message?: string): never;
12
+ /**
13
+ * Checks if an error is a cancellation error from a prompt
14
+ * @param error - The error to check
15
+ * @returns `true` if the error is a cancellation, `false` otherwise
16
+ */
17
+ export declare function isCancel(error: unknown): error is PromptCancelledError;
18
+ /**
19
+ * Exits the process with exit code 0 after logging a cancellation message
20
+ * @param message - The message to log before exiting (default: "Operation cancelled")
21
+ */
22
+ export declare function exitCancelled(message?: string): never;
23
+ /**
24
+ * Sets up a global handler for unhandled PromptCancelledError
25
+ * This ensures that if a developer doesn't explicitly handle cancellations,
26
+ * the app will exit cleanly instead of crashing.
27
+ *
28
+ * Call this once at the start of your application if you want automatic
29
+ * cancellation handling.
30
+ */
31
+ export declare function setupAutoCancelHandler(): void;
package/dist/cancel.js ADDED
@@ -0,0 +1,28 @@
1
+ export class PromptCancelledError extends Error {
2
+ constructor(message = "Cancelled") {
3
+ super(message);
4
+ this.name = "PromptCancelledError";
5
+ }
6
+ }
7
+ export function cancel(message = "Cancelled") {
8
+ throw new PromptCancelledError(message);
9
+ }
10
+ export function isCancel(error) {
11
+ return error instanceof PromptCancelledError;
12
+ }
13
+ export function exitCancelled(message = "Operation cancelled") {
14
+ console.log(message);
15
+ process.exit(0);
16
+ }
17
+ export function setupAutoCancelHandler() {
18
+ process.on("unhandledRejection", (reason) => {
19
+ if (reason instanceof PromptCancelledError) {
20
+ exitCancelled("Operation cancelled");
21
+ }
22
+ });
23
+ process.on("uncaughtException", (error) => {
24
+ if (error instanceof PromptCancelledError) {
25
+ exitCancelled("Operation cancelled");
26
+ }
27
+ });
28
+ }
package/dist/ffi.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const symbols: any;
package/dist/ffi.js ADDED
@@ -0,0 +1,165 @@
1
+ import { dlopen, FFIType, suffix } from "bun:ffi";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ const { platform, arch } = process;
5
+ function getBinaryLocation() {
6
+ let filename;
7
+ if (arch === "x64") {
8
+ filename = `../release/rempts-${platform}-amd64.${suffix}`;
9
+ } else {
10
+ filename = `../release/rempts-${platform}-${arch}.${suffix}`;
11
+ }
12
+ return Bun.fileURLToPath(new URL(filename, import.meta.url));
13
+ }
14
+ let cachedSymbols;
15
+ function loadSymbols() {
16
+ if (cachedSymbols) {
17
+ return cachedSymbols;
18
+ }
19
+ const location = getBinaryLocation();
20
+ if (!existsSync(location)) {
21
+ const releaseDir = path.dirname(location);
22
+ const expectedFile = path.basename(location);
23
+ let errorMessage = `
24
+ \u274C Native binary not found! (Did you have Docker launched?)
25
+
26
+ `;
27
+ errorMessage += `Expected file: ${location}
28
+
29
+ `;
30
+ if (!existsSync(releaseDir)) {
31
+ errorMessage += `The release directory does not exist: ${releaseDir}
32
+
33
+ `;
34
+ } else {
35
+ errorMessage += `The release directory exists but the binary is missing.
36
+ `;
37
+ errorMessage += `Expected: ${expectedFile}
38
+
39
+ `;
40
+ }
41
+ errorMessage += `To fix this, run:
42
+ `;
43
+ errorMessage += ` bun run build
44
+
45
+ `;
46
+ errorMessage += `Or use native build (no Docker):
47
+ `;
48
+ errorMessage += ` bun run build --provider native
49
+ `;
50
+ throw new Error(errorMessage);
51
+ }
52
+ try {
53
+ cachedSymbols = dlopen(location, {
54
+ CreateSelection: {
55
+ args: [
56
+ FFIType.ptr,
57
+ FFIType.ptr,
58
+ FFIType.ptr,
59
+ FFIType.int,
60
+ FFIType.bool,
61
+ FFIType.ptr,
62
+ FFIType.ptr
63
+ ],
64
+ returns: FFIType.ptr
65
+ },
66
+ CreatePrompt: {
67
+ args: [
68
+ FFIType.ptr,
69
+ FFIType.ptr,
70
+ FFIType.ptr,
71
+ FFIType.ptr,
72
+ FFIType.ptr,
73
+ FFIType.ptr,
74
+ FFIType.bool,
75
+ FFIType.int
76
+ ],
77
+ returns: FFIType.ptr
78
+ },
79
+ CreateMultiselect: {
80
+ args: [
81
+ FFIType.ptr,
82
+ FFIType.ptr,
83
+ FFIType.ptr,
84
+ FFIType.int,
85
+ FFIType.bool,
86
+ FFIType.ptr,
87
+ FFIType.ptr
88
+ ],
89
+ returns: FFIType.ptr
90
+ },
91
+ CreateConfirm: {
92
+ args: [FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr, FFIType.ptr],
93
+ returns: FFIType.ptr
94
+ },
95
+ CreateGroupMultiselect: {
96
+ args: [
97
+ FFIType.ptr,
98
+ FFIType.ptr,
99
+ FFIType.ptr,
100
+ FFIType.int,
101
+ FFIType.bool,
102
+ FFIType.bool,
103
+ FFIType.ptr,
104
+ FFIType.ptr,
105
+ FFIType.int
106
+ ],
107
+ returns: FFIType.ptr
108
+ },
109
+ FreeString: {
110
+ args: [FFIType.ptr],
111
+ returns: FFIType.void
112
+ }
113
+ }).symbols;
114
+ } catch (error) {
115
+ let errorMessage = `
116
+ \u274C Failed to load native binary!
117
+
118
+ `;
119
+ errorMessage += `File: ${location}
120
+ `;
121
+ errorMessage += `Error: ${error.message || error}
122
+
123
+ `;
124
+ if (error.code === "ERR_DLOPEN_FAILED" || error.errno === 126) {
125
+ errorMessage += `This usually means:
126
+ `;
127
+ errorMessage += ` \u2022 The binary is corrupted or incomplete
128
+ `;
129
+ errorMessage += ` \u2022 Missing dependencies (DLLs, shared libraries)
130
+ `;
131
+ errorMessage += ` \u2022 Architecture mismatch
132
+
133
+ `;
134
+ errorMessage += `Try rebuilding:
135
+ `;
136
+ errorMessage += ` bun run build
137
+ `;
138
+ } else {
139
+ errorMessage += `Please rebuild the native binaries:
140
+ `;
141
+ errorMessage += ` bun run build
142
+ `;
143
+ }
144
+ throw new Error(errorMessage);
145
+ }
146
+ return cachedSymbols;
147
+ }
148
+ export const symbols = new Proxy({}, {
149
+ get(_target, prop) {
150
+ const symbols2 = loadSymbols();
151
+ return symbols2[prop];
152
+ },
153
+ has(_target, prop) {
154
+ const symbols2 = loadSymbols();
155
+ return prop in symbols2;
156
+ },
157
+ ownKeys(_target) {
158
+ const symbols2 = loadSymbols();
159
+ return Object.keys(symbols2);
160
+ },
161
+ getOwnPropertyDescriptor(_target, prop) {
162
+ const symbols2 = loadSymbols();
163
+ return Object.getOwnPropertyDescriptor(symbols2, prop);
164
+ }
165
+ });
@@ -0,0 +1,16 @@
1
+ import { PromptCancelledError } from "./cancel.js";
2
+ type MaybePromise<T> = T | Promise<T>;
3
+ export interface GroupPromptContext<TResult extends Record<string, unknown>, TKey extends keyof TResult> {
4
+ key: TKey;
5
+ results: Readonly<Partial<TResult>>;
6
+ }
7
+ export type GroupPromptHandler<TResult extends Record<string, unknown>, TKey extends keyof TResult> = (context: GroupPromptContext<TResult, TKey>) => MaybePromise<TResult[TKey]>;
8
+ export type GroupPromptHandlers<TResult extends Record<string, unknown>> = {
9
+ [K in keyof TResult]: GroupPromptHandler<TResult, K>;
10
+ };
11
+ export interface GroupPromptOptions {
12
+ onCancel?: (error: PromptCancelledError) => MaybePromise<void>;
13
+ }
14
+ export declare function groupPrompt<TResult extends Record<string, unknown>>(handlers: GroupPromptHandlers<TResult>, options?: GroupPromptOptions): Promise<TResult>;
15
+ export declare const group: typeof groupPrompt;
16
+ export {};
package/dist/group.js ADDED
@@ -0,0 +1,22 @@
1
+ import { PromptCancelledError } from "./cancel.js";
2
+ export async function groupPrompt(handlers, options) {
3
+ const results = {};
4
+ const keys = Object.keys(handlers);
5
+ for (const key of keys) {
6
+ const handler = handlers[key];
7
+ try {
8
+ const value = await handler({
9
+ key,
10
+ results
11
+ });
12
+ results[key] = value;
13
+ } catch (error) {
14
+ if (error instanceof PromptCancelledError && options?.onCancel) {
15
+ await options.onCancel(error);
16
+ }
17
+ throw error;
18
+ }
19
+ }
20
+ return results;
21
+ }
22
+ export const group = groupPrompt;
@@ -0,0 +1,8 @@
1
+ import type { CmdArgsSchema, CmdDefinition, CmdHandler, CmdMeta } from "./types.js";
2
+ export declare const defineArgs: <const T extends CmdArgsSchema>(args: T) => T;
3
+ export interface DefineCommandOptions<Args extends CmdArgsSchema, Meta extends CmdMeta> {
4
+ meta: Meta;
5
+ args: Args;
6
+ run: CmdHandler<Args>;
7
+ }
8
+ export declare const defineCommand: <const Args extends CmdArgsSchema, const Meta extends CmdMeta>({ meta, args, run, }: DefineCommandOptions<Args, Meta>) => CmdDefinition<Args>;
@@ -0,0 +1,10 @@
1
+ export const defineArgs = (args) => args;
2
+ export const defineCommand = ({
3
+ meta,
4
+ args,
5
+ run
6
+ }) => ({
7
+ handler: run,
8
+ args,
9
+ meta
10
+ });
@@ -0,0 +1,3 @@
1
+ import type { CmdDefinition, DiscoveryResult } from "./types.js";
2
+ export declare const discoverCommands: (cmdsDir: string, baseDir?: string, verbose?: boolean) => Promise<DiscoveryResult>;
3
+ export declare const validateCommandStructure: (definition: unknown) => definition is CmdDefinition;
@@ -0,0 +1,207 @@
1
+ import { statSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import pMap from "@reliverse/mapkit";
4
+ import { Glob } from "bun";
5
+ import { CommandLoadError } from "./errors.js";
6
+ const createLazyMetadataLoader = (filePath, cmdName) => {
7
+ return async () => {
8
+ try {
9
+ const module = await import(filePath);
10
+ const definition = module.default;
11
+ if (!definition || !definition.meta) {
12
+ throw new Error("Invalid command definition");
13
+ }
14
+ return {
15
+ name: definition.meta.name,
16
+ description: definition.meta.description,
17
+ aliases: definition.meta.aliases,
18
+ version: definition.meta.version,
19
+ examples: definition.meta.examples
20
+ };
21
+ } catch (error) {
22
+ throw new CommandLoadError(cmdName, error);
23
+ }
24
+ };
25
+ };
26
+ const resolveCommandsDirectory = (baseDir, cmdsDir, verbose) => {
27
+ if (verbose) {
28
+ console.debug(`\u{1F50D} Resolving commands directory:`);
29
+ console.debug(` baseDir: ${baseDir}`);
30
+ console.debug(` cmdsDir: ${cmdsDir}`);
31
+ }
32
+ if (cmdsDir.startsWith("/") || cmdsDir.includes("\\")) {
33
+ const resolved = join(baseDir, cmdsDir);
34
+ if (verbose) {
35
+ console.debug(` Using absolute path: ${resolved}`);
36
+ }
37
+ return resolved;
38
+ }
39
+ const parentDir = dirname(baseDir);
40
+ const parentSrcPath = join(parentDir, "src", cmdsDir);
41
+ try {
42
+ const srcStats = statSync(parentSrcPath);
43
+ if (srcStats.isDirectory()) {
44
+ if (verbose) {
45
+ console.debug(
46
+ ` \u2705 Found: ${parentSrcPath} (parent src - development)`
47
+ );
48
+ }
49
+ return parentSrcPath;
50
+ }
51
+ } catch {
52
+ if (verbose) {
53
+ console.debug(` \u274C Not found: ${parentSrcPath}`);
54
+ }
55
+ }
56
+ const parentDistPath = join(parentDir, "dist", cmdsDir);
57
+ try {
58
+ const distStats = statSync(parentDistPath);
59
+ if (distStats.isDirectory()) {
60
+ if (verbose) {
61
+ console.debug(
62
+ ` \u2705 Found: ${parentDistPath} (parent dist - production)`
63
+ );
64
+ }
65
+ return parentDistPath;
66
+ }
67
+ } catch {
68
+ if (verbose) {
69
+ console.debug(` \u274C Not found: ${parentDistPath}`);
70
+ }
71
+ }
72
+ const srcPath = join(baseDir, "src", cmdsDir);
73
+ try {
74
+ const srcStats = statSync(srcPath);
75
+ if (srcStats.isDirectory()) {
76
+ if (verbose) {
77
+ console.debug(` \u2705 Found: ${srcPath} (development)`);
78
+ }
79
+ return srcPath;
80
+ }
81
+ } catch {
82
+ if (verbose) {
83
+ console.debug(` \u274C Not found: ${srcPath}`);
84
+ }
85
+ }
86
+ const distPath = join(baseDir, "dist", cmdsDir);
87
+ try {
88
+ const distStats = statSync(distPath);
89
+ if (distStats.isDirectory()) {
90
+ if (verbose) {
91
+ console.debug(` \u2705 Found: ${distPath} (production)`);
92
+ }
93
+ return distPath;
94
+ }
95
+ } catch {
96
+ if (verbose) {
97
+ console.debug(` \u274C Not found: ${distPath}`);
98
+ }
99
+ }
100
+ const fallbackPath = join(baseDir, cmdsDir);
101
+ try {
102
+ const fallbackStats = statSync(fallbackPath);
103
+ if (fallbackStats.isDirectory()) {
104
+ if (verbose) {
105
+ console.debug(` \u2705 Found: ${fallbackPath} (fallback)`);
106
+ }
107
+ return fallbackPath;
108
+ }
109
+ } catch {
110
+ if (verbose) {
111
+ console.debug(` \u274C Not found: ${fallbackPath}`);
112
+ }
113
+ }
114
+ throw new Error(
115
+ `Commands directory not found. Checked: ${srcPath}, ${distPath}, ${fallbackPath}`
116
+ );
117
+ };
118
+ export const discoverCommands = async (cmdsDir, baseDir, verbose) => {
119
+ const registry = /* @__PURE__ */ new Map();
120
+ const aliases = /* @__PURE__ */ new Map();
121
+ const metadata = /* @__PURE__ */ new Map();
122
+ const hierarchy = /* @__PURE__ */ new Map();
123
+ const rootCommands = /* @__PURE__ */ new Set();
124
+ const glob = new Glob("**/cmd.{ts,js}");
125
+ const actualBaseDir = baseDir || process.cwd();
126
+ if (verbose) {
127
+ console.debug(`
128
+ \u{1F50D} Discovering commands:`);
129
+ console.debug(` actualBaseDir: ${actualBaseDir}`);
130
+ }
131
+ const resolvedCommandsDir = resolveCommandsDirectory(
132
+ actualBaseDir,
133
+ cmdsDir,
134
+ verbose
135
+ );
136
+ if (verbose) {
137
+ console.debug(` resolvedCommandsDir: ${resolvedCommandsDir}`);
138
+ }
139
+ const files = await Array.fromAsync(glob.scan(resolvedCommandsDir));
140
+ if (verbose) {
141
+ console.debug(` Found ${files.length} command files`);
142
+ }
143
+ const fileData = await pMap(
144
+ files,
145
+ async (file) => {
146
+ const pathParts = file.split(/[/\\]/);
147
+ const cmdName = pathParts[pathParts.length - 2];
148
+ const filePath = `${resolvedCommandsDir}/${file}`;
149
+ const depth = pathParts.length - 1;
150
+ const parent = depth > 1 ? pathParts[0] : void 0;
151
+ const fullPath = pathParts.slice(0, -1).join("/");
152
+ return {
153
+ cmdName,
154
+ filePath,
155
+ depth,
156
+ parent,
157
+ fullPath
158
+ };
159
+ },
160
+ { concurrency: 10 }
161
+ // Limit concurrency to avoid overwhelming filesystem
162
+ );
163
+ for (const { cmdName, filePath, depth, parent, fullPath } of fileData) {
164
+ const loader = async () => {
165
+ try {
166
+ const module = await import(filePath);
167
+ const definition = module.default;
168
+ if (!definition || typeof definition.handler !== "function") {
169
+ throw new Error("Invalid command definition");
170
+ }
171
+ return definition;
172
+ } catch (error) {
173
+ throw new CommandLoadError(cmdName, error);
174
+ }
175
+ };
176
+ const lazyMetadataLoader = createLazyMetadataLoader(filePath, cmdName);
177
+ metadata.set(cmdName, lazyMetadataLoader);
178
+ registry.set(cmdName, loader);
179
+ const cmdNode = {
180
+ name: cmdName,
181
+ path: fullPath,
182
+ depth,
183
+ parent,
184
+ children: /* @__PURE__ */ new Map(),
185
+ loader,
186
+ metadata: metadata.get(cmdName)
187
+ };
188
+ hierarchy.set(cmdName, cmdNode);
189
+ if (depth === 1) {
190
+ rootCommands.add(cmdName);
191
+ }
192
+ }
193
+ for (const [cmdName, node] of hierarchy) {
194
+ if (node.parent) {
195
+ const parentNode = hierarchy.get(node.parent);
196
+ if (parentNode) {
197
+ parentNode.children.set(cmdName, node);
198
+ }
199
+ }
200
+ }
201
+ return { registry, aliases, metadata, hierarchy, rootCommands };
202
+ };
203
+ export const validateCommandStructure = (definition) => {
204
+ if (typeof definition !== "object" || definition === null) return false;
205
+ const def = definition;
206
+ return typeof def.handler === "function" && typeof def.args === "object" && def.args !== null && typeof def.meta === "object" && def.meta !== null && typeof def.meta.name === "string";
207
+ };
@@ -0,0 +1,15 @@
1
+ export declare class LauncherError extends Error {
2
+ code: string;
3
+ constructor(message: string, code: string);
4
+ }
5
+ export declare class CommandNotFoundError extends LauncherError {
6
+ constructor(cmdName: string, availableCmds: string[]);
7
+ }
8
+ export declare class ArgumentValidationError extends LauncherError {
9
+ argName: string;
10
+ reason: string;
11
+ constructor(argName: string, reason: string);
12
+ }
13
+ export declare class CommandLoadError extends LauncherError {
14
+ constructor(cmdName: string, cause: unknown);
15
+ }
@@ -0,0 +1,31 @@
1
+ export class LauncherError extends Error {
2
+ constructor(message, code) {
3
+ super(message);
4
+ this.code = code;
5
+ this.name = "LauncherError";
6
+ }
7
+ }
8
+ export class CommandNotFoundError extends LauncherError {
9
+ constructor(cmdName, availableCmds) {
10
+ super(
11
+ `Command "${cmdName}" not found. Available commands: ${availableCmds.join(", ")}`,
12
+ "CMD_NOT_FOUND"
13
+ );
14
+ this.name = "CommandNotFoundError";
15
+ }
16
+ }
17
+ export class ArgumentValidationError extends LauncherError {
18
+ constructor(argName, reason) {
19
+ super(`Invalid argument "${argName}": ${reason}`, "ARG_VALIDATION_ERROR");
20
+ this.argName = argName;
21
+ this.reason = reason;
22
+ this.name = "ArgumentValidationError";
23
+ }
24
+ }
25
+ export class CommandLoadError extends LauncherError {
26
+ constructor(cmdName, cause) {
27
+ super(`Failed to load command "${cmdName}"`, "CMD_LOAD_ERROR");
28
+ this.name = "CommandLoadError";
29
+ this.cause = cause;
30
+ }
31
+ }
@@ -0,0 +1,3 @@
1
+ import type { CmdDefinition, DiscoveryResult } from "./types.js";
2
+ export declare const generateCommandHelp: (definition: CmdDefinition) => Promise<string>;
3
+ export declare const generateGlobalHelp: (registry: DiscoveryResult) => Promise<string>;