@veedubin/boomerang-v3 0.1.0

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 (53) hide show
  1. package/.github/workflows/npm-publish.yml +58 -0
  2. package/.opencode/skills/boomerang-agent-builder/SKILL.md +226 -0
  3. package/.opencode/skills/boomerang-architect/SKILL.md +252 -0
  4. package/.opencode/skills/boomerang-coder/SKILL.md +283 -0
  5. package/.opencode/skills/boomerang-explorer/SKILL.md +58 -0
  6. package/.opencode/skills/boomerang-git/SKILL.md +115 -0
  7. package/.opencode/skills/boomerang-handoff/SKILL.md +209 -0
  8. package/.opencode/skills/boomerang-init/SKILL.md +117 -0
  9. package/.opencode/skills/boomerang-linter/SKILL.md +92 -0
  10. package/.opencode/skills/boomerang-orchestrator/SKILL.md +401 -0
  11. package/.opencode/skills/boomerang-release/SKILL.md +116 -0
  12. package/.opencode/skills/boomerang-scraper/SKILL.md +105 -0
  13. package/.opencode/skills/boomerang-tester/SKILL.md +107 -0
  14. package/.opencode/skills/boomerang-writer/SKILL.md +93 -0
  15. package/.opencode/skills/mcp-specialist/SKILL.md +130 -0
  16. package/.opencode/skills/researcher/SKILL.md +118 -0
  17. package/AGENTS.md +333 -0
  18. package/README.md +305 -0
  19. package/dist/index.js +13 -0
  20. package/dist/memini-client/index.js +560 -0
  21. package/dist/memini-client/schema.js +13 -0
  22. package/dist/memory/contradictions.js +119 -0
  23. package/dist/memory/graph.js +86 -0
  24. package/dist/memory/index.js +314 -0
  25. package/dist/memory/kg.js +111 -0
  26. package/dist/memory/schema.js +10 -0
  27. package/dist/memory/tiered.js +104 -0
  28. package/dist/memory/trust.js +148 -0
  29. package/dist/protocol/types.js +6 -0
  30. package/package.json +41 -0
  31. package/packages/opencode-plugin/src/asset-loader.ts +201 -0
  32. package/packages/opencode-plugin/src/git.ts +77 -0
  33. package/packages/opencode-plugin/src/index.ts +346 -0
  34. package/packages/opencode-plugin/src/memory.ts +109 -0
  35. package/packages/opencode-plugin/src/orchestrator.ts +263 -0
  36. package/packages/opencode-plugin/src/quality-gates.ts +75 -0
  37. package/packages/opencode-plugin/src/types.ts +141 -0
  38. package/src/index.ts +16 -0
  39. package/src/memini-client/index.ts +762 -0
  40. package/src/memini-client/schema.ts +60 -0
  41. package/src/memory/contradictions.ts +164 -0
  42. package/src/memory/graph.ts +116 -0
  43. package/src/memory/index.ts +422 -0
  44. package/src/memory/kg.ts +166 -0
  45. package/src/memory/schema.ts +274 -0
  46. package/src/memory/tiered.ts +133 -0
  47. package/src/memory/trust.ts +218 -0
  48. package/src/protocol/types.ts +79 -0
  49. package/tests/index.test.ts +58 -0
  50. package/tests/memini-client.test.ts +321 -0
  51. package/tests/memory/index.test.ts +214 -0
  52. package/tsconfig.json +17 -0
  53. package/vitest.config.ts +19 -0
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Boomerang v3.0.0 - OpenCode Plugin Interface
3
+ *
4
+ * Self-contained plugin that integrates with OpenCode's plugin system.
5
+ * Uses memini-ai for memory operations.
6
+ */
7
+
8
+ import { createOrchestrator } from './orchestrator.js';
9
+ import { loadAgents, loadSkills, listAvailableAgents, listAvailableSkills } from './asset-loader.js';
10
+ import { getMemorySystem } from './memory.js';
11
+ import { runAllQualityGates, DEFAULT_QUALITY_GATES } from './quality-gates.js';
12
+ import type { BoomerangConfig, PluginContext } from './types.js';
13
+
14
+ export { createOrchestrator } from './orchestrator.js';
15
+ export type { OrchestrationResult, ContextPackage } from './orchestrator.js';
16
+
17
+ const VERSION = '3.0.0';
18
+
19
+ // Default configuration
20
+ const DEFAULT_CONFIG: BoomerangConfig = {
21
+ memoryEnabled: true,
22
+ qualityGates: {
23
+ lint: true,
24
+ typecheck: true,
25
+ test: true,
26
+ },
27
+ };
28
+
29
+ // memini-ai configuration
30
+ const MEMINI_SERVER_URL = process.env.MEMINI_SERVER_URL || 'http://localhost:8000';
31
+
32
+ /**
33
+ * Plugin registration with OpenCode
34
+ */
35
+ export function register(registry: PluginRegistry): void {
36
+ // Load and register agents
37
+ const agents = loadAgents();
38
+ for (const agent of agents) {
39
+ registry.registerAgent(agent.name, {
40
+ name: agent.name,
41
+ description: agent.description,
42
+ systemPrompt: agent.systemPrompt || '',
43
+ skills: agent.skills,
44
+ });
45
+ }
46
+
47
+ // Load and register skills
48
+ const skills = loadSkills();
49
+ for (const skill of skills) {
50
+ registry.registerSkill(skill.name, {
51
+ name: skill.name,
52
+ description: skill.description,
53
+ instructions: skill.instructions,
54
+ });
55
+ }
56
+
57
+ // Register commands
58
+ registry.registerCommand('boomerang', handleBoomerangCommand);
59
+ registry.registerCommand('/handoff', handleHandoffCommand);
60
+ }
61
+
62
+ export interface PluginRegistry {
63
+ registerCommand(name: string, handler: CommandHandler): void;
64
+ registerAgent(name: string, definition: AgentDefinition): void;
65
+ registerSkill(name: string, definition: SkillDefinition): void;
66
+ }
67
+
68
+ interface AgentDefinition {
69
+ name: string;
70
+ description: string;
71
+ systemPrompt: string;
72
+ skills: string[];
73
+ }
74
+
75
+ interface SkillDefinition {
76
+ name: string;
77
+ description: string;
78
+ instructions: string;
79
+ }
80
+
81
+ type CommandHandler = (context: PluginContext) => Promise<void>;
82
+
83
+ /**
84
+ * Plugin activation - initialize memory and prepare for execution
85
+ */
86
+ export async function activate(context: PluginContext): Promise<void> {
87
+ // Initialize memory system
88
+ const memorySystem = getMemorySystem();
89
+ try {
90
+ await memorySystem.initialize(MEMINI_SERVER_URL);
91
+ console.log('[boomerang] Memory system initialized');
92
+ } catch (error) {
93
+ console.warn('[boomerang] Memory initialization failed (fallback mode):', error instanceof Error ? error.message : error);
94
+ }
95
+
96
+ // Handle commands
97
+ const command = context.args[0];
98
+ if (command === 'boomerang' || command === undefined) {
99
+ await handleBoomerangCommand(context);
100
+ } else if (command === '/handoff') {
101
+ await handleHandoffCommand(context);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Handle boomerang command - analyze request and prepare context for OpenCode execution
107
+ */
108
+ async function handleBoomerangCommand(context: PluginContext): Promise<void> {
109
+ const request = context.args.slice(1).join(' ') || 'help';
110
+
111
+ if (request === 'help') {
112
+ console.log(`
113
+ Boomerang v${VERSION} - Multi-Agent Orchestration Plugin for OpenCode
114
+
115
+ Commands:
116
+ boomerang <task> - Orchestrate a task with appropriate agent
117
+ boomerang /handoff - End session and save context
118
+
119
+ Available agents:
120
+ - boomerang: General purpose orchestrator
121
+ - boomerang-coder: Fast code generation
122
+ - boomerang-tester: Testing specialist
123
+ - boomerang-explorer: Codebase exploration
124
+ - boomerang-architect: Architecture and design
125
+ - boomerang-writer: Documentation
126
+ - boomerang-git: Version control
127
+ - boomerang-linter: Quality enforcement
128
+ - boomerang-release: Release automation
129
+ - boomerang-scraper: Web research
130
+
131
+ Examples:
132
+ boomerang implement user authentication
133
+ boomerang test payment processing
134
+ boomerang explore find files with API endpoints
135
+ `);
136
+ return;
137
+ }
138
+
139
+ try {
140
+ const orchestrator = createOrchestrator();
141
+ const result = await orchestrator.orchestrate(request);
142
+
143
+ console.log('[boomerang] Orchestration complete');
144
+ console.log(`[boomerang] Agent: ${result.agent}`);
145
+ console.log(`[boomerang] Context Package built with ${result.contextPackage.relevantFiles.length} relevant files`);
146
+ console.log('[boomerang] Suggestions:', result.suggestions);
147
+ } catch (error) {
148
+ console.error('[boomerang] Orchestration failed:', error instanceof Error ? error.message : error);
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Handle handoff command - save context and prepare for session end
154
+ */
155
+ async function handleHandoffCommand(context: PluginContext): Promise<void> {
156
+ console.log('[boomerang] Starting session handoff...');
157
+
158
+ try {
159
+ const memorySystem = getMemorySystem();
160
+
161
+ if (memorySystem.isInitialized()) {
162
+ const summary = `Session handoff completed at ${new Date().toISOString()}`;
163
+ await memorySystem.saveContext(context.cwd, summary);
164
+ console.log('[boomerang] Context saved to memory');
165
+ }
166
+
167
+ console.log('[boomerang] Handoff complete. Session summary saved.');
168
+ } catch (error) {
169
+ console.error('[boomerang] Handoff failed:', error instanceof Error ? error.message : error);
170
+ }
171
+ }
172
+
173
+ // Export BoomerangPlugin as default for OpenCode
174
+ export const BoomerangPlugin = async (ctx: PluginContext): Promise<unknown> => {
175
+ const config = DEFAULT_CONFIG;
176
+
177
+ try {
178
+ (ctx.client as { app?: { log?: (msg: string) => void } }).app?.log?.('Boomerang Protocol v3.0.0 activated');
179
+ } catch {
180
+ // Logging not available
181
+ }
182
+
183
+ // Initialize memory system
184
+ try {
185
+ const memorySystem = getMemorySystem();
186
+ await memorySystem.initialize(MEMINI_SERVER_URL);
187
+ (ctx.client as { app?: { log?: (msg: string) => void } }).app?.log?.('memini-ai connected');
188
+ } catch (err) {
189
+ console.error('Failed to initialize memory:', err);
190
+ }
191
+
192
+ // Log bundled assets
193
+ console.log(`Loaded ${listAvailableAgents().length} agents`);
194
+ console.log(`Loaded ${listAvailableSkills().length} skills`);
195
+
196
+ return {
197
+ tool: {
198
+ boomerang_status: {
199
+ description: 'Check Boomerang Protocol status and configuration',
200
+ inputSchema: {},
201
+ async execute() {
202
+ return `Boomerang Protocol v${VERSION} Status:
203
+ - Memory Enabled: ${config.memoryEnabled}
204
+ - Quality Gates: lint=${config.qualityGates.lint}, typecheck=${config.qualityGates.typecheck}, test=${config.qualityGates.test}
205
+ - Agents Loaded: ${listAvailableAgents().length}
206
+ - Skills Loaded: ${listAvailableSkills().length}`;
207
+ },
208
+ },
209
+
210
+ boomerang_orchestrate: {
211
+ description: 'Orchestrate a task - analyze request, query memory, build context package',
212
+ inputSchema: {
213
+ type: 'object',
214
+ properties: {
215
+ prompt: { type: 'string', description: 'The task or request to orchestrate' },
216
+ },
217
+ required: ['prompt'],
218
+ },
219
+ async execute(args: { prompt: string }) {
220
+ try {
221
+ const orchestrator = createOrchestrator();
222
+ const result = await orchestrator.orchestrate(args.prompt);
223
+
224
+ return JSON.stringify({
225
+ agent: result.agent,
226
+ systemPrompt: result.systemPrompt,
227
+ contextPackage: result.contextPackage,
228
+ suggestions: result.suggestions,
229
+ }, null, 2);
230
+ } catch (error) {
231
+ return `Orchestration failed: ${error instanceof Error ? error.message : String(error)}`;
232
+ }
233
+ },
234
+ },
235
+
236
+ boomerang_quality_gates: {
237
+ description: 'Run quality gates: lint, typecheck, and tests',
238
+ inputSchema: {},
239
+ async execute() {
240
+ const result = await runAllQualityGates(DEFAULT_QUALITY_GATES);
241
+ return result.summary;
242
+ },
243
+ },
244
+
245
+ boomerang_memory_search: {
246
+ description: 'Search memini-ai for relevant context',
247
+ inputSchema: {
248
+ type: 'object',
249
+ properties: {
250
+ query: { type: 'string', description: 'Search query' },
251
+ limit: { type: 'number', description: 'Max results' },
252
+ },
253
+ required: ['query'],
254
+ },
255
+ async execute(args: { query: string; limit?: number }) {
256
+ try {
257
+ const memorySystem = getMemorySystem();
258
+ const results = await memorySystem.search(args.query, { topK: args.limit || 10 });
259
+
260
+ if (results.length === 0) {
261
+ return 'No relevant memories found.';
262
+ }
263
+
264
+ return results
265
+ .map(r => `- [${r.score.toFixed(2)}] ${r.entry.text.substring(0, 200)}`)
266
+ .join('\n');
267
+ } catch (error) {
268
+ return `Memory search failed: ${error instanceof Error ? error.message : String(error)}`;
269
+ }
270
+ },
271
+ },
272
+
273
+ boomerang_memory_add: {
274
+ description: 'Save context to memini-ai',
275
+ inputSchema: {
276
+ type: 'object',
277
+ properties: {
278
+ content: { type: 'string', description: 'Content to save' },
279
+ sourceType: { type: 'string', description: 'Source type (default: manual)' },
280
+ },
281
+ required: ['content'],
282
+ },
283
+ async execute(args: { content: string; sourceType?: string }) {
284
+ try {
285
+ const memorySystem = getMemorySystem();
286
+ const entry = await memorySystem.addMemory({
287
+ text: args.content,
288
+ sourceType: (args.sourceType as 'session' | 'file' | 'web' | 'boomerang' | 'project') || 'manual',
289
+ sourcePath: '',
290
+ });
291
+ return `Saved memory (ID: ${entry.id})`;
292
+ } catch (error) {
293
+ return `Failed to save: ${error instanceof Error ? error.message : String(error)}`;
294
+ }
295
+ },
296
+ },
297
+
298
+ boomerang_get_trust_score: {
299
+ description: 'Get trust score for a memory entry',
300
+ inputSchema: {
301
+ type: 'object',
302
+ properties: {
303
+ memory_id: { type: 'string', description: 'Memory ID' },
304
+ },
305
+ required: ['memory_id'],
306
+ },
307
+ async execute(args: { memory_id: string }) {
308
+ try {
309
+ const memorySystem = getMemorySystem();
310
+ const score = await memorySystem.getTrustScore(args.memory_id);
311
+ if (score === null) {
312
+ return 'Memory not found or trust score unavailable';
313
+ }
314
+ return `Trust score: ${score.toFixed(3)}`;
315
+ } catch (error) {
316
+ return `Failed to get trust score: ${error instanceof Error ? error.message : String(error)}`;
317
+ }
318
+ },
319
+ },
320
+ },
321
+
322
+ event: async ({ event }: { event: unknown }) => {
323
+ const eventType = (event as { type?: string }).type;
324
+ if (eventType === 'session.created') {
325
+ try {
326
+ (ctx.client as { app?: { log?: (msg: string) => void } }).app?.log?.('Session created - Boomerang ready');
327
+ } catch {}
328
+ }
329
+ if (eventType === 'session.idle') {
330
+ try {
331
+ (ctx.client as { app?: { log?: (msg: string) => void } }).app?.log?.('Session idle - Boomerang orchestration available');
332
+ } catch {}
333
+ }
334
+ },
335
+
336
+ config: async (cfg: Record<string, unknown>) => {
337
+ cfg.boomerang = config;
338
+ },
339
+
340
+ cleanup: async () => {
341
+ // Cleanup if needed
342
+ },
343
+ };
344
+ };
345
+
346
+ export default BoomerangPlugin;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Boomerang Plugin Memory v4.0.0 - Memini Client Integration
3
+ *
4
+ * Uses memini-ai MCP client for memory operations.
5
+ * Maintains the same API surface as Super-Memory-TS integration.
6
+ */
7
+
8
+ import { getMeminiClient, MeminiClient } from '../../src/memini-client/index.js';
9
+ import type { MemoryEntry, SearchOptions } from './types.js';
10
+
11
+ const PROJECT_ID = process.env.BOOMERANG_PROJECT_ID || 'boomerang-plugin';
12
+
13
+ class PluginMemorySystem {
14
+ private static instance: PluginMemorySystem | null = null;
15
+ private _initialized: boolean = false;
16
+ private _client: MeminiClient;
17
+
18
+ private constructor() {
19
+ this._client = getMeminiClient();
20
+ }
21
+
22
+ static getInstance(): PluginMemorySystem {
23
+ if (!PluginMemorySystem.instance) {
24
+ PluginMemorySystem.instance = new PluginMemorySystem();
25
+ }
26
+ return PluginMemorySystem.instance;
27
+ }
28
+
29
+ isInitialized(): boolean {
30
+ return this._initialized || this._client.isInitialized();
31
+ }
32
+
33
+ async initialize(): Promise<void> {
34
+ if (this._initialized) {
35
+ return;
36
+ }
37
+ await this._client.initialize();
38
+ this._initialized = true;
39
+ }
40
+
41
+ async search(query: string, options?: Partial<SearchOptions>): Promise<{ entry: MemoryEntry; score: number }[]> {
42
+ this.ensureInitialized();
43
+
44
+ const topK = options?.topK || 10;
45
+ const results = await this._client.search(query, { topK });
46
+
47
+ return results.map((result) => ({
48
+ entry: {
49
+ id: result.entry.id,
50
+ text: result.entry.text,
51
+ sourceType: result.entry.sourceType as MemoryEntry['sourceType'],
52
+ sourcePath: result.entry.sourcePath || '',
53
+ timestamp: result.entry.timestamp,
54
+ metadataJson: result.entry.metadataJson || '{}',
55
+ },
56
+ score: result.score,
57
+ }));
58
+ }
59
+
60
+ async addMemory(entry: Partial<MemoryEntry>): Promise<MemoryEntry> {
61
+ this.ensureInitialized();
62
+
63
+ const input = {
64
+ text: entry.text || '',
65
+ sourceType: entry.sourceType || 'boomerang',
66
+ sourcePath: entry.sourcePath || '',
67
+ metadataJson: entry.metadataJson || '{}',
68
+ sessionId: entry.sessionId || '',
69
+ };
70
+
71
+ const result = await this._client.addMemory(input);
72
+ return result;
73
+ }
74
+
75
+ async saveContext(sessionId: string, context: string): Promise<MemoryEntry> {
76
+ return this.addMemory({
77
+ text: context,
78
+ sourceType: 'session',
79
+ sourcePath: `session://${sessionId}`,
80
+ metadataJson: JSON.stringify({ type: 'context' }),
81
+ sessionId,
82
+ });
83
+ }
84
+
85
+ async getTrustScore(memoryId: string): Promise<number | null> {
86
+ return this._client.getTrustScore(memoryId);
87
+ }
88
+
89
+ async adjustTrust(memoryId: string, signal: 'agent_used' | 'agent_ignored' | 'user_corrected' | 'user_confirmed'): Promise<void> {
90
+ return this._client.adjustTrust(memoryId, signal);
91
+ }
92
+
93
+ private ensureInitialized(): void {
94
+ if (!this.isInitialized()) {
95
+ throw new Error('PluginMemorySystem not initialized. Call initialize() first.');
96
+ }
97
+ }
98
+ }
99
+
100
+ let memoryInstance: PluginMemorySystem | null = null;
101
+
102
+ export function getMemorySystem(): PluginMemorySystem {
103
+ if (!memoryInstance) {
104
+ memoryInstance = PluginMemorySystem.getInstance();
105
+ }
106
+ return memoryInstance;
107
+ }
108
+
109
+ export { PluginMemorySystem };