opencode-swarm-plugin 0.1.0 → 0.2.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.
package/src/index.ts CHANGED
@@ -265,3 +265,63 @@ export {
265
265
  formatSubtaskPrompt,
266
266
  formatEvaluationPrompt,
267
267
  } from "./swarm";
268
+
269
+ /**
270
+ * Re-export storage module
271
+ *
272
+ * Includes:
273
+ * - createStorage, createStorageWithFallback - Factory functions
274
+ * - getStorage, setStorage, resetStorage - Global instance management
275
+ * - InMemoryStorage, SemanticMemoryStorage - Storage implementations
276
+ * - isSemanticMemoryAvailable - Availability check
277
+ * - DEFAULT_STORAGE_CONFIG - Default configuration
278
+ *
279
+ * Types:
280
+ * - LearningStorage - Unified storage interface
281
+ * - StorageConfig, StorageBackend, StorageCollections - Configuration types
282
+ */
283
+ export {
284
+ createStorage,
285
+ createStorageWithFallback,
286
+ getStorage,
287
+ setStorage,
288
+ resetStorage,
289
+ InMemoryStorage,
290
+ SemanticMemoryStorage,
291
+ isSemanticMemoryAvailable,
292
+ DEFAULT_STORAGE_CONFIG,
293
+ type LearningStorage,
294
+ type StorageConfig,
295
+ type StorageBackend,
296
+ type StorageCollections,
297
+ } from "./storage";
298
+
299
+ /**
300
+ * Re-export tool-availability module
301
+ *
302
+ * Includes:
303
+ * - checkTool, isToolAvailable - Check individual tool availability
304
+ * - checkAllTools - Check all tools at once
305
+ * - withToolFallback, ifToolAvailable - Execute with graceful fallback
306
+ * - formatToolAvailability - Format availability for display
307
+ * - resetToolCache - Reset cached availability (for testing)
308
+ *
309
+ * Types:
310
+ * - ToolName - Supported tool names
311
+ * - ToolStatus, ToolAvailability - Status types
312
+ */
313
+ export {
314
+ checkTool,
315
+ isToolAvailable,
316
+ checkAllTools,
317
+ getToolAvailability,
318
+ withToolFallback,
319
+ ifToolAvailable,
320
+ warnMissingTool,
321
+ requireTool,
322
+ formatToolAvailability,
323
+ resetToolCache,
324
+ type ToolName,
325
+ type ToolStatus,
326
+ type ToolAvailability,
327
+ } from "./tool-availability";
@@ -7,7 +7,7 @@
7
7
  * These tests don't require external services - they test the learning
8
8
  * algorithms and their integration with swarm tools.
9
9
  */
10
- import { describe, it, expect, beforeEach } from "vitest";
10
+ import { describe, it, expect, beforeEach, vi } from "vitest";
11
11
 
12
12
  // Learning module
13
13
  import {
@@ -1102,3 +1102,328 @@ describe("InMemoryMaturityStorage", () => {
1102
1102
  expect(proven).toHaveLength(1);
1103
1103
  });
1104
1104
  });
1105
+
1106
+ // ============================================================================
1107
+ // Storage Module Tests
1108
+ // ============================================================================
1109
+
1110
+ import {
1111
+ createStorage,
1112
+ createStorageWithFallback,
1113
+ getStorage,
1114
+ setStorage,
1115
+ resetStorage,
1116
+ InMemoryStorage,
1117
+ SemanticMemoryStorage,
1118
+ isSemanticMemoryAvailable,
1119
+ type LearningStorage,
1120
+ } from "./storage";
1121
+
1122
+ describe("Storage Module", () => {
1123
+ describe("createStorage", () => {
1124
+ it("creates InMemoryStorage when backend is memory", () => {
1125
+ const storage = createStorage({ backend: "memory" });
1126
+ expect(storage).toBeInstanceOf(InMemoryStorage);
1127
+ });
1128
+
1129
+ it("creates SemanticMemoryStorage when backend is semantic-memory", () => {
1130
+ const storage = createStorage({ backend: "semantic-memory" });
1131
+ expect(storage).toBeInstanceOf(SemanticMemoryStorage);
1132
+ });
1133
+
1134
+ it("uses semantic-memory as default backend", () => {
1135
+ const storage = createStorage();
1136
+ expect(storage).toBeInstanceOf(SemanticMemoryStorage);
1137
+ });
1138
+
1139
+ it("throws on unknown backend", () => {
1140
+ expect(() => createStorage({ backend: "unknown" as any })).toThrow(
1141
+ "Unknown storage backend",
1142
+ );
1143
+ });
1144
+ });
1145
+
1146
+ describe("InMemoryStorage", () => {
1147
+ let storage: InMemoryStorage;
1148
+
1149
+ beforeEach(() => {
1150
+ storage = new InMemoryStorage();
1151
+ });
1152
+
1153
+ it("stores and retrieves feedback", async () => {
1154
+ const event = createFeedbackEvent("type_safe", "helpful");
1155
+ await storage.storeFeedback(event);
1156
+
1157
+ const all = await storage.getAllFeedback();
1158
+ expect(all).toHaveLength(1);
1159
+ expect(all[0].id).toBe(event.id);
1160
+ });
1161
+
1162
+ it("retrieves feedback by criterion", async () => {
1163
+ await storage.storeFeedback(createFeedbackEvent("type_safe", "helpful"));
1164
+ await storage.storeFeedback(createFeedbackEvent("no_bugs", "harmful"));
1165
+
1166
+ const typeSafe = await storage.getFeedbackByCriterion("type_safe");
1167
+ expect(typeSafe).toHaveLength(1);
1168
+ expect(typeSafe[0].criterion).toBe("type_safe");
1169
+ });
1170
+
1171
+ it("retrieves feedback by bead ID", async () => {
1172
+ const event1 = {
1173
+ ...createFeedbackEvent("type_safe", "helpful"),
1174
+ bead_id: "bead-1",
1175
+ };
1176
+ const event2 = {
1177
+ ...createFeedbackEvent("no_bugs", "harmful"),
1178
+ bead_id: "bead-2",
1179
+ };
1180
+
1181
+ await storage.storeFeedback(event1);
1182
+ await storage.storeFeedback(event2);
1183
+
1184
+ const bead1Events = await storage.getFeedbackByBead("bead-1");
1185
+ expect(bead1Events).toHaveLength(1);
1186
+ expect(bead1Events[0].bead_id).toBe("bead-1");
1187
+ });
1188
+
1189
+ it("finds similar feedback (returns all in memory)", async () => {
1190
+ await storage.storeFeedback(createFeedbackEvent("type_safe", "helpful"));
1191
+ await storage.storeFeedback(createFeedbackEvent("no_bugs", "harmful"));
1192
+
1193
+ const similar = await storage.findSimilarFeedback("type", 10);
1194
+ expect(similar.length).toBeGreaterThan(0);
1195
+ });
1196
+
1197
+ it("stores and retrieves patterns", async () => {
1198
+ const pattern = createPattern("Test pattern");
1199
+ await storage.storePattern(pattern);
1200
+
1201
+ const retrieved = await storage.getPattern(pattern.id);
1202
+ expect(retrieved).not.toBeNull();
1203
+ expect(retrieved!.content).toBe("Test pattern");
1204
+ });
1205
+
1206
+ it("retrieves all patterns", async () => {
1207
+ await storage.storePattern(createPattern("Pattern 1"));
1208
+ await storage.storePattern(createPattern("Pattern 2"));
1209
+
1210
+ const all = await storage.getAllPatterns();
1211
+ expect(all).toHaveLength(2);
1212
+ });
1213
+
1214
+ it("filters anti-patterns", async () => {
1215
+ const pattern = createPattern("Good pattern");
1216
+ const antiPattern = {
1217
+ ...createPattern("Bad pattern"),
1218
+ kind: "anti_pattern" as const,
1219
+ is_negative: true,
1220
+ };
1221
+
1222
+ await storage.storePattern(pattern);
1223
+ await storage.storePattern(antiPattern);
1224
+
1225
+ const antiPatterns = await storage.getAntiPatterns();
1226
+ expect(antiPatterns).toHaveLength(1);
1227
+ expect(antiPatterns[0].kind).toBe("anti_pattern");
1228
+ });
1229
+
1230
+ it("retrieves patterns by tag", async () => {
1231
+ const pattern1 = {
1232
+ ...createPattern("Pattern 1"),
1233
+ tags: ["decomposition"],
1234
+ };
1235
+ const pattern2 = { ...createPattern("Pattern 2"), tags: ["testing"] };
1236
+
1237
+ await storage.storePattern(pattern1);
1238
+ await storage.storePattern(pattern2);
1239
+
1240
+ const decompositionPatterns =
1241
+ await storage.getPatternsByTag("decomposition");
1242
+ expect(decompositionPatterns).toHaveLength(1);
1243
+ });
1244
+
1245
+ it("finds similar patterns by content", async () => {
1246
+ await storage.storePattern(createPattern("Split by file type"));
1247
+ await storage.storePattern(createPattern("Split by component"));
1248
+
1249
+ const similar = await storage.findSimilarPatterns("split");
1250
+ expect(similar.length).toBeGreaterThan(0);
1251
+ });
1252
+
1253
+ it("stores and retrieves maturity", async () => {
1254
+ const maturity = createPatternMaturity("pattern-1");
1255
+ await storage.storeMaturity(maturity);
1256
+
1257
+ const retrieved = await storage.getMaturity("pattern-1");
1258
+ expect(retrieved).not.toBeNull();
1259
+ expect(retrieved!.pattern_id).toBe("pattern-1");
1260
+ });
1261
+
1262
+ it("retrieves all maturity records", async () => {
1263
+ await storage.storeMaturity(createPatternMaturity("p1"));
1264
+ await storage.storeMaturity(createPatternMaturity("p2"));
1265
+
1266
+ const all = await storage.getAllMaturity();
1267
+ expect(all).toHaveLength(2);
1268
+ });
1269
+
1270
+ it("filters maturity by state", async () => {
1271
+ const candidate = createPatternMaturity("p1");
1272
+ const proven: PatternMaturity = {
1273
+ pattern_id: "p2",
1274
+ state: "proven",
1275
+ helpful_count: 10,
1276
+ harmful_count: 0,
1277
+ last_validated: new Date().toISOString(),
1278
+ };
1279
+
1280
+ await storage.storeMaturity(candidate);
1281
+ await storage.storeMaturity(proven);
1282
+
1283
+ const candidates = await storage.getMaturityByState("candidate");
1284
+ expect(candidates).toHaveLength(1);
1285
+ });
1286
+
1287
+ it("stores and retrieves maturity feedback", async () => {
1288
+ const feedback = createMaturityFeedback("pattern-1", "helpful");
1289
+ await storage.storeMaturityFeedback(feedback);
1290
+
1291
+ const retrieved = await storage.getMaturityFeedback("pattern-1");
1292
+ expect(retrieved).toHaveLength(1);
1293
+ expect(retrieved[0].type).toBe("helpful");
1294
+ });
1295
+
1296
+ it("closes without error", async () => {
1297
+ await expect(storage.close()).resolves.toBeUndefined();
1298
+ });
1299
+ });
1300
+
1301
+ describe("SemanticMemoryStorage", () => {
1302
+ let storage: SemanticMemoryStorage;
1303
+ let isAvailable: boolean;
1304
+
1305
+ beforeEach(async () => {
1306
+ isAvailable = await isSemanticMemoryAvailable();
1307
+ if (isAvailable) {
1308
+ storage = new SemanticMemoryStorage({
1309
+ collections: {
1310
+ feedback: "test-feedback",
1311
+ patterns: "test-patterns",
1312
+ maturity: "test-maturity",
1313
+ },
1314
+ });
1315
+ }
1316
+ });
1317
+
1318
+ it("skips tests if semantic-memory not available", async () => {
1319
+ if (!isAvailable) {
1320
+ expect(isAvailable).toBe(false);
1321
+ return;
1322
+ }
1323
+ expect(isAvailable).toBe(true);
1324
+ });
1325
+
1326
+ it("stores and retrieves feedback", async () => {
1327
+ if (!isAvailable) return;
1328
+
1329
+ const event = createFeedbackEvent("type_safe", "helpful");
1330
+ await storage.storeFeedback(event);
1331
+
1332
+ // Give semantic-memory time to index
1333
+ await new Promise((resolve) => setTimeout(resolve, 100));
1334
+
1335
+ const retrieved = await storage.getFeedbackByCriterion("type_safe");
1336
+ expect(retrieved.length).toBeGreaterThan(0);
1337
+ });
1338
+
1339
+ it("stores and retrieves patterns", async () => {
1340
+ if (!isAvailable) return;
1341
+
1342
+ const pattern = createPattern("Test pattern for semantic search");
1343
+ await storage.storePattern(pattern);
1344
+
1345
+ // Give semantic-memory time to persist
1346
+ await new Promise((resolve) => setTimeout(resolve, 100));
1347
+
1348
+ const retrieved = await storage.getPattern(pattern.id);
1349
+ expect(retrieved).not.toBeNull();
1350
+ expect(retrieved?.id).toBe(pattern.id);
1351
+ });
1352
+
1353
+ it("closes without error", async () => {
1354
+ if (!isAvailable) return;
1355
+
1356
+ await expect(storage.close()).resolves.toBeUndefined();
1357
+ });
1358
+ });
1359
+
1360
+ describe("createStorageWithFallback", () => {
1361
+ it("returns InMemoryStorage when backend is memory", async () => {
1362
+ const storage = await createStorageWithFallback({ backend: "memory" });
1363
+ expect(storage).toBeInstanceOf(InMemoryStorage);
1364
+ });
1365
+
1366
+ it("returns appropriate backend based on availability", async () => {
1367
+ const storage = await createStorageWithFallback();
1368
+ const isAvailable = await isSemanticMemoryAvailable();
1369
+
1370
+ if (isAvailable) {
1371
+ expect(storage).toBeInstanceOf(SemanticMemoryStorage);
1372
+ } else {
1373
+ expect(storage).toBeInstanceOf(InMemoryStorage);
1374
+ }
1375
+ });
1376
+ });
1377
+
1378
+ describe("Global Storage Management", () => {
1379
+ beforeEach(async () => {
1380
+ await resetStorage();
1381
+ });
1382
+
1383
+ it("getStorage returns a storage instance", async () => {
1384
+ const storage = await getStorage();
1385
+ expect(storage).toBeDefined();
1386
+ expect(storage).toHaveProperty("storeFeedback");
1387
+ expect(storage).toHaveProperty("storePattern");
1388
+ expect(storage).toHaveProperty("storeMaturity");
1389
+ });
1390
+
1391
+ it("getStorage returns same instance on multiple calls", async () => {
1392
+ const storage1 = await getStorage();
1393
+ const storage2 = await getStorage();
1394
+ expect(storage1).toBe(storage2);
1395
+ });
1396
+
1397
+ it("setStorage replaces global instance", async () => {
1398
+ const customStorage = new InMemoryStorage();
1399
+ setStorage(customStorage);
1400
+
1401
+ const retrieved = await getStorage();
1402
+ expect(retrieved).toBe(customStorage);
1403
+ });
1404
+
1405
+ it("resetStorage clears global instance", async () => {
1406
+ const storage1 = await getStorage();
1407
+ await resetStorage();
1408
+ const storage2 = await getStorage();
1409
+
1410
+ expect(storage1).not.toBe(storage2);
1411
+ });
1412
+
1413
+ it("resetStorage calls close on existing instance", async () => {
1414
+ const storage = await getStorage();
1415
+ const closeSpy = vi.spyOn(storage, "close");
1416
+
1417
+ await resetStorage();
1418
+
1419
+ expect(closeSpy).toHaveBeenCalled();
1420
+ });
1421
+ });
1422
+
1423
+ describe("isSemanticMemoryAvailable", () => {
1424
+ it("returns boolean", async () => {
1425
+ const available = await isSemanticMemoryAvailable();
1426
+ expect(typeof available).toBe("boolean");
1427
+ });
1428
+ });
1429
+ });