@reliverse/rempts-core 1.6.1 → 2.3.2

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
package/dist/mod.js ADDED
@@ -0,0 +1,19 @@
1
+ export { SchemaError } from "@standard-schema/utils";
2
+ export { createApp, createCLI } from "./cli.js";
3
+ export { defineConfig, remptsConfigSchema } from "./config.js";
4
+ export { loadConfig } from "./config-loader.js";
5
+ export { createFileCommandLoader, loadCommandsFromDirectory } from "./file-loader.js";
6
+ export { GLOBAL_FLAGS } from "./global-flags.js";
7
+ export {
8
+ clearTuiRenderer,
9
+ getTuiRenderer,
10
+ registerTuiRenderer
11
+ } from "./tui/registry.js";
12
+ export { defineCommand, option } from "./types.js";
13
+ export {
14
+ createBatchValidator,
15
+ createValidator,
16
+ isValueOfType,
17
+ validateValue,
18
+ validateValues
19
+ } from "./validation.js";
@@ -0,0 +1,6 @@
1
+ import type { InferOptions, Options } from "./types.js";
2
+ export interface ParsedArgs<TOptions extends Options = Options> {
3
+ flags: InferOptions<TOptions>;
4
+ positional: string[];
5
+ }
6
+ export declare function parseArgs<TOptions extends Options = Options>(args: string[], options: TOptions, commandName?: string): Promise<ParsedArgs<TOptions>>;
package/dist/parser.js ADDED
@@ -0,0 +1,137 @@
1
+ import { RemptsValidationError } from "./types.js";
2
+ export async function parseArgs(args, options, commandName = "unknown") {
3
+ const flags = {};
4
+ const positional = [];
5
+ const shortToName = /* @__PURE__ */ new Map();
6
+ for (const [name, opt] of Object.entries(options)) {
7
+ if (opt.short) {
8
+ shortToName.set(opt.short, name);
9
+ }
10
+ }
11
+ let stopParsingFlags = false;
12
+ for (let i = 0; i < args.length; i++) {
13
+ const arg = args[i];
14
+ if (!arg) {
15
+ continue;
16
+ }
17
+ if (arg === "--") {
18
+ stopParsingFlags = true;
19
+ continue;
20
+ }
21
+ if (stopParsingFlags) {
22
+ positional.push(arg);
23
+ continue;
24
+ }
25
+ if (arg.startsWith("--")) {
26
+ const eqIndex = arg.indexOf("=");
27
+ const name = eqIndex > 0 ? arg.slice(2, eqIndex) : arg.slice(2);
28
+ const inlineValue = eqIndex > 0 ? arg.slice(eqIndex + 1) : void 0;
29
+ if (!(name && options[name])) {
30
+ continue;
31
+ }
32
+ let value = inlineValue;
33
+ if (value === void 0 && i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
34
+ value = args[++i];
35
+ }
36
+ flags[name] = await validateOption(name, value ?? "true", options[name]?.schema, commandName);
37
+ } else if (arg.startsWith("-") && arg.length > 1) {
38
+ const short = arg.slice(1);
39
+ const name = shortToName.get(short);
40
+ if (name && options[name]) {
41
+ let value;
42
+ if (i + 1 < args.length && !args[i + 1]?.startsWith("-")) {
43
+ value = args[++i];
44
+ }
45
+ flags[name] = await validateOption(
46
+ name,
47
+ value ?? "true",
48
+ options[name]?.schema,
49
+ commandName
50
+ );
51
+ }
52
+ } else {
53
+ positional.push(arg);
54
+ }
55
+ }
56
+ for (const [name, opt] of Object.entries(options)) {
57
+ if (!(name in flags)) {
58
+ if (opt.default !== void 0) {
59
+ const defaultValidated = await validateOption(name, opt.default, opt.schema, commandName);
60
+ flags[name] = defaultValidated;
61
+ } else {
62
+ const validatedValue = await validateOption(name, void 0, opt.schema, commandName);
63
+ if (validatedValue === void 0) {
64
+ const booleanTest = await opt.schema["~standard"].validate(false);
65
+ if (booleanTest.issues) {
66
+ flags[name] = void 0;
67
+ } else {
68
+ flags[name] = false;
69
+ }
70
+ } else {
71
+ flags[name] = validatedValue;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return { flags, positional };
77
+ }
78
+ async function validateOption(name, value, schema, commandName = "unknown") {
79
+ let processedValue = value;
80
+ if (typeof value === "string" && (value === "true" || value === "false")) {
81
+ const testResult = await schema["~standard"].validate(true);
82
+ if (!testResult.issues) {
83
+ processedValue = value === "true";
84
+ }
85
+ }
86
+ const result = await schema["~standard"].validate(processedValue);
87
+ if (result.issues && result.issues.length > 0) {
88
+ const issue = result.issues[0];
89
+ if (!issue) {
90
+ return processedValue;
91
+ }
92
+ const expectedType = extractSchemaType(schema);
93
+ const hint = generateHint(schema, value);
94
+ throw new RemptsValidationError(`Invalid option '${name}': ${issue.message}`, {
95
+ option: name,
96
+ value,
97
+ command: commandName,
98
+ expectedType,
99
+ hint
100
+ });
101
+ }
102
+ return "value" in result ? result.value : processedValue;
103
+ }
104
+ function extractSchemaType(schema) {
105
+ if ("type" in schema && typeof schema.type === "string") {
106
+ return schema.type;
107
+ }
108
+ if ("enum" in schema) {
109
+ return "enum";
110
+ }
111
+ if ("items" in schema) {
112
+ return "array";
113
+ }
114
+ if ("properties" in schema) {
115
+ return "object";
116
+ }
117
+ if ("format" in schema) {
118
+ return "string";
119
+ }
120
+ return "unknown";
121
+ }
122
+ function generateHint(schema, value) {
123
+ const type = extractSchemaType(schema);
124
+ if (type === "boolean" && typeof value === "string") {
125
+ return "Use --flag or --no-flag for boolean options";
126
+ }
127
+ if (type === "number" && typeof value === "string") {
128
+ return "Provide a numeric value";
129
+ }
130
+ if (type === "array" && !Array.isArray(value)) {
131
+ return "Provide a comma-separated list of values";
132
+ }
133
+ if (type === "enum" && typeof value === "string") {
134
+ return "Choose from the available options";
135
+ }
136
+ return "";
137
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Plugin context implementations - functional approach
3
+ */
4
+ import type { PluginStore } from "./store.js.js";
5
+ import type { EnvironmentInfo, CommandContext as ICommandContext } from "./types.js";
6
+ /**
7
+ * Create environment info for contexts
8
+ */
9
+ export declare function createEnvironmentInfo(): EnvironmentInfo;
10
+ /**
11
+ * Create command context for command execution
12
+ */
13
+ export declare function createCommandContext<TStore = {}>(command: string, commandDef: any, args: string[], flags: Record<string, any>, env: EnvironmentInfo, store?: PluginStore<TStore>): ICommandContext<TStore>;
@@ -0,0 +1,53 @@
1
+ export function createEnvironmentInfo() {
2
+ const isCI = !!(process.env.CI || process.env.CONTINUOUS_INTEGRATION || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS);
3
+ return {
4
+ cwd: process.cwd(),
5
+ home: require("node:os").homedir(),
6
+ temp: require("node:os").tmpdir(),
7
+ platform: process.platform,
8
+ arch: process.arch,
9
+ nodeVersion: process.version,
10
+ isCI,
11
+ // Initialize plugin-extended properties with defaults
12
+ isAIAgent: false,
13
+ aiAgents: []
14
+ };
15
+ }
16
+ export function createCommandContext(command, commandDef, args, flags, env, store) {
17
+ return {
18
+ command,
19
+ commandDef,
20
+ args,
21
+ flags,
22
+ env,
23
+ store,
24
+ /**
25
+ * Type-safe store value access
26
+ * Provides compile-time type checking for store properties
27
+ */
28
+ getStoreValue(key) {
29
+ if (!store) return void 0;
30
+ const state = store.getState();
31
+ return state[key];
32
+ },
33
+ /**
34
+ * Type-safe store value update
35
+ * Provides compile-time type checking for store property updates
36
+ */
37
+ setStoreValue(key, value) {
38
+ if (!store) return;
39
+ store.setState((prevState) => ({
40
+ ...prevState,
41
+ [key]: value
42
+ }));
43
+ },
44
+ /**
45
+ * Check if a store property exists
46
+ */
47
+ hasStoreValue(key) {
48
+ if (!store) return false;
49
+ const state = store.getState();
50
+ return key in state;
51
+ }
52
+ };
53
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Plugin development utilities
3
+ */
4
+ import type { Plugin, PluginFactory, PluginHooks } from "./types.js";
5
+ /**
6
+ * Create a plugin - supports both direct plugins and plugin factories
7
+ *
8
+ * @example Direct plugin with explicit store type:
9
+ * ```typescript
10
+ * interface MyStore {
11
+ * count: number
12
+ * message: string
13
+ * }
14
+ *
15
+ * const myPlugin = createPlugin<MyStore>({
16
+ * name: 'my-plugin',
17
+ * store: {
18
+ * count: 0,
19
+ * message: ''
20
+ * },
21
+ * beforeCommand(context) {
22
+ * context.store.count++ // TypeScript knows the type!
23
+ * }
24
+ * })
25
+ * ```
26
+ *
27
+ * @example Plugin factory with options:
28
+ * ```typescript
29
+ * const myPlugin = createPlugin((options: { prefix: string }) => ({
30
+ * name: 'my-plugin',
31
+ * store: {
32
+ * count: 0
33
+ * },
34
+ * beforeCommand(context) {
35
+ * console.log(`${options.prefix}: ${context.store.count}`)
36
+ * }
37
+ * } satisfies PluginHooks<{ count: number }>))
38
+ *
39
+ * // Use it:
40
+ * myPlugin({ prefix: 'Hello' })
41
+ * ```
42
+ */
43
+ export declare function createPlugin<TStore = {}>(plugin: Plugin<TStore>): Plugin<TStore>;
44
+ export declare function createPlugin<TOptions, TStore = {}>(factory: (options: TOptions) => Plugin<TStore>): (options: TOptions) => Plugin<TStore>;
45
+ /**
46
+ * Infer plugin options type from a plugin factory
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * type Options = InferPluginOptions<typeof myPlugin>
51
+ * ```
52
+ */
53
+ export type InferPluginOptions<T> = T extends PluginFactory<infer O, any> ? O : never;
54
+ /**
55
+ * Infer plugin store type
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * type Store = InferPluginStore<typeof myPlugin>
60
+ * ```
61
+ */
62
+ export type InferPluginStore<T> = T extends Plugin<infer S> ? S : T extends PluginFactory<any, infer S> ? S : {};
63
+ /**
64
+ * Create a test plugin for development and testing
65
+ *
66
+ * @example
67
+ * ```typescript
68
+ * const testPlugin = createTestPlugin(
69
+ * { count: 0, message: '' },
70
+ * {
71
+ * beforeCommand(context) {
72
+ * context.store.count++
73
+ * console.log(`Count: ${context.store.count}`)
74
+ * }
75
+ * }
76
+ * )
77
+ * ```
78
+ */
79
+ export declare function createTestPlugin<TStore = {}>(initialState: TStore, hooks: Partial<PluginHooks<TStore>>): Plugin<TStore>;
80
+ /**
81
+ * Compose multiple plugins into a single plugin
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * const composedPlugin = composePlugins(
86
+ * authPlugin({ provider: 'github' }),
87
+ * loggingPlugin({ level: 'debug' }),
88
+ * metricsPlugin({ enabled: true })
89
+ * )
90
+ * ```
91
+ */
92
+ export declare function composePlugins<T extends Plugin[]>(...plugins: T): Plugin;
@@ -0,0 +1,61 @@
1
+ import { createPluginStore } from "./store.js";
2
+ export function createPlugin(input) {
3
+ return input;
4
+ }
5
+ export function createTestPlugin(initialState, hooks) {
6
+ return () => ({
7
+ store: createPluginStore(initialState),
8
+ ...hooks
9
+ });
10
+ }
11
+ export function composePlugins(...plugins) {
12
+ return () => {
13
+ const hooksArray = plugins.map((plugin) => plugin());
14
+ const stores = {};
15
+ hooksArray.forEach((hooks, index) => {
16
+ if (hooks.store) {
17
+ stores[`plugin_${index}`] = hooks.store;
18
+ }
19
+ });
20
+ const composedStore = Object.keys(stores).length > 0 ? createPluginStore(
21
+ Object.keys(stores).reduce((acc, key) => {
22
+ const store = stores[key];
23
+ if (store) {
24
+ acc[key] = store.getState();
25
+ }
26
+ return acc;
27
+ }, {})
28
+ ) : void 0;
29
+ return {
30
+ store: composedStore,
31
+ async setup(context) {
32
+ for (const hooks of hooksArray) {
33
+ if (hooks.setup) {
34
+ await hooks.setup(context);
35
+ }
36
+ }
37
+ },
38
+ async configResolved(config) {
39
+ for (const hooks of hooksArray) {
40
+ if (hooks.configResolved) {
41
+ await hooks.configResolved(config);
42
+ }
43
+ }
44
+ },
45
+ async beforeCommand(context) {
46
+ for (const hooks of hooksArray) {
47
+ if (hooks.beforeCommand) {
48
+ await hooks.beforeCommand(context);
49
+ }
50
+ }
51
+ },
52
+ async afterCommand(context) {
53
+ for (const hooks of hooksArray) {
54
+ if (hooks.afterCommand) {
55
+ await hooks.afterCommand(context);
56
+ }
57
+ }
58
+ }
59
+ };
60
+ };
61
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Plugin loader implementation - functional approach
3
+ */
4
+ import type { Plugin, PluginConfig } from "./types.js";
5
+ export interface PluginLoader {
6
+ loadPlugin(config: PluginConfig): Promise<Plugin>;
7
+ validatePlugin(plugin: Plugin): void;
8
+ }
9
+ /**
10
+ * Create a plugin loader
11
+ */
12
+ export declare function createPluginLoader(): PluginLoader;
@@ -0,0 +1,65 @@
1
+ import { join } from "node:path";
2
+ function isPluginFunction(obj) {
3
+ return obj && typeof obj === "function";
4
+ }
5
+ async function loadFromPath(path) {
6
+ try {
7
+ const resolvedPath = path.startsWith(".") ? join(process.cwd(), path) : path;
8
+ const module = await import(resolvedPath);
9
+ const plugin = module.default || module.plugin || module;
10
+ if (typeof plugin === "function" && !isPluginFunction(plugin)) {
11
+ return plugin();
12
+ }
13
+ if (!isPluginFunction(plugin)) {
14
+ throw new Error("Module does not export a valid plugin");
15
+ }
16
+ return plugin;
17
+ } catch (error) {
18
+ throw new Error(`Failed to load plugin from ${path}: ${error.message}`);
19
+ }
20
+ }
21
+ export function createPluginLoader() {
22
+ return {
23
+ /**
24
+ * Load a plugin from various configuration formats
25
+ */
26
+ async loadPlugin(config) {
27
+ if (typeof config === "string") {
28
+ return loadFromPath(config);
29
+ }
30
+ if (isPluginFunction(config)) {
31
+ return config;
32
+ }
33
+ if (typeof config === "function") {
34
+ return config();
35
+ }
36
+ if (Array.isArray(config) && config.length === 2) {
37
+ const [factory, options] = config;
38
+ if (typeof factory === "function") {
39
+ return factory(options);
40
+ }
41
+ }
42
+ throw new Error(`Invalid plugin configuration: ${JSON.stringify(config)}`);
43
+ },
44
+ /**
45
+ * Validate loaded plugin
46
+ */
47
+ validatePlugin(plugin) {
48
+ if (typeof plugin !== "function") {
49
+ throw new Error("Plugin must be a function");
50
+ }
51
+ try {
52
+ const hooks = plugin();
53
+ const hookNames = ["setup", "configResolved", "beforeCommand", "afterCommand"];
54
+ for (const hook of hookNames) {
55
+ const value = hooks[hook];
56
+ if (value !== void 0 && typeof value !== "function") {
57
+ throw new Error(`Plugin hook ${hook} must be a function`);
58
+ }
59
+ }
60
+ } catch (error) {
61
+ throw new Error(`Plugin validation failed: ${error.message}`);
62
+ }
63
+ }
64
+ };
65
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Plugin lifecycle manager - functional implementation
3
+ */
4
+ import type { RemptsConfig, ResolvedConfig } from "../types.js";
5
+ import { createLogger } from "../utils/logger.js";
6
+ import { createCommandContext } from "./context.js";
7
+ import { createPluginLoader } from "./loader.js";
8
+ import { type PluginStore } from "./store.js.js";
9
+ import type { CommandDefinition, CommandResult, Middleware, Plugin, PluginConfig, PluginHooks } from "./types.js";
10
+ export interface PluginSetupResult {
11
+ config: Partial<RemptsConfig>;
12
+ commands: CommandDefinition[];
13
+ middlewares: Middleware[];
14
+ }
15
+ export interface PluginManagerState<TStore = {}> {
16
+ plugins: Plugin[];
17
+ pluginHooks: PluginHooks[];
18
+ combinedStore?: PluginStore<TStore>;
19
+ loader: ReturnType<typeof createPluginLoader>;
20
+ logger: ReturnType<typeof createLogger>;
21
+ }
22
+ /**
23
+ * Create a new plugin manager state
24
+ */
25
+ export declare function createPluginManager<TStore = {}>(): PluginManagerState<TStore>;
26
+ /**
27
+ * Load and validate plugins
28
+ */
29
+ export declare function loadPlugins<TStore>(state: PluginManagerState<TStore>, configs: PluginConfig[]): Promise<void>;
30
+ /**
31
+ * Run setup hooks for all plugins
32
+ */
33
+ export declare function runSetup<TStore>(state: PluginManagerState<TStore>, config: Partial<RemptsConfig>): Promise<PluginSetupResult>;
34
+ /**
35
+ * Run configResolved hooks
36
+ */
37
+ export declare function runConfigResolved<TStore>(state: PluginManagerState<TStore>, config: ResolvedConfig): Promise<void>;
38
+ /**
39
+ * Run beforeCommand hooks
40
+ */
41
+ export declare function runBeforeCommand<TStore>(state: PluginManagerState<TStore>, command: string, commandDef: any, args: string[], flags: Record<string, any>): Promise<ReturnType<typeof createCommandContext<TStore>>>;
42
+ /**
43
+ * Run afterCommand hooks
44
+ */
45
+ export declare function runAfterCommand<TStore>(state: PluginManagerState<TStore>, context: ReturnType<typeof createCommandContext<TStore>>, result: CommandResult): Promise<void>;
46
+ /**
47
+ * Get loaded plugins (for debugging/listing)
48
+ */
49
+ export declare function getPlugins<TStore>(state: PluginManagerState<TStore>): readonly Plugin[];
50
+ /**
51
+ * Get plugin hooks by index
52
+ */
53
+ export declare function getPlugin<TStore>(state: PluginManagerState<TStore>, index: number): PluginHooks | undefined;
@@ -0,0 +1,135 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import { createLogger } from "../utils/logger.js";
4
+ import { deepMerge } from "../utils/merge.js";
5
+ import { createCommandContext, createEnvironmentInfo } from "./context.js";
6
+ import { createPluginLoader } from "./loader.js";
7
+ import { combinePluginStores } from "./store.js";
8
+ export function createPluginManager() {
9
+ return {
10
+ plugins: [],
11
+ pluginHooks: [],
12
+ combinedStore: void 0,
13
+ loader: createPluginLoader(),
14
+ logger: createLogger("plugin-manager")
15
+ };
16
+ }
17
+ export async function loadPlugins(state, configs) {
18
+ const loadPromises = configs.map(async (config) => {
19
+ try {
20
+ const plugin = await state.loader.loadPlugin(config);
21
+ state.loader.validatePlugin(plugin);
22
+ return plugin;
23
+ } catch (error) {
24
+ state.logger.error(`Failed to load plugin: ${error.message}`);
25
+ throw error;
26
+ }
27
+ });
28
+ state.plugins = await Promise.all(loadPromises);
29
+ state.pluginHooks = state.plugins.map((plugin) => plugin());
30
+ const stores = {};
31
+ state.pluginHooks.forEach((hooks, index) => {
32
+ if (hooks.store) {
33
+ stores[`plugin_${index}`] = hooks.store;
34
+ }
35
+ });
36
+ if (Object.keys(stores).length > 0) {
37
+ state.combinedStore = combinePluginStores(stores);
38
+ }
39
+ state.logger.info(`Loaded ${state.plugins.length} plugins`);
40
+ }
41
+ export async function runSetup(state, config) {
42
+ const setupState = {
43
+ configUpdates: [],
44
+ commands: [],
45
+ middlewares: []
46
+ };
47
+ const context = {
48
+ config,
49
+ store: state.combinedStore,
50
+ logger: createLogger("plugins"),
51
+ paths: {
52
+ cwd: process.cwd(),
53
+ home: homedir(),
54
+ config: join(homedir(), ".config", config.name || "rempts")
55
+ },
56
+ updateConfig(partial) {
57
+ setupState.configUpdates.push(partial);
58
+ },
59
+ registerCommand(command) {
60
+ setupState.commands.push(command);
61
+ },
62
+ use(middleware) {
63
+ setupState.middlewares.push(middleware);
64
+ }
65
+ };
66
+ for (const hooks of state.pluginHooks) {
67
+ if (hooks.setup) {
68
+ state.logger.debug(`Running setup for plugin`);
69
+ try {
70
+ await hooks.setup(context);
71
+ } catch (error) {
72
+ throw new Error(`Plugin setup failed: ${error.message}`);
73
+ }
74
+ }
75
+ }
76
+ const mergedConfig = setupState.configUpdates.length > 0 ? deepMerge(config, ...setupState.configUpdates) : config;
77
+ return {
78
+ config: mergedConfig,
79
+ commands: setupState.commands,
80
+ middlewares: setupState.middlewares
81
+ };
82
+ }
83
+ export async function runConfigResolved(state, config) {
84
+ for (const hooks of state.pluginHooks) {
85
+ if (hooks.configResolved) {
86
+ state.logger.debug(`Running configResolved for plugin`);
87
+ try {
88
+ await hooks.configResolved(config);
89
+ } catch (error) {
90
+ state.logger.error(`Plugin configResolved error: ${error.message}`);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ export async function runBeforeCommand(state, command, commandDef, args, flags) {
96
+ const commandStore = state.combinedStore;
97
+ const context = createCommandContext(
98
+ command,
99
+ commandDef,
100
+ args,
101
+ flags,
102
+ createEnvironmentInfo(),
103
+ commandStore
104
+ );
105
+ for (const hooks of state.pluginHooks) {
106
+ if (hooks.beforeCommand) {
107
+ state.logger.debug(`Running beforeCommand for plugin`);
108
+ try {
109
+ await hooks.beforeCommand(context);
110
+ } catch (error) {
111
+ throw new Error(`Plugin beforeCommand failed: ${error.message}`);
112
+ }
113
+ }
114
+ }
115
+ return context;
116
+ }
117
+ export async function runAfterCommand(state, context, result) {
118
+ const fullContext = Object.assign(context, result);
119
+ for (const hooks of state.pluginHooks) {
120
+ if (hooks.afterCommand) {
121
+ state.logger.debug(`Running afterCommand for plugin`);
122
+ try {
123
+ await hooks.afterCommand(fullContext);
124
+ } catch (error) {
125
+ state.logger.error(`Plugin afterCommand error: ${error.message}`);
126
+ }
127
+ }
128
+ }
129
+ }
130
+ export function getPlugins(state) {
131
+ return state.plugins;
132
+ }
133
+ export function getPlugin(state, index) {
134
+ return state.pluginHooks[index];
135
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Plugin system public API
3
+ */
4
+ export { createCommandContext, createEnvironmentInfo, } from "./context.js";
5
+ export { composePlugins, createPlugin, createTestPlugin } from "./create.js";
6
+ export { createPluginManager, getPlugin, getPlugins, loadPlugins, runAfterCommand, runBeforeCommand, runConfigResolved, runSetup, } from "./manager.js";
7
+ export { combinePluginStores, createPluginStore, createPluginStoreWithLogging, type PluginStore, type PluginStoreState, } from "./store.js";
8
+ export { assertPluginBehavior, createMockCommandContext, createMockPluginContext, testPluginHooks, } from "./testing.js";
9
+ export type { CommandContext as ICommandContext, CommandResult, EnvironmentInfo, MergePluginStores, MergeStores, Middleware, PathInfo, Plugin, PluginConfig, PluginContext as IPluginContext, PluginFactory, PluginHooks, } from "./types.js";
10
+ export * from "./types.js";
@@ -0,0 +1,27 @@
1
+ export {
2
+ createCommandContext,
3
+ createEnvironmentInfo
4
+ } from "./context.js";
5
+ export { composePlugins, createPlugin, createTestPlugin } from "./create.js";
6
+ export {
7
+ createPluginManager,
8
+ getPlugin,
9
+ getPlugins,
10
+ loadPlugins,
11
+ runAfterCommand,
12
+ runBeforeCommand,
13
+ runConfigResolved,
14
+ runSetup
15
+ } from "./manager.js";
16
+ export {
17
+ combinePluginStores,
18
+ createPluginStore,
19
+ createPluginStoreWithLogging
20
+ } from "./store.js";
21
+ export {
22
+ assertPluginBehavior,
23
+ createMockCommandContext,
24
+ createMockPluginContext,
25
+ testPluginHooks
26
+ } from "./testing.js";
27
+ export * from "./types.js";