opencode-swarm-plugin 0.44.1 → 0.45.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 (48) hide show
  1. package/README.md +277 -54
  2. package/bin/swarm.ts +3 -3
  3. package/dist/decision-trace-integration.d.ts +204 -0
  4. package/dist/decision-trace-integration.d.ts.map +1 -0
  5. package/dist/hive.d.ts.map +1 -1
  6. package/dist/hive.js +14834 -0
  7. package/dist/index.d.ts +50 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +640 -27
  10. package/dist/plugin.js +395 -27
  11. package/dist/query-tools.d.ts +20 -12
  12. package/dist/query-tools.d.ts.map +1 -1
  13. package/dist/swarm-decompose.d.ts +4 -4
  14. package/dist/swarm-decompose.d.ts.map +1 -1
  15. package/dist/swarm-prompts.d.ts.map +1 -1
  16. package/dist/swarm-prompts.js +39605 -0
  17. package/dist/swarm-review.d.ts.map +1 -1
  18. package/dist/swarm-signature.d.ts +106 -0
  19. package/dist/swarm-signature.d.ts.map +1 -0
  20. package/dist/swarm-strategies.d.ts +16 -3
  21. package/dist/swarm-strategies.d.ts.map +1 -1
  22. package/dist/swarm-validation.d.ts +127 -0
  23. package/dist/swarm-validation.d.ts.map +1 -0
  24. package/dist/swarm.d.ts +4 -2
  25. package/dist/swarm.d.ts.map +1 -1
  26. package/dist/validators/index.d.ts +7 -0
  27. package/dist/validators/index.d.ts.map +1 -0
  28. package/dist/validators/schema-validator.d.ts +58 -0
  29. package/dist/validators/schema-validator.d.ts.map +1 -0
  30. package/examples/commands/swarm.md +745 -0
  31. package/examples/plugin-wrapper-template.ts +2611 -0
  32. package/examples/skills/hive-workflow/SKILL.md +212 -0
  33. package/examples/skills/skill-creator/SKILL.md +223 -0
  34. package/examples/skills/swarm-coordination/SKILL.md +292 -0
  35. package/global-skills/cli-builder/SKILL.md +344 -0
  36. package/global-skills/cli-builder/references/advanced-patterns.md +244 -0
  37. package/global-skills/learning-systems/SKILL.md +644 -0
  38. package/global-skills/skill-creator/LICENSE.txt +202 -0
  39. package/global-skills/skill-creator/SKILL.md +352 -0
  40. package/global-skills/skill-creator/references/output-patterns.md +82 -0
  41. package/global-skills/skill-creator/references/workflows.md +28 -0
  42. package/global-skills/swarm-coordination/SKILL.md +995 -0
  43. package/global-skills/swarm-coordination/references/coordinator-patterns.md +235 -0
  44. package/global-skills/swarm-coordination/references/strategies.md +138 -0
  45. package/global-skills/system-design/SKILL.md +213 -0
  46. package/global-skills/testing-patterns/SKILL.md +430 -0
  47. package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +586 -0
  48. package/package.json +6 -3
package/dist/index.js CHANGED
@@ -13177,6 +13177,74 @@ var init_eval_capture = __esm(() => {
13177
13177
  inProgressRecords = new Map;
13178
13178
  });
13179
13179
 
13180
+ // src/swarm-validation.ts
13181
+ var exports_swarm_validation = {};
13182
+ __export(exports_swarm_validation, {
13183
+ runPostSwarmValidation: () => runPostSwarmValidation,
13184
+ reportIssue: () => reportIssue,
13185
+ ValidationIssueSeverity: () => ValidationIssueSeverity,
13186
+ ValidationIssueSchema: () => ValidationIssueSchema,
13187
+ ValidationIssueCategory: () => ValidationIssueCategory
13188
+ });
13189
+ async function runPostSwarmValidation(ctx, events) {
13190
+ const startTime = Date.now();
13191
+ const issues = [];
13192
+ await ctx.emit({
13193
+ type: "validation_started",
13194
+ project_key: ctx.project_key,
13195
+ timestamp: startTime,
13196
+ epic_id: ctx.epic_id,
13197
+ swarm_id: ctx.swarm_id,
13198
+ started_at: ctx.started_at.getTime()
13199
+ });
13200
+ const duration_ms = Date.now() - startTime;
13201
+ await ctx.emit({
13202
+ type: "validation_completed",
13203
+ project_key: ctx.project_key,
13204
+ timestamp: Date.now(),
13205
+ epic_id: ctx.epic_id,
13206
+ swarm_id: ctx.swarm_id,
13207
+ passed: issues.length === 0,
13208
+ issue_count: issues.length,
13209
+ duration_ms
13210
+ });
13211
+ return { passed: issues.length === 0, issues };
13212
+ }
13213
+ async function reportIssue(ctx, issue2) {
13214
+ await ctx.emit({
13215
+ type: "validation_issue",
13216
+ project_key: ctx.project_key,
13217
+ timestamp: new Date().toISOString(),
13218
+ epic_id: ctx.epic_id,
13219
+ severity: issue2.severity,
13220
+ category: issue2.category,
13221
+ message: issue2.message,
13222
+ location: issue2.location
13223
+ });
13224
+ }
13225
+ var ValidationIssueSeverity, ValidationIssueCategory, ValidationIssueSchema;
13226
+ var init_swarm_validation = __esm(() => {
13227
+ init_zod();
13228
+ ValidationIssueSeverity = exports_external.enum(["error", "warning", "info"]);
13229
+ ValidationIssueCategory = exports_external.enum([
13230
+ "schema_mismatch",
13231
+ "missing_event",
13232
+ "undefined_value",
13233
+ "dashboard_render",
13234
+ "websocket_delivery"
13235
+ ]);
13236
+ ValidationIssueSchema = exports_external.object({
13237
+ severity: ValidationIssueSeverity,
13238
+ category: ValidationIssueCategory,
13239
+ message: exports_external.string(),
13240
+ location: exports_external.object({
13241
+ event_type: exports_external.string().optional(),
13242
+ field: exports_external.string().optional(),
13243
+ component: exports_external.string().optional()
13244
+ }).optional()
13245
+ });
13246
+ });
13247
+
13180
13248
  // ../../node_modules/.bun/@ioredis+commands@1.4.0/node_modules/@ioredis/commands/built/commands.json
13181
13249
  var require_commands = __commonJS((exports, module) => {
13182
13250
  module.exports = {
@@ -22492,7 +22560,7 @@ __export(exports_swarm_strategies, {
22492
22560
  NEGATIVE_MARKERS: () => NEGATIVE_MARKERS,
22493
22561
  DecompositionStrategySchema: () => DecompositionStrategySchema
22494
22562
  });
22495
- function selectStrategy(task) {
22563
+ async function selectStrategy(task, projectKey) {
22496
22564
  const taskLower = task.toLowerCase();
22497
22565
  const scores = {
22498
22566
  "file-based": 0,
@@ -22520,7 +22588,8 @@ function selectStrategy(task) {
22520
22588
  const [winner, winnerScore] = entries[0];
22521
22589
  const [, runnerUpScore] = entries[1] || [null, 0];
22522
22590
  const totalScore = entries.reduce((sum, [, score]) => sum + score, 0);
22523
- const confidence = totalScore > 0 ? Math.min(0.95, 0.5 + (winnerScore - runnerUpScore) / totalScore) : 0.5;
22591
+ let confidence = totalScore > 0 ? Math.min(0.95, 0.5 + (winnerScore - runnerUpScore) / totalScore) : 0.5;
22592
+ const finalStrategy = winnerScore === 0 ? "feature-based" : winner;
22524
22593
  let reasoning;
22525
22594
  if (winnerScore === 0) {
22526
22595
  reasoning = `No strong keyword signals. Defaulting to feature-based as it's most versatile.`;
@@ -22528,12 +22597,73 @@ function selectStrategy(task) {
22528
22597
  const matchedKeywords = STRATEGIES[winner].keywords.filter((k) => taskLower.includes(k));
22529
22598
  reasoning = `Matched keywords: ${matchedKeywords.join(", ")}. ${STRATEGIES[winner].description}`;
22530
22599
  }
22531
- const finalStrategy = winnerScore === 0 ? "feature-based" : winner;
22600
+ let precedent;
22601
+ if (projectKey) {
22602
+ try {
22603
+ const {
22604
+ createLibSQLAdapter,
22605
+ getDatabasePath,
22606
+ findSimilarDecisions,
22607
+ getStrategySuccessRates
22608
+ } = await import("swarm-mail");
22609
+ const dbPath = getDatabasePath(projectKey);
22610
+ const db = await createLibSQLAdapter({ url: `file:${dbPath}` });
22611
+ const similarDecisions = await findSimilarDecisions(db, task, 5);
22612
+ const successRates = await getStrategySuccessRates(db);
22613
+ precedent = {
22614
+ similar_decisions: similarDecisions.length
22615
+ };
22616
+ if (similarDecisions.length > 0) {
22617
+ const epicIds = [];
22618
+ for (const decision of similarDecisions) {
22619
+ try {
22620
+ const decisionData = typeof decision.decision === "string" ? JSON.parse(decision.decision) : decision.decision;
22621
+ if (decisionData.epic_id) {
22622
+ epicIds.push(decisionData.epic_id);
22623
+ }
22624
+ } catch {}
22625
+ }
22626
+ if (epicIds.length > 0) {
22627
+ precedent.cited_epics = epicIds;
22628
+ }
22629
+ const precedentStrategies = similarDecisions.map((d) => {
22630
+ try {
22631
+ const data = typeof d.decision === "string" ? JSON.parse(d.decision) : d.decision;
22632
+ return data.strategy;
22633
+ } catch {
22634
+ return null;
22635
+ }
22636
+ }).filter(Boolean);
22637
+ const agreesWithKeywords = precedentStrategies.includes(finalStrategy);
22638
+ if (agreesWithKeywords) {
22639
+ confidence = Math.min(0.95, confidence + 0.1);
22640
+ reasoning += ` Precedent confirms: ${precedent.similar_decisions} similar decision(s) also chose ${finalStrategy}.`;
22641
+ }
22642
+ }
22643
+ const strategyRate = successRates.find((r) => r.strategy === finalStrategy);
22644
+ if (strategyRate) {
22645
+ precedent.strategy_success_rate = strategyRate.success_rate;
22646
+ if (strategyRate.success_rate >= 0.7) {
22647
+ confidence = Math.min(0.95, confidence + 0.05);
22648
+ reasoning += ` ${finalStrategy} has ${Math.round(strategyRate.success_rate * 100)}% success rate.`;
22649
+ } else if (strategyRate.success_rate < 0.3) {
22650
+ confidence = Math.max(0.1, confidence - 0.1);
22651
+ reasoning += ` Warning: ${finalStrategy} has low success rate (${Math.round(strategyRate.success_rate * 100)}%).`;
22652
+ }
22653
+ }
22654
+ if (db.close) {
22655
+ await db.close();
22656
+ }
22657
+ } catch (error45) {
22658
+ console.warn("Failed to query precedent data:", error45);
22659
+ }
22660
+ }
22532
22661
  return {
22533
22662
  strategy: finalStrategy,
22534
22663
  confidence,
22535
22664
  reasoning,
22536
- alternatives: entries.filter(([s]) => s !== finalStrategy).map(([strategy, score]) => ({ strategy, score }))
22665
+ alternatives: entries.filter(([s]) => s !== finalStrategy).map(([strategy, score]) => ({ strategy, score })),
22666
+ precedent
22537
22667
  };
22538
22668
  }
22539
22669
  function formatStrategyGuidelines(strategy) {
@@ -22742,13 +22872,14 @@ var init_swarm_strategies = __esm(() => {
22742
22872
  }
22743
22873
  };
22744
22874
  swarm_select_strategy = tool({
22745
- description: "Analyze task and recommend decomposition strategy (file-based, feature-based, or risk-based)",
22875
+ description: "Analyze task and recommend decomposition strategy (file-based, feature-based, or risk-based) with optional precedent data",
22746
22876
  args: {
22747
22877
  task: tool.schema.string().min(1).describe("Task description to analyze"),
22748
- codebase_context: tool.schema.string().optional().describe("Optional codebase context (file structure, tech stack, etc.)")
22878
+ codebase_context: tool.schema.string().optional().describe("Optional codebase context (file structure, tech stack, etc.)"),
22879
+ projectKey: tool.schema.string().optional().describe("Optional project path for precedent-aware strategy selection")
22749
22880
  },
22750
22881
  async execute(args) {
22751
- const result = selectStrategy(args.task);
22882
+ const result = await selectStrategy(args.task, args.projectKey);
22752
22883
  let enhancedReasoning = result.reasoning;
22753
22884
  if (args.codebase_context) {
22754
22885
  enhancedReasoning += `
@@ -22766,7 +22897,8 @@ Codebase context considered: ${args.codebase_context.slice(0, 200)}...`;
22766
22897
  strategy: alt.strategy,
22767
22898
  description: STRATEGIES[alt.strategy].description,
22768
22899
  score: alt.score
22769
- }))
22900
+ })),
22901
+ precedent: result.precedent
22770
22902
  }, null, 2);
22771
22903
  }
22772
22904
  });
@@ -38949,7 +39081,8 @@ import {
38949
39081
  importFromJSONL,
38950
39082
  syncMemories,
38951
39083
  getSwarmMailLibSQL,
38952
- resolvePartialId
39084
+ resolvePartialId,
39085
+ findCellsByPartialId
38953
39086
  } from "swarm-mail";
38954
39087
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
38955
39088
  import { join as join2 } from "node:path";
@@ -40216,6 +40349,37 @@ var hive_close = tool({
40216
40349
  total_files_touched: totalFilesTouched
40217
40350
  });
40218
40351
  await appendEvent2(swarmCompletedEvent, projectKey);
40352
+ try {
40353
+ const { runPostSwarmValidation: runPostSwarmValidation2 } = await Promise.resolve().then(() => (init_swarm_validation(), exports_swarm_validation));
40354
+ const { readEvents } = await import("swarm-mail");
40355
+ const swarmEvents = await readEvents({
40356
+ projectKey,
40357
+ types: [
40358
+ "swarm_started",
40359
+ "swarm_completed",
40360
+ "worker_spawned",
40361
+ "subtask_outcome",
40362
+ "decomposition_generated"
40363
+ ]
40364
+ }, projectKey);
40365
+ runPostSwarmValidation2({
40366
+ project_key: projectKey,
40367
+ epic_id: cellId,
40368
+ swarm_id: cellId,
40369
+ started_at: new Date,
40370
+ emit: async (event) => {
40371
+ await appendEvent2(event, projectKey);
40372
+ }
40373
+ }, swarmEvents).then((result) => {
40374
+ if (!result.passed) {
40375
+ console.warn(`[Validation] Found ${result.issues.length} issues in swarm ${cellId}`);
40376
+ }
40377
+ }).catch((err) => {
40378
+ console.error(`[Validation] Failed:`, err);
40379
+ });
40380
+ } catch (error45) {
40381
+ console.warn("[hive_close] Validation hook failed (non-fatal):", error45);
40382
+ }
40219
40383
  } catch (error45) {
40220
40384
  console.warn("[hive_close] Failed to emit SwarmCompletedEvent:", error45);
40221
40385
  }
@@ -40309,13 +40473,12 @@ PREFER THIS OVER hive_query when you need to:
40309
40473
  const adapter = await getHiveAdapter(projectKey);
40310
40474
  try {
40311
40475
  if (args.id) {
40312
- const fullId = await resolvePartialId(adapter, projectKey, args.id) || args.id;
40313
- const cell = await adapter.getCell(projectKey, fullId);
40314
- if (!cell) {
40476
+ const matchingCells = await findCellsByPartialId(adapter, projectKey, args.id);
40477
+ if (matchingCells.length === 0) {
40315
40478
  throw new HiveError(`No cell found matching ID '${args.id}'`, "hive_cells");
40316
40479
  }
40317
- const formatted2 = formatCellForOutput(cell);
40318
- return JSON.stringify([formatted2], null, 2);
40480
+ const formatted2 = matchingCells.map((c) => formatCellForOutput(c));
40481
+ return JSON.stringify(formatted2, null, 2);
40319
40482
  }
40320
40483
  if (args.ready) {
40321
40484
  const ready = await adapter.getNextReadyCell(projectKey);
@@ -40334,10 +40497,10 @@ PREFER THIS OVER hive_query when you need to:
40334
40497
  const formatted = cells.map((c) => formatCellForOutput(c));
40335
40498
  return JSON.stringify(formatted, null, 2);
40336
40499
  } catch (error45) {
40337
- const message = error45 instanceof Error ? error45.message : String(error45);
40338
- if (message.includes("Ambiguous hash")) {
40339
- throw new HiveError(`Ambiguous ID '${args.id}': multiple cells match. Please provide more characters.`, "hive_cells");
40500
+ if (error45 instanceof HiveError) {
40501
+ throw error45;
40340
40502
  }
40503
+ const message = error45 instanceof Error ? error45.message : String(error45);
40341
40504
  if (message.includes("Bead not found") || message.includes("Cell not found")) {
40342
40505
  throw new HiveError(`No cell found matching ID '${args.id || "unknown"}'`, "hive_cells");
40343
40506
  }
@@ -42784,6 +42947,139 @@ init_dist();
42784
42947
  init_zod();
42785
42948
  init_swarm_strategies();
42786
42949
  init_eval_capture();
42950
+
42951
+ // src/decision-trace-integration.ts
42952
+ import {
42953
+ createDecisionTrace,
42954
+ createEntityLink
42955
+ } from "swarm-mail";
42956
+ import { createLibSQLAdapter } from "swarm-mail";
42957
+ import { getDatabasePath } from "swarm-mail";
42958
+ async function getTraceDb(projectPath) {
42959
+ const dbPath = getDatabasePath(projectPath);
42960
+ return createLibSQLAdapter({ url: `file:${dbPath}` });
42961
+ }
42962
+ function extractMemoryIds(precedentCited) {
42963
+ if (!precedentCited) {
42964
+ return [];
42965
+ }
42966
+ if (precedentCited.memoryIds && Array.isArray(precedentCited.memoryIds)) {
42967
+ return precedentCited.memoryIds;
42968
+ }
42969
+ if (precedentCited.memoryId) {
42970
+ return [precedentCited.memoryId];
42971
+ }
42972
+ return [];
42973
+ }
42974
+ async function traceStrategySelection(input) {
42975
+ try {
42976
+ const db = await getTraceDb(input.projectKey);
42977
+ const trace = await createDecisionTrace(db, {
42978
+ decision_type: "strategy_selection",
42979
+ epic_id: input.epicId,
42980
+ bead_id: input.beadId,
42981
+ agent_name: input.agentName,
42982
+ project_key: input.projectKey,
42983
+ decision: {
42984
+ strategy: input.strategy,
42985
+ confidence: input.confidence,
42986
+ task_preview: input.taskPreview
42987
+ },
42988
+ rationale: input.reasoning,
42989
+ inputs_gathered: input.inputsGathered,
42990
+ alternatives: input.alternatives,
42991
+ precedent_cited: input.precedentCited
42992
+ });
42993
+ const memoryIds = extractMemoryIds(input.precedentCited);
42994
+ for (const memoryId of memoryIds) {
42995
+ await createEntityLink(db, {
42996
+ source_decision_id: trace.id,
42997
+ target_entity_type: "memory",
42998
+ target_entity_id: memoryId,
42999
+ link_type: "cites_precedent",
43000
+ strength: input.precedentCited?.similarity ?? 1,
43001
+ context: "Cited as precedent for strategy selection"
43002
+ });
43003
+ }
43004
+ await db.close?.();
43005
+ return trace.id;
43006
+ } catch (error45) {
43007
+ console.warn("[decision-trace] Failed to trace strategy_selection:", error45);
43008
+ return "";
43009
+ }
43010
+ }
43011
+ async function traceWorkerSpawn(input) {
43012
+ try {
43013
+ const db = await getTraceDb(input.projectKey);
43014
+ const trace = await createDecisionTrace(db, {
43015
+ decision_type: "worker_spawn",
43016
+ epic_id: input.epicId,
43017
+ bead_id: input.beadId,
43018
+ agent_name: input.agentName,
43019
+ project_key: input.projectKey,
43020
+ decision: {
43021
+ worker: input.workerName || "worker",
43022
+ subtask_title: input.subtaskTitle,
43023
+ files: input.files,
43024
+ model: input.model,
43025
+ spawn_order: input.spawnOrder,
43026
+ is_parallel: input.isParallel
43027
+ },
43028
+ rationale: input.rationale || `Spawning worker for: ${input.subtaskTitle}`
43029
+ });
43030
+ for (const file2 of input.files) {
43031
+ await createEntityLink(db, {
43032
+ source_decision_id: trace.id,
43033
+ target_entity_type: "file",
43034
+ target_entity_id: file2,
43035
+ link_type: "assigns_file",
43036
+ strength: 1,
43037
+ context: `File assigned to worker ${input.workerName || "worker"}`
43038
+ });
43039
+ }
43040
+ await db.close?.();
43041
+ return trace.id;
43042
+ } catch (error45) {
43043
+ console.warn("[decision-trace] Failed to trace worker_spawn:", error45);
43044
+ return "";
43045
+ }
43046
+ }
43047
+ async function traceReviewDecision(input) {
43048
+ try {
43049
+ const db = await getTraceDb(input.projectKey);
43050
+ const trace = await createDecisionTrace(db, {
43051
+ decision_type: "review_decision",
43052
+ epic_id: input.epicId,
43053
+ bead_id: input.beadId,
43054
+ agent_name: input.agentName,
43055
+ project_key: input.projectKey,
43056
+ decision: {
43057
+ status: input.status,
43058
+ worker_id: input.workerId,
43059
+ issues_count: input.issues?.length || 0,
43060
+ attempt_number: input.attemptNumber,
43061
+ remaining_attempts: input.remainingAttempts
43062
+ },
43063
+ rationale: input.rationale || input.summary || `Review ${input.status}`,
43064
+ inputs_gathered: input.issues ? [{ source: "code_review", issues: input.issues }] : undefined
43065
+ });
43066
+ await createEntityLink(db, {
43067
+ source_decision_id: trace.id,
43068
+ target_entity_type: "agent",
43069
+ target_entity_id: input.workerId,
43070
+ link_type: "reviewed_work_by",
43071
+ strength: 1,
43072
+ context: `Review ${input.status} for ${input.workerId}`
43073
+ });
43074
+ await db.close?.();
43075
+ return trace.id;
43076
+ } catch (error45) {
43077
+ console.warn("[decision-trace] Failed to trace review_decision:", error45);
43078
+ return "";
43079
+ }
43080
+ }
43081
+
43082
+ // src/swarm-decompose.ts
42787
43083
  var DECOMPOSITION_PROMPT = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
42788
43084
 
42789
43085
  ## Task
@@ -43215,7 +43511,7 @@ var swarm_delegate_planning = tool({
43215
43511
  selectedStrategy = args.strategy;
43216
43512
  strategyReasoning = `User-specified strategy: ${selectedStrategy}`;
43217
43513
  } else {
43218
- const selection = selectStrategy2(args.task);
43514
+ const selection = await selectStrategy2(args.task);
43219
43515
  selectedStrategy = selection.strategy;
43220
43516
  strategyReasoning = selection.reasoning;
43221
43517
  }
@@ -43257,6 +43553,27 @@ var swarm_delegate_planning = tool({
43257
43553
  } else {
43258
43554
  cassResultInfo = { queried: false, reason: "disabled" };
43259
43555
  }
43556
+ try {
43557
+ await traceStrategySelection({
43558
+ projectKey: args.context?.includes("/") ? args.context.split(" ")[0] : process.cwd(),
43559
+ agentName: "coordinator",
43560
+ epicId: undefined,
43561
+ strategy: selectedStrategy,
43562
+ reasoning: strategyReasoning,
43563
+ confidence: undefined,
43564
+ taskPreview: args.task.slice(0, 200),
43565
+ inputsGathered: cassResultInfo.queried ? [
43566
+ {
43567
+ source: "cass",
43568
+ query: args.task,
43569
+ results: cassResultInfo.results_found ?? 0
43570
+ }
43571
+ ] : undefined,
43572
+ alternatives: undefined
43573
+ });
43574
+ } catch (error45) {
43575
+ console.warn("[swarm_delegate_planning] Failed to trace strategy_selection:", error45);
43576
+ }
43260
43577
  let skillsContext = "";
43261
43578
  let skillsInfo = {
43262
43579
  included: false
@@ -43394,7 +43711,7 @@ Limit: ${memoryQuery.limit}`;
43394
43711
  }
43395
43712
  } catch (error45) {}
43396
43713
  if (mode === "fast") {
43397
- const strategyResult = selectStrategy2(args.task);
43714
+ const strategyResult = await selectStrategy2(args.task);
43398
43715
  const guidelines = formatStrategyGuidelines2(strategyResult.strategy);
43399
43716
  const output = {
43400
43717
  mode: "fast",
@@ -43413,7 +43730,7 @@ ${guidelines}`
43413
43730
  return JSON.stringify(output, null, 2);
43414
43731
  }
43415
43732
  if (mode === "auto") {
43416
- const strategyResult = selectStrategy2(args.task);
43733
+ const strategyResult = await selectStrategy2(args.task);
43417
43734
  const output = {
43418
43735
  mode: "auto",
43419
43736
  phase: "ready",
@@ -43508,7 +43825,7 @@ ${guidelines}`
43508
43825
  return JSON.stringify(output, null, 2);
43509
43826
  }
43510
43827
  if (currentPhase === "alternatives") {
43511
- const strategyResult = selectStrategy2(args.task);
43828
+ const strategyResult = await selectStrategy2(args.task);
43512
43829
  const alternatives = [];
43513
43830
  alternatives.push({
43514
43831
  name: strategyResult.strategy,
@@ -43535,7 +43852,7 @@ ${guidelines}`
43535
43852
  return JSON.stringify(output, null, 2);
43536
43853
  }
43537
43854
  if (currentPhase === "recommendation") {
43538
- const strategyResult = selectStrategy2(args.task);
43855
+ const strategyResult = await selectStrategy2(args.task);
43539
43856
  const guidelines = formatStrategyGuidelines2(strategyResult.strategy);
43540
43857
  const output = {
43541
43858
  mode: "socratic",
@@ -45606,6 +45923,22 @@ var swarm_review_feedback = tool({
45606
45923
  } catch (error45) {
45607
45924
  console.warn("[swarm_review_feedback] Failed to capture review_completed:", error45);
45608
45925
  }
45926
+ try {
45927
+ await traceReviewDecision({
45928
+ projectKey: args.project_key,
45929
+ agentName: "coordinator",
45930
+ epicId,
45931
+ beadId: args.task_id,
45932
+ workerId: args.worker_id,
45933
+ status: "approved",
45934
+ summary: args.summary,
45935
+ attemptNumber: 1,
45936
+ remainingAttempts: MAX_REVIEW_ATTEMPTS,
45937
+ rationale: args.summary || "Review approved"
45938
+ });
45939
+ } catch (error45) {
45940
+ console.warn("[swarm_review_feedback] Failed to trace review_decision:", error45);
45941
+ }
45609
45942
  try {
45610
45943
  const { createEvent: createEvent2, appendEvent: appendEvent2 } = await import("swarm-mail");
45611
45944
  const attempt = getReviewStatus(args.task_id).attempt_count || 1;
@@ -45660,6 +45993,23 @@ You may now complete the task with \`swarm_complete\`.`,
45660
45993
  } catch (error45) {
45661
45994
  console.warn("[swarm_review_feedback] Failed to capture review_completed:", error45);
45662
45995
  }
45996
+ try {
45997
+ await traceReviewDecision({
45998
+ projectKey: args.project_key,
45999
+ agentName: "coordinator",
46000
+ epicId,
46001
+ beadId: args.task_id,
46002
+ workerId: args.worker_id,
46003
+ status: "needs_changes",
46004
+ summary: args.summary,
46005
+ issues: parsedIssues,
46006
+ attemptNumber,
46007
+ remainingAttempts: remaining,
46008
+ rationale: args.summary || `Review rejected: ${parsedIssues.length} issue(s) found`
46009
+ });
46010
+ } catch (error45) {
46011
+ console.warn("[swarm_review_feedback] Failed to trace review_decision:", error45);
46012
+ }
45663
46013
  try {
45664
46014
  const { createEvent: createEvent2, appendEvent: appendEvent2 } = await import("swarm-mail");
45665
46015
  const status = remaining <= 0 ? "blocked" : "needs_changes";
@@ -61840,9 +62190,9 @@ async function getPromptInsights(options2) {
61840
62190
  }
61841
62191
  async function getCoordinatorInsights(project_key) {
61842
62192
  try {
61843
- const { createLibSQLAdapter, createSwarmMailAdapter: createSwarmMailAdapter2 } = await import("swarm-mail");
62193
+ const { createLibSQLAdapter: createLibSQLAdapter2, createSwarmMailAdapter: createSwarmMailAdapter2 } = await import("swarm-mail");
61844
62194
  const { getStrategyInsights: getStrategyInsights2, getPatternInsights: getPatternInsights2, formatInsightsForPrompt: formatInsightsForPrompt2 } = await Promise.resolve().then(() => (init_swarm_insights(), exports_swarm_insights));
61845
- const dbAdapter = await createLibSQLAdapter({ url: "file:./.swarm-mail/streams.db" });
62195
+ const dbAdapter = await createLibSQLAdapter2({ url: "file:./.swarm-mail/streams.db" });
61846
62196
  const adapter = createSwarmMailAdapter2(dbAdapter, project_key || "default");
61847
62197
  const [strategies, patterns] = await Promise.all([
61848
62198
  getStrategyInsights2(adapter, ""),
@@ -61870,7 +62220,7 @@ ${formatted}
61870
62220
  }
61871
62221
  async function getWorkerInsights(files, domain2) {
61872
62222
  try {
61873
- const { createLibSQLAdapter, createSwarmMailAdapter: createSwarmMailAdapter2 } = await import("swarm-mail");
62223
+ const { createLibSQLAdapter: createLibSQLAdapter2, createSwarmMailAdapter: createSwarmMailAdapter2 } = await import("swarm-mail");
61874
62224
  const { getFileInsights: getFileInsights2, formatInsightsForPrompt: formatInsightsForPrompt2 } = await Promise.resolve().then(() => (init_swarm_insights(), exports_swarm_insights));
61875
62225
  const memoryAdapter = await getMemoryAdapter();
61876
62226
  let query = "";
@@ -61887,7 +62237,7 @@ async function getWorkerInsights(files, domain2) {
61887
62237
  if (!files || files.length === 0)
61888
62238
  return [];
61889
62239
  try {
61890
- const dbAdapter = await createLibSQLAdapter({ url: "file:./.swarm-mail/streams.db" });
62240
+ const dbAdapter = await createLibSQLAdapter2({ url: "file:./.swarm-mail/streams.db" });
61891
62241
  const swarmMail = createSwarmMailAdapter2(dbAdapter, "default");
61892
62242
  return await getFileInsights2(swarmMail, files);
61893
62243
  } catch (e) {
@@ -62096,6 +62446,23 @@ var swarm_spawn_subtask = tool({
62096
62446
  } catch (error45) {
62097
62447
  console.warn("[swarm_spawn_subtask] Failed to capture worker_spawned:", error45);
62098
62448
  }
62449
+ try {
62450
+ await traceWorkerSpawn({
62451
+ projectKey: args2.project_path || process.cwd(),
62452
+ agentName: "coordinator",
62453
+ epicId: args2.epic_id,
62454
+ beadId: args2.bead_id,
62455
+ workerName: "worker",
62456
+ subtaskTitle: args2.subtask_title,
62457
+ files: args2.files,
62458
+ model: selectedModel,
62459
+ spawnOrder: 0,
62460
+ isParallel: false,
62461
+ rationale: args2.subtask_description || `Spawning worker for: ${args2.subtask_title}`
62462
+ });
62463
+ } catch (error45) {
62464
+ console.warn("[swarm_spawn_subtask] Failed to trace worker_spawn:", error45);
62465
+ }
62099
62466
  if (args2.project_path) {
62100
62467
  try {
62101
62468
  const { createEvent: createEvent3, appendEvent: appendEvent3 } = await import("swarm-mail");
@@ -62325,7 +62692,7 @@ var swarm_plan_prompt = tool({
62325
62692
  selectedStrategy = args2.strategy;
62326
62693
  strategyReasoning = `User-specified strategy: ${selectedStrategy}`;
62327
62694
  } else {
62328
- const selection = selectStrategy2(args2.task);
62695
+ const selection = await selectStrategy2(args2.task);
62329
62696
  selectedStrategy = selection.strategy;
62330
62697
  strategyReasoning = selection.reasoning;
62331
62698
  }
@@ -66430,6 +66797,243 @@ async function resetStorage() {
66430
66797
 
66431
66798
  // src/index.ts
66432
66799
  init_skills();
66800
+ init_swarm_validation();
66801
+
66802
+ // src/swarm-signature.ts
66803
+ function projectSwarmState(events) {
66804
+ const state = {
66805
+ isSwarm: false,
66806
+ subtasks: new Map,
66807
+ counts: {
66808
+ total: 0,
66809
+ created: 0,
66810
+ spawned: 0,
66811
+ inProgress: 0,
66812
+ completed: 0,
66813
+ closed: 0
66814
+ }
66815
+ };
66816
+ let hasEpic = false;
66817
+ let hasSpawn = false;
66818
+ for (const event of events) {
66819
+ state.lastEventAt = event.timestamp;
66820
+ switch (event.tool) {
66821
+ case "hive_create_epic": {
66822
+ const epicId = parseEpicId(event.output);
66823
+ const epicTitle = typeof event.input.epic_title === "string" ? event.input.epic_title : undefined;
66824
+ if (epicId) {
66825
+ state.epic = {
66826
+ id: epicId,
66827
+ title: epicTitle || "Unknown Epic",
66828
+ status: "open",
66829
+ createdAt: event.timestamp
66830
+ };
66831
+ hasEpic = true;
66832
+ const subtasks = event.input.subtasks;
66833
+ if (Array.isArray(subtasks)) {
66834
+ for (const subtask of subtasks) {
66835
+ if (typeof subtask === "object" && subtask !== null) {
66836
+ const st = subtask;
66837
+ const title = typeof st.title === "string" ? st.title : "Unknown";
66838
+ const files = Array.isArray(st.files) ? st.files : [];
66839
+ state.counts.created++;
66840
+ state.counts.total++;
66841
+ }
66842
+ }
66843
+ }
66844
+ const subtaskIds = parseSubtaskIds(event.output);
66845
+ for (const id of subtaskIds) {
66846
+ if (!state.subtasks.has(id)) {
66847
+ state.subtasks.set(id, {
66848
+ id,
66849
+ title: "Unknown",
66850
+ status: "created",
66851
+ files: []
66852
+ });
66853
+ state.counts.total++;
66854
+ state.counts.created++;
66855
+ }
66856
+ }
66857
+ }
66858
+ break;
66859
+ }
66860
+ case "swarm_spawn_subtask": {
66861
+ const beadId = typeof event.input.bead_id === "string" ? event.input.bead_id : undefined;
66862
+ const epicId = typeof event.input.epic_id === "string" ? event.input.epic_id : undefined;
66863
+ const title = typeof event.input.subtask_title === "string" ? event.input.subtask_title : "Unknown";
66864
+ const files = Array.isArray(event.input.files) ? event.input.files : [];
66865
+ if (beadId) {
66866
+ hasSpawn = true;
66867
+ const existing = state.subtasks.get(beadId);
66868
+ if (existing) {
66869
+ if (existing.status === "created") {
66870
+ state.counts.created--;
66871
+ state.counts.spawned++;
66872
+ }
66873
+ existing.status = "spawned";
66874
+ existing.title = title;
66875
+ existing.files = files;
66876
+ existing.spawnedAt = event.timestamp;
66877
+ } else {
66878
+ state.subtasks.set(beadId, {
66879
+ id: beadId,
66880
+ title,
66881
+ status: "spawned",
66882
+ files,
66883
+ spawnedAt: event.timestamp
66884
+ });
66885
+ state.counts.total++;
66886
+ state.counts.spawned++;
66887
+ }
66888
+ if (epicId && !state.epic) {
66889
+ state.epic = {
66890
+ id: epicId,
66891
+ title: "Unknown Epic",
66892
+ status: "in_progress",
66893
+ createdAt: event.timestamp
66894
+ };
66895
+ }
66896
+ }
66897
+ break;
66898
+ }
66899
+ case "hive_start": {
66900
+ const id = typeof event.input.id === "string" ? event.input.id : undefined;
66901
+ if (id) {
66902
+ const subtask = state.subtasks.get(id);
66903
+ if (subtask && subtask.status !== "completed" && subtask.status !== "closed") {
66904
+ if (subtask.status === "created")
66905
+ state.counts.created--;
66906
+ else if (subtask.status === "spawned")
66907
+ state.counts.spawned--;
66908
+ subtask.status = "in_progress";
66909
+ state.counts.inProgress++;
66910
+ }
66911
+ if (state.epic && state.epic.id === id) {
66912
+ state.epic.status = "in_progress";
66913
+ }
66914
+ }
66915
+ break;
66916
+ }
66917
+ case "swarm_complete": {
66918
+ const beadId = typeof event.input.bead_id === "string" ? event.input.bead_id : undefined;
66919
+ if (beadId) {
66920
+ const subtask = state.subtasks.get(beadId);
66921
+ if (subtask && subtask.status !== "closed") {
66922
+ if (subtask.status === "created")
66923
+ state.counts.created--;
66924
+ else if (subtask.status === "spawned")
66925
+ state.counts.spawned--;
66926
+ else if (subtask.status === "in_progress")
66927
+ state.counts.inProgress--;
66928
+ subtask.status = "completed";
66929
+ subtask.completedAt = event.timestamp;
66930
+ state.counts.completed++;
66931
+ }
66932
+ }
66933
+ break;
66934
+ }
66935
+ case "hive_close": {
66936
+ const id = typeof event.input.id === "string" ? event.input.id : undefined;
66937
+ if (id) {
66938
+ const subtask = state.subtasks.get(id);
66939
+ if (subtask) {
66940
+ if (subtask.status === "created")
66941
+ state.counts.created--;
66942
+ else if (subtask.status === "spawned")
66943
+ state.counts.spawned--;
66944
+ else if (subtask.status === "in_progress")
66945
+ state.counts.inProgress--;
66946
+ else if (subtask.status === "completed")
66947
+ state.counts.completed--;
66948
+ subtask.status = "closed";
66949
+ state.counts.closed++;
66950
+ }
66951
+ if (state.epic && state.epic.id === id) {
66952
+ state.epic.status = "closed";
66953
+ }
66954
+ }
66955
+ break;
66956
+ }
66957
+ case "swarmmail_init": {
66958
+ try {
66959
+ const parsed = JSON.parse(event.output);
66960
+ if (parsed.agent_name) {
66961
+ state.coordinatorName = parsed.agent_name;
66962
+ }
66963
+ if (parsed.project_key) {
66964
+ state.projectPath = parsed.project_key;
66965
+ }
66966
+ } catch {}
66967
+ break;
66968
+ }
66969
+ }
66970
+ }
66971
+ state.isSwarm = hasEpic && hasSpawn;
66972
+ return state;
66973
+ }
66974
+ function parseEpicId(output) {
66975
+ try {
66976
+ const parsed = JSON.parse(output);
66977
+ return parsed.epic?.id || parsed.id;
66978
+ } catch {
66979
+ return;
66980
+ }
66981
+ }
66982
+ function parseSubtaskIds(output) {
66983
+ try {
66984
+ const parsed = JSON.parse(output);
66985
+ const subtasks = parsed.subtasks || parsed.epic?.subtasks || [];
66986
+ return subtasks.map((s) => {
66987
+ if (typeof s === "object" && s !== null && "id" in s) {
66988
+ return s.id;
66989
+ }
66990
+ return;
66991
+ }).filter((id) => typeof id === "string");
66992
+ } catch {
66993
+ return [];
66994
+ }
66995
+ }
66996
+ function hasSwarmSignature(events) {
66997
+ let hasEpic = false;
66998
+ let hasSpawn = false;
66999
+ for (const event of events) {
67000
+ if (event.tool === "hive_create_epic") {
67001
+ hasEpic = true;
67002
+ } else if (event.tool === "swarm_spawn_subtask") {
67003
+ hasSpawn = true;
67004
+ }
67005
+ if (hasEpic && hasSpawn) {
67006
+ return true;
67007
+ }
67008
+ }
67009
+ return false;
67010
+ }
67011
+ function isSwarmActive(projection) {
67012
+ if (!projection.isSwarm) {
67013
+ return false;
67014
+ }
67015
+ return projection.counts.created > 0 || projection.counts.spawned > 0 || projection.counts.inProgress > 0 || projection.counts.completed > 0;
67016
+ }
67017
+ function getSwarmSummary(projection) {
67018
+ if (!projection.isSwarm) {
67019
+ return "No swarm detected";
67020
+ }
67021
+ const { counts, epic } = projection;
67022
+ const parts2 = [];
67023
+ if (epic) {
67024
+ parts2.push(`Epic: ${epic.id} - ${epic.title} [${epic.status}]`);
67025
+ }
67026
+ parts2.push(`Subtasks: ${counts.total} total (${counts.spawned} spawned, ${counts.inProgress} in_progress, ${counts.completed} completed, ${counts.closed} closed)`);
67027
+ if (isSwarmActive(projection)) {
67028
+ parts2.push("Status: ACTIVE - has pending work");
67029
+ } else {
67030
+ parts2.push("Status: COMPLETE - all work closed");
67031
+ }
67032
+ return parts2.join(`
67033
+ `);
67034
+ }
67035
+
67036
+ // src/index.ts
66433
67037
  var SwarmPlugin = async (input) => {
66434
67038
  const { $, directory, client } = input;
66435
67039
  setHiveWorkingDirectory(directory);
@@ -66608,18 +67212,21 @@ export {
66608
67212
  setAgentMailProjectDirectory,
66609
67213
  selectStrategy,
66610
67214
  scanSessionMessages,
67215
+ runPostSwarmValidation,
66611
67216
  resetToolCache,
66612
67217
  resetStorage,
66613
67218
  resetMemoryCache,
66614
67219
  resetMandateStorage,
66615
67220
  researchTools,
66616
67221
  requireTool,
67222
+ reportIssue,
66617
67223
  repoCrawlTools,
66618
67224
  recordPhaseStart,
66619
67225
  recordPhaseComplete,
66620
67226
  recordPatternSkipped,
66621
67227
  recordPatternExtracted,
66622
67228
  recordEvalRun,
67229
+ projectSwarmState,
66623
67230
  parseFrontmatter,
66624
67231
  migrateBeadsToHive,
66625
67232
  mergeHistoricBeads,
@@ -66630,6 +67237,7 @@ export {
66630
67237
  logger,
66631
67238
  listSkills,
66632
67239
  isToolAvailable,
67240
+ isSwarmActive,
66633
67241
  isStateTransitionEvent,
66634
67242
  isSemanticMemoryAvailable,
66635
67243
  isProjectNotFoundError,
@@ -66652,9 +67260,11 @@ export {
66652
67260
  hive_close,
66653
67261
  hive_cells,
66654
67262
  hiveTools,
67263
+ hasSwarmSignature,
66655
67264
  guardrailOutput,
66656
67265
  groupByTransition,
66657
67266
  getToolAvailability,
67267
+ getSwarmSummary,
66658
67268
  getSwarmMailProjectDirectory,
66659
67269
  getStorage,
66660
67270
  getStatusChanges,
@@ -66729,6 +67339,9 @@ export {
66729
67339
  VoteTypeSchema,
66730
67340
  VoteSchema,
66731
67341
  ValidationResultSchema,
67342
+ ValidationIssueSeverity,
67343
+ ValidationIssueSchema,
67344
+ ValidationIssueCategory,
66732
67345
  VARIANCE_THRESHOLD,
66733
67346
  UpdateSwarmContextArgsSchema,
66734
67347
  TaskDecompositionSchema,