opencode-swarm-plugin 0.42.5 → 0.42.7
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/.changeset/swarm-insights-data-layer.md +63 -0
- package/.hive/issues.jsonl +19 -1
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +54 -0
- package/README.md +147 -0
- package/bin/swarm.ts +4 -4
- package/dist/hive.d.ts +12 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.d.ts +86 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +236 -42
- package/dist/plugin.js +236 -42
- package/dist/schemas/cell.d.ts +2 -0
- package/dist/schemas/cell.d.ts.map +1 -1
- package/dist/swarm-insights.d.ts +155 -0
- package/dist/swarm-insights.d.ts.map +1 -0
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/examples/plugin-wrapper-template.ts +30 -0
- package/package.json +2 -2
- package/src/hive.integration.test.ts +105 -0
- package/src/hive.ts +8 -0
- package/src/index.ts +1 -0
- package/src/schemas/cell.ts +1 -0
- package/src/swarm-insights.test.ts +214 -0
- package/src/swarm-insights.ts +459 -0
- package/src/swarm-prompts.test.ts +165 -0
- package/src/swarm-prompts.ts +74 -56
package/dist/index.js
CHANGED
|
@@ -27652,6 +27652,178 @@ echo "Project directory: $1"
|
|
|
27652
27652
|
};
|
|
27653
27653
|
});
|
|
27654
27654
|
|
|
27655
|
+
// src/swarm-insights.ts
|
|
27656
|
+
var exports_swarm_insights = {};
|
|
27657
|
+
__export(exports_swarm_insights, {
|
|
27658
|
+
getStrategyInsights: () => getStrategyInsights,
|
|
27659
|
+
getPatternInsights: () => getPatternInsights,
|
|
27660
|
+
getFileInsights: () => getFileInsights,
|
|
27661
|
+
getCachedInsights: () => getCachedInsights,
|
|
27662
|
+
formatInsightsForPrompt: () => formatInsightsForPrompt,
|
|
27663
|
+
clearInsightsCache: () => clearInsightsCache
|
|
27664
|
+
});
|
|
27665
|
+
async function getStrategyInsights(swarmMail, _task) {
|
|
27666
|
+
const db = await swarmMail.getDatabase();
|
|
27667
|
+
const query = `
|
|
27668
|
+
SELECT
|
|
27669
|
+
json_extract(data, '$.strategy') as strategy,
|
|
27670
|
+
COUNT(*) as total_attempts,
|
|
27671
|
+
SUM(CASE WHEN json_extract(data, '$.success') = 'true' THEN 1 ELSE 0 END) as successes
|
|
27672
|
+
FROM events
|
|
27673
|
+
WHERE type = 'subtask_outcome'
|
|
27674
|
+
AND json_extract(data, '$.strategy') IS NOT NULL
|
|
27675
|
+
GROUP BY json_extract(data, '$.strategy')
|
|
27676
|
+
ORDER BY total_attempts DESC
|
|
27677
|
+
`;
|
|
27678
|
+
const result = await db.query(query, []);
|
|
27679
|
+
const rows = result.rows;
|
|
27680
|
+
return rows.map((row) => {
|
|
27681
|
+
const successRate = row.successes / row.total_attempts * 100;
|
|
27682
|
+
return {
|
|
27683
|
+
strategy: row.strategy,
|
|
27684
|
+
successRate: Math.round(successRate * 100) / 100,
|
|
27685
|
+
totalAttempts: row.total_attempts,
|
|
27686
|
+
recommendation: getStrategyRecommendation(row.strategy, successRate)
|
|
27687
|
+
};
|
|
27688
|
+
});
|
|
27689
|
+
}
|
|
27690
|
+
function getStrategyRecommendation(strategy, successRate) {
|
|
27691
|
+
if (successRate >= 80) {
|
|
27692
|
+
return `${strategy} is performing well (${successRate.toFixed(0)}% success)`;
|
|
27693
|
+
}
|
|
27694
|
+
if (successRate >= 60) {
|
|
27695
|
+
return `${strategy} is moderate - monitor for issues`;
|
|
27696
|
+
}
|
|
27697
|
+
if (successRate >= 40) {
|
|
27698
|
+
return `${strategy} has low success - consider alternatives`;
|
|
27699
|
+
}
|
|
27700
|
+
return `AVOID ${strategy} - high failure rate (${successRate.toFixed(0)}%)`;
|
|
27701
|
+
}
|
|
27702
|
+
async function getFileInsights(swarmMail, files) {
|
|
27703
|
+
if (files.length === 0)
|
|
27704
|
+
return [];
|
|
27705
|
+
const db = await swarmMail.getDatabase();
|
|
27706
|
+
const insights = [];
|
|
27707
|
+
for (const file2 of files) {
|
|
27708
|
+
const query = `
|
|
27709
|
+
SELECT
|
|
27710
|
+
COUNT(*) as failure_count,
|
|
27711
|
+
MAX(timestamp) as last_failure
|
|
27712
|
+
FROM events
|
|
27713
|
+
WHERE type = 'subtask_outcome'
|
|
27714
|
+
AND json_extract(data, '$.success') = 'false'
|
|
27715
|
+
AND json_extract(data, '$.files_touched') LIKE ?
|
|
27716
|
+
`;
|
|
27717
|
+
const result = await db.query(query, [`%${file2}%`]);
|
|
27718
|
+
const row = result.rows[0];
|
|
27719
|
+
if (row && row.failure_count > 0) {
|
|
27720
|
+
const gotchas = await getFileGotchas(swarmMail, file2);
|
|
27721
|
+
insights.push({
|
|
27722
|
+
file: file2,
|
|
27723
|
+
failureCount: row.failure_count,
|
|
27724
|
+
lastFailure: row.last_failure,
|
|
27725
|
+
gotchas
|
|
27726
|
+
});
|
|
27727
|
+
}
|
|
27728
|
+
}
|
|
27729
|
+
return insights;
|
|
27730
|
+
}
|
|
27731
|
+
async function getFileGotchas(_swarmMail, _file2) {
|
|
27732
|
+
return [];
|
|
27733
|
+
}
|
|
27734
|
+
async function getPatternInsights(swarmMail) {
|
|
27735
|
+
const db = await swarmMail.getDatabase();
|
|
27736
|
+
const patterns = [];
|
|
27737
|
+
const query = `
|
|
27738
|
+
SELECT
|
|
27739
|
+
json_extract(data, '$.error_type') as error_type,
|
|
27740
|
+
COUNT(*) as frequency
|
|
27741
|
+
FROM events
|
|
27742
|
+
WHERE type = 'subtask_outcome'
|
|
27743
|
+
AND json_extract(data, '$.success') = 'false'
|
|
27744
|
+
AND json_extract(data, '$.error_type') IS NOT NULL
|
|
27745
|
+
GROUP BY json_extract(data, '$.error_type')
|
|
27746
|
+
HAVING COUNT(*) >= 2
|
|
27747
|
+
ORDER BY frequency DESC
|
|
27748
|
+
LIMIT 5
|
|
27749
|
+
`;
|
|
27750
|
+
const result = await db.query(query, []);
|
|
27751
|
+
const rows = result.rows;
|
|
27752
|
+
for (const row of rows) {
|
|
27753
|
+
patterns.push({
|
|
27754
|
+
pattern: row.error_type,
|
|
27755
|
+
frequency: row.frequency,
|
|
27756
|
+
recommendation: getPatternRecommendation(row.error_type)
|
|
27757
|
+
});
|
|
27758
|
+
}
|
|
27759
|
+
return patterns;
|
|
27760
|
+
}
|
|
27761
|
+
function getPatternRecommendation(errorType) {
|
|
27762
|
+
const recommendations = {
|
|
27763
|
+
type_error: "Add explicit type annotations and null checks",
|
|
27764
|
+
timeout: "Consider breaking into smaller tasks",
|
|
27765
|
+
conflict: "Check file reservations before editing",
|
|
27766
|
+
test_failure: "Run tests incrementally during implementation"
|
|
27767
|
+
};
|
|
27768
|
+
return recommendations[errorType] || `Address ${errorType} issues`;
|
|
27769
|
+
}
|
|
27770
|
+
function formatInsightsForPrompt(bundle, options2 = {}) {
|
|
27771
|
+
const { maxTokens = 500 } = options2;
|
|
27772
|
+
const sections = [];
|
|
27773
|
+
if (bundle.strategies && bundle.strategies.length > 0) {
|
|
27774
|
+
const strategyLines = bundle.strategies.slice(0, 3).map((s) => `- ${s.strategy}: ${s.successRate.toFixed(0)}% success (${s.totalAttempts} attempts)`);
|
|
27775
|
+
sections.push(`**Strategy Performance:**
|
|
27776
|
+
${strategyLines.join(`
|
|
27777
|
+
`)}`);
|
|
27778
|
+
}
|
|
27779
|
+
if (bundle.files && bundle.files.length > 0) {
|
|
27780
|
+
const fileLines = bundle.files.slice(0, 5).map((f) => {
|
|
27781
|
+
const gotchaStr = f.gotchas.length > 0 ? ` - ${f.gotchas[0]}` : "";
|
|
27782
|
+
return `- ${f.file}: ${f.failureCount} past failures${gotchaStr}`;
|
|
27783
|
+
});
|
|
27784
|
+
sections.push(`**File-Specific Gotchas:**
|
|
27785
|
+
${fileLines.join(`
|
|
27786
|
+
`)}`);
|
|
27787
|
+
}
|
|
27788
|
+
if (bundle.patterns && bundle.patterns.length > 0) {
|
|
27789
|
+
const patternLines = bundle.patterns.slice(0, 3).map((p) => `- ${p.pattern} (${p.frequency}x): ${p.recommendation}`);
|
|
27790
|
+
sections.push(`**Common Pitfalls:**
|
|
27791
|
+
${patternLines.join(`
|
|
27792
|
+
`)}`);
|
|
27793
|
+
}
|
|
27794
|
+
if (sections.length === 0) {
|
|
27795
|
+
return "";
|
|
27796
|
+
}
|
|
27797
|
+
let result = sections.join(`
|
|
27798
|
+
|
|
27799
|
+
`);
|
|
27800
|
+
const maxChars = maxTokens * 4;
|
|
27801
|
+
if (result.length > maxChars) {
|
|
27802
|
+
result = result.slice(0, maxChars - 3) + "...";
|
|
27803
|
+
}
|
|
27804
|
+
return result;
|
|
27805
|
+
}
|
|
27806
|
+
async function getCachedInsights(_swarmMail, cacheKey, computeFn) {
|
|
27807
|
+
const cached5 = insightsCache.get(cacheKey);
|
|
27808
|
+
if (cached5 && cached5.expires > Date.now()) {
|
|
27809
|
+
return cached5.data;
|
|
27810
|
+
}
|
|
27811
|
+
const data = await computeFn();
|
|
27812
|
+
insightsCache.set(cacheKey, {
|
|
27813
|
+
data,
|
|
27814
|
+
expires: Date.now() + CACHE_TTL_MS
|
|
27815
|
+
});
|
|
27816
|
+
return data;
|
|
27817
|
+
}
|
|
27818
|
+
function clearInsightsCache() {
|
|
27819
|
+
insightsCache.clear();
|
|
27820
|
+
}
|
|
27821
|
+
var insightsCache, CACHE_TTL_MS;
|
|
27822
|
+
var init_swarm_insights = __esm(() => {
|
|
27823
|
+
insightsCache = new Map;
|
|
27824
|
+
CACHE_TTL_MS = 5 * 60 * 1000;
|
|
27825
|
+
});
|
|
27826
|
+
|
|
27655
27827
|
// src/model-selection.ts
|
|
27656
27828
|
var exports_model_selection = {};
|
|
27657
27829
|
__export(exports_model_selection, {
|
|
@@ -91404,6 +91576,7 @@ var CellQueryArgsSchema = exports_external.object({
|
|
|
91404
91576
|
status: CellStatusSchema.optional(),
|
|
91405
91577
|
type: CellTypeSchema.optional(),
|
|
91406
91578
|
ready: exports_external.boolean().optional(),
|
|
91579
|
+
parent_id: exports_external.string().optional(),
|
|
91407
91580
|
limit: exports_external.number().int().positive().default(20)
|
|
91408
91581
|
});
|
|
91409
91582
|
var SubtaskSpecSchema = exports_external.object({
|
|
@@ -92449,6 +92622,7 @@ var hive_query = tool({
|
|
|
92449
92622
|
status: tool.schema.enum(["open", "in_progress", "blocked", "closed"]).optional().describe("Filter by status"),
|
|
92450
92623
|
type: tool.schema.enum(["bug", "feature", "task", "epic", "chore"]).optional().describe("Filter by type"),
|
|
92451
92624
|
ready: tool.schema.boolean().optional().describe("Only show unblocked cells"),
|
|
92625
|
+
parent_id: tool.schema.string().optional().describe("Filter by parent epic ID (returns children of an epic)"),
|
|
92452
92626
|
limit: tool.schema.number().optional().describe("Max results to return (default: 20)")
|
|
92453
92627
|
},
|
|
92454
92628
|
async execute(args, ctx) {
|
|
@@ -92464,6 +92638,7 @@ var hive_query = tool({
|
|
|
92464
92638
|
cells = await adapter.queryCells(projectKey, {
|
|
92465
92639
|
status: validated.status,
|
|
92466
92640
|
type: validated.type,
|
|
92641
|
+
parent_id: validated.parent_id,
|
|
92467
92642
|
limit: validated.limit || 20
|
|
92468
92643
|
});
|
|
92469
92644
|
}
|
|
@@ -92600,6 +92775,7 @@ USE THIS TOOL TO:
|
|
|
92600
92775
|
- Find cells by type: hive_cells({ type: "bug" })
|
|
92601
92776
|
- Get a specific cell by partial ID: hive_cells({ id: "mjkmd" })
|
|
92602
92777
|
- Get the next ready (unblocked) cell: hive_cells({ ready: true })
|
|
92778
|
+
- Get children of an epic: hive_cells({ parent_id: "epic-id" })
|
|
92603
92779
|
- Combine filters: hive_cells({ status: "open", type: "task" })
|
|
92604
92780
|
|
|
92605
92781
|
RETURNS: Array of cells with id, title, status, priority, type, parent_id, created_at, updated_at
|
|
@@ -92613,6 +92789,7 @@ PREFER THIS OVER hive_query when you need to:
|
|
|
92613
92789
|
id: tool.schema.string().optional().describe("Partial or full cell ID to look up"),
|
|
92614
92790
|
status: tool.schema.enum(["open", "in_progress", "blocked", "closed"]).optional().describe("Filter by status"),
|
|
92615
92791
|
type: tool.schema.enum(["task", "bug", "feature", "epic", "chore"]).optional().describe("Filter by type"),
|
|
92792
|
+
parent_id: tool.schema.string().optional().describe("Filter by parent epic ID (returns children of an epic)"),
|
|
92616
92793
|
ready: tool.schema.boolean().optional().describe("If true, return only the next unblocked cell"),
|
|
92617
92794
|
limit: tool.schema.number().optional().describe("Max cells to return (default 20)")
|
|
92618
92795
|
},
|
|
@@ -92640,6 +92817,7 @@ PREFER THIS OVER hive_query when you need to:
|
|
|
92640
92817
|
const cells = await adapter.queryCells(projectKey, {
|
|
92641
92818
|
status: args.status,
|
|
92642
92819
|
type: args.type,
|
|
92820
|
+
parent_id: args.parent_id,
|
|
92643
92821
|
limit: args.limit || 20
|
|
92644
92822
|
});
|
|
92645
92823
|
const formatted = cells.map((c) => formatCellForOutput(c));
|
|
@@ -114092,38 +114270,28 @@ async function getPromptInsights(options2) {
|
|
|
114092
114270
|
}
|
|
114093
114271
|
async function getCoordinatorInsights(project_key) {
|
|
114094
114272
|
try {
|
|
114095
|
-
const { createLibSQLAdapter, createSwarmMailAdapter: createSwarmMailAdapter2
|
|
114273
|
+
const { createLibSQLAdapter, createSwarmMailAdapter: createSwarmMailAdapter2 } = await import("swarm-mail");
|
|
114274
|
+
const { getStrategyInsights: getStrategyInsights2, getPatternInsights: getPatternInsights2, formatInsightsForPrompt: formatInsightsForPrompt2 } = await Promise.resolve().then(() => (init_swarm_insights(), exports_swarm_insights));
|
|
114096
114275
|
const dbAdapter = await createLibSQLAdapter({ url: "file:./.swarm-mail/streams.db" });
|
|
114097
114276
|
const adapter = createSwarmMailAdapter2(dbAdapter, project_key || "default");
|
|
114098
|
-
const
|
|
114099
|
-
|
|
114100
|
-
|
|
114101
|
-
|
|
114277
|
+
const [strategies, patterns] = await Promise.all([
|
|
114278
|
+
getStrategyInsights2(adapter, ""),
|
|
114279
|
+
getPatternInsights2(adapter)
|
|
114280
|
+
]);
|
|
114281
|
+
const bundle = {
|
|
114282
|
+
strategies,
|
|
114283
|
+
patterns
|
|
114284
|
+
};
|
|
114285
|
+
const formatted = formatInsightsForPrompt2(bundle, { maxTokens: 500 });
|
|
114286
|
+
if (!formatted) {
|
|
114102
114287
|
return "";
|
|
114103
114288
|
}
|
|
114104
|
-
const rows = result.rows.map((r) => {
|
|
114105
|
-
const strategy = r.strategy || "unknown";
|
|
114106
|
-
const total = r.total_attempts || 0;
|
|
114107
|
-
const successRate = r.success_rate || 0;
|
|
114108
|
-
const emoji3 = successRate >= 80 ? "✅" : successRate >= 60 ? "⚠️" : "❌";
|
|
114109
|
-
return `| ${emoji3} ${strategy} | ${successRate.toFixed(1)}% | ${total} |`;
|
|
114110
|
-
});
|
|
114111
|
-
const topRows = rows.slice(0, 5);
|
|
114112
|
-
const antiPatterns = result.rows.filter((r) => r.success_rate < 60).map((r) => `- AVOID: ${r.strategy} strategy (${r.success_rate.toFixed(1)}% success rate)`).slice(0, 3);
|
|
114113
|
-
const antiPatternsSection = antiPatterns.length > 0 ? `
|
|
114114
|
-
|
|
114115
|
-
**Anti-Patterns:**
|
|
114116
|
-
${antiPatterns.join(`
|
|
114117
|
-
`)}` : "";
|
|
114118
114289
|
return `
|
|
114119
|
-
## \uD83D\uDCCA
|
|
114290
|
+
## \uD83D\uDCCA Historical Insights
|
|
114120
114291
|
|
|
114121
|
-
|
|
114122
|
-
|----------|--------------|----------------|
|
|
114123
|
-
${topRows.join(`
|
|
114124
|
-
`)}
|
|
114292
|
+
${formatted}
|
|
114125
114293
|
|
|
114126
|
-
**Use these
|
|
114294
|
+
**Use these learnings when selecting decomposition strategies and planning subtasks.**
|
|
114127
114295
|
`;
|
|
114128
114296
|
} catch (e) {
|
|
114129
114297
|
console.warn("Failed to get coordinator insights:", e);
|
|
@@ -114132,7 +114300,9 @@ ${topRows.join(`
|
|
|
114132
114300
|
}
|
|
114133
114301
|
async function getWorkerInsights(files, domain2) {
|
|
114134
114302
|
try {
|
|
114135
|
-
const
|
|
114303
|
+
const { createLibSQLAdapter, createSwarmMailAdapter: createSwarmMailAdapter2 } = await import("swarm-mail");
|
|
114304
|
+
const { getFileInsights: getFileInsights2, formatInsightsForPrompt: formatInsightsForPrompt2 } = await Promise.resolve().then(() => (init_swarm_insights(), exports_swarm_insights));
|
|
114305
|
+
const memoryAdapter = await getMemoryAdapter();
|
|
114136
114306
|
let query = "";
|
|
114137
114307
|
if (files && files.length > 0) {
|
|
114138
114308
|
const keywords = files.flatMap((f) => f.split(/[\/\\.]/).filter((part) => part.length > 2)).slice(0, 5);
|
|
@@ -114142,25 +114312,48 @@ async function getWorkerInsights(files, domain2) {
|
|
|
114142
114312
|
} else {
|
|
114143
114313
|
return "";
|
|
114144
114314
|
}
|
|
114145
|
-
const
|
|
114146
|
-
|
|
114147
|
-
|
|
114148
|
-
|
|
114149
|
-
|
|
114150
|
-
|
|
114151
|
-
|
|
114152
|
-
|
|
114153
|
-
|
|
114154
|
-
|
|
114155
|
-
|
|
114156
|
-
|
|
114157
|
-
|
|
114315
|
+
const [fileInsights, memoryResult] = await Promise.all([
|
|
114316
|
+
(async () => {
|
|
114317
|
+
if (!files || files.length === 0)
|
|
114318
|
+
return [];
|
|
114319
|
+
try {
|
|
114320
|
+
const dbAdapter = await createLibSQLAdapter({ url: "file:./.swarm-mail/streams.db" });
|
|
114321
|
+
const swarmMail = createSwarmMailAdapter2(dbAdapter, "default");
|
|
114322
|
+
return await getFileInsights2(swarmMail, files);
|
|
114323
|
+
} catch (e) {
|
|
114324
|
+
console.warn("Failed to get file insights from event store:", e);
|
|
114325
|
+
return [];
|
|
114326
|
+
}
|
|
114327
|
+
})(),
|
|
114328
|
+
memoryAdapter.find({
|
|
114329
|
+
query: `${query} gotcha pitfall pattern bug`,
|
|
114330
|
+
limit: 3
|
|
114331
|
+
})
|
|
114332
|
+
]);
|
|
114333
|
+
const bundle = {
|
|
114334
|
+
files: fileInsights
|
|
114335
|
+
};
|
|
114336
|
+
const formattedFileInsights = formatInsightsForPrompt2(bundle, { maxTokens: 300 });
|
|
114337
|
+
let formattedMemory = "";
|
|
114338
|
+
if (memoryResult.count > 0) {
|
|
114339
|
+
const learnings = memoryResult.results.map((r) => {
|
|
114340
|
+
const content = r.content.length > 150 ? r.content.slice(0, 150) + "..." : r.content;
|
|
114341
|
+
return `- ${content}`;
|
|
114342
|
+
});
|
|
114343
|
+
formattedMemory = `## \uD83D\uDCA1 Relevant Learnings (from past agents)
|
|
114158
114344
|
|
|
114159
114345
|
${learnings.join(`
|
|
114160
114346
|
`)}
|
|
114161
114347
|
|
|
114162
|
-
**Check semantic-memory for full details if needed
|
|
114163
|
-
|
|
114348
|
+
**Check semantic-memory for full details if needed.**`;
|
|
114349
|
+
}
|
|
114350
|
+
const sections = [formattedFileInsights, formattedMemory].filter((s) => s.length > 0);
|
|
114351
|
+
if (sections.length === 0) {
|
|
114352
|
+
return "";
|
|
114353
|
+
}
|
|
114354
|
+
return sections.join(`
|
|
114355
|
+
|
|
114356
|
+
`);
|
|
114164
114357
|
} catch (e) {
|
|
114165
114358
|
console.warn("Failed to get worker insights:", e);
|
|
114166
114359
|
return "";
|
|
@@ -121254,7 +121447,8 @@ var allTools = {
|
|
|
121254
121447
|
...repoCrawlTools,
|
|
121255
121448
|
...skillsTools,
|
|
121256
121449
|
...mandateTools,
|
|
121257
|
-
...memoryTools
|
|
121450
|
+
...memoryTools,
|
|
121451
|
+
...observabilityTools
|
|
121258
121452
|
};
|
|
121259
121453
|
export {
|
|
121260
121454
|
withToolFallback,
|