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/.beads/issues.jsonl +308 -0
- package/README.md +157 -11
- package/dist/index.js +804 -23
- package/dist/plugin.js +443 -23
- package/package.json +7 -3
- package/src/agent-mail.ts +46 -3
- package/src/index.ts +60 -0
- package/src/learning.integration.test.ts +326 -1
- package/src/storage.integration.test.ts +341 -0
- package/src/storage.ts +679 -0
- package/src/swarm.integration.test.ts +194 -3
- package/src/swarm.ts +185 -32
- package/src/tool-availability.ts +389 -0
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
|
+
});
|