@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.
Files changed (80) hide show
  1. package/README.md +401 -0
  2. package/__tests__/collection-manager.test.ts +332 -0
  3. package/__tests__/dependency-graph.test.ts +434 -0
  4. package/__tests__/enhanced-plugin-registry.test.ts +488 -0
  5. package/__tests__/plugin-registry.test.ts +368 -0
  6. package/__tests__/ruvector-bridge.test.ts +2429 -0
  7. package/__tests__/ruvector-integration.test.ts +1602 -0
  8. package/__tests__/ruvector-migrations.test.ts +1099 -0
  9. package/__tests__/ruvector-quantization.test.ts +846 -0
  10. package/__tests__/ruvector-streaming.test.ts +1088 -0
  11. package/__tests__/sdk.test.ts +325 -0
  12. package/__tests__/security.test.ts +348 -0
  13. package/__tests__/utils/ruvector-test-utils.ts +860 -0
  14. package/examples/plugin-creator/index.ts +636 -0
  15. package/examples/plugin-creator/plugin-creator.test.ts +312 -0
  16. package/examples/ruvector/README.md +288 -0
  17. package/examples/ruvector/attention-patterns.ts +394 -0
  18. package/examples/ruvector/basic-usage.ts +288 -0
  19. package/examples/ruvector/docker-compose.yml +75 -0
  20. package/examples/ruvector/gnn-analysis.ts +501 -0
  21. package/examples/ruvector/hyperbolic-hierarchies.ts +557 -0
  22. package/examples/ruvector/init-db.sql +119 -0
  23. package/examples/ruvector/quantization.ts +680 -0
  24. package/examples/ruvector/self-learning.ts +447 -0
  25. package/examples/ruvector/semantic-search.ts +576 -0
  26. package/examples/ruvector/streaming-large-data.ts +507 -0
  27. package/examples/ruvector/transactions.ts +594 -0
  28. package/examples/ruvector-plugins/hook-pattern-library.ts +486 -0
  29. package/examples/ruvector-plugins/index.ts +79 -0
  30. package/examples/ruvector-plugins/intent-router.ts +354 -0
  31. package/examples/ruvector-plugins/mcp-tool-optimizer.ts +424 -0
  32. package/examples/ruvector-plugins/reasoning-bank.ts +657 -0
  33. package/examples/ruvector-plugins/ruvector-plugins.test.ts +518 -0
  34. package/examples/ruvector-plugins/semantic-code-search.ts +498 -0
  35. package/examples/ruvector-plugins/shared/index.ts +20 -0
  36. package/examples/ruvector-plugins/shared/vector-utils.ts +257 -0
  37. package/examples/ruvector-plugins/sona-learning.ts +445 -0
  38. package/package.json +97 -0
  39. package/src/collections/collection-manager.ts +661 -0
  40. package/src/collections/index.ts +56 -0
  41. package/src/collections/official/index.ts +1040 -0
  42. package/src/core/base-plugin.ts +416 -0
  43. package/src/core/plugin-interface.ts +215 -0
  44. package/src/hooks/index.ts +685 -0
  45. package/src/index.ts +378 -0
  46. package/src/integrations/agentic-flow.ts +743 -0
  47. package/src/integrations/index.ts +88 -0
  48. package/src/integrations/ruvector/ARCHITECTURE.md +1245 -0
  49. package/src/integrations/ruvector/attention-advanced.ts +1040 -0
  50. package/src/integrations/ruvector/attention-executor.ts +782 -0
  51. package/src/integrations/ruvector/attention-mechanisms.ts +757 -0
  52. package/src/integrations/ruvector/attention.ts +1063 -0
  53. package/src/integrations/ruvector/gnn.ts +3050 -0
  54. package/src/integrations/ruvector/hyperbolic.ts +1948 -0
  55. package/src/integrations/ruvector/index.ts +394 -0
  56. package/src/integrations/ruvector/migrations/001_create_extension.sql +135 -0
  57. package/src/integrations/ruvector/migrations/002_create_vector_tables.sql +259 -0
  58. package/src/integrations/ruvector/migrations/003_create_indices.sql +328 -0
  59. package/src/integrations/ruvector/migrations/004_create_functions.sql +598 -0
  60. package/src/integrations/ruvector/migrations/005_create_attention_functions.sql +654 -0
  61. package/src/integrations/ruvector/migrations/006_create_gnn_functions.sql +728 -0
  62. package/src/integrations/ruvector/migrations/007_create_hyperbolic_functions.sql +762 -0
  63. package/src/integrations/ruvector/migrations/index.ts +35 -0
  64. package/src/integrations/ruvector/migrations/migrations.ts +647 -0
  65. package/src/integrations/ruvector/quantization.ts +2036 -0
  66. package/src/integrations/ruvector/ruvector-bridge.ts +2000 -0
  67. package/src/integrations/ruvector/self-learning.ts +2376 -0
  68. package/src/integrations/ruvector/streaming.ts +1737 -0
  69. package/src/integrations/ruvector/types.ts +1945 -0
  70. package/src/providers/index.ts +643 -0
  71. package/src/registry/dependency-graph.ts +568 -0
  72. package/src/registry/enhanced-plugin-registry.ts +994 -0
  73. package/src/registry/plugin-registry.ts +604 -0
  74. package/src/sdk/index.ts +563 -0
  75. package/src/security/index.ts +594 -0
  76. package/src/types/index.ts +446 -0
  77. package/src/workers/index.ts +700 -0
  78. package/tmp.json +0 -0
  79. package/tsconfig.json +25 -0
  80. package/vitest.config.ts +23 -0
@@ -0,0 +1,424 @@
1
+ /**
2
+ * MCP Tool Optimizer Plugin
3
+ *
4
+ * Learn tool usage patterns and suggest optimal tool sequences.
5
+ * Uses @ruvector/wasm for pattern storage and @ruvector/learning-wasm for optimization.
6
+ *
7
+ * Features:
8
+ * - Track tool usage patterns
9
+ * - Learn successful tool sequences
10
+ * - Suggest optimal tool combinations
11
+ * - Identify redundant tool calls
12
+ * - Performance optimization recommendations
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { mcpToolOptimizerPlugin } from '@sparkleideas/plugins/examples/ruvector-plugins';
17
+ * await getDefaultRegistry().register(mcpToolOptimizerPlugin);
18
+ * ```
19
+ */
20
+
21
+ import {
22
+ PluginBuilder,
23
+ MCPToolBuilder,
24
+ HookBuilder,
25
+ HookEvent,
26
+ HookPriority,
27
+ Security,
28
+ } from '../../src/index.js';
29
+
30
+ // Import shared vector utilities (consolidated from all plugins)
31
+ import {
32
+ IVectorDB,
33
+ ILoRAEngine,
34
+ createVectorDB,
35
+ createLoRAEngine,
36
+ generateHashEmbedding,
37
+ } from './shared/vector-utils.js';
38
+
39
+ // ============================================================================
40
+ // Types
41
+ // ============================================================================
42
+
43
+ export interface ToolUsagePattern {
44
+ id: string;
45
+ toolName: string;
46
+ context: string;
47
+ inputPatterns: string[];
48
+ outcome: 'success' | 'failure' | 'partial';
49
+ duration: number;
50
+ followedBy?: string[];
51
+ precededBy?: string[];
52
+ metadata: {
53
+ usageCount: number;
54
+ avgDuration: number;
55
+ successRate: number;
56
+ lastUsed: Date;
57
+ };
58
+ }
59
+
60
+ export interface ToolSequence {
61
+ id: string;
62
+ tools: string[];
63
+ context: string;
64
+ outcome: 'success' | 'failure';
65
+ totalDuration: number;
66
+ efficiency: number;
67
+ embedding?: Float32Array;
68
+ }
69
+
70
+ export interface OptimizationSuggestion {
71
+ type: 'sequence' | 'replacement' | 'parallel' | 'removal';
72
+ description: string;
73
+ currentTools: string[];
74
+ suggestedTools: string[];
75
+ expectedImprovement: number;
76
+ confidence: number;
77
+ }
78
+
79
+ // ============================================================================
80
+ // MCP Tool Optimizer Core
81
+ // ============================================================================
82
+
83
+ export class MCPToolOptimizer {
84
+ private vectorDb: IVectorDB | null = null;
85
+ private loraEngine: ILoRAEngine | null = null;
86
+ private patterns = new Map<string, ToolUsagePattern>();
87
+ private sequences = new Map<string, ToolSequence>();
88
+ private currentSession: { tools: string[]; startTime: number; context: string } | null = null;
89
+ private dimensions = 512;
90
+ private nextId = 1;
91
+ private initPromise: Promise<void> | null = null;
92
+
93
+ private toolRelations = new Map<string, { parallelWith: string[]; alternatives: string[]; bestAfter: string[] }>([
94
+ ['Glob', { parallelWith: ['Grep'], alternatives: [], bestAfter: [] }],
95
+ ['Grep', { parallelWith: ['Glob'], alternatives: [], bestAfter: ['Glob'] }],
96
+ ['Read', { parallelWith: ['Read'], alternatives: [], bestAfter: ['Glob', 'Grep'] }],
97
+ ['Edit', { parallelWith: [], alternatives: ['Write'], bestAfter: ['Read'] }],
98
+ ['Write', { parallelWith: [], alternatives: ['Edit'], bestAfter: ['Read'] }],
99
+ ['Bash', { parallelWith: ['Bash'], alternatives: [], bestAfter: [] }],
100
+ ]);
101
+
102
+ async initialize(): Promise<void> {
103
+ if (this.vectorDb && this.loraEngine) return;
104
+ if (this.initPromise) return this.initPromise;
105
+
106
+ this.initPromise = (async () => {
107
+ this.vectorDb = await createVectorDB(this.dimensions);
108
+ this.loraEngine = await createLoRAEngine();
109
+ })();
110
+
111
+ return this.initPromise;
112
+ }
113
+
114
+ private async ensureInitialized(): Promise<{ db: IVectorDB; lora: ILoRAEngine }> {
115
+ await this.initialize();
116
+ return { db: this.vectorDb!, lora: this.loraEngine! };
117
+ }
118
+
119
+ /**
120
+ * Record a tool usage.
121
+ */
122
+ async recordUsage(
123
+ toolName: string,
124
+ context: string,
125
+ inputSummary: string,
126
+ outcome: 'success' | 'failure' | 'partial',
127
+ duration: number
128
+ ): Promise<ToolUsagePattern> {
129
+ const safeToolName = Security.validateString(toolName, { maxLength: 100 });
130
+ const safeContext = Security.validateString(context, { maxLength: 500 });
131
+ const safeInput = Security.validateString(inputSummary, { maxLength: 500 });
132
+
133
+ const patternKey = `${safeToolName}:${this.hashContext(safeContext)}`;
134
+ let pattern = this.patterns.get(patternKey);
135
+
136
+ if (pattern) {
137
+ pattern.metadata.usageCount++;
138
+ pattern.metadata.avgDuration = (pattern.metadata.avgDuration * (pattern.metadata.usageCount - 1) + duration) / pattern.metadata.usageCount;
139
+ pattern.metadata.successRate = (pattern.metadata.successRate * (pattern.metadata.usageCount - 1) + (outcome === 'success' ? 1 : 0)) / pattern.metadata.usageCount;
140
+ pattern.metadata.lastUsed = new Date();
141
+ if (!pattern.inputPatterns.includes(safeInput)) {
142
+ pattern.inputPatterns.push(safeInput);
143
+ if (pattern.inputPatterns.length > 10) pattern.inputPatterns.shift();
144
+ }
145
+ } else {
146
+ const id = `pattern-${this.nextId++}`;
147
+ pattern = {
148
+ id, toolName: safeToolName, context: safeContext, inputPatterns: [safeInput], outcome, duration,
149
+ followedBy: [], precededBy: [],
150
+ metadata: { usageCount: 1, avgDuration: duration, successRate: outcome === 'success' ? 1 : 0, lastUsed: new Date() },
151
+ };
152
+ this.patterns.set(patternKey, pattern);
153
+ }
154
+
155
+ if (this.currentSession) {
156
+ const lastTool = this.currentSession.tools[this.currentSession.tools.length - 1];
157
+ if (lastTool) {
158
+ const lastPatternKey = `${lastTool}:${this.hashContext(this.currentSession.context)}`;
159
+ const lastPattern = this.patterns.get(lastPatternKey);
160
+ if (lastPattern) {
161
+ if (!lastPattern.followedBy) lastPattern.followedBy = [];
162
+ if (!lastPattern.followedBy.includes(safeToolName)) lastPattern.followedBy.push(safeToolName);
163
+ }
164
+ if (!pattern.precededBy) pattern.precededBy = [];
165
+ if (!pattern.precededBy.includes(lastTool)) pattern.precededBy.push(lastTool);
166
+ }
167
+ this.currentSession.tools.push(safeToolName);
168
+ }
169
+
170
+ return pattern;
171
+ }
172
+
173
+ startSession(context: string): void {
174
+ this.currentSession = { tools: [], startTime: Date.now(), context: Security.validateString(context, { maxLength: 500 }) };
175
+ }
176
+
177
+ async endSession(outcome: 'success' | 'failure'): Promise<ToolSequence | null> {
178
+ const { db } = await this.ensureInitialized();
179
+
180
+ if (!this.currentSession || this.currentSession.tools.length === 0) {
181
+ this.currentSession = null;
182
+ return null;
183
+ }
184
+
185
+ const id = `seq-${this.nextId++}`;
186
+ const totalDuration = Date.now() - this.currentSession.startTime;
187
+ const uniqueTools = new Set(this.currentSession.tools);
188
+ const redundancy = 1 - (uniqueTools.size / this.currentSession.tools.length);
189
+ const efficiency = outcome === 'success' ? (1 - redundancy * 0.5) : 0.3;
190
+
191
+ const embedding = this.generateSequenceEmbedding(this.currentSession.tools, this.currentSession.context);
192
+ const sequence: ToolSequence = { id, tools: [...this.currentSession.tools], context: this.currentSession.context, outcome, totalDuration, efficiency, embedding };
193
+
194
+ db.insert(embedding, id, { tools: sequence.tools.join(','), outcome, efficiency });
195
+ this.sequences.set(id, sequence);
196
+ this.currentSession = null;
197
+
198
+ return sequence;
199
+ }
200
+
201
+ async optimize(tools: string[], context: string): Promise<OptimizationSuggestion[]> {
202
+ const { db } = await this.ensureInitialized();
203
+ const suggestions: OptimizationSuggestion[] = [];
204
+ const safeTools = tools.map(t => Security.validateString(t, { maxLength: 100 }));
205
+ const safeContext = Security.validateString(context, { maxLength: 500 });
206
+
207
+ const embedding = this.generateSequenceEmbedding(safeTools, safeContext);
208
+ const similarSequences = db.search(embedding, 5)
209
+ .map(r => this.sequences.get(r.id))
210
+ .filter((s): s is ToolSequence => s !== undefined && s.outcome === 'success' && s.efficiency > 0.7);
211
+
212
+ // Parallel execution suggestions
213
+ for (let i = 0; i < safeTools.length - 1; i++) {
214
+ const tool = safeTools[i];
215
+ const nextTool = safeTools[i + 1];
216
+ const relations = this.toolRelations.get(tool);
217
+ if (relations?.parallelWith.includes(nextTool)) {
218
+ suggestions.push({ type: 'parallel', description: `Run ${tool} and ${nextTool} in parallel`, currentTools: [tool, nextTool], suggestedTools: [`${tool} || ${nextTool}`], expectedImprovement: 0.4, confidence: 0.8 });
219
+ }
220
+ }
221
+
222
+ // Redundant tool suggestions
223
+ const toolCounts = new Map<string, number>();
224
+ for (const tool of safeTools) toolCounts.set(tool, (toolCounts.get(tool) ?? 0) + 1);
225
+ for (const [tool, count] of toolCounts) {
226
+ if (count > 2 && !['Read', 'Bash'].includes(tool)) {
227
+ suggestions.push({ type: 'removal', description: `Combine ${count} ${tool} calls`, currentTools: Array(count).fill(tool), suggestedTools: [tool], expectedImprovement: 0.3 * (count - 1), confidence: 0.7 });
228
+ }
229
+ }
230
+
231
+ // Better sequences from history
232
+ for (const seq of similarSequences) {
233
+ if (seq.tools.length < safeTools.length && seq.efficiency > 0.8) {
234
+ suggestions.push({ type: 'sequence', description: `Similar task completed with fewer tools`, currentTools: safeTools, suggestedTools: seq.tools, expectedImprovement: (safeTools.length - seq.tools.length) * 0.1, confidence: 0.6 });
235
+ break;
236
+ }
237
+ }
238
+
239
+ return suggestions.sort((a, b) => b.expectedImprovement * b.confidence - a.expectedImprovement * a.confidence);
240
+ }
241
+
242
+ async suggestNext(currentTool: string, context: string): Promise<Array<{ tool: string; probability: number; reason: string }>> {
243
+ const suggestions: Array<{ tool: string; probability: number; reason: string }> = [];
244
+ const safeTool = Security.validateString(currentTool, { maxLength: 100 });
245
+
246
+ const patternKey = `${safeTool}:${this.hashContext(context)}`;
247
+ const pattern = this.patterns.get(patternKey);
248
+
249
+ if (pattern?.followedBy) {
250
+ const counts = new Map<string, number>();
251
+ for (const tool of pattern.followedBy) counts.set(tool, (counts.get(tool) ?? 0) + 1);
252
+ const total = pattern.followedBy.length;
253
+ for (const [tool, count] of counts) {
254
+ suggestions.push({ tool, probability: count / total, reason: `Followed ${safeTool} ${count}x` });
255
+ }
256
+ }
257
+
258
+ const relations = this.toolRelations.get(safeTool);
259
+ if (relations?.bestAfter.length === 0) {
260
+ for (const [tool, rel] of this.toolRelations) {
261
+ if (rel.bestAfter.includes(safeTool)) {
262
+ suggestions.push({ tool, probability: 0.6, reason: `${tool} often follows ${safeTool}` });
263
+ }
264
+ }
265
+ }
266
+
267
+ return suggestions.sort((a, b) => b.probability - a.probability).slice(0, 5);
268
+ }
269
+
270
+ getStats(): {
271
+ totalPatterns: number;
272
+ totalSequences: number;
273
+ topTools: Array<{ name: string; usageCount: number; successRate: number; avgDuration: number }>;
274
+ avgEfficiency: number;
275
+ commonSequences: Array<{ tools: string[]; count: number }>;
276
+ } {
277
+ const toolStats = new Map<string, { usageCount: number; successTotal: number; durationTotal: number }>();
278
+
279
+ for (const pattern of this.patterns.values()) {
280
+ const existing = toolStats.get(pattern.toolName) ?? { usageCount: 0, successTotal: 0, durationTotal: 0 };
281
+ existing.usageCount += pattern.metadata.usageCount;
282
+ existing.successTotal += pattern.metadata.successRate * pattern.metadata.usageCount;
283
+ existing.durationTotal += pattern.metadata.avgDuration * pattern.metadata.usageCount;
284
+ toolStats.set(pattern.toolName, existing);
285
+ }
286
+
287
+ const topTools = Array.from(toolStats.entries())
288
+ .map(([name, stats]) => ({ name, usageCount: stats.usageCount, successRate: stats.usageCount > 0 ? stats.successTotal / stats.usageCount : 0, avgDuration: stats.usageCount > 0 ? stats.durationTotal / stats.usageCount : 0 }))
289
+ .sort((a, b) => b.usageCount - a.usageCount).slice(0, 10);
290
+
291
+ let totalEfficiency = 0;
292
+ const sequenceCounts = new Map<string, number>();
293
+ for (const seq of this.sequences.values()) {
294
+ totalEfficiency += seq.efficiency;
295
+ const key = seq.tools.join(' → ');
296
+ sequenceCounts.set(key, (sequenceCounts.get(key) ?? 0) + 1);
297
+ }
298
+
299
+ const commonSequences = Array.from(sequenceCounts.entries())
300
+ .map(([tools, count]) => ({ tools: tools.split(' → '), count }))
301
+ .sort((a, b) => b.count - a.count).slice(0, 5);
302
+
303
+ return { totalPatterns: this.patterns.size, totalSequences: this.sequences.size, topTools, avgEfficiency: this.sequences.size > 0 ? totalEfficiency / this.sequences.size : 0, commonSequences };
304
+ }
305
+
306
+ private hashContext(context: string): string {
307
+ let hash = 0;
308
+ for (let i = 0; i < context.length; i++) { hash = ((hash << 5) - hash) + context.charCodeAt(i); hash = hash & hash; }
309
+ return hash.toString(16);
310
+ }
311
+
312
+ private generateSequenceEmbedding(tools: string[], context: string): Float32Array {
313
+ const text = `${tools.join(' ')} ${context}`.toLowerCase();
314
+ const embedding = new Float32Array(this.dimensions);
315
+ let hash = 0;
316
+ for (let i = 0; i < text.length; i++) { hash = ((hash << 5) - hash) + text.charCodeAt(i); hash = hash & hash; }
317
+ for (let i = 0; i < this.dimensions; i++) { embedding[i] = Math.sin(hash * (i + 1) * 0.001) * 0.5 + 0.5; }
318
+ let norm = 0;
319
+ for (let i = 0; i < this.dimensions; i++) norm += embedding[i] * embedding[i];
320
+ norm = Math.sqrt(norm);
321
+ for (let i = 0; i < this.dimensions; i++) embedding[i] /= norm;
322
+ return embedding;
323
+ }
324
+ }
325
+
326
+ // ============================================================================
327
+ // Plugin Definition
328
+ // ============================================================================
329
+
330
+ let optimizerInstance: MCPToolOptimizer | null = null;
331
+
332
+ async function getOptimizer(): Promise<MCPToolOptimizer> {
333
+ if (!optimizerInstance) {
334
+ optimizerInstance = new MCPToolOptimizer();
335
+ await optimizerInstance.initialize();
336
+ }
337
+ return optimizerInstance;
338
+ }
339
+
340
+ export const mcpToolOptimizerPlugin = new PluginBuilder('mcp-tool-optimizer', '1.0.0')
341
+ .withDescription('Learn tool patterns and suggest optimal sequences using @ruvector/wasm + @ruvector/learning-wasm')
342
+ .withAuthor('Claude Flow Team')
343
+ .withTags(['optimization', 'tools', 'patterns', 'ruvector', 'learning', 'hnsw'])
344
+ .withMCPTools([
345
+ new MCPToolBuilder('tool-optimize')
346
+ .withDescription('Get optimization suggestions')
347
+ .addStringParam('tools', 'Comma-separated tool names', { required: true })
348
+ .addStringParam('context', 'Task context', { required: true })
349
+ .withHandler(async (params) => {
350
+ try {
351
+ const optimizer = await getOptimizer();
352
+ const tools = (params.tools as string).split(',').map(t => t.trim());
353
+ const suggestions = await optimizer.optimize(tools, params.context as string);
354
+ if (suggestions.length === 0) return { content: [{ type: 'text', text: '✅ Tool sequence looks optimal!' }] };
355
+ const output = suggestions.map((s, i) => `**${i + 1}. ${s.type.toUpperCase()}** (${(s.confidence * 100).toFixed(0)}%)\n ${s.description}`).join('\n\n');
356
+ return { content: [{ type: 'text', text: `🔧 **Optimizations:**\n\n${output}` }] };
357
+ } catch (error) {
358
+ return { content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
359
+ }
360
+ })
361
+ .build(),
362
+
363
+ new MCPToolBuilder('tool-suggest-next')
364
+ .withDescription('Suggest next tool')
365
+ .addStringParam('currentTool', 'Current tool', { required: true })
366
+ .addStringParam('context', 'Context')
367
+ .withHandler(async (params) => {
368
+ try {
369
+ const optimizer = await getOptimizer();
370
+ const suggestions = await optimizer.suggestNext(params.currentTool as string, (params.context as string) ?? '');
371
+ if (suggestions.length === 0) return { content: [{ type: 'text', text: '🤔 No suggestions.' }] };
372
+ const output = suggestions.map((s, i) => `${i + 1}. **${s.tool}** (${(s.probability * 100).toFixed(0)}%) - ${s.reason}`).join('\n');
373
+ return { content: [{ type: 'text', text: `💡 **Next tools:**\n\n${output}` }] };
374
+ } catch (error) {
375
+ return { content: [{ type: 'text', text: `❌ Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
376
+ }
377
+ })
378
+ .build(),
379
+
380
+ new MCPToolBuilder('tool-stats')
381
+ .withDescription('Get tool statistics')
382
+ .withHandler(async () => {
383
+ const optimizer = await getOptimizer();
384
+ const stats = optimizer.getStats();
385
+ const topToolsOutput = stats.topTools.slice(0, 5).map((t, i) => ` ${i + 1}. ${t.name}: ${t.usageCount} uses (${(t.successRate * 100).toFixed(0)}%)`).join('\n');
386
+ return { content: [{ type: 'text', text: `📊 **Tool Optimizer:**\n\n**Patterns:** ${stats.totalPatterns}\n**Sequences:** ${stats.totalSequences}\n**Efficiency:** ${(stats.avgEfficiency * 100).toFixed(1)}%\n\n**Top Tools:**\n${topToolsOutput || ' None'}\n\n**Backend:** @ruvector/wasm + @ruvector/learning-wasm` }] };
387
+ })
388
+ .build(),
389
+ ])
390
+ .withHooks([
391
+ new HookBuilder(HookEvent.PostToolCall)
392
+ .withName('tool-usage-record')
393
+ .withDescription('Record tool usage')
394
+ .withPriority(HookPriority.Low)
395
+ .handle(async (ctx) => {
396
+ const data = ctx.data as { toolName?: string; context?: string; input?: string; success?: boolean; duration?: number } | undefined;
397
+ if (!data?.toolName) return { success: true };
398
+ try {
399
+ const optimizer = await getOptimizer();
400
+ await optimizer.recordUsage(data.toolName, data.context ?? 'unknown', data.input ?? '', data.success ? 'success' : 'failure', data.duration ?? 0);
401
+ } catch { /* silent */ }
402
+ return { success: true };
403
+ })
404
+ .build(),
405
+
406
+ new HookBuilder(HookEvent.PostTaskComplete)
407
+ .withName('tool-session-end')
408
+ .withDescription('End tool session')
409
+ .withPriority(HookPriority.Low)
410
+ .handle(async (ctx) => {
411
+ const data = ctx.data as { success?: boolean } | undefined;
412
+ try { await (await getOptimizer()).endSession(data?.success ? 'success' : 'failure'); } catch { /* silent */ }
413
+ return { success: true };
414
+ })
415
+ .build(),
416
+ ])
417
+ .onInitialize(async (ctx) => {
418
+ ctx.logger.info('MCP Tool Optimizer initializing with @ruvector/wasm + @ruvector/learning-wasm...');
419
+ await getOptimizer();
420
+ ctx.logger.info('MCP Tool Optimizer ready - HNSW + LoRA enabled');
421
+ })
422
+ .build();
423
+
424
+ export default mcpToolOptimizerPlugin;