@reliverse/rempts-core 1.6.1 → 2.3.1

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 (155) hide show
  1. package/README.md +398 -102
  2. package/dist/cli.d.ts +32 -0
  3. package/dist/cli.js +731 -0
  4. package/dist/config-loader.d.ts +42 -0
  5. package/dist/config-loader.js +20 -0
  6. package/dist/config.d.ts +99 -0
  7. package/dist/config.js +188 -0
  8. package/dist/file-loader.d.ts +43 -0
  9. package/dist/file-loader.js +199 -0
  10. package/dist/global-flags.d.ts +36 -0
  11. package/dist/global-flags.js +36 -0
  12. package/dist/mod.d.ts +13 -0
  13. package/dist/mod.js +19 -0
  14. package/dist/parser.d.ts +6 -0
  15. package/dist/parser.js +137 -0
  16. package/dist/plugin/context.d.ts +13 -0
  17. package/dist/plugin/context.js +53 -0
  18. package/dist/plugin/create.d.ts +92 -0
  19. package/dist/plugin/create.js +61 -0
  20. package/dist/plugin/loader.d.ts +12 -0
  21. package/dist/plugin/loader.js +65 -0
  22. package/dist/plugin/manager.d.ts +53 -0
  23. package/dist/plugin/manager.js +135 -0
  24. package/dist/plugin/mod.d.ts +10 -0
  25. package/dist/plugin/mod.js +27 -0
  26. package/dist/plugin/store.d.ts +45 -0
  27. package/dist/plugin/store.js +60 -0
  28. package/dist/plugin/testing.d.ts +38 -0
  29. package/dist/plugin/testing.js +175 -0
  30. package/dist/plugin/types.d.ts +146 -0
  31. package/dist/tui/registry.d.ts +8 -0
  32. package/dist/tui/registry.js +10 -0
  33. package/dist/tui/types.d.ts +58 -0
  34. package/dist/tui/types.js +10 -0
  35. package/dist/types.d.ts +178 -0
  36. package/dist/types.js +25 -0
  37. package/dist/utils/logger.d.ts +10 -0
  38. package/dist/utils/logger.js +27 -0
  39. package/dist/utils/merge.d.ts +13 -0
  40. package/dist/utils/merge.js +25 -0
  41. package/dist/utils/mod.d.ts +6 -0
  42. package/dist/utils/mod.js +2 -0
  43. package/dist/utils/type-helpers.d.ts +41 -0
  44. package/dist/utils/type-helpers.js +0 -0
  45. package/dist/validation.d.ts +30 -0
  46. package/dist/validation.js +121 -0
  47. package/package.json +47 -44
  48. package/src/cli.ts +1049 -0
  49. package/src/config-loader.ts +71 -0
  50. package/src/config.ts +270 -0
  51. package/src/file-loader.ts +346 -0
  52. package/src/global-flags.ts +50 -0
  53. package/src/mod.ts +74 -0
  54. package/src/parser.ts +212 -0
  55. package/src/plugin/context.ts +88 -0
  56. package/src/plugin/create.ts +174 -0
  57. package/src/plugin/loader.ts +111 -0
  58. package/src/plugin/manager.ts +244 -0
  59. package/src/plugin/mod.ts +51 -0
  60. package/src/plugin/store.ts +124 -0
  61. package/src/plugin/testing.ts +236 -0
  62. package/src/plugin/types.ts +206 -0
  63. package/src/tui/registry.ts +22 -0
  64. package/src/tui/types.ts +79 -0
  65. package/src/types.ts +285 -0
  66. package/src/utils/logger.ts +43 -0
  67. package/src/utils/merge.ts +54 -0
  68. package/src/utils/mod.ts +7 -0
  69. package/src/utils/type-helpers.ts +151 -0
  70. package/src/validation.ts +177 -0
  71. package/LICENSE +0 -21
  72. package/bin/core-impl/anykey/anykey-mod.d.ts +0 -12
  73. package/bin/core-impl/anykey/anykey-mod.js +0 -125
  74. package/bin/core-impl/date/date.d.ts +0 -2
  75. package/bin/core-impl/date/date.js +0 -236
  76. package/bin/core-impl/editor/editor-mod.d.ts +0 -25
  77. package/bin/core-impl/editor/editor-mod.js +0 -896
  78. package/bin/core-impl/figures/figures-mod.d.ts +0 -233
  79. package/bin/core-impl/figures/figures-mod.js +0 -286
  80. package/bin/core-impl/figures/figures.test.d.ts +0 -1
  81. package/bin/core-impl/figures/figures.test.js +0 -474
  82. package/bin/core-impl/input/confirm-prompt.d.ts +0 -5
  83. package/bin/core-impl/input/confirm-prompt.js +0 -173
  84. package/bin/core-impl/input/input-prompt.d.ts +0 -16
  85. package/bin/core-impl/input/input-prompt.js +0 -370
  86. package/bin/core-impl/launcher/_parser.d.ts +0 -2
  87. package/bin/core-impl/launcher/_parser.js +0 -122
  88. package/bin/core-impl/launcher/_utils.d.ts +0 -8
  89. package/bin/core-impl/launcher/_utils.js +0 -29
  90. package/bin/core-impl/launcher/args.d.ts +0 -3
  91. package/bin/core-impl/launcher/args.js +0 -89
  92. package/bin/core-impl/launcher/command.d.ts +0 -8
  93. package/bin/core-impl/launcher/command.js +0 -68
  94. package/bin/core-impl/launcher/launcher-mod.d.ts +0 -8
  95. package/bin/core-impl/launcher/launcher-mod.js +0 -34
  96. package/bin/core-impl/launcher/usage.d.ts +0 -3
  97. package/bin/core-impl/launcher/usage.js +0 -104
  98. package/bin/core-impl/msg-fmt/colors.d.ts +0 -30
  99. package/bin/core-impl/msg-fmt/colors.js +0 -42
  100. package/bin/core-impl/msg-fmt/logger.d.ts +0 -17
  101. package/bin/core-impl/msg-fmt/logger.js +0 -106
  102. package/bin/core-impl/msg-fmt/mapping.d.ts +0 -3
  103. package/bin/core-impl/msg-fmt/mapping.js +0 -49
  104. package/bin/core-impl/msg-fmt/messages.d.ts +0 -35
  105. package/bin/core-impl/msg-fmt/messages.js +0 -314
  106. package/bin/core-impl/msg-fmt/terminal.d.ts +0 -15
  107. package/bin/core-impl/msg-fmt/terminal.js +0 -59
  108. package/bin/core-impl/msg-fmt/variants.d.ts +0 -11
  109. package/bin/core-impl/msg-fmt/variants.js +0 -52
  110. package/bin/core-impl/next-steps/next-steps.d.ts +0 -14
  111. package/bin/core-impl/next-steps/next-steps.js +0 -24
  112. package/bin/core-impl/number/number-mod.d.ts +0 -28
  113. package/bin/core-impl/number/number-mod.js +0 -197
  114. package/bin/core-impl/results/results.d.ts +0 -7
  115. package/bin/core-impl/results/results.js +0 -27
  116. package/bin/core-impl/select/multiselect-prompt.d.ts +0 -2
  117. package/bin/core-impl/select/multiselect-prompt.js +0 -341
  118. package/bin/core-impl/select/nummultiselect-prompt.d.ts +0 -6
  119. package/bin/core-impl/select/nummultiselect-prompt.js +0 -105
  120. package/bin/core-impl/select/numselect-prompt.d.ts +0 -7
  121. package/bin/core-impl/select/numselect-prompt.js +0 -115
  122. package/bin/core-impl/select/select-prompt.d.ts +0 -33
  123. package/bin/core-impl/select/select-prompt.js +0 -302
  124. package/bin/core-impl/select/toggle-prompt.d.ts +0 -5
  125. package/bin/core-impl/select/toggle-prompt.js +0 -208
  126. package/bin/core-impl/st-end/end.d.ts +0 -2
  127. package/bin/core-impl/st-end/end.js +0 -42
  128. package/bin/core-impl/st-end/start.d.ts +0 -17
  129. package/bin/core-impl/st-end/start.js +0 -66
  130. package/bin/core-impl/task/progress.d.ts +0 -2
  131. package/bin/core-impl/task/progress.js +0 -57
  132. package/bin/core-impl/task/spinner.d.ts +0 -15
  133. package/bin/core-impl/task/spinner.js +0 -110
  134. package/bin/core-impl/utils/colorize.d.ts +0 -2
  135. package/bin/core-impl/utils/colorize.js +0 -134
  136. package/bin/core-impl/utils/errors.d.ts +0 -1
  137. package/bin/core-impl/utils/errors.js +0 -15
  138. package/bin/core-impl/utils/prevent.d.ts +0 -10
  139. package/bin/core-impl/utils/prevent.js +0 -69
  140. package/bin/core-impl/utils/prompt-end.d.ts +0 -8
  141. package/bin/core-impl/utils/prompt-end.js +0 -33
  142. package/bin/core-impl/utils/stream-text.d.ts +0 -18
  143. package/bin/core-impl/utils/stream-text.js +0 -136
  144. package/bin/core-impl/utils/system.d.ts +0 -6
  145. package/bin/core-impl/utils/system.js +0 -7
  146. package/bin/core-impl/utils/validate.d.ts +0 -22
  147. package/bin/core-impl/utils/validate.js +0 -17
  148. package/bin/core-impl/visual/animate/animate.d.ts +0 -14
  149. package/bin/core-impl/visual/animate/animate.js +0 -64
  150. package/bin/core-impl/visual/ascii-art/ascii-art.d.ts +0 -6
  151. package/bin/core-impl/visual/ascii-art/ascii-art.js +0 -12
  152. package/bin/core-types.d.ts +0 -434
  153. package/bin/main.d.ts +0 -41
  154. package/bin/main.js +0 -96
  155. /package/{bin/core-types.js → dist/plugin/types.js} +0 -0
@@ -0,0 +1,42 @@
1
+ export interface LoadedConfig {
2
+ name?: string;
3
+ version?: string;
4
+ description?: string;
5
+ commands?: {
6
+ manifest?: string;
7
+ directory?: string;
8
+ generateReport?: boolean;
9
+ };
10
+ build: {
11
+ entry?: string | string[];
12
+ outdir?: string;
13
+ targets: string[];
14
+ compress: boolean;
15
+ minify: boolean;
16
+ external?: string[];
17
+ sourcemap: boolean;
18
+ };
19
+ dev: {
20
+ watch: boolean;
21
+ inspect: boolean;
22
+ port?: number;
23
+ };
24
+ test: {
25
+ pattern: string | string[];
26
+ coverage: boolean;
27
+ watch: boolean;
28
+ };
29
+ workspace: {
30
+ packages?: string[];
31
+ shared?: any;
32
+ versionStrategy: "fixed" | "independent";
33
+ };
34
+ release: {
35
+ npm: boolean;
36
+ github: boolean;
37
+ tagFormat: string;
38
+ conventionalCommits: boolean;
39
+ };
40
+ plugins: any[];
41
+ }
42
+ export declare function loadConfig(cwd?: any): Promise<LoadedConfig>;
@@ -0,0 +1,20 @@
1
+ import { existsSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { remptsConfigSchema } from "./config.js";
4
+ const CONFIG_NAMES = ["dler.config.ts", "dler.config.js", "dler.config.mjs"];
5
+ export async function loadConfig(cwd = process.cwd()) {
6
+ for (const configName of CONFIG_NAMES) {
7
+ const configPath = path.join(cwd, configName);
8
+ if (existsSync(configPath)) {
9
+ try {
10
+ const module = await import(configPath);
11
+ const config = remptsConfigSchema.assert(module.default || module);
12
+ return config;
13
+ } catch (error) {
14
+ console.error(`Error loading config from ${configPath}:`, error);
15
+ throw error;
16
+ }
17
+ }
18
+ }
19
+ throw new Error(`No configuration file found. Please create one of: ${CONFIG_NAMES.join(", ")}`);
20
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Comprehensive Rempts configuration schema with enhanced validation
3
+ * Codegen and TypeScript are REQUIRED for all Rempts projects
4
+ */
5
+ export declare const remptsConfigSchema: import("arktype/out/variants/object").ObjectType<{
6
+ name?: string | undefined;
7
+ version?: string | undefined;
8
+ description?: string | undefined;
9
+ commands?: {
10
+ directory?: string | undefined;
11
+ generateReport?: boolean | undefined;
12
+ } | undefined;
13
+ build?: {
14
+ entry?: string | string[] | undefined;
15
+ outdir?: string | undefined;
16
+ targets?: string[] | undefined;
17
+ compress?: boolean | undefined;
18
+ minify?: boolean | undefined;
19
+ external?: string[] | undefined;
20
+ sourcemap?: boolean | undefined;
21
+ } | undefined;
22
+ dev?: {
23
+ watch?: boolean | undefined;
24
+ inspect?: boolean | undefined;
25
+ port?: number | undefined;
26
+ } | undefined;
27
+ test?: {
28
+ pattern?: string | string[] | undefined;
29
+ coverage?: boolean | undefined;
30
+ watch?: boolean | undefined;
31
+ } | undefined;
32
+ workspace?: {
33
+ packages?: string[] | undefined;
34
+ shared?: unknown;
35
+ versionStrategy?: "fixed" | "independent" | undefined;
36
+ } | undefined;
37
+ release?: {
38
+ npm?: boolean | undefined;
39
+ github?: boolean | undefined;
40
+ tagFormat?: string | undefined;
41
+ conventionalCommits?: boolean | undefined;
42
+ } | undefined;
43
+ plugins?: unknown[] | undefined;
44
+ }, {}>;
45
+ /**
46
+ * Inferred TypeScript type from the schema
47
+ * This ensures runtime validation matches compile-time types
48
+ */
49
+ export type RemptsConfig = typeof remptsConfigSchema.infer;
50
+ /**
51
+ * Strict schema for CLI creation that requires name and version
52
+ * Codegen and TypeScript are automatically enabled
53
+ */
54
+ export declare const remptsConfigStrictSchema: import("arktype/out/variants/object").ObjectType<{
55
+ name: string;
56
+ version: string;
57
+ description?: string | undefined;
58
+ commands?: {
59
+ directory?: string | undefined;
60
+ generateReport?: boolean | undefined;
61
+ } | undefined;
62
+ build?: {
63
+ entry?: string | string[] | undefined;
64
+ outdir?: string | undefined;
65
+ targets?: string[] | undefined;
66
+ compress?: boolean | undefined;
67
+ minify?: boolean | undefined;
68
+ external?: string[] | undefined;
69
+ sourcemap?: boolean | undefined;
70
+ } | undefined;
71
+ dev?: {
72
+ watch?: boolean | undefined;
73
+ inspect?: boolean | undefined;
74
+ port?: number | undefined;
75
+ } | undefined;
76
+ test?: {
77
+ pattern?: string | string[] | undefined;
78
+ coverage?: boolean | undefined;
79
+ watch?: boolean | undefined;
80
+ } | undefined;
81
+ workspace?: {
82
+ packages?: string[] | undefined;
83
+ shared?: unknown;
84
+ versionStrategy?: "fixed" | "independent" | undefined;
85
+ } | undefined;
86
+ release?: {
87
+ npm?: boolean | undefined;
88
+ github?: boolean | undefined;
89
+ tagFormat?: string | undefined;
90
+ conventionalCommits?: boolean | undefined;
91
+ } | undefined;
92
+ plugins?: unknown[] | undefined;
93
+ }, {}>;
94
+ export type RemptsConfigStrict = typeof remptsConfigStrictSchema.infer;
95
+ /**
96
+ * Helper function to define configuration with type safety
97
+ * Codegen and TypeScript are automatically configured
98
+ */
99
+ export declare function defineConfig(config: RemptsConfig): RemptsConfig;
package/dist/config.js ADDED
@@ -0,0 +1,188 @@
1
+ import { type } from "arktype";
2
+ const VersionStrategy = type("'fixed'|'independent'");
3
+ const PortNumber = type("number.integer > 0 & number.integer < 65536").configure({
4
+ description: "a valid port number"
5
+ }).narrow((n) => {
6
+ const reservedPorts = [22, 80, 443, 3306, 5432];
7
+ if (reservedPorts.includes(n)) {
8
+ console.warn(`Port ${n} is commonly used by system services and may cause conflicts`);
9
+ }
10
+ return true;
11
+ });
12
+ const SafePath = type("string").configure({
13
+ description: "a valid file path"
14
+ }).narrow((path, ctx) => {
15
+ if (!path.trim()) {
16
+ return ctx.reject("path cannot be empty or only whitespace");
17
+ }
18
+ if (path.includes("../") || path.includes("..\\")) {
19
+ console.warn("path contains directory traversal (..) which may be unsafe");
20
+ }
21
+ return true;
22
+ });
23
+ const BuildTargets = type("string[]").configure({
24
+ description: "an array of valid build targets"
25
+ }).narrow((targets, ctx) => {
26
+ const validTargets = [
27
+ "darwin-arm64",
28
+ "darwin-x64",
29
+ "linux-arm64",
30
+ "linux-x64",
31
+ "windows-x64",
32
+ "bun-linux-x64-modern",
33
+ "bun-darwin-x64-modern",
34
+ "bun-windows-x64-modern"
35
+ ];
36
+ const invalidTargets = targets.filter((target) => !validTargets.includes(target));
37
+ if (invalidTargets.length > 0) {
38
+ return ctx.reject({
39
+ expected: `valid build targets: ${validTargets.join(", ")}`,
40
+ actual: `invalid targets: ${invalidTargets.join(", ")}`
41
+ });
42
+ }
43
+ return true;
44
+ });
45
+ export const remptsConfigSchema = type({
46
+ // Base configuration (required for CLI creation, optional for partial configs)
47
+ "name?": type("string").configure({
48
+ description: "package name (npm naming conventions)"
49
+ }).narrow((name, ctx) => {
50
+ if (name.length < 1) {
51
+ return ctx.reject("package name cannot be empty");
52
+ }
53
+ if (name.length > 214) {
54
+ return ctx.reject("package name too long (max 214 characters)");
55
+ }
56
+ return true;
57
+ }),
58
+ "version?": type("string.semver").configure({
59
+ description: "semantic version string"
60
+ }),
61
+ "description?": type("string").configure({
62
+ description: "package description"
63
+ }).narrow((desc, ctx) => {
64
+ return desc.length <= 300 || ctx.reject("description too long (max 300 characters)");
65
+ }),
66
+ // Commands configuration
67
+ "commands?": type({
68
+ "directory?": SafePath.configure({
69
+ description: "directory containing command files following pattern: <cmd-name>/cmd.{ts,js,mjs}"
70
+ }),
71
+ "generateReport?": "boolean"
72
+ }).configure({
73
+ description: "command-related configuration"
74
+ }),
75
+ // Build configuration - TypeScript REQUIRED
76
+ "build?": type({
77
+ "entry?": type("string|string[]").configure({
78
+ description: "entry file(s) for bundling"
79
+ }).narrow((entry, _ctx) => {
80
+ const entries = Array.isArray(entry) ? entry : [entry];
81
+ if (entries.length === 0) {
82
+ return _ctx.reject("at least one entry file must be specified");
83
+ }
84
+ const hasValidExtension = entries.every((e) => /\.(ts|tsx|js|jsx|mjs|cjs)$/.test(e));
85
+ if (!hasValidExtension) {
86
+ console.warn(
87
+ "entry files should typically have .ts, .tsx, .js, .jsx, .mjs, or .cjs extensions"
88
+ );
89
+ }
90
+ return true;
91
+ }),
92
+ "outdir?": SafePath.configure({
93
+ description: "output directory for build artifacts"
94
+ }),
95
+ "targets?": BuildTargets,
96
+ "compress?": "boolean",
97
+ "minify?": "boolean",
98
+ "external?": type("string[]").configure({
99
+ description: "external dependencies to exclude from bundle"
100
+ }).narrow((externals, _ctx) => {
101
+ const problematic = externals.filter(
102
+ (ext) => ext.startsWith("@types/") || ext.includes("*")
103
+ );
104
+ if (problematic.length > 0) {
105
+ console.warn(`potentially problematic externals detected: ${problematic.join(", ")}`);
106
+ }
107
+ return true;
108
+ }),
109
+ "sourcemap?": "boolean"
110
+ }).configure({
111
+ description: "build configuration for bundling and compilation"
112
+ }),
113
+ // Development configuration
114
+ "dev?": type({
115
+ "watch?": "boolean",
116
+ "inspect?": "boolean",
117
+ "port?": PortNumber
118
+ }).configure({
119
+ description: "development server configuration"
120
+ }),
121
+ // Test configuration
122
+ "test?": type({
123
+ "pattern?": type("string|string[]").configure({
124
+ description: "glob patterns for test files"
125
+ }).narrow((patterns, _ctx) => {
126
+ const patternList = Array.isArray(patterns) ? patterns : [patterns];
127
+ const invalidPatterns = patternList.filter((p) => p.includes("../") || p.startsWith("/"));
128
+ if (invalidPatterns.length > 0) {
129
+ console.warn(`glob patterns should be relative: ${invalidPatterns.join(", ")}`);
130
+ }
131
+ return true;
132
+ }),
133
+ "coverage?": "boolean",
134
+ "watch?": "boolean"
135
+ }).configure({
136
+ description: "test configuration"
137
+ }),
138
+ // Workspace configuration
139
+ "workspace?": type({
140
+ "packages?": type("string[]").configure({
141
+ description: "array of package paths in the workspace"
142
+ }).narrow((packages, _ctx) => {
143
+ if (packages.length === 0) {
144
+ console.warn("workspace.packages is empty - no packages will be included");
145
+ }
146
+ const absolutePaths = packages.filter((pkg) => pkg.startsWith("/"));
147
+ if (absolutePaths.length > 0) {
148
+ console.warn("workspace packages should use relative paths, not absolute paths");
149
+ }
150
+ return true;
151
+ }),
152
+ "shared?": "unknown",
153
+ "versionStrategy?": VersionStrategy.configure({
154
+ description: "how versions are managed across workspace packages"
155
+ })
156
+ }).configure({
157
+ description: "monorepo workspace configuration"
158
+ }),
159
+ // Release configuration
160
+ "release?": type({
161
+ "npm?": "boolean",
162
+ "github?": "boolean",
163
+ "tagFormat?": type("string").configure({
164
+ description: "format for git tags (e.g., 'v${version}')"
165
+ }).narrow((format, ctx) => {
166
+ if (!format.includes("${version}")) {
167
+ return ctx.reject("tagFormat must include '${version}' placeholder");
168
+ }
169
+ return true;
170
+ }),
171
+ "conventionalCommits?": "boolean"
172
+ }).configure({
173
+ description: "release and publishing configuration"
174
+ }),
175
+ // Plugins configuration
176
+ "plugins?": type("unknown[]").configure({
177
+ description: "array of plugin configurations"
178
+ })
179
+ }).configure({
180
+ description: "Rempts CLI configuration schema with comprehensive validation"
181
+ });
182
+ export const remptsConfigStrictSchema = remptsConfigSchema.and({
183
+ name: "string",
184
+ version: "string.semver"
185
+ });
186
+ export function defineConfig(config) {
187
+ return config;
188
+ }
@@ -0,0 +1,43 @@
1
+ import type { Command } from "./types.js";
2
+ /**
3
+ * Create a file command loader
4
+ */
5
+ export declare function createFileCommandLoader(): {
6
+ /**
7
+ * Load commands from a directory structure
8
+ * @param cmdsDir Directory containing command files following pattern: <cmd-name>/cmd.{ts,js,mjs}
9
+ * @returns Promise resolving to loaded command tree
10
+ */
11
+ loadFromDirectory(cmdsDir: string): Promise<CommandFileTree>;
12
+ /**
13
+ * Load and register commands from file tree
14
+ * Returns commands with their names inferred from file paths
15
+ */
16
+ loadCommandsFromTree(tree: CommandFileTree): Promise<CommandWithName[]>;
17
+ };
18
+ /**
19
+ * Command with its inferred name from file path
20
+ */
21
+ export interface CommandWithName {
22
+ name: string;
23
+ command: Command<any, any>;
24
+ }
25
+ /**
26
+ * Types for file-based command loading
27
+ */
28
+ export interface CommandFileInfo {
29
+ filePath: string;
30
+ importPath: string;
31
+ commandName: string;
32
+ }
33
+ export interface CommandFileTree {
34
+ [key: string]: CommandFileTree | CommandFileInfo | undefined;
35
+ }
36
+ export interface CommandConflict {
37
+ commandName: string;
38
+ files: string[];
39
+ }
40
+ /**
41
+ * Utility function to load commands from directory
42
+ */
43
+ export declare function loadCommandsFromDirectory(cmdsDir: string): Promise<CommandWithName[]>;
@@ -0,0 +1,199 @@
1
+ import { join, relative } from "node:path";
2
+ const CMD_FILE_PATTERN = /\/cmd\.[^.]+$/;
3
+ const PATH_SEPARATOR_PATTERN = /\//g;
4
+ export function createFileCommandLoader() {
5
+ return {
6
+ /**
7
+ * Load commands from a directory structure
8
+ * @param cmdsDir Directory containing command files following pattern: <cmd-name>/cmd.{ts,js,mjs}
9
+ * @returns Promise resolving to loaded command tree
10
+ */
11
+ async loadFromDirectory(cmdsDir) {
12
+ const commandFiles = await scanCommandFiles(cmdsDir);
13
+ const conflicts = detectConflicts(commandFiles, cmdsDir);
14
+ if (conflicts.length > 0) {
15
+ const conflictMessages = conflicts.map(
16
+ (conflict) => `Command "${conflict.commandName}" conflicts between:
17
+ - ${conflict.files[0]}
18
+ - ${conflict.files[1]}`
19
+ );
20
+ throw new Error(`Command conflicts detected:
21
+ ${conflictMessages.join("\n\n")}`);
22
+ }
23
+ return buildCommandTree(commandFiles, cmdsDir);
24
+ },
25
+ /**
26
+ * Load and register commands from file tree
27
+ * Returns commands with their names inferred from file paths
28
+ */
29
+ async loadCommandsFromTree(tree) {
30
+ return loadCommandsFromTree(tree);
31
+ }
32
+ };
33
+ }
34
+ async function scanCommandFiles(cmdsDir) {
35
+ try {
36
+ const glob = new Bun.Glob("**/cmd.{ts,js,mjs}");
37
+ const files = await Array.fromAsync(glob.scan({ cwd: cmdsDir }));
38
+ const commandFiles = [];
39
+ for (const file of files) {
40
+ const fullPath = join(cmdsDir, file);
41
+ commandFiles.push(fullPath);
42
+ }
43
+ return commandFiles;
44
+ } catch {
45
+ console.warn(`Warning: Could not scan commands directory: ${cmdsDir}`);
46
+ return [];
47
+ }
48
+ }
49
+ function detectConflicts(commandFiles, cmdsDir) {
50
+ const conflicts = [];
51
+ const commandMap = /* @__PURE__ */ new Map();
52
+ const directoryMap = /* @__PURE__ */ new Map();
53
+ for (const filePath of commandFiles) {
54
+ const relativePath = relative(cmdsDir, filePath);
55
+ const commandName = getCommandName(relativePath);
56
+ if (!commandMap.has(commandName)) {
57
+ commandMap.set(commandName, []);
58
+ }
59
+ commandMap.get(commandName)?.push(filePath);
60
+ const directory = relativePath.replace(CMD_FILE_PATTERN, "");
61
+ if (!directoryMap.has(directory)) {
62
+ directoryMap.set(directory, []);
63
+ }
64
+ directoryMap.get(directory)?.push(filePath);
65
+ }
66
+ const directoriesWithVariants = /* @__PURE__ */ new Set();
67
+ for (const [directory, files] of directoryMap) {
68
+ if (files.length > 1) {
69
+ conflicts.push({
70
+ commandName: `${directory} has multiple file variants`,
71
+ files
72
+ });
73
+ directoriesWithVariants.add(directory);
74
+ }
75
+ }
76
+ for (const [commandName, files] of commandMap) {
77
+ if (files.length > 1 && !directoriesWithVariants.has(commandName)) {
78
+ conflicts.push({ commandName, files });
79
+ }
80
+ }
81
+ return conflicts;
82
+ }
83
+ function getCommandName(filePath) {
84
+ const pathWithoutCmd = filePath.replace(CMD_FILE_PATTERN, "");
85
+ const trimmed = pathWithoutCmd.replace(/\/$/, "");
86
+ return trimmed.replace(PATH_SEPARATOR_PATTERN, " ").replace(/\s+/g, " ").trim();
87
+ }
88
+ async function buildCommandTree(commandFiles, cmdsDir) {
89
+ const tree = {};
90
+ for (const filePath of commandFiles) {
91
+ const relativePath = relative(cmdsDir, filePath);
92
+ const commandName = getCommandName(relativePath);
93
+ const commandNameParts = commandName.split(" ");
94
+ let current = tree;
95
+ for (let i = 0; i < commandNameParts.length - 1; i++) {
96
+ const part = commandNameParts[i];
97
+ if (!current[part]) {
98
+ current[part] = {};
99
+ } else if ("filePath" in current[part]) {
100
+ const existingCommand = current[part];
101
+ current[part] = {
102
+ // Store the parent command file under a special key or as the base
103
+ // We'll handle this in loadCommandsFromTree
104
+ };
105
+ const treeNode = current[part];
106
+ treeNode.__parent__ = existingCommand;
107
+ }
108
+ current = current[part];
109
+ }
110
+ const finalPart = commandNameParts.at(-1);
111
+ if (commandNameParts.length === 1 && finalPart in current && "filePath" in current[finalPart]) {
112
+ const existing = current[finalPart];
113
+ if (existing.filePath !== relativePath) {
114
+ throw new Error(
115
+ `Command conflict: "${finalPart}" is defined in both "${existing.filePath}" and "${relativePath}"`
116
+ );
117
+ }
118
+ }
119
+ current[finalPart] = {
120
+ filePath: relativePath,
121
+ importPath: getImportPath(filePath),
122
+ commandName
123
+ };
124
+ }
125
+ return tree;
126
+ }
127
+ function getImportPath(filePath) {
128
+ return `file://${filePath}`;
129
+ }
130
+ async function loadCommandsFromTree(tree) {
131
+ async function loadFromTree(obj, path = []) {
132
+ const loadedCommands = [];
133
+ for (const [key, value] of Object.entries(obj)) {
134
+ if (key === "__parent__") {
135
+ const commandInfo = value;
136
+ try {
137
+ const module = await import(commandInfo.importPath);
138
+ const command = module.default || module;
139
+ loadedCommands.push({
140
+ name: commandInfo.commandName,
141
+ command
142
+ });
143
+ } catch (error) {
144
+ console.warn(`Failed to load command from ${commandInfo.importPath}:`, error);
145
+ }
146
+ continue;
147
+ }
148
+ if (typeof value === "object" && "filePath" in value) {
149
+ const commandInfo = value;
150
+ try {
151
+ const module = await import(commandInfo.importPath);
152
+ const command = module.default || module;
153
+ const inferredName = commandInfo.commandName;
154
+ loadedCommands.push({
155
+ name: inferredName,
156
+ command
157
+ });
158
+ } catch (error) {
159
+ console.warn(`Failed to load command from ${commandInfo.importPath}:`, error);
160
+ }
161
+ } else {
162
+ const subCommands = await loadFromTree(value, [...path, key]);
163
+ if (subCommands.length > 0) {
164
+ const parentCommandInfo = value.__parent__;
165
+ if (parentCommandInfo) {
166
+ try {
167
+ const module = await import(parentCommandInfo.importPath);
168
+ const parentCommand = module.default || module;
169
+ loadedCommands.push({
170
+ name: parentCommandInfo.commandName,
171
+ command: parentCommand
172
+ });
173
+ } catch (error) {
174
+ console.warn(
175
+ `Failed to load parent command from ${parentCommandInfo.importPath}:`,
176
+ error
177
+ );
178
+ }
179
+ } else {
180
+ const parentCommand = {
181
+ description: `${key} commands`
182
+ };
183
+ loadedCommands.push({
184
+ name: key,
185
+ command: parentCommand
186
+ });
187
+ }
188
+ }
189
+ }
190
+ }
191
+ return loadedCommands;
192
+ }
193
+ return await loadFromTree(tree);
194
+ }
195
+ export async function loadCommandsFromDirectory(cmdsDir) {
196
+ const loader = createFileCommandLoader();
197
+ const tree = await loader.loadFromDirectory(cmdsDir);
198
+ return loader.loadCommandsFromTree(tree);
199
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Built-in global flags available to all commands
3
+ * Enhanced with better descriptions and validation
4
+ */
5
+ export declare const GLOBAL_FLAGS: {
6
+ interactive: {
7
+ schema: import("arktype").BaseType<boolean | undefined, {}>;
8
+ short: string;
9
+ description: string;
10
+ };
11
+ tui: {
12
+ schema: import("arktype").BaseType<boolean | undefined, {}>;
13
+ description: string;
14
+ };
15
+ "no-tui": {
16
+ schema: import("arktype").BaseType<boolean | undefined, {}>;
17
+ description: string;
18
+ };
19
+ help: {
20
+ schema: import("arktype").BaseType<boolean | undefined, {}>;
21
+ short: string;
22
+ description: string;
23
+ };
24
+ version: {
25
+ schema: import("arktype").BaseType<boolean | undefined, {}>;
26
+ short: string;
27
+ description: string;
28
+ };
29
+ };
30
+ export interface GlobalFlags {
31
+ interactive?: boolean;
32
+ tui?: boolean;
33
+ "no-tui"?: boolean;
34
+ help?: boolean;
35
+ version?: boolean;
36
+ }
@@ -0,0 +1,36 @@
1
+ import { type } from "arktype";
2
+ export const GLOBAL_FLAGS = {
3
+ interactive: {
4
+ schema: type("boolean | undefined").configure({
5
+ description: "enable interactive terminal user interface mode"
6
+ }),
7
+ short: "i",
8
+ description: "Run in interactive TUI mode"
9
+ },
10
+ tui: {
11
+ schema: type("boolean | undefined").configure({
12
+ description: "force terminal user interface mode"
13
+ }),
14
+ description: "Force TUI mode (same as --interactive)"
15
+ },
16
+ "no-tui": {
17
+ schema: type("boolean | undefined").configure({
18
+ description: "disable terminal user interface mode"
19
+ }),
20
+ description: "Disable TUI mode, use CLI handler instead"
21
+ },
22
+ help: {
23
+ schema: type("boolean | undefined").configure({
24
+ description: "display help information"
25
+ }),
26
+ short: "h",
27
+ description: "Show help"
28
+ },
29
+ version: {
30
+ schema: type("boolean | undefined").configure({
31
+ description: "display version information"
32
+ }),
33
+ short: "v",
34
+ description: "Show version"
35
+ }
36
+ };
package/dist/mod.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export { SchemaError } from "@standard-schema/utils";
2
+ export { createApp, createCLI } from "./cli.js";
3
+ export { defineConfig, type RemptsConfig, remptsConfigSchema } from "./config.js";
4
+ export { type LoadedConfig, loadConfig } from "./config-loader.js";
5
+ export type { CommandConflict, CommandFileInfo, CommandFileTree, } from "./file-loader.js";
6
+ export { createFileCommandLoader, loadCommandsFromDirectory } from "./file-loader.js";
7
+ export type { GlobalFlags } from "./global-flags.js";
8
+ export { GLOBAL_FLAGS } from "./global-flags.js";
9
+ export { clearTuiRenderer, getTuiRenderer, registerTuiRenderer, } from "./tui/registry.js";
10
+ export type { CLI, CLIOption, Command, CommandOptions, Handler, HandlerArgs, Options, PluginConfig, RegisteredCommands, RenderArgs, RenderFunction, RenderResult, ResolvedConfig, RuntimeInfo, TerminalInfo, } from "./types.js";
11
+ export { defineCommand, option } from "./types.js";
12
+ export type { Assign, Constrain, DeepPartial, Expand, ExtractObjects, ExtractPrimitives, IntersectAssign, IsAny, IsNonEmptyObject, IsUnion, MakeDifferenceOptional, MergeAll, MergeAllObjects, NoInfer, PartialMergeAll, PickAsRequired, PickOptional, PickRequired, UnionToIntersection, WithoutEmpty, } from "./utils/type-helpers.js";
13
+ export { createBatchValidator, createValidator, isValueOfType, validateValue, validateValues, } from "./validation.js";