kist 0.1.45 → 0.1.58

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 (38) hide show
  1. package/js/actions/DirectoryCleanAction/DirectoryCleanAction.js +2 -2
  2. package/js/actions/SvgPackagerAction/SvgPackagerAction.js +2 -2
  3. package/js/actions/TemplateRenderAction/TemplateRenderAction.js +1 -1
  4. package/js/actions/VersionWriteAction/VersionWriteAction.js +2 -2
  5. package/js/config/actions.config.d.ts +35 -0
  6. package/js/config/actions.config.js +119 -0
  7. package/js/core/config/ConfigLoader.js +1 -30
  8. package/js/core/pipeline/Action.d.ts +1 -1
  9. package/js/core/pipeline/Action.js +1 -1
  10. package/js/core/pipeline/ActionRegistry.d.ts +5 -1
  11. package/js/core/pipeline/ActionRegistry.js +18 -27
  12. package/js/core/pipeline/Step.js +1 -1
  13. package/js/core/plugin/PluginManager.d.ts +70 -0
  14. package/js/core/plugin/PluginManager.js +288 -0
  15. package/js/index.d.ts +7 -0
  16. package/js/index.js +14 -1
  17. package/js/interface/ActionPlugin.d.ts +43 -0
  18. package/js/interface/PluginMetadata.d.ts +33 -0
  19. package/js/interface/PluginMetadata.js +5 -0
  20. package/js/kist.js +25 -4
  21. package/package.json +17 -9
  22. package/ts/actions/DirectoryCleanAction/DirectoryCleanAction.ts +2 -2
  23. package/ts/actions/StyleProcessingAction/postcss.config.expanded.ts +0 -1
  24. package/ts/actions/SvgPackagerAction/SvgPackagerAction.ts +2 -2
  25. package/ts/actions/TemplateRenderAction/TemplateRenderAction.ts +1 -1
  26. package/ts/actions/VersionWriteAction/VersionWriteAction.ts +6 -2
  27. package/ts/config/actions.config.ts +137 -0
  28. package/ts/core/config/ConfigLoader.ts +1 -35
  29. package/ts/core/config/ConfigStore copy.ts +27 -1
  30. package/ts/core/pipeline/Action.ts +1 -1
  31. package/ts/core/pipeline/ActionRegistry.ts +22 -36
  32. package/ts/core/pipeline/Step.ts +1 -1
  33. package/ts/core/plugin/PluginManager.ts +310 -0
  34. package/ts/index.ts +25 -0
  35. package/ts/interface/ActionPlugin.ts +48 -1
  36. package/ts/interface/PluginMetadata.ts +43 -0
  37. package/ts/interface/SVG.ts +2 -0
  38. package/ts/kist.ts +25 -2
@@ -0,0 +1,137 @@
1
+ // ============================================================================
2
+ // Core Actions Configuration
3
+ // ============================================================================
4
+
5
+ /**
6
+ * Defines which actions are considered "core" and will remain in the main
7
+ * kist package. All other actions should be moved to separate plugin packages.
8
+ *
9
+ * Core Actions (Lightweight essentials):
10
+ * - Directory operations (create, copy, clean)
11
+ * - File operations (copy, rename)
12
+ * - Version management
13
+ * - Template rendering (lightweight)
14
+ *
15
+ * Plugin Actions (Move to separate repos):
16
+ * - StyleProcessingAction -> @getkist/action-sass
17
+ * - TypeScriptCompilerAction -> @getkist/action-typescript
18
+ * - JavaScriptMinifyAction -> @getkist/action-terser
19
+ * - SvgPackagerAction -> @getkist/action-svg
20
+ * - SvgSpriteAction -> @getkist/action-svg
21
+ * - SvgToPngAction -> @getkist/action-svg
22
+ * - SvgReaderAction -> @getkist/action-svg
23
+ * - LintAction -> @getkist/action-lint
24
+ * - DocumentationAction -> @getkist/action-docs
25
+ * - PackageManagerAction -> @getkist/action-package-manager
26
+ * - RunScriptAction -> @getkist/action-scripts
27
+ */
28
+
29
+ export const CORE_ACTIONS = [
30
+ "DirectoryCleanAction",
31
+ "DirectoryCopyAction",
32
+ "DirectoryCreateAction",
33
+ "FileCopyAction",
34
+ "FileRenameAction",
35
+ "TemplateRenderAction",
36
+ "VersionWriteAction",
37
+ ] as const;
38
+
39
+ export const PLUGIN_ACTIONS = [
40
+ // @getkist/action-sass
41
+ "StyleProcessingAction",
42
+
43
+ // @getkist/action-typescript
44
+ "TypeScriptCompilerAction",
45
+
46
+ // @getkist/action-terser
47
+ "JavaScriptMinifyAction",
48
+
49
+ // @getkist/action-svg
50
+ "SvgPackagerAction",
51
+ "SvgReaderAction",
52
+ "SvgSpriteAction",
53
+ "SvgToPngAction",
54
+
55
+ // @getkist/action-lint
56
+ "LintAction",
57
+
58
+ // @getkist/action-docs
59
+ "DocumentationAction",
60
+
61
+ // @getkist/action-package-manager
62
+ "PackageManagerAction",
63
+
64
+ // @getkist/action-scripts
65
+ "RunScriptAction",
66
+ ] as const;
67
+
68
+ export type CoreActionName = (typeof CORE_ACTIONS)[number];
69
+ export type PluginActionName = (typeof PLUGIN_ACTIONS)[number];
70
+
71
+ /**
72
+ * Plugin package mappings for migration guide
73
+ */
74
+ export const PLUGIN_PACKAGES: Record<
75
+ PluginActionName,
76
+ {
77
+ package: string;
78
+ npm: string;
79
+ github: string;
80
+ }
81
+ > = {
82
+ StyleProcessingAction: {
83
+ package: "@getkist/action-sass",
84
+ npm: "npm install --save-dev @getkist/action-sass",
85
+ github: "https://github.com/getkist/kist-action-sass",
86
+ },
87
+ TypeScriptCompilerAction: {
88
+ package: "@getkist/action-typescript",
89
+ npm: "npm install --save-dev @getkist/action-typescript",
90
+ github: "https://github.com/getkist/kist-action-typescript",
91
+ },
92
+ JavaScriptMinifyAction: {
93
+ package: "@getkist/action-terser",
94
+ npm: "npm install --save-dev @getkist/action-terser",
95
+ github: "https://github.com/getkist/kist-action-terser",
96
+ },
97
+ SvgPackagerAction: {
98
+ package: "@getkist/action-svg",
99
+ npm: "npm install --save-dev @getkist/action-svg",
100
+ github: "https://github.com/getkist/kist-action-svg",
101
+ },
102
+ SvgReaderAction: {
103
+ package: "@getkist/action-svg",
104
+ npm: "npm install --save-dev @getkist/action-svg",
105
+ github: "https://github.com/getkist/kist-action-svg",
106
+ },
107
+ SvgSpriteAction: {
108
+ package: "@getkist/action-svg",
109
+ npm: "npm install --save-dev @getkist/action-svg",
110
+ github: "https://github.com/getkist/kist-action-svg",
111
+ },
112
+ SvgToPngAction: {
113
+ package: "@getkist/action-svg",
114
+ npm: "npm install --save-dev @getkist/action-svg",
115
+ github: "https://github.com/getkist/kist-action-svg",
116
+ },
117
+ LintAction: {
118
+ package: "@getkist/action-lint",
119
+ npm: "npm install --save-dev @getkist/action-lint",
120
+ github: "https://github.com/getkist/kist-action-lint",
121
+ },
122
+ DocumentationAction: {
123
+ package: "@getkist/action-docs",
124
+ npm: "npm install --save-dev @getkist/action-docs",
125
+ github: "https://github.com/getkist/kist-action-docs",
126
+ },
127
+ PackageManagerAction: {
128
+ package: "@getkist/action-package-manager",
129
+ npm: "npm install --save-dev @getkist/action-package-manager",
130
+ github: "https://github.com/getkist/kist-action-package-manager",
131
+ },
132
+ RunScriptAction: {
133
+ package: "@getkist/action-scripts",
134
+ npm: "npm install --save-dev @getkist/action-scripts",
135
+ github: "https://github.com/getkist/kist-action-scripts",
136
+ },
137
+ };
@@ -71,7 +71,7 @@ export class ConfigLoader extends AbstractProcess {
71
71
  this.configPath = resolvedPath;
72
72
  this.logDebug(`Configuration file found: ${resolvedPath}`);
73
73
  return;
74
- } catch (error) {
74
+ } catch (_error) {
75
75
  this.logDebug(`File not accessible: ${resolvedPath}`);
76
76
 
77
77
  // ❗ If user explicitly provided --config and it fails, stop immediately
@@ -87,40 +87,6 @@ export class ConfigLoader extends AbstractProcess {
87
87
  "No configuration file found. Proceeding with default settings.",
88
88
  );
89
89
  }
90
- // public async initialize(): Promise<void> {
91
- // const parser = new ArgumentParser();
92
- // const cliFlags = parser.getAllFlags();
93
- // const cliPath =
94
- // typeof cliFlags.config === "string" ? cliFlags.config : undefined;
95
-
96
- // const searchPaths = cliPath ? [cliPath] : this.defaultFilenames;
97
-
98
- // this.logDebug(`Current working directory: ${process.cwd()}`);
99
- // this.logDebug(
100
- // `Searching for config file${cliPath ? ` from --config=${cliPath}` : ""}...`,
101
- // );
102
-
103
- // for (const fileName of searchPaths) {
104
- // const resolvedPath = path.resolve(process.cwd(), fileName);
105
- // this.logDebug(`Checking: ${resolvedPath}`);
106
-
107
- // try {
108
- // await fs.promises.access(
109
- // resolvedPath,
110
- // fs.constants.F_OK | fs.constants.R_OK,
111
- // );
112
- // this.configPath = resolvedPath;
113
- // this.logDebug(`Configuration file found: ${resolvedPath}`);
114
- // return;
115
- // } catch (error) {
116
- // this.logDebug(`File not accessible: ${resolvedPath}`);
117
- // }
118
- // }
119
-
120
- // this.logWarn(
121
- // "No configuration file found. Proceeding with default settings.",
122
- // );
123
- // }
124
90
 
125
91
  /**
126
92
  * Loads and validates the configuration file.
@@ -97,13 +97,39 @@ export class ConfigStore extends AbstractProcess {
97
97
 
98
98
  for (let i = 0; i < keys.length - 1; i++) {
99
99
  const k = keys[i];
100
+
101
+ // Guard against prototype pollution
102
+ if (
103
+ k === "__proto__" ||
104
+ k === "constructor" ||
105
+ k === "prototype"
106
+ ) {
107
+ this.logWarn(
108
+ `Attempted to set protected key "${k}". This operation is blocked for security reasons.`,
109
+ );
110
+ return;
111
+ }
112
+
100
113
  if (!current[k] || typeof current[k] !== "object") {
101
114
  current[k] = {};
102
115
  }
103
116
  current = current[k];
104
117
  }
105
118
 
106
- current[keys[keys.length - 1]] = value;
119
+ // Guard against prototype pollution for the final key
120
+ const finalKey = keys[keys.length - 1];
121
+ if (
122
+ finalKey === "__proto__" ||
123
+ finalKey === "constructor" ||
124
+ finalKey === "prototype"
125
+ ) {
126
+ this.logWarn(
127
+ `Attempted to set protected key "${finalKey}". This operation is blocked for security reasons.`,
128
+ );
129
+ return;
130
+ }
131
+
132
+ current[finalKey] = value;
107
133
  this.logDebug(
108
134
  `Set configuration key "${key}" to: ${JSON.stringify(value)}`,
109
135
  );
@@ -55,7 +55,7 @@ export abstract class Action
55
55
  * @returns A boolean indicating whether the options are valid. Default
56
56
  * implementation always returns true.
57
57
  */
58
- validateOptions(options: ActionOptionsType): boolean {
58
+ validateOptions(_options: ActionOptionsType): boolean {
59
59
  // Default validation: always returns true, can be overridden in
60
60
  // derived classes
61
61
  return true;
@@ -2,12 +2,10 @@
2
2
  // Import
3
3
  // ============================================================================
4
4
 
5
- import { readdirSync } from "fs";
6
- import { join } from "path";
7
5
  import { coreActions } from "../../actions/CoreActions";
8
6
  import { ActionInterface } from "../../interface/ActionInterface";
9
- import { ActionPlugin } from "../../interface/ActionPlugin";
10
7
  import { AbstractProcess } from "../abstract/AbstractProcess";
8
+ import { PluginManager } from "../plugin/PluginManager";
11
9
 
12
10
  // ============================================================================
13
11
  // Class
@@ -46,7 +44,8 @@ export class ActionRegistry extends AbstractProcess {
46
44
  this.registry = new Map();
47
45
  // Automatically register core actions
48
46
  this.registerCoreActions();
49
- this.discoverPlugins();
47
+ // Register plugin actions via PluginManager
48
+ this.registerPluginActions();
50
49
  this.logInfo("ActionRegistry initialized.");
51
50
  }
52
51
 
@@ -169,40 +168,27 @@ export class ActionRegistry extends AbstractProcess {
169
168
  this.logInfo("Core actions registered successfully.");
170
169
  }
171
170
 
172
- private discoverPlugins(): void {
173
- this.logInfo("Discovering external plugins...");
174
-
175
- const nodeModulesPath = join(process.cwd(), "node_modules");
176
- const pluginPrefix = "@kist/plugin-";
177
-
178
- try {
179
- const directories = readdirSync(nodeModulesPath, {
180
- withFileTypes: true,
181
- });
182
-
183
- for (const dir of directories) {
184
- if (dir.isDirectory() && dir.name.startsWith(pluginPrefix)) {
185
- const pluginPath = join(nodeModulesPath, dir.name);
186
- const plugin: ActionPlugin = require(pluginPath).default;
187
-
188
- if (
189
- plugin &&
190
- typeof plugin.registerActions === "function"
191
- ) {
192
- const actions = plugin.registerActions();
193
- for (const [name, actionClass] of Object.entries(
194
- actions,
195
- )) {
196
- this.registerAction(actionClass);
197
- }
198
- }
199
- }
171
+ /**
172
+ * Registers actions from loaded plugins via PluginManager.
173
+ * This method integrates the plugin system with the action registry.
174
+ */
175
+ private registerPluginActions(): void {
176
+ const pluginManager = PluginManager.getInstance();
177
+ const pluginActions = pluginManager.getPluginActions();
178
+
179
+ for (const [actionName, actionClass] of pluginActions.entries()) {
180
+ try {
181
+ this.registerAction(actionClass);
182
+ this.logDebug(`Registered plugin action: ${actionName}`);
183
+ } catch (error) {
184
+ this.logError(
185
+ `Failed to register plugin action ${actionName}:`,
186
+ error,
187
+ );
200
188
  }
201
-
202
- this.logInfo("Plugins loaded successfully.");
203
- } catch (error) {
204
- this.logError("Failed to discover plugins.", error);
205
189
  }
190
+
191
+ this.logInfo(`Registered ${pluginActions.size} actions from plugins.`);
206
192
  }
207
193
 
208
194
  /**
@@ -46,7 +46,7 @@ export class Step extends AbstractProcess {
46
46
  // const ActionClass = actionRegistry.getAction(step.action.name);
47
47
  const ActionClass = actionRegistry.getAction(String(step.action));
48
48
  if (!ActionClass) {
49
- let msg = `
49
+ const msg = `
50
50
  Unknown action "${step.action}" for step "${this.name}".
51
51
  Ensure the action is registered in the registry.
52
52
  `;
@@ -0,0 +1,310 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { readdirSync } from "fs";
6
+ import { join } from "path";
7
+ import { ActionInterface } from "../../interface/ActionInterface";
8
+ import { ActionPlugin } from "../../interface/ActionPlugin";
9
+ import { PluginMetadata } from "../../interface/PluginMetadata";
10
+ import { AbstractProcess } from "../abstract/AbstractProcess";
11
+
12
+ // ============================================================================
13
+ // Class
14
+ // ============================================================================
15
+
16
+ /**
17
+ * PluginManager handles discovery, loading, and lifecycle of kist plugins.
18
+ * Supports both npm-installed plugins (@getkist/plugin-*) and local plugins.
19
+ */
20
+ export class PluginManager extends AbstractProcess {
21
+ // Parameters
22
+ // ========================================================================
23
+
24
+ private static instance: PluginManager | null = null;
25
+ private loadedPlugins: Map<string, PluginMetadata> = new Map();
26
+ private pluginActions: Map<string, new () => ActionInterface> = new Map();
27
+
28
+ // Constructor
29
+ // ========================================================================
30
+
31
+ private constructor() {
32
+ super();
33
+ this.logInfo("PluginManager initialized.");
34
+ }
35
+
36
+ // Singleton Methods
37
+ // ========================================================================
38
+
39
+ public static getInstance(): PluginManager {
40
+ if (!PluginManager.instance) {
41
+ PluginManager.instance = new PluginManager();
42
+ }
43
+ return PluginManager.instance;
44
+ }
45
+
46
+ public static resetInstance(): void {
47
+ PluginManager.instance = null;
48
+ }
49
+
50
+ // Plugin Discovery
51
+ // ========================================================================
52
+
53
+ /**
54
+ * Discovers and loads all available plugins from:
55
+ * - node_modules/@getkist/plugin-*
56
+ * - node_modules/kist-plugin-*
57
+ * - Local plugins directory (if configured)
58
+ */
59
+ public async discoverPlugins(options?: {
60
+ localPluginsPath?: string;
61
+ pluginPrefixes?: string[];
62
+ }): Promise<void> {
63
+ this.logInfo("Starting plugin discovery...");
64
+
65
+ const prefixes = options?.pluginPrefixes || [
66
+ "@getkist/action-",
67
+ "kist-plugin-",
68
+ ];
69
+
70
+ // Discover npm plugins
71
+ await this.discoverNpmPlugins(prefixes);
72
+
73
+ // Discover local plugins if path provided
74
+ if (options?.localPluginsPath) {
75
+ await this.discoverLocalPlugins(options.localPluginsPath);
76
+ }
77
+
78
+ this.logInfo(
79
+ `Plugin discovery complete. Loaded ${this.loadedPlugins.size} plugins.`,
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Discovers plugins installed via npm with specified prefixes
85
+ */
86
+ private async discoverNpmPlugins(prefixes: string[]): Promise<void> {
87
+ const nodeModulesPath = join(process.cwd(), "node_modules");
88
+
89
+ try {
90
+ const directories = readdirSync(nodeModulesPath, {
91
+ withFileTypes: true,
92
+ });
93
+
94
+ for (const dir of directories) {
95
+ // Check for scoped packages (@getkist/plugin-*)
96
+ if (dir.isDirectory() && dir.name.startsWith("@")) {
97
+ await this.discoverScopedPlugins(
98
+ join(nodeModulesPath, dir.name),
99
+ prefixes,
100
+ );
101
+ }
102
+
103
+ // Check for non-scoped packages (kist-plugin-*)
104
+ for (const prefix of prefixes) {
105
+ if (
106
+ dir.isDirectory() &&
107
+ !prefix.startsWith("@") &&
108
+ dir.name.startsWith(prefix)
109
+ ) {
110
+ await this.loadPlugin(
111
+ join(nodeModulesPath, dir.name),
112
+ dir.name,
113
+ );
114
+ }
115
+ }
116
+ }
117
+ } catch (error) {
118
+ this.logError("Failed to discover npm plugins.", error);
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Discovers plugins in scoped packages (@getkist/*)
124
+ */
125
+ private async discoverScopedPlugins(
126
+ scopePath: string,
127
+ prefixes: string[],
128
+ ): Promise<void> {
129
+ try {
130
+ const packages = readdirSync(scopePath, { withFileTypes: true });
131
+
132
+ for (const pkg of packages) {
133
+ for (const prefix of prefixes) {
134
+ const scopePrefix = prefix.split("/")[1]; // Extract "plugin-" from "@getkist/plugin-"
135
+ if (
136
+ pkg.isDirectory() &&
137
+ scopePrefix &&
138
+ pkg.name.startsWith(scopePrefix)
139
+ ) {
140
+ const fullName = `${scopePath.split("/").pop()}/${pkg.name}`;
141
+ await this.loadPlugin(
142
+ join(scopePath, pkg.name),
143
+ fullName,
144
+ );
145
+ }
146
+ }
147
+ }
148
+ } catch (_error) {
149
+ this.logDebug(`No scoped plugins found in ${scopePath}`);
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Discovers plugins from a local directory
155
+ */
156
+ private async discoverLocalPlugins(localPath: string): Promise<void> {
157
+ try {
158
+ const pluginPath = join(process.cwd(), localPath);
159
+ const directories = readdirSync(pluginPath, {
160
+ withFileTypes: true,
161
+ });
162
+
163
+ for (const dir of directories) {
164
+ if (dir.isDirectory()) {
165
+ await this.loadPlugin(
166
+ join(pluginPath, dir.name),
167
+ `local:${dir.name}`,
168
+ );
169
+ }
170
+ }
171
+ } catch (_error) {
172
+ this.logDebug(`No local plugins found at ${localPath}`);
173
+ }
174
+ }
175
+
176
+ // Plugin Loading
177
+ // ========================================================================
178
+
179
+ /**
180
+ * Loads a single plugin from the specified path
181
+ */
182
+ private async loadPlugin(
183
+ pluginPath: string,
184
+ pluginName: string,
185
+ ): Promise<void> {
186
+ try {
187
+ this.logDebug(`Loading plugin: ${pluginName}`);
188
+
189
+ const pluginModule = await import(pluginPath);
190
+ const plugin: ActionPlugin = pluginModule.default || pluginModule;
191
+
192
+ if (!plugin || typeof plugin.registerActions !== "function") {
193
+ this.logWarn(
194
+ `Plugin ${pluginName} does not implement ActionPlugin interface.`,
195
+ );
196
+ return;
197
+ }
198
+
199
+ // Get plugin metadata if available
200
+ const metadata: PluginMetadata = {
201
+ name: pluginName,
202
+ version:
203
+ typeof plugin.version === "string"
204
+ ? plugin.version
205
+ : "unknown",
206
+ description:
207
+ typeof plugin.description === "string"
208
+ ? plugin.description
209
+ : undefined,
210
+ actions: [],
211
+ };
212
+
213
+ // Register actions from the plugin
214
+ const actions = plugin.registerActions();
215
+ for (const [actionName, actionClass] of Object.entries(actions)) {
216
+ this.pluginActions.set(actionName, actionClass);
217
+ metadata.actions.push(actionName);
218
+ this.logDebug(` - Registered action: ${actionName}`);
219
+ }
220
+
221
+ this.loadedPlugins.set(pluginName, metadata);
222
+ this.logInfo(
223
+ `Plugin "${pluginName}" loaded successfully with ${metadata.actions.length} actions.`,
224
+ );
225
+ } catch (error) {
226
+ this.logError(`Failed to load plugin ${pluginName}:`, error);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Manually register a plugin programmatically
232
+ */
233
+ public registerPlugin(plugin: ActionPlugin, name: string): void {
234
+ this.logInfo(`Manually registering plugin: ${name}`);
235
+
236
+ const metadata: PluginMetadata = {
237
+ name,
238
+ version:
239
+ typeof plugin.version === "string"
240
+ ? plugin.version
241
+ : "unknown",
242
+ description:
243
+ typeof plugin.description === "string"
244
+ ? plugin.description
245
+ : undefined,
246
+ actions: [],
247
+ };
248
+
249
+ const actions = plugin.registerActions();
250
+ for (const [actionName, actionClass] of Object.entries(actions)) {
251
+ this.pluginActions.set(actionName, actionClass);
252
+ metadata.actions.push(actionName);
253
+ }
254
+
255
+ this.loadedPlugins.set(name, metadata);
256
+ this.logInfo(
257
+ `Plugin "${name}" registered with ${metadata.actions.length} actions.`,
258
+ );
259
+ }
260
+
261
+ // Plugin Queries
262
+ // ========================================================================
263
+
264
+ /**
265
+ * Gets all actions from loaded plugins
266
+ */
267
+ public getPluginActions(): Map<string, new () => ActionInterface> {
268
+ return new Map(this.pluginActions);
269
+ }
270
+
271
+ /**
272
+ * Gets metadata for all loaded plugins
273
+ */
274
+ public getLoadedPlugins(): PluginMetadata[] {
275
+ return Array.from(this.loadedPlugins.values());
276
+ }
277
+
278
+ /**
279
+ * Gets metadata for a specific plugin
280
+ */
281
+ public getPluginMetadata(name: string): PluginMetadata | undefined {
282
+ return this.loadedPlugins.get(name);
283
+ }
284
+
285
+ /**
286
+ * Checks if a specific plugin is loaded
287
+ */
288
+ public isPluginLoaded(name: string): boolean {
289
+ return this.loadedPlugins.has(name);
290
+ }
291
+
292
+ /**
293
+ * Lists all action names from plugins
294
+ */
295
+ public listPluginActions(): string[] {
296
+ return Array.from(this.pluginActions.keys());
297
+ }
298
+
299
+ // Cleanup
300
+ // ========================================================================
301
+
302
+ /**
303
+ * Clears all loaded plugins and their actions
304
+ */
305
+ public clearPlugins(): void {
306
+ this.loadedPlugins.clear();
307
+ this.pluginActions.clear();
308
+ this.logInfo("All plugins cleared.");
309
+ }
310
+ }
package/ts/index.ts CHANGED
@@ -22,3 +22,28 @@ export * from "./types";
22
22
 
23
23
  // CLI Functions (if required programmatically)
24
24
  export * from "./cli.js";
25
+
26
+ // ============================================================================
27
+ // Plugin System Exports
28
+ // ============================================================================
29
+
30
+ // Core plugin interfaces and types
31
+ export { ActionInterface } from "./interface/ActionInterface";
32
+ export { ActionPlugin } from "./interface/ActionPlugin";
33
+ export { PluginMetadata } from "./interface/PluginMetadata";
34
+
35
+ // Plugin management
36
+ export { PluginManager } from "./core/plugin/PluginManager";
37
+
38
+ // Action system
39
+ export { Action } from "./core/pipeline/Action";
40
+ export { ActionRegistry } from "./core/pipeline/ActionRegistry";
41
+
42
+ // Configuration for plugin developers
43
+ export {
44
+ CORE_ACTIONS,
45
+ PLUGIN_ACTIONS,
46
+ PLUGIN_PACKAGES,
47
+ type CoreActionName,
48
+ type PluginActionName,
49
+ } from "./config/actions.config";