@sparkleideas/plugins 3.0.0-alpha.10
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.
- package/README.md +401 -0
- package/__tests__/collection-manager.test.ts +332 -0
- package/__tests__/dependency-graph.test.ts +434 -0
- package/__tests__/enhanced-plugin-registry.test.ts +488 -0
- package/__tests__/plugin-registry.test.ts +368 -0
- package/__tests__/ruvector-bridge.test.ts +2429 -0
- package/__tests__/ruvector-integration.test.ts +1602 -0
- package/__tests__/ruvector-migrations.test.ts +1099 -0
- package/__tests__/ruvector-quantization.test.ts +846 -0
- package/__tests__/ruvector-streaming.test.ts +1088 -0
- package/__tests__/sdk.test.ts +325 -0
- package/__tests__/security.test.ts +348 -0
- package/__tests__/utils/ruvector-test-utils.ts +860 -0
- package/examples/plugin-creator/index.ts +636 -0
- package/examples/plugin-creator/plugin-creator.test.ts +312 -0
- package/examples/ruvector/README.md +288 -0
- package/examples/ruvector/attention-patterns.ts +394 -0
- package/examples/ruvector/basic-usage.ts +288 -0
- package/examples/ruvector/docker-compose.yml +75 -0
- package/examples/ruvector/gnn-analysis.ts +501 -0
- package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
- package/examples/ruvector/init-db.sql +119 -0
- package/examples/ruvector/quantization.ts +680 -0
- package/examples/ruvector/self-learning.ts +447 -0
- package/examples/ruvector/semantic-search.ts +576 -0
- package/examples/ruvector/streaming-large-data.ts +507 -0
- package/examples/ruvector/transactions.ts +594 -0
- package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
- package/examples/ruvector-plugins/index.ts +79 -0
- package/examples/ruvector-plugins/intent-router.ts +354 -0
- package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
- package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
- package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
- package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
- package/examples/ruvector-plugins/shared/index.ts +20 -0
- package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
- package/examples/ruvector-plugins/sona-learning.ts +445 -0
- package/package.json +97 -0
- package/src/collections/collection-manager.ts +661 -0
- package/src/collections/index.ts +56 -0
- package/src/collections/official/index.ts +1040 -0
- package/src/core/base-plugin.ts +416 -0
- package/src/core/plugin-interface.ts +215 -0
- package/src/hooks/index.ts +685 -0
- package/src/index.ts +378 -0
- package/src/integrations/agentic-flow.ts +743 -0
- package/src/integrations/index.ts +88 -0
- package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
- package/src/integrations/ruvector/attention-advanced.ts +1040 -0
- package/src/integrations/ruvector/attention-executor.ts +782 -0
- package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
- package/src/integrations/ruvector/attention.ts +1063 -0
- package/src/integrations/ruvector/gnn.ts +3050 -0
- package/src/integrations/ruvector/hyperbolic.ts +1948 -0
- package/src/integrations/ruvector/index.ts +394 -0
- package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
- package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
- package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
- package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
- package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
- package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
- package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
- package/src/integrations/ruvector/migrations/index.ts +35 -0
- package/src/integrations/ruvector/migrations/migrations.ts +647 -0
- package/src/integrations/ruvector/quantization.ts +2036 -0
- package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
- package/src/integrations/ruvector/self-learning.ts +2376 -0
- package/src/integrations/ruvector/streaming.ts +1737 -0
- package/src/integrations/ruvector/types.ts +1945 -0
- package/src/providers/index.ts +643 -0
- package/src/registry/dependency-graph.ts +568 -0
- package/src/registry/enhanced-plugin-registry.ts +994 -0
- package/src/registry/plugin-registry.ts +604 -0
- package/src/sdk/index.ts +563 -0
- package/src/security/index.ts +594 -0
- package/src/types/index.ts +446 -0
- package/src/workers/index.ts +700 -0
- package/tmp.json +0 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +23 -0
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Collection Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages collections of plugins with activation/deactivation,
|
|
5
|
+
* category filtering, and state persistence.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { IPlugin, PluginFactory } from '../core/plugin-interface.js';
|
|
9
|
+
import type { PluginConfig } from '../types/index.js';
|
|
10
|
+
import type { PluginRegistry } from '../registry/plugin-registry.js';
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Types
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
export type PluginCategory =
|
|
17
|
+
| 'agent' // Agent types and definitions
|
|
18
|
+
| 'task' // Task types and handlers
|
|
19
|
+
| 'tool' // MCP tools
|
|
20
|
+
| 'memory' // Memory backends
|
|
21
|
+
| 'provider' // LLM providers
|
|
22
|
+
| 'hook' // Lifecycle hooks
|
|
23
|
+
| 'worker' // Background workers
|
|
24
|
+
| 'integration' // External integrations
|
|
25
|
+
| 'database' // Database backends and integrations
|
|
26
|
+
| 'utility'; // General utilities
|
|
27
|
+
|
|
28
|
+
export type PluginCapability =
|
|
29
|
+
| 'network' // Can make network requests
|
|
30
|
+
| 'filesystem' // Can access filesystem
|
|
31
|
+
| 'subprocess' // Can spawn processes
|
|
32
|
+
| 'memory' // Can store persistent data
|
|
33
|
+
| 'database' // Can access databases
|
|
34
|
+
| 'llm' // Can call LLM APIs
|
|
35
|
+
| 'mcp'; // Can register MCP tools
|
|
36
|
+
|
|
37
|
+
export interface PluginCollectionEntry {
|
|
38
|
+
readonly plugin: IPlugin | PluginFactory;
|
|
39
|
+
readonly defaultEnabled: boolean;
|
|
40
|
+
readonly category: PluginCategory;
|
|
41
|
+
readonly tags?: string[];
|
|
42
|
+
readonly requiredCapabilities?: PluginCapability[];
|
|
43
|
+
readonly description?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface PluginCollection {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly name: string;
|
|
49
|
+
readonly version: string;
|
|
50
|
+
readonly description?: string;
|
|
51
|
+
readonly author?: string;
|
|
52
|
+
readonly license?: string;
|
|
53
|
+
readonly repository?: string;
|
|
54
|
+
readonly categories?: PluginCategory[];
|
|
55
|
+
readonly plugins: PluginCollectionEntry[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface CollectionManagerState {
|
|
59
|
+
version: string;
|
|
60
|
+
collections: string[];
|
|
61
|
+
enabledPlugins: Record<string, string[]>; // collectionId -> pluginNames
|
|
62
|
+
disabledPlugins: Record<string, string[]>; // collectionId -> pluginNames (overrides default)
|
|
63
|
+
pluginSettings: Record<string, Record<string, unknown>>; // pluginName -> settings
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CollectionManagerConfig {
|
|
67
|
+
registry: PluginRegistry;
|
|
68
|
+
autoInitialize?: boolean;
|
|
69
|
+
stateFile?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface CollectionStats {
|
|
73
|
+
totalCollections: number;
|
|
74
|
+
totalPlugins: number;
|
|
75
|
+
enabledPlugins: number;
|
|
76
|
+
disabledPlugins: number;
|
|
77
|
+
byCategory: Record<PluginCategory, number>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Internal resolved entry with plugin name cached
|
|
81
|
+
interface ResolvedEntry {
|
|
82
|
+
entry: PluginCollectionEntry;
|
|
83
|
+
pluginName: string;
|
|
84
|
+
plugin: IPlugin;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Collection Manager
|
|
89
|
+
// ============================================================================
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Manages plugin collections with activation/deactivation and persistence.
|
|
93
|
+
*
|
|
94
|
+
* Features:
|
|
95
|
+
* - Load/unload plugin collections
|
|
96
|
+
* - Enable/disable individual plugins
|
|
97
|
+
* - Category-based filtering
|
|
98
|
+
* - Tag-based search
|
|
99
|
+
* - State persistence
|
|
100
|
+
* - Bulk operations
|
|
101
|
+
*/
|
|
102
|
+
export class PluginCollectionManager {
|
|
103
|
+
private collections = new Map<string, PluginCollection>();
|
|
104
|
+
private resolvedEntries = new Map<string, ResolvedEntry[]>(); // collectionId -> entries
|
|
105
|
+
private enabledOverrides = new Map<string, Set<string>>(); // Explicitly enabled
|
|
106
|
+
private disabledOverrides = new Map<string, Set<string>>(); // Explicitly disabled
|
|
107
|
+
private pluginSettings = new Map<string, Record<string, unknown>>();
|
|
108
|
+
private registry: PluginRegistry;
|
|
109
|
+
private autoInitialize: boolean;
|
|
110
|
+
|
|
111
|
+
constructor(config: CollectionManagerConfig) {
|
|
112
|
+
this.registry = config.registry;
|
|
113
|
+
this.autoInitialize = config.autoInitialize ?? true;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// =========================================================================
|
|
117
|
+
// Collection Management
|
|
118
|
+
// =========================================================================
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Load a plugin collection.
|
|
122
|
+
*/
|
|
123
|
+
async loadCollection(collection: PluginCollection): Promise<void> {
|
|
124
|
+
if (this.collections.has(collection.id)) {
|
|
125
|
+
throw new Error(`Collection ${collection.id} already loaded`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Resolve all plugins upfront to cache names
|
|
129
|
+
const resolved: ResolvedEntry[] = [];
|
|
130
|
+
for (const entry of collection.plugins) {
|
|
131
|
+
const plugin = await this.resolvePlugin(entry.plugin);
|
|
132
|
+
resolved.push({
|
|
133
|
+
entry,
|
|
134
|
+
pluginName: plugin.metadata.name,
|
|
135
|
+
plugin,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.collections.set(collection.id, collection);
|
|
140
|
+
this.resolvedEntries.set(collection.id, resolved);
|
|
141
|
+
this.enabledOverrides.set(collection.id, new Set());
|
|
142
|
+
this.disabledOverrides.set(collection.id, new Set());
|
|
143
|
+
|
|
144
|
+
// Register enabled plugins
|
|
145
|
+
for (const { entry, pluginName, plugin } of resolved) {
|
|
146
|
+
if (this.isPluginEnabledSync(collection.id, pluginName, entry.defaultEnabled)) {
|
|
147
|
+
await this.registerResolvedPlugin(plugin, entry);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Unload a plugin collection.
|
|
154
|
+
*/
|
|
155
|
+
async unloadCollection(collectionId: string): Promise<void> {
|
|
156
|
+
const resolved = this.resolvedEntries.get(collectionId);
|
|
157
|
+
if (!resolved) {
|
|
158
|
+
throw new Error(`Collection ${collectionId} not found`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Unregister all plugins from this collection
|
|
162
|
+
for (const { pluginName } of resolved) {
|
|
163
|
+
if (this.registry.getPlugin(pluginName)) {
|
|
164
|
+
try {
|
|
165
|
+
await this.registry.unregister(pluginName);
|
|
166
|
+
} catch {
|
|
167
|
+
// Ignore errors during unload
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.collections.delete(collectionId);
|
|
173
|
+
this.resolvedEntries.delete(collectionId);
|
|
174
|
+
this.enabledOverrides.delete(collectionId);
|
|
175
|
+
this.disabledOverrides.delete(collectionId);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* List all loaded collections.
|
|
180
|
+
*/
|
|
181
|
+
listCollections(): PluginCollection[] {
|
|
182
|
+
return Array.from(this.collections.values());
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get a collection by ID.
|
|
187
|
+
*/
|
|
188
|
+
getCollection(id: string): PluginCollection | undefined {
|
|
189
|
+
return this.collections.get(id);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// =========================================================================
|
|
193
|
+
// Plugin Activation
|
|
194
|
+
// =========================================================================
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Enable a plugin from a collection.
|
|
198
|
+
*/
|
|
199
|
+
async enablePlugin(collectionId: string, pluginName: string): Promise<void> {
|
|
200
|
+
const resolved = this.findResolvedEntry(collectionId, pluginName);
|
|
201
|
+
if (!resolved) {
|
|
202
|
+
throw new Error(`Plugin ${pluginName} not found in collection ${collectionId}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Remove from disabled, add to enabled
|
|
206
|
+
this.disabledOverrides.get(collectionId)?.delete(pluginName);
|
|
207
|
+
this.enabledOverrides.get(collectionId)?.add(pluginName);
|
|
208
|
+
|
|
209
|
+
// Register if not already registered
|
|
210
|
+
if (!this.registry.getPlugin(pluginName)) {
|
|
211
|
+
await this.registerResolvedPlugin(resolved.plugin, resolved.entry);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Disable a plugin from a collection.
|
|
217
|
+
*/
|
|
218
|
+
async disablePlugin(collectionId: string, pluginName: string): Promise<void> {
|
|
219
|
+
const resolved = this.findResolvedEntry(collectionId, pluginName);
|
|
220
|
+
if (!resolved) {
|
|
221
|
+
throw new Error(`Plugin ${pluginName} not found in collection ${collectionId}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Remove from enabled, add to disabled
|
|
225
|
+
this.enabledOverrides.get(collectionId)?.delete(pluginName);
|
|
226
|
+
this.disabledOverrides.get(collectionId)?.add(pluginName);
|
|
227
|
+
|
|
228
|
+
// Unregister if registered
|
|
229
|
+
if (this.registry.getPlugin(pluginName)) {
|
|
230
|
+
try {
|
|
231
|
+
await this.registry.unregister(pluginName);
|
|
232
|
+
} catch {
|
|
233
|
+
// May fail if other plugins depend on it
|
|
234
|
+
throw new Error(`Cannot disable ${pluginName}: other plugins may depend on it`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Check if a plugin is enabled.
|
|
241
|
+
*/
|
|
242
|
+
isEnabled(collectionId: string, pluginName: string): boolean {
|
|
243
|
+
const resolved = this.findResolvedEntry(collectionId, pluginName);
|
|
244
|
+
if (!resolved) return false;
|
|
245
|
+
|
|
246
|
+
return this.isPluginEnabledSync(collectionId, pluginName, resolved.entry.defaultEnabled);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Toggle a plugin's enabled state.
|
|
251
|
+
*/
|
|
252
|
+
async togglePlugin(collectionId: string, pluginName: string): Promise<boolean> {
|
|
253
|
+
if (this.isEnabled(collectionId, pluginName)) {
|
|
254
|
+
await this.disablePlugin(collectionId, pluginName);
|
|
255
|
+
return false;
|
|
256
|
+
} else {
|
|
257
|
+
await this.enablePlugin(collectionId, pluginName);
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// =========================================================================
|
|
263
|
+
// Bulk Operations
|
|
264
|
+
// =========================================================================
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Enable all plugins in a collection.
|
|
268
|
+
*/
|
|
269
|
+
async enableAll(collectionId: string): Promise<void> {
|
|
270
|
+
const resolved = this.resolvedEntries.get(collectionId);
|
|
271
|
+
if (!resolved) {
|
|
272
|
+
throw new Error(`Collection ${collectionId} not found`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const { pluginName } of resolved) {
|
|
276
|
+
await this.enablePlugin(collectionId, pluginName);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Disable all plugins in a collection.
|
|
282
|
+
*/
|
|
283
|
+
async disableAll(collectionId: string): Promise<void> {
|
|
284
|
+
const resolved = this.resolvedEntries.get(collectionId);
|
|
285
|
+
if (!resolved) {
|
|
286
|
+
throw new Error(`Collection ${collectionId} not found`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Disable in reverse order to handle dependencies
|
|
290
|
+
const entries = [...resolved].reverse();
|
|
291
|
+
for (const { pluginName } of entries) {
|
|
292
|
+
try {
|
|
293
|
+
await this.disablePlugin(collectionId, pluginName);
|
|
294
|
+
} catch {
|
|
295
|
+
// Continue disabling others
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Enable all plugins in a category across all collections.
|
|
302
|
+
*/
|
|
303
|
+
async enableCategory(category: PluginCategory): Promise<void> {
|
|
304
|
+
for (const [collectionId, resolved] of this.resolvedEntries) {
|
|
305
|
+
for (const { pluginName, entry } of resolved) {
|
|
306
|
+
if (entry.category === category) {
|
|
307
|
+
try {
|
|
308
|
+
await this.enablePlugin(collectionId, pluginName);
|
|
309
|
+
} catch {
|
|
310
|
+
// Continue with others
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Disable all plugins in a category across all collections.
|
|
319
|
+
*/
|
|
320
|
+
async disableCategory(category: PluginCategory): Promise<void> {
|
|
321
|
+
for (const [collectionId, resolved] of this.resolvedEntries) {
|
|
322
|
+
const entries = [...resolved].reverse();
|
|
323
|
+
for (const { pluginName, entry } of entries) {
|
|
324
|
+
if (entry.category === category) {
|
|
325
|
+
try {
|
|
326
|
+
await this.disablePlugin(collectionId, pluginName);
|
|
327
|
+
} catch {
|
|
328
|
+
// Continue with others
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// =========================================================================
|
|
336
|
+
// Filtering & Search
|
|
337
|
+
// =========================================================================
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get all plugins by category.
|
|
341
|
+
*/
|
|
342
|
+
getPluginsByCategory(category: PluginCategory): Array<{
|
|
343
|
+
collectionId: string;
|
|
344
|
+
entry: PluginCollectionEntry;
|
|
345
|
+
enabled: boolean;
|
|
346
|
+
}> {
|
|
347
|
+
const results: Array<{
|
|
348
|
+
collectionId: string;
|
|
349
|
+
entry: PluginCollectionEntry;
|
|
350
|
+
enabled: boolean;
|
|
351
|
+
}> = [];
|
|
352
|
+
|
|
353
|
+
for (const [collectionId, resolved] of this.resolvedEntries) {
|
|
354
|
+
for (const { entry, pluginName } of resolved) {
|
|
355
|
+
if (entry.category === category) {
|
|
356
|
+
results.push({
|
|
357
|
+
collectionId,
|
|
358
|
+
entry,
|
|
359
|
+
enabled: this.isPluginEnabledSync(collectionId, pluginName, entry.defaultEnabled),
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return results;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Get all plugins by tag.
|
|
370
|
+
*/
|
|
371
|
+
getPluginsByTag(tag: string): Array<{
|
|
372
|
+
collectionId: string;
|
|
373
|
+
entry: PluginCollectionEntry;
|
|
374
|
+
enabled: boolean;
|
|
375
|
+
}> {
|
|
376
|
+
const results: Array<{
|
|
377
|
+
collectionId: string;
|
|
378
|
+
entry: PluginCollectionEntry;
|
|
379
|
+
enabled: boolean;
|
|
380
|
+
}> = [];
|
|
381
|
+
|
|
382
|
+
for (const [collectionId, resolved] of this.resolvedEntries) {
|
|
383
|
+
for (const { entry, pluginName } of resolved) {
|
|
384
|
+
if (entry.tags?.includes(tag)) {
|
|
385
|
+
results.push({
|
|
386
|
+
collectionId,
|
|
387
|
+
entry,
|
|
388
|
+
enabled: this.isPluginEnabledSync(collectionId, pluginName, entry.defaultEnabled),
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return results;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Search plugins by name or description.
|
|
399
|
+
*/
|
|
400
|
+
searchPlugins(query: string): Array<{
|
|
401
|
+
collectionId: string;
|
|
402
|
+
entry: PluginCollectionEntry;
|
|
403
|
+
pluginName: string;
|
|
404
|
+
enabled: boolean;
|
|
405
|
+
score: number;
|
|
406
|
+
}> {
|
|
407
|
+
const results: Array<{
|
|
408
|
+
collectionId: string;
|
|
409
|
+
entry: PluginCollectionEntry;
|
|
410
|
+
pluginName: string;
|
|
411
|
+
enabled: boolean;
|
|
412
|
+
score: number;
|
|
413
|
+
}> = [];
|
|
414
|
+
|
|
415
|
+
const queryLower = query.toLowerCase();
|
|
416
|
+
|
|
417
|
+
for (const [collectionId, resolved] of this.resolvedEntries) {
|
|
418
|
+
for (const { entry, pluginName, plugin } of resolved) {
|
|
419
|
+
const name = pluginName.toLowerCase();
|
|
420
|
+
const description = (entry.description ?? plugin.metadata.description ?? '').toLowerCase();
|
|
421
|
+
|
|
422
|
+
let score = 0;
|
|
423
|
+
|
|
424
|
+
// Exact name match
|
|
425
|
+
if (name === queryLower) score += 100;
|
|
426
|
+
// Name contains query
|
|
427
|
+
else if (name.includes(queryLower)) score += 50;
|
|
428
|
+
|
|
429
|
+
// Description contains query
|
|
430
|
+
if (description.includes(queryLower)) score += 25;
|
|
431
|
+
|
|
432
|
+
// Tag match
|
|
433
|
+
if (entry.tags?.some(t => t.toLowerCase().includes(queryLower))) {
|
|
434
|
+
score += 30;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Category match
|
|
438
|
+
if (entry.category.toLowerCase().includes(queryLower)) {
|
|
439
|
+
score += 20;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (score > 0) {
|
|
443
|
+
results.push({
|
|
444
|
+
collectionId,
|
|
445
|
+
entry,
|
|
446
|
+
pluginName,
|
|
447
|
+
enabled: this.isPluginEnabledSync(collectionId, pluginName, entry.defaultEnabled),
|
|
448
|
+
score,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Sort by score descending
|
|
455
|
+
return results.sort((a, b) => b.score - a.score);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// =========================================================================
|
|
459
|
+
// Settings
|
|
460
|
+
// =========================================================================
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Get settings for a plugin.
|
|
464
|
+
*/
|
|
465
|
+
getPluginSettings(pluginName: string): Record<string, unknown> {
|
|
466
|
+
return this.pluginSettings.get(pluginName) ?? {};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Set settings for a plugin.
|
|
471
|
+
*/
|
|
472
|
+
setPluginSettings(pluginName: string, settings: Record<string, unknown>): void {
|
|
473
|
+
this.pluginSettings.set(pluginName, settings);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Update settings for a plugin (merge with existing).
|
|
478
|
+
*/
|
|
479
|
+
updatePluginSettings(pluginName: string, settings: Record<string, unknown>): void {
|
|
480
|
+
const existing = this.pluginSettings.get(pluginName) ?? {};
|
|
481
|
+
this.pluginSettings.set(pluginName, { ...existing, ...settings });
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// =========================================================================
|
|
485
|
+
// State Persistence
|
|
486
|
+
// =========================================================================
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Export current state.
|
|
490
|
+
*/
|
|
491
|
+
exportState(): CollectionManagerState {
|
|
492
|
+
const enabledPlugins: Record<string, string[]> = {};
|
|
493
|
+
const disabledPlugins: Record<string, string[]> = {};
|
|
494
|
+
|
|
495
|
+
for (const [collectionId, enabled] of this.enabledOverrides) {
|
|
496
|
+
if (enabled.size > 0) {
|
|
497
|
+
enabledPlugins[collectionId] = Array.from(enabled);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
for (const [collectionId, disabled] of this.disabledOverrides) {
|
|
502
|
+
if (disabled.size > 0) {
|
|
503
|
+
disabledPlugins[collectionId] = Array.from(disabled);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const pluginSettingsObj: Record<string, Record<string, unknown>> = {};
|
|
508
|
+
for (const [name, settings] of this.pluginSettings) {
|
|
509
|
+
pluginSettingsObj[name] = settings;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return {
|
|
513
|
+
version: '1.0.0',
|
|
514
|
+
collections: Array.from(this.collections.keys()),
|
|
515
|
+
enabledPlugins,
|
|
516
|
+
disabledPlugins,
|
|
517
|
+
pluginSettings: pluginSettingsObj,
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Import state (does not load collections, just restores overrides).
|
|
523
|
+
*/
|
|
524
|
+
importState(state: CollectionManagerState): void {
|
|
525
|
+
// Restore enabled overrides
|
|
526
|
+
for (const [collectionId, plugins] of Object.entries(state.enabledPlugins)) {
|
|
527
|
+
this.enabledOverrides.set(collectionId, new Set(plugins));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Restore disabled overrides
|
|
531
|
+
for (const [collectionId, plugins] of Object.entries(state.disabledPlugins)) {
|
|
532
|
+
this.disabledOverrides.set(collectionId, new Set(plugins));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Restore plugin settings
|
|
536
|
+
for (const [name, settings] of Object.entries(state.pluginSettings)) {
|
|
537
|
+
this.pluginSettings.set(name, settings);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Save state to a JSON string.
|
|
543
|
+
*/
|
|
544
|
+
serializeState(): string {
|
|
545
|
+
return JSON.stringify(this.exportState(), null, 2);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Load state from a JSON string.
|
|
550
|
+
*/
|
|
551
|
+
deserializeState(json: string): void {
|
|
552
|
+
const state = JSON.parse(json) as CollectionManagerState;
|
|
553
|
+
this.importState(state);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// =========================================================================
|
|
557
|
+
// Statistics
|
|
558
|
+
// =========================================================================
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Get collection statistics.
|
|
562
|
+
*/
|
|
563
|
+
getStats(): CollectionStats {
|
|
564
|
+
const byCategory: Record<PluginCategory, number> = {
|
|
565
|
+
agent: 0,
|
|
566
|
+
task: 0,
|
|
567
|
+
tool: 0,
|
|
568
|
+
memory: 0,
|
|
569
|
+
provider: 0,
|
|
570
|
+
hook: 0,
|
|
571
|
+
worker: 0,
|
|
572
|
+
integration: 0,
|
|
573
|
+
database: 0,
|
|
574
|
+
utility: 0,
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
let totalPlugins = 0;
|
|
578
|
+
let enabledPlugins = 0;
|
|
579
|
+
let disabledPlugins = 0;
|
|
580
|
+
|
|
581
|
+
for (const [collectionId, resolved] of this.resolvedEntries) {
|
|
582
|
+
for (const { entry, pluginName } of resolved) {
|
|
583
|
+
totalPlugins++;
|
|
584
|
+
byCategory[entry.category]++;
|
|
585
|
+
|
|
586
|
+
if (this.isPluginEnabledSync(collectionId, pluginName, entry.defaultEnabled)) {
|
|
587
|
+
enabledPlugins++;
|
|
588
|
+
} else {
|
|
589
|
+
disabledPlugins++;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return {
|
|
595
|
+
totalCollections: this.collections.size,
|
|
596
|
+
totalPlugins,
|
|
597
|
+
enabledPlugins,
|
|
598
|
+
disabledPlugins,
|
|
599
|
+
byCategory,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// =========================================================================
|
|
604
|
+
// Private Helpers
|
|
605
|
+
// =========================================================================
|
|
606
|
+
|
|
607
|
+
private async resolvePlugin(plugin: IPlugin | PluginFactory): Promise<IPlugin> {
|
|
608
|
+
return typeof plugin === 'function' ? await plugin() : plugin;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private findResolvedEntry(collectionId: string, pluginName: string): ResolvedEntry | undefined {
|
|
612
|
+
const resolved = this.resolvedEntries.get(collectionId);
|
|
613
|
+
if (!resolved) return undefined;
|
|
614
|
+
return resolved.find(r => r.pluginName === pluginName);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private isPluginEnabledSync(
|
|
618
|
+
collectionId: string,
|
|
619
|
+
pluginName: string,
|
|
620
|
+
defaultEnabled: boolean
|
|
621
|
+
): boolean {
|
|
622
|
+
// Check explicit overrides first
|
|
623
|
+
if (this.enabledOverrides.get(collectionId)?.has(pluginName)) {
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
if (this.disabledOverrides.get(collectionId)?.has(pluginName)) {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Fall back to default
|
|
631
|
+
return defaultEnabled;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
private async registerResolvedPlugin(
|
|
635
|
+
plugin: IPlugin,
|
|
636
|
+
entry: PluginCollectionEntry
|
|
637
|
+
): Promise<void> {
|
|
638
|
+
const settings = this.pluginSettings.get(plugin.metadata.name);
|
|
639
|
+
|
|
640
|
+
const config: Partial<PluginConfig> = {
|
|
641
|
+
enabled: true,
|
|
642
|
+
settings: settings ?? {},
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
await this.registry.register(plugin, config);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// ============================================================================
|
|
650
|
+
// Default Instance
|
|
651
|
+
// ============================================================================
|
|
652
|
+
|
|
653
|
+
let defaultManager: PluginCollectionManager | null = null;
|
|
654
|
+
|
|
655
|
+
export function getDefaultCollectionManager(): PluginCollectionManager | null {
|
|
656
|
+
return defaultManager;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
export function setDefaultCollectionManager(manager: PluginCollectionManager): void {
|
|
660
|
+
defaultManager = manager;
|
|
661
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Collections
|
|
3
|
+
*
|
|
4
|
+
* Re-exports for the collection management system.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
PluginCollectionManager,
|
|
9
|
+
getDefaultCollectionManager,
|
|
10
|
+
setDefaultCollectionManager,
|
|
11
|
+
type PluginCollection,
|
|
12
|
+
type PluginCollectionEntry,
|
|
13
|
+
type PluginCategory,
|
|
14
|
+
type PluginCapability,
|
|
15
|
+
type CollectionManagerState,
|
|
16
|
+
type CollectionManagerConfig,
|
|
17
|
+
type CollectionStats,
|
|
18
|
+
} from './collection-manager.js';
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
// Individual plugins
|
|
22
|
+
sessionPlugin,
|
|
23
|
+
memoryCoordinatorPlugin,
|
|
24
|
+
eventBusPlugin,
|
|
25
|
+
coderAgentPlugin,
|
|
26
|
+
testerAgentPlugin,
|
|
27
|
+
reviewerAgentPlugin,
|
|
28
|
+
gitIntegrationPlugin,
|
|
29
|
+
linterPlugin,
|
|
30
|
+
sonaPlugin,
|
|
31
|
+
reasoningBankPlugin,
|
|
32
|
+
patternLearningPlugin,
|
|
33
|
+
hiveMindPlugin,
|
|
34
|
+
maestroPlugin,
|
|
35
|
+
consensusPlugin,
|
|
36
|
+
coordinatorAgentPlugin,
|
|
37
|
+
inputValidationPlugin,
|
|
38
|
+
pathSecurityPlugin,
|
|
39
|
+
auditLogPlugin,
|
|
40
|
+
securityScanPlugin,
|
|
41
|
+
metricsPlugin,
|
|
42
|
+
cachePlugin,
|
|
43
|
+
|
|
44
|
+
// Collections
|
|
45
|
+
coreCollection,
|
|
46
|
+
developmentCollection,
|
|
47
|
+
intelligenceCollection,
|
|
48
|
+
swarmCollection,
|
|
49
|
+
securityCollection,
|
|
50
|
+
utilityCollection,
|
|
51
|
+
officialCollections,
|
|
52
|
+
|
|
53
|
+
// Helpers
|
|
54
|
+
getAllOfficialPlugins,
|
|
55
|
+
getOfficialCollection,
|
|
56
|
+
} from './official/index.js';
|