opencode-swarm-plugin 0.37.0 → 0.39.1

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 (79) hide show
  1. package/.env +2 -0
  2. package/.hive/eval-results.json +26 -0
  3. package/.hive/issues.jsonl +20 -5
  4. package/.hive/memories.jsonl +35 -1
  5. package/.opencode/eval-history.jsonl +12 -0
  6. package/.turbo/turbo-build.log +4 -4
  7. package/.turbo/turbo-test.log +319 -319
  8. package/CHANGELOG.md +258 -0
  9. package/README.md +50 -0
  10. package/bin/swarm.test.ts +475 -0
  11. package/bin/swarm.ts +385 -208
  12. package/dist/compaction-hook.d.ts +1 -1
  13. package/dist/compaction-hook.d.ts.map +1 -1
  14. package/dist/compaction-prompt-scoring.d.ts +124 -0
  15. package/dist/compaction-prompt-scoring.d.ts.map +1 -0
  16. package/dist/eval-capture.d.ts +81 -1
  17. package/dist/eval-capture.d.ts.map +1 -1
  18. package/dist/eval-gates.d.ts +84 -0
  19. package/dist/eval-gates.d.ts.map +1 -0
  20. package/dist/eval-history.d.ts +117 -0
  21. package/dist/eval-history.d.ts.map +1 -0
  22. package/dist/eval-learning.d.ts +216 -0
  23. package/dist/eval-learning.d.ts.map +1 -0
  24. package/dist/hive.d.ts +59 -0
  25. package/dist/hive.d.ts.map +1 -1
  26. package/dist/index.d.ts +87 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +823 -131
  29. package/dist/plugin.js +655 -131
  30. package/dist/post-compaction-tracker.d.ts +133 -0
  31. package/dist/post-compaction-tracker.d.ts.map +1 -0
  32. package/dist/swarm-decompose.d.ts +30 -0
  33. package/dist/swarm-decompose.d.ts.map +1 -1
  34. package/dist/swarm-orchestrate.d.ts +23 -0
  35. package/dist/swarm-orchestrate.d.ts.map +1 -1
  36. package/dist/swarm-prompts.d.ts +25 -1
  37. package/dist/swarm-prompts.d.ts.map +1 -1
  38. package/dist/swarm.d.ts +19 -0
  39. package/dist/swarm.d.ts.map +1 -1
  40. package/evals/README.md +595 -94
  41. package/evals/compaction-prompt.eval.ts +149 -0
  42. package/evals/coordinator-behavior.eval.ts +8 -8
  43. package/evals/fixtures/compaction-prompt-cases.ts +305 -0
  44. package/evals/lib/compaction-loader.test.ts +248 -0
  45. package/evals/lib/compaction-loader.ts +320 -0
  46. package/evals/lib/data-loader.test.ts +345 -0
  47. package/evals/lib/data-loader.ts +107 -6
  48. package/evals/scorers/compaction-prompt-scorers.ts +145 -0
  49. package/evals/scorers/compaction-scorers.ts +13 -13
  50. package/evals/scorers/coordinator-discipline.evalite-test.ts +3 -2
  51. package/evals/scorers/coordinator-discipline.ts +13 -13
  52. package/examples/plugin-wrapper-template.ts +177 -8
  53. package/package.json +7 -2
  54. package/scripts/migrate-unknown-sessions.ts +349 -0
  55. package/src/compaction-capture.integration.test.ts +257 -0
  56. package/src/compaction-hook.test.ts +139 -2
  57. package/src/compaction-hook.ts +113 -2
  58. package/src/compaction-prompt-scorers.test.ts +299 -0
  59. package/src/compaction-prompt-scoring.ts +298 -0
  60. package/src/eval-capture.test.ts +422 -0
  61. package/src/eval-capture.ts +94 -2
  62. package/src/eval-gates.test.ts +306 -0
  63. package/src/eval-gates.ts +218 -0
  64. package/src/eval-history.test.ts +508 -0
  65. package/src/eval-history.ts +214 -0
  66. package/src/eval-learning.test.ts +378 -0
  67. package/src/eval-learning.ts +360 -0
  68. package/src/index.ts +61 -1
  69. package/src/post-compaction-tracker.test.ts +251 -0
  70. package/src/post-compaction-tracker.ts +237 -0
  71. package/src/swarm-decompose.test.ts +40 -47
  72. package/src/swarm-decompose.ts +2 -2
  73. package/src/swarm-orchestrate.test.ts +270 -7
  74. package/src/swarm-orchestrate.ts +100 -13
  75. package/src/swarm-prompts.test.ts +121 -0
  76. package/src/swarm-prompts.ts +297 -4
  77. package/src/swarm-research.integration.test.ts +157 -0
  78. package/src/swarm-review.ts +3 -3
  79. /package/evals/{evalite.config.ts → evalite.config.ts.bak} +0 -0
package/dist/plugin.js CHANGED
@@ -22178,6 +22178,383 @@ Codebase context considered: ${args.codebase_context.slice(0, 200)}...`;
22178
22178
  };
22179
22179
  });
22180
22180
 
22181
+ // src/eval-capture.ts
22182
+ var exports_eval_capture = {};
22183
+ __export(exports_eval_capture, {
22184
+ updateEvalRecord: () => updateEvalRecord,
22185
+ saveSession: () => saveSession,
22186
+ readSessionEvents: () => readSessionEvents,
22187
+ readPartialRecords: () => readPartialRecords,
22188
+ readEvalRecords: () => readEvalRecords,
22189
+ getSessionPath: () => getSessionPath,
22190
+ getSessionDir: () => getSessionDir,
22191
+ getEvalDataStats: () => getEvalDataStats,
22192
+ getEvalDataPath: () => getEvalDataPath,
22193
+ finalizeEvalRecord: () => finalizeEvalRecord,
22194
+ exportForEvalite: () => exportForEvalite,
22195
+ ensureSessionDir: () => ensureSessionDir,
22196
+ ensureEvalDataDir: () => ensureEvalDataDir,
22197
+ captureSubtaskOutcome: () => captureSubtaskOutcome,
22198
+ captureHumanFeedback: () => captureHumanFeedback,
22199
+ captureDecomposition: () => captureDecomposition,
22200
+ captureCoordinatorEvent: () => captureCoordinatorEvent,
22201
+ captureCompactionEvent: () => captureCompactionEvent,
22202
+ appendEvalRecord: () => appendEvalRecord,
22203
+ SubtaskOutcomeSchema: () => SubtaskOutcomeSchema,
22204
+ EvalRecordSchema: () => EvalRecordSchema,
22205
+ DEFAULT_EVAL_DATA_PATH: () => DEFAULT_EVAL_DATA_PATH,
22206
+ CoordinatorSessionSchema: () => CoordinatorSessionSchema,
22207
+ CoordinatorEventSchema: () => CoordinatorEventSchema
22208
+ });
22209
+ import * as fs from "node:fs";
22210
+ import * as os from "node:os";
22211
+ import * as path from "node:path";
22212
+ function getEvalDataPath(projectPath) {
22213
+ return path.join(projectPath, DEFAULT_EVAL_DATA_PATH);
22214
+ }
22215
+ function ensureEvalDataDir(projectPath) {
22216
+ const evalPath = getEvalDataPath(projectPath);
22217
+ const dir = path.dirname(evalPath);
22218
+ if (!fs.existsSync(dir)) {
22219
+ fs.mkdirSync(dir, { recursive: true });
22220
+ }
22221
+ }
22222
+ function appendEvalRecord(projectPath, record2) {
22223
+ ensureEvalDataDir(projectPath);
22224
+ const evalPath = getEvalDataPath(projectPath);
22225
+ const line = `${JSON.stringify(record2)}
22226
+ `;
22227
+ fs.appendFileSync(evalPath, line, "utf-8");
22228
+ }
22229
+ function readEvalRecords(projectPath) {
22230
+ const evalPath = getEvalDataPath(projectPath);
22231
+ if (!fs.existsSync(evalPath)) {
22232
+ return [];
22233
+ }
22234
+ const content = fs.readFileSync(evalPath, "utf-8");
22235
+ const lines = content.trim().split(`
22236
+ `).filter(Boolean);
22237
+ return lines.map((line) => {
22238
+ const parsed = JSON.parse(line);
22239
+ return EvalRecordSchema.parse(parsed);
22240
+ });
22241
+ }
22242
+ function readPartialRecords(projectPath) {
22243
+ const evalPath = getEvalDataPath(projectPath);
22244
+ if (!fs.existsSync(evalPath)) {
22245
+ return [];
22246
+ }
22247
+ const content = fs.readFileSync(evalPath, "utf-8");
22248
+ const lines = content.trim().split(`
22249
+ `).filter(Boolean);
22250
+ return lines.map((line) => JSON.parse(line));
22251
+ }
22252
+ function updateEvalRecord(projectPath, id, updates) {
22253
+ const records = readPartialRecords(projectPath);
22254
+ const index = records.findIndex((r) => r.id === id);
22255
+ if (index === -1) {
22256
+ return false;
22257
+ }
22258
+ records[index] = { ...records[index], ...updates };
22259
+ const evalPath = getEvalDataPath(projectPath);
22260
+ const content = `${records.map((r) => JSON.stringify(r)).join(`
22261
+ `)}
22262
+ `;
22263
+ fs.writeFileSync(evalPath, content, "utf-8");
22264
+ return true;
22265
+ }
22266
+ function captureDecomposition(params) {
22267
+ const record2 = {
22268
+ id: params.epicId,
22269
+ timestamp: new Date().toISOString(),
22270
+ project_path: params.projectPath,
22271
+ task: params.task,
22272
+ context: params.context,
22273
+ strategy: params.strategy,
22274
+ subtask_count: params.subtasks.length,
22275
+ epic_title: params.epicTitle,
22276
+ epic_description: params.epicDescription,
22277
+ subtasks: params.subtasks,
22278
+ outcomes: []
22279
+ };
22280
+ inProgressRecords.set(params.epicId, record2);
22281
+ appendEvalRecord(params.projectPath, record2);
22282
+ return record2;
22283
+ }
22284
+ function captureSubtaskOutcome(params) {
22285
+ const outcome = {
22286
+ bead_id: params.beadId,
22287
+ title: params.title,
22288
+ planned_files: params.plannedFiles,
22289
+ actual_files: params.actualFiles,
22290
+ duration_ms: params.durationMs,
22291
+ error_count: params.errorCount,
22292
+ retry_count: params.retryCount,
22293
+ success: params.success,
22294
+ failure_mode: params.failureMode
22295
+ };
22296
+ const record2 = inProgressRecords.get(params.epicId);
22297
+ if (record2) {
22298
+ record2.outcomes = record2.outcomes || [];
22299
+ record2.outcomes.push(outcome);
22300
+ }
22301
+ updateEvalRecord(params.projectPath, params.epicId, {
22302
+ outcomes: record2?.outcomes
22303
+ });
22304
+ }
22305
+ function finalizeEvalRecord(params) {
22306
+ const record2 = inProgressRecords.get(params.epicId);
22307
+ if (!record2 || !record2.outcomes || record2.outcomes.length === 0) {
22308
+ return null;
22309
+ }
22310
+ const outcomes = record2.outcomes;
22311
+ const overallSuccess = outcomes.every((o) => o.success);
22312
+ const totalDurationMs = outcomes.reduce((sum, o) => sum + o.duration_ms, 0);
22313
+ const totalErrors = outcomes.reduce((sum, o) => sum + o.error_count, 0);
22314
+ const allPlannedFiles = record2.subtasks?.flatMap((s) => s.files) || [];
22315
+ const fileOccurrences = new Map;
22316
+ for (const file2 of allPlannedFiles) {
22317
+ fileOccurrences.set(file2, (fileOccurrences.get(file2) || 0) + 1);
22318
+ }
22319
+ const fileOverlapCount = Array.from(fileOccurrences.values()).filter((count) => count > 1).length;
22320
+ const plannedFileSet = new Set(allPlannedFiles);
22321
+ const actualFileSet = new Set(outcomes.flatMap((o) => o.actual_files));
22322
+ const scopeAccuracy = plannedFileSet.size > 0 ? actualFileSet.size / plannedFileSet.size : 1;
22323
+ const durations = outcomes.map((o) => o.duration_ms).filter((d) => d > 0);
22324
+ const timeBalanceRatio = durations.length > 1 ? Math.max(...durations) / Math.min(...durations) : 1;
22325
+ const finalRecord = {
22326
+ ...record2,
22327
+ overall_success: overallSuccess,
22328
+ total_duration_ms: totalDurationMs,
22329
+ total_errors: totalErrors,
22330
+ file_overlap_count: fileOverlapCount,
22331
+ scope_accuracy: scopeAccuracy,
22332
+ time_balance_ratio: timeBalanceRatio
22333
+ };
22334
+ updateEvalRecord(params.projectPath, params.epicId, finalRecord);
22335
+ inProgressRecords.delete(params.epicId);
22336
+ return finalRecord;
22337
+ }
22338
+ function captureHumanFeedback(params) {
22339
+ updateEvalRecord(params.projectPath, params.epicId, {
22340
+ human_accepted: params.accepted,
22341
+ human_modified: params.modified,
22342
+ human_notes: params.notes
22343
+ });
22344
+ }
22345
+ function exportForEvalite(projectPath) {
22346
+ const records = readEvalRecords(projectPath);
22347
+ return records.filter((r) => r.outcomes && r.outcomes.length > 0).map((record2) => ({
22348
+ input: {
22349
+ task: record2.task,
22350
+ context: record2.context
22351
+ },
22352
+ expected: {
22353
+ minSubtasks: 2,
22354
+ subtaskCount: record2.subtask_count,
22355
+ requiredFiles: record2.subtasks.flatMap((s) => s.files),
22356
+ overallSuccess: record2.overall_success
22357
+ },
22358
+ actual: record2
22359
+ }));
22360
+ }
22361
+ function getEvalDataStats(projectPath) {
22362
+ const records = readEvalRecords(projectPath);
22363
+ const complete = records.filter((r) => r.outcomes && r.outcomes.length > 0);
22364
+ if (complete.length === 0) {
22365
+ return {
22366
+ totalRecords: records.length,
22367
+ completeRecords: 0,
22368
+ successRate: 0,
22369
+ avgSubtasks: 0,
22370
+ avgDurationMs: 0,
22371
+ avgScopeAccuracy: 0,
22372
+ avgTimeBalance: 0
22373
+ };
22374
+ }
22375
+ const successCount = complete.filter((r) => r.overall_success).length;
22376
+ const avgSubtasks = complete.reduce((sum, r) => sum + (r.outcomes?.length || 0), 0) / complete.length;
22377
+ const avgDurationMs = complete.reduce((sum, r) => sum + (r.total_duration_ms || 0), 0) / complete.length;
22378
+ const avgScopeAccuracy = complete.reduce((sum, r) => sum + (r.scope_accuracy || 1), 0) / complete.length;
22379
+ const avgTimeBalance = complete.reduce((sum, r) => sum + (r.time_balance_ratio || 1), 0) / complete.length;
22380
+ return {
22381
+ totalRecords: records.length,
22382
+ completeRecords: complete.length,
22383
+ successRate: successCount / complete.length,
22384
+ avgSubtasks,
22385
+ avgDurationMs,
22386
+ avgScopeAccuracy,
22387
+ avgTimeBalance
22388
+ };
22389
+ }
22390
+ function getSessionDir() {
22391
+ return path.join(os.homedir(), ".config", "swarm-tools", "sessions");
22392
+ }
22393
+ function getSessionPath(sessionId) {
22394
+ return path.join(getSessionDir(), `${sessionId}.jsonl`);
22395
+ }
22396
+ function ensureSessionDir() {
22397
+ const sessionDir = getSessionDir();
22398
+ if (!fs.existsSync(sessionDir)) {
22399
+ fs.mkdirSync(sessionDir, { recursive: true });
22400
+ }
22401
+ }
22402
+ function captureCoordinatorEvent(event) {
22403
+ CoordinatorEventSchema.parse(event);
22404
+ ensureSessionDir();
22405
+ const sessionPath = getSessionPath(event.session_id);
22406
+ const line = `${JSON.stringify(event)}
22407
+ `;
22408
+ fs.appendFileSync(sessionPath, line, "utf-8");
22409
+ }
22410
+ function captureCompactionEvent(params) {
22411
+ const event = {
22412
+ session_id: params.session_id,
22413
+ epic_id: params.epic_id,
22414
+ timestamp: new Date().toISOString(),
22415
+ event_type: "COMPACTION",
22416
+ compaction_type: params.compaction_type,
22417
+ payload: params.payload
22418
+ };
22419
+ captureCoordinatorEvent(event);
22420
+ }
22421
+ function readSessionEvents(sessionId) {
22422
+ const sessionPath = getSessionPath(sessionId);
22423
+ if (!fs.existsSync(sessionPath)) {
22424
+ return [];
22425
+ }
22426
+ const content = fs.readFileSync(sessionPath, "utf-8");
22427
+ const lines = content.trim().split(`
22428
+ `).filter(Boolean);
22429
+ return lines.map((line) => {
22430
+ const parsed = JSON.parse(line);
22431
+ return CoordinatorEventSchema.parse(parsed);
22432
+ });
22433
+ }
22434
+ function saveSession(params) {
22435
+ const events = readSessionEvents(params.session_id);
22436
+ if (events.length === 0) {
22437
+ return null;
22438
+ }
22439
+ const timestamps = events.map((e) => new Date(e.timestamp).getTime());
22440
+ const startTime = new Date(Math.min(...timestamps)).toISOString();
22441
+ const endTime = new Date(Math.max(...timestamps)).toISOString();
22442
+ const session = {
22443
+ session_id: params.session_id,
22444
+ epic_id: params.epic_id,
22445
+ start_time: startTime,
22446
+ end_time: endTime,
22447
+ events
22448
+ };
22449
+ return session;
22450
+ }
22451
+ var SubtaskOutcomeSchema, EvalRecordSchema, CoordinatorEventSchema, CoordinatorSessionSchema, DEFAULT_EVAL_DATA_PATH = ".opencode/eval-data.jsonl", inProgressRecords;
22452
+ var init_eval_capture = __esm(() => {
22453
+ init_zod();
22454
+ SubtaskOutcomeSchema = exports_external.object({
22455
+ bead_id: exports_external.string(),
22456
+ title: exports_external.string(),
22457
+ planned_files: exports_external.array(exports_external.string()),
22458
+ actual_files: exports_external.array(exports_external.string()),
22459
+ duration_ms: exports_external.number().int().min(0),
22460
+ error_count: exports_external.number().int().min(0),
22461
+ retry_count: exports_external.number().int().min(0),
22462
+ success: exports_external.boolean(),
22463
+ failure_mode: exports_external.string().optional()
22464
+ });
22465
+ EvalRecordSchema = exports_external.object({
22466
+ id: exports_external.string(),
22467
+ timestamp: exports_external.string(),
22468
+ project_path: exports_external.string(),
22469
+ task: exports_external.string(),
22470
+ context: exports_external.string().optional(),
22471
+ strategy: exports_external.enum(["file-based", "feature-based", "risk-based", "auto"]),
22472
+ subtask_count: exports_external.number().int().min(1),
22473
+ epic_title: exports_external.string(),
22474
+ epic_description: exports_external.string().optional(),
22475
+ subtasks: exports_external.array(exports_external.object({
22476
+ title: exports_external.string(),
22477
+ description: exports_external.string().optional(),
22478
+ files: exports_external.array(exports_external.string()),
22479
+ dependencies: exports_external.array(exports_external.number()).optional(),
22480
+ estimated_complexity: exports_external.number().int().min(1).max(5).optional()
22481
+ })),
22482
+ outcomes: exports_external.array(SubtaskOutcomeSchema).optional(),
22483
+ overall_success: exports_external.boolean().optional(),
22484
+ total_duration_ms: exports_external.number().int().min(0).optional(),
22485
+ total_errors: exports_external.number().int().min(0).optional(),
22486
+ human_accepted: exports_external.boolean().optional(),
22487
+ human_modified: exports_external.boolean().optional(),
22488
+ human_notes: exports_external.string().optional(),
22489
+ file_overlap_count: exports_external.number().int().min(0).optional(),
22490
+ scope_accuracy: exports_external.number().min(0).max(2).optional(),
22491
+ time_balance_ratio: exports_external.number().min(1).optional()
22492
+ });
22493
+ CoordinatorEventSchema = exports_external.discriminatedUnion("event_type", [
22494
+ exports_external.object({
22495
+ session_id: exports_external.string(),
22496
+ epic_id: exports_external.string(),
22497
+ timestamp: exports_external.string(),
22498
+ event_type: exports_external.literal("DECISION"),
22499
+ decision_type: exports_external.enum([
22500
+ "strategy_selected",
22501
+ "worker_spawned",
22502
+ "review_completed",
22503
+ "decomposition_complete"
22504
+ ]),
22505
+ payload: exports_external.any()
22506
+ }),
22507
+ exports_external.object({
22508
+ session_id: exports_external.string(),
22509
+ epic_id: exports_external.string(),
22510
+ timestamp: exports_external.string(),
22511
+ event_type: exports_external.literal("VIOLATION"),
22512
+ violation_type: exports_external.enum([
22513
+ "coordinator_edited_file",
22514
+ "coordinator_ran_tests",
22515
+ "coordinator_reserved_files",
22516
+ "no_worker_spawned"
22517
+ ]),
22518
+ payload: exports_external.any()
22519
+ }),
22520
+ exports_external.object({
22521
+ session_id: exports_external.string(),
22522
+ epic_id: exports_external.string(),
22523
+ timestamp: exports_external.string(),
22524
+ event_type: exports_external.literal("OUTCOME"),
22525
+ outcome_type: exports_external.enum([
22526
+ "subtask_success",
22527
+ "subtask_retry",
22528
+ "subtask_failed",
22529
+ "epic_complete"
22530
+ ]),
22531
+ payload: exports_external.any()
22532
+ }),
22533
+ exports_external.object({
22534
+ session_id: exports_external.string(),
22535
+ epic_id: exports_external.string(),
22536
+ timestamp: exports_external.string(),
22537
+ event_type: exports_external.literal("COMPACTION"),
22538
+ compaction_type: exports_external.enum([
22539
+ "detection_complete",
22540
+ "prompt_generated",
22541
+ "context_injected",
22542
+ "resumption_started",
22543
+ "tool_call_tracked"
22544
+ ]),
22545
+ payload: exports_external.any()
22546
+ })
22547
+ ]);
22548
+ CoordinatorSessionSchema = exports_external.object({
22549
+ session_id: exports_external.string(),
22550
+ epic_id: exports_external.string(),
22551
+ start_time: exports_external.string(),
22552
+ end_time: exports_external.string().optional(),
22553
+ events: exports_external.array(CoordinatorEventSchema)
22554
+ });
22555
+ inProgressRecords = new Map;
22556
+ });
22557
+
22181
22558
  // src/learning.ts
22182
22559
  var exports_learning = {};
22183
22560
  __export(exports_learning, {
@@ -39165,6 +39542,71 @@ var hive_ready = tool({
39165
39542
  }
39166
39543
  }
39167
39544
  });
39545
+ var hive_cells = tool({
39546
+ description: `Query cells from the hive database with flexible filtering.
39547
+
39548
+ USE THIS TOOL TO:
39549
+ - List all open cells: hive_cells()
39550
+ - Find cells by status: hive_cells({ status: "in_progress" })
39551
+ - Find cells by type: hive_cells({ type: "bug" })
39552
+ - Get a specific cell by partial ID: hive_cells({ id: "mjkmd" })
39553
+ - Get the next ready (unblocked) cell: hive_cells({ ready: true })
39554
+ - Combine filters: hive_cells({ status: "open", type: "task" })
39555
+
39556
+ RETURNS: Array of cells with id, title, status, priority, type, parent_id, created_at, updated_at
39557
+
39558
+ PREFER THIS OVER hive_query when you need to:
39559
+ - See what work is available
39560
+ - Check status of multiple cells
39561
+ - Find cells matching criteria
39562
+ - Look up a cell by partial ID`,
39563
+ args: {
39564
+ id: tool.schema.string().optional().describe("Partial or full cell ID to look up"),
39565
+ status: tool.schema.enum(["open", "in_progress", "blocked", "closed"]).optional().describe("Filter by status"),
39566
+ type: tool.schema.enum(["task", "bug", "feature", "epic", "chore"]).optional().describe("Filter by type"),
39567
+ ready: tool.schema.boolean().optional().describe("If true, return only the next unblocked cell"),
39568
+ limit: tool.schema.number().optional().describe("Max cells to return (default 20)")
39569
+ },
39570
+ async execute(args, ctx) {
39571
+ const projectKey = getHiveWorkingDirectory();
39572
+ const adapter = await getHiveAdapter(projectKey);
39573
+ try {
39574
+ if (args.id) {
39575
+ const fullId = await resolvePartialId(adapter, projectKey, args.id) || args.id;
39576
+ const cell = await adapter.getCell(projectKey, fullId);
39577
+ if (!cell) {
39578
+ throw new HiveError(`No cell found matching ID '${args.id}'`, "hive_cells");
39579
+ }
39580
+ const formatted2 = formatCellForOutput(cell);
39581
+ return JSON.stringify([formatted2], null, 2);
39582
+ }
39583
+ if (args.ready) {
39584
+ const ready = await adapter.getNextReadyCell(projectKey);
39585
+ if (!ready) {
39586
+ return JSON.stringify([], null, 2);
39587
+ }
39588
+ const formatted2 = formatCellForOutput(ready);
39589
+ return JSON.stringify([formatted2], null, 2);
39590
+ }
39591
+ const cells = await adapter.queryCells(projectKey, {
39592
+ status: args.status,
39593
+ type: args.type,
39594
+ limit: args.limit || 20
39595
+ });
39596
+ const formatted = cells.map((c) => formatCellForOutput(c));
39597
+ return JSON.stringify(formatted, null, 2);
39598
+ } catch (error45) {
39599
+ const message = error45 instanceof Error ? error45.message : String(error45);
39600
+ if (message.includes("Ambiguous hash")) {
39601
+ throw new HiveError(`Ambiguous ID '${args.id}': multiple cells match. Please provide more characters.`, "hive_cells");
39602
+ }
39603
+ if (message.includes("Bead not found") || message.includes("Cell not found")) {
39604
+ throw new HiveError(`No cell found matching ID '${args.id || "unknown"}'`, "hive_cells");
39605
+ }
39606
+ throw new HiveError(`Failed to query cells: ${message}`, "hive_cells");
39607
+ }
39608
+ }
39609
+ });
39168
39610
  var hive_sync = tool({
39169
39611
  description: "Sync hive to git and push (MANDATORY at session end)",
39170
39612
  args: {
@@ -39306,6 +39748,7 @@ var hiveTools = {
39306
39748
  hive_close,
39307
39749
  hive_start,
39308
39750
  hive_ready,
39751
+ hive_cells,
39309
39752
  hive_sync,
39310
39753
  hive_link_thread
39311
39754
  };
@@ -41464,122 +41907,7 @@ init_swarm_strategies();
41464
41907
  init_dist();
41465
41908
  init_zod();
41466
41909
  init_swarm_strategies();
41467
-
41468
- // src/eval-capture.ts
41469
- init_zod();
41470
- import * as fs from "node:fs";
41471
- import * as os from "node:os";
41472
- import * as path from "node:path";
41473
- var SubtaskOutcomeSchema = exports_external.object({
41474
- bead_id: exports_external.string(),
41475
- title: exports_external.string(),
41476
- planned_files: exports_external.array(exports_external.string()),
41477
- actual_files: exports_external.array(exports_external.string()),
41478
- duration_ms: exports_external.number().int().min(0),
41479
- error_count: exports_external.number().int().min(0),
41480
- retry_count: exports_external.number().int().min(0),
41481
- success: exports_external.boolean(),
41482
- failure_mode: exports_external.string().optional()
41483
- });
41484
- var EvalRecordSchema = exports_external.object({
41485
- id: exports_external.string(),
41486
- timestamp: exports_external.string(),
41487
- project_path: exports_external.string(),
41488
- task: exports_external.string(),
41489
- context: exports_external.string().optional(),
41490
- strategy: exports_external.enum(["file-based", "feature-based", "risk-based", "auto"]),
41491
- subtask_count: exports_external.number().int().min(1),
41492
- epic_title: exports_external.string(),
41493
- epic_description: exports_external.string().optional(),
41494
- subtasks: exports_external.array(exports_external.object({
41495
- title: exports_external.string(),
41496
- description: exports_external.string().optional(),
41497
- files: exports_external.array(exports_external.string()),
41498
- dependencies: exports_external.array(exports_external.number()).optional(),
41499
- estimated_complexity: exports_external.number().int().min(1).max(5).optional()
41500
- })),
41501
- outcomes: exports_external.array(SubtaskOutcomeSchema).optional(),
41502
- overall_success: exports_external.boolean().optional(),
41503
- total_duration_ms: exports_external.number().int().min(0).optional(),
41504
- total_errors: exports_external.number().int().min(0).optional(),
41505
- human_accepted: exports_external.boolean().optional(),
41506
- human_modified: exports_external.boolean().optional(),
41507
- human_notes: exports_external.string().optional(),
41508
- file_overlap_count: exports_external.number().int().min(0).optional(),
41509
- scope_accuracy: exports_external.number().min(0).max(2).optional(),
41510
- time_balance_ratio: exports_external.number().min(1).optional()
41511
- });
41512
- var CoordinatorEventSchema = exports_external.discriminatedUnion("event_type", [
41513
- exports_external.object({
41514
- session_id: exports_external.string(),
41515
- epic_id: exports_external.string(),
41516
- timestamp: exports_external.string(),
41517
- event_type: exports_external.literal("DECISION"),
41518
- decision_type: exports_external.enum([
41519
- "strategy_selected",
41520
- "worker_spawned",
41521
- "review_completed",
41522
- "decomposition_complete"
41523
- ]),
41524
- payload: exports_external.any()
41525
- }),
41526
- exports_external.object({
41527
- session_id: exports_external.string(),
41528
- epic_id: exports_external.string(),
41529
- timestamp: exports_external.string(),
41530
- event_type: exports_external.literal("VIOLATION"),
41531
- violation_type: exports_external.enum([
41532
- "coordinator_edited_file",
41533
- "coordinator_ran_tests",
41534
- "coordinator_reserved_files",
41535
- "no_worker_spawned"
41536
- ]),
41537
- payload: exports_external.any()
41538
- }),
41539
- exports_external.object({
41540
- session_id: exports_external.string(),
41541
- epic_id: exports_external.string(),
41542
- timestamp: exports_external.string(),
41543
- event_type: exports_external.literal("OUTCOME"),
41544
- outcome_type: exports_external.enum([
41545
- "subtask_success",
41546
- "subtask_retry",
41547
- "subtask_failed",
41548
- "epic_complete"
41549
- ]),
41550
- payload: exports_external.any()
41551
- })
41552
- ]);
41553
- var CoordinatorSessionSchema = exports_external.object({
41554
- session_id: exports_external.string(),
41555
- epic_id: exports_external.string(),
41556
- start_time: exports_external.string(),
41557
- end_time: exports_external.string().optional(),
41558
- events: exports_external.array(CoordinatorEventSchema)
41559
- });
41560
- var inProgressRecords = new Map;
41561
- function getSessionDir() {
41562
- return path.join(os.homedir(), ".config", "swarm-tools", "sessions");
41563
- }
41564
- function getSessionPath(sessionId) {
41565
- return path.join(getSessionDir(), `${sessionId}.jsonl`);
41566
- }
41567
- function ensureSessionDir() {
41568
- const sessionDir = getSessionDir();
41569
- if (!fs.existsSync(sessionDir)) {
41570
- fs.mkdirSync(sessionDir, { recursive: true });
41571
- }
41572
- }
41573
- function captureCoordinatorEvent(event) {
41574
- CoordinatorEventSchema.parse(event);
41575
- ensureSessionDir();
41576
- const sessionPath = getSessionPath(event.session_id);
41577
- const line = `${JSON.stringify(event)}
41578
- `;
41579
- fs.appendFileSync(sessionPath, line, "utf-8");
41580
- }
41581
-
41582
- // src/swarm-decompose.ts
41910
+ init_eval_capture();
41583
41911
  var DECOMPOSITION_PROMPT = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
41584
41912
 
41585
41913
  ## Task
@@ -41897,9 +42225,14 @@ ${fullContext}` : `## Additional Context
41897
42225
  }
41898
42226
  });
41899
42227
  var swarm_validate_decomposition = tool({
41900
- description: "Validate a decomposition response against CellTreeSchema",
42228
+ description: "Validate a decomposition response against CellTreeSchema and capture for eval",
41901
42229
  args: {
41902
- response: tool.schema.string().describe("JSON response from agent (CellTree format)")
42230
+ response: tool.schema.string().describe("JSON response from agent (CellTree format)"),
42231
+ project_path: tool.schema.string().optional().describe("Project path for eval capture"),
42232
+ task: tool.schema.string().optional().describe("Original task description for eval capture"),
42233
+ context: tool.schema.string().optional().describe("Context provided for decomposition"),
42234
+ strategy: tool.schema.enum(["file-based", "feature-based", "risk-based", "auto"]).optional().describe("Decomposition strategy used"),
42235
+ epic_id: tool.schema.string().optional().describe("Epic ID for eval capture")
41903
42236
  },
41904
42237
  async execute(args) {
41905
42238
  try {
@@ -41933,6 +42266,29 @@ var swarm_validate_decomposition = tool({
41933
42266
  }
41934
42267
  }
41935
42268
  const instructionConflicts = detectInstructionConflicts(validated.subtasks);
42269
+ if (args.project_path && args.task && args.strategy && args.epic_id) {
42270
+ try {
42271
+ const { captureDecomposition: captureDecomposition2 } = await Promise.resolve().then(() => (init_eval_capture(), exports_eval_capture));
42272
+ captureDecomposition2({
42273
+ epicId: args.epic_id,
42274
+ projectPath: args.project_path,
42275
+ task: args.task,
42276
+ context: args.context,
42277
+ strategy: args.strategy,
42278
+ epicTitle: validated.epic.title,
42279
+ epicDescription: validated.epic.description,
42280
+ subtasks: validated.subtasks.map((s) => ({
42281
+ title: s.title,
42282
+ description: s.description,
42283
+ files: s.files,
42284
+ dependencies: s.dependencies,
42285
+ estimated_complexity: s.estimated_complexity
42286
+ }))
42287
+ });
42288
+ } catch (error45) {
42289
+ console.warn("[swarm_validate_decomposition] Failed to capture decomposition:", error45);
42290
+ }
42291
+ }
41936
42292
  return JSON.stringify({
41937
42293
  valid: true,
41938
42294
  cell_tree: validated,
@@ -41973,7 +42329,7 @@ var swarm_delegate_planning = tool({
41973
42329
  strategy: tool.schema.enum(["auto", "file-based", "feature-based", "risk-based"]).optional().default("auto").describe("Decomposition strategy (default: auto-detect)"),
41974
42330
  query_cass: tool.schema.boolean().optional().default(true).describe("Query CASS for similar past tasks (default: true)")
41975
42331
  },
41976
- async execute(args) {
42332
+ async execute(args, _ctx) {
41977
42333
  const { selectStrategy: selectStrategy2, formatStrategyGuidelines: formatStrategyGuidelines2 } = await Promise.resolve().then(() => (init_swarm_strategies(), exports_swarm_strategies));
41978
42334
  const { formatMemoryQueryForDecomposition: formatMemoryQueryForDecomposition2 } = await Promise.resolve().then(() => (init_learning(), exports_learning));
41979
42335
  const { listSkills: listSkills2, getSkillsContextForSwarm: getSkillsContextForSwarm2, findRelevantSkills: findRelevantSkills2 } = await Promise.resolve().then(() => (init_skills(), exports_skills));
@@ -41989,7 +42345,7 @@ var swarm_delegate_planning = tool({
41989
42345
  }
41990
42346
  try {
41991
42347
  captureCoordinatorEvent({
41992
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
42348
+ session_id: _ctx.sessionID || "unknown",
41993
42349
  epic_id: "planning",
41994
42350
  timestamp: new Date().toISOString(),
41995
42351
  event_type: "DECISION",
@@ -44080,6 +44436,7 @@ var worktreeTools = {
44080
44436
  init_dist();
44081
44437
  init_zod();
44082
44438
  import { sendSwarmMessage as sendSwarmMessage2 } from "swarm-mail";
44439
+ init_eval_capture();
44083
44440
  var ReviewIssueSchema = exports_external.object({
44084
44441
  file: exports_external.string(),
44085
44442
  line: exports_external.number().optional(),
@@ -44304,7 +44661,7 @@ var swarm_review_feedback = tool({
44304
44661
  summary: exports_external.string().optional().describe("Review summary"),
44305
44662
  issues: exports_external.string().optional().describe("JSON array of ReviewIssue objects (for needs_changes)")
44306
44663
  },
44307
- async execute(args) {
44664
+ async execute(args, _ctx) {
44308
44665
  let parsedIssues = [];
44309
44666
  if (args.issues) {
44310
44667
  try {
@@ -44327,7 +44684,7 @@ var swarm_review_feedback = tool({
44327
44684
  markReviewApproved(args.task_id);
44328
44685
  try {
44329
44686
  captureCoordinatorEvent({
44330
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
44687
+ session_id: _ctx.sessionID || "unknown",
44331
44688
  epic_id: epicId,
44332
44689
  timestamp: new Date().toISOString(),
44333
44690
  event_type: "DECISION",
@@ -44365,7 +44722,7 @@ You may now complete the task with \`swarm_complete\`.`,
44365
44722
  const remaining = MAX_REVIEW_ATTEMPTS - attemptNumber;
44366
44723
  try {
44367
44724
  captureCoordinatorEvent({
44368
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
44725
+ session_id: _ctx.sessionID || "unknown",
44369
44726
  epic_id: epicId,
44370
44727
  timestamp: new Date().toISOString(),
44371
44728
  event_type: "DECISION",
@@ -44436,6 +44793,7 @@ var reviewTools = {
44436
44793
  };
44437
44794
 
44438
44795
  // src/swarm-orchestrate.ts
44796
+ init_eval_capture();
44439
44797
  function generateWorkerHandoff(params) {
44440
44798
  const handoff = {
44441
44799
  contract: {
@@ -45336,10 +45694,29 @@ Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
45336
45694
  reason: "No files_owned contract found (non-epic subtask or decomposition event missing)"
45337
45695
  }
45338
45696
  };
45697
+ try {
45698
+ const { captureSubtaskOutcome: captureSubtaskOutcome2 } = await Promise.resolve().then(() => (init_eval_capture(), exports_eval_capture));
45699
+ const durationMs2 = args.start_time ? Date.now() - args.start_time : 0;
45700
+ const evalEpicId = cell.parent_id || epicId2;
45701
+ captureSubtaskOutcome2({
45702
+ epicId: evalEpicId,
45703
+ projectPath: args.project_key,
45704
+ beadId: args.bead_id,
45705
+ title: cell.title,
45706
+ plannedFiles: args.planned_files || [],
45707
+ actualFiles: args.files_touched || [],
45708
+ durationMs: durationMs2,
45709
+ errorCount: args.error_count || 0,
45710
+ retryCount: args.retry_count || 0,
45711
+ success: true
45712
+ });
45713
+ } catch (error45) {
45714
+ console.warn("[swarm_complete] Failed to capture subtask outcome:", error45);
45715
+ }
45339
45716
  try {
45340
45717
  const durationMs2 = args.start_time ? Date.now() - args.start_time : 0;
45341
45718
  captureCoordinatorEvent({
45342
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
45719
+ session_id: _ctx.sessionID || "unknown",
45343
45720
  epic_id: epicId2,
45344
45721
  timestamp: new Date().toISOString(),
45345
45722
  event_type: "OUTCOME",
@@ -45421,7 +45798,7 @@ ${errorStack.slice(0, 1000)}
45421
45798
  try {
45422
45799
  const durationMs = args.start_time ? Date.now() - args.start_time : 0;
45423
45800
  captureCoordinatorEvent({
45424
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
45801
+ session_id: _ctx.sessionID || "unknown",
45425
45802
  epic_id: epicId,
45426
45803
  timestamp: new Date().toISOString(),
45427
45804
  event_type: "OUTCOME",
@@ -45488,7 +45865,9 @@ var swarm_record_outcome = tool({
45488
45865
  "user_cancelled",
45489
45866
  "unknown"
45490
45867
  ]).optional().describe("Failure classification (only when success=false). Auto-classified if not provided."),
45491
- failure_details: tool.schema.string().optional().describe("Detailed failure context (error message, stack trace, etc.)")
45868
+ failure_details: tool.schema.string().optional().describe("Detailed failure context (error message, stack trace, etc.)"),
45869
+ project_path: tool.schema.string().optional().describe("Project path (for finalizing eval records when all subtasks complete)"),
45870
+ epic_id: tool.schema.string().optional().describe("Epic ID (for finalizing eval records when all subtasks complete)")
45492
45871
  },
45493
45872
  async execute(args) {
45494
45873
  const signals = {
@@ -45510,6 +45889,18 @@ var swarm_record_outcome = tool({
45510
45889
  const validated = OutcomeSignalsSchema.parse(signals);
45511
45890
  const scored = scoreImplicitFeedback(validated, DEFAULT_LEARNING_CONFIG);
45512
45891
  const errorStats = await globalErrorAccumulator.getErrorStats(args.bead_id);
45892
+ let finalizedRecord = null;
45893
+ if (args.project_path && args.epic_id) {
45894
+ try {
45895
+ const { finalizeEvalRecord: finalizeEvalRecord2 } = await Promise.resolve().then(() => (init_eval_capture(), exports_eval_capture));
45896
+ finalizedRecord = finalizeEvalRecord2({
45897
+ epicId: args.epic_id,
45898
+ projectPath: args.project_path
45899
+ });
45900
+ } catch (error45) {
45901
+ console.warn("[swarm_record_outcome] Failed to finalize eval record:", error45);
45902
+ }
45903
+ }
45513
45904
  const criteriaToScore = args.criteria ?? [
45514
45905
  "type_safe",
45515
45906
  "no_bugs",
@@ -45551,6 +45942,7 @@ var swarm_record_outcome = tool({
45551
45942
  accumulated_errors: errorStats.total,
45552
45943
  unresolved_errors: errorStats.unresolved
45553
45944
  },
45945
+ finalized_eval_record: finalizedRecord || undefined,
45554
45946
  note: "Feedback events should be stored for criterion weight calculation. Use learning.ts functions to apply weights."
45555
45947
  }, null, 2);
45556
45948
  }
@@ -45582,12 +45974,31 @@ async function runResearchPhase(task, projectPath, options2) {
45582
45974
  if (techStack.length === 0) {
45583
45975
  return {
45584
45976
  tech_stack: [],
45977
+ spawn_instructions: [],
45585
45978
  summaries: {},
45586
45979
  memory_ids: []
45587
45980
  };
45588
45981
  }
45982
+ const spawnInstructions = [];
45983
+ for (const tech of techStack) {
45984
+ const researchId = `research-${tech}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
45985
+ const prompt = formatResearcherPrompt({
45986
+ research_id: researchId,
45987
+ epic_id: "standalone-research",
45988
+ tech_stack: [tech],
45989
+ project_path: projectPath,
45990
+ check_upgrades: options2?.checkUpgrades ?? false
45991
+ });
45992
+ spawnInstructions.push({
45993
+ research_id: researchId,
45994
+ tech,
45995
+ prompt,
45996
+ subagent_type: "swarm/researcher"
45997
+ });
45998
+ }
45589
45999
  return {
45590
46000
  tech_stack: techStack,
46001
+ spawn_instructions: spawnInstructions,
45591
46002
  summaries: {},
45592
46003
  memory_ids: []
45593
46004
  };
@@ -46024,6 +46435,7 @@ var orchestrateTools = {
46024
46435
  };
46025
46436
 
46026
46437
  // src/swarm-prompts.ts
46438
+ init_eval_capture();
46027
46439
  var STRATEGY_DECOMPOSITION_PROMPT2 = `You are decomposing a task into parallelizable subtasks for a swarm of agents.
46028
46440
 
46029
46441
  ## Task
@@ -46820,7 +47232,7 @@ var swarm_spawn_subtask = tool({
46820
47232
  }).optional().describe("Recovery context from checkpoint compaction"),
46821
47233
  model: tool.schema.string().optional().describe("Optional explicit model override (auto-selected if not provided)")
46822
47234
  },
46823
- async execute(args) {
47235
+ async execute(args, _ctx) {
46824
47236
  const prompt = formatSubtaskPromptV2({
46825
47237
  bead_id: args.bead_id,
46826
47238
  epic_id: args.epic_id,
@@ -46849,7 +47261,7 @@ var swarm_spawn_subtask = tool({
46849
47261
  const postCompletionInstructions = COORDINATOR_POST_WORKER_CHECKLIST.replace(/{project_key}/g, args.project_path || "$PWD").replace(/{epic_id}/g, args.epic_id).replace(/{task_id}/g, args.bead_id).replace(/{files_touched}/g, filesJoined).replace(/{worker_id}/g, "worker");
46850
47262
  try {
46851
47263
  captureCoordinatorEvent({
46852
- session_id: process.env.OPENCODE_SESSION_ID || "unknown",
47264
+ session_id: _ctx.sessionID || "unknown",
46853
47265
  epic_id: args.epic_id,
46854
47266
  timestamp: new Date().toISOString(),
46855
47267
  event_type: "DECISION",
@@ -62712,6 +63124,7 @@ function guardrailOutput(toolName, output, config2 = DEFAULT_GUARDRAIL_CONFIG) {
62712
63124
  }
62713
63125
 
62714
63126
  // src/planning-guardrails.ts
63127
+ init_eval_capture();
62715
63128
  var FILE_MODIFICATION_PATTERNS = [
62716
63129
  /\bimplement\b/i,
62717
63130
  /\bcreate\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
@@ -62997,9 +63410,21 @@ function getLog() {
62997
63410
  }
62998
63411
  return _logger;
62999
63412
  }
63000
- var SWARM_COMPACTION_CONTEXT = `## \uD83D\uDC1D SWARM ACTIVE - You Are The COORDINATOR
63413
+ var SWARM_COMPACTION_CONTEXT = `
63414
+ ┌─────────────────────────────────────────────────────────────┐
63415
+ │ │
63416
+ │ \uD83D\uDC1D YOU ARE THE COORDINATOR \uD83D\uDC1D │
63417
+ │ │
63418
+ │ NOT A WORKER. NOT AN IMPLEMENTER. │
63419
+ │ YOU ORCHESTRATE. │
63420
+ │ │
63421
+ └─────────────────────────────────────────────────────────────┘
63422
+
63423
+ ## \uD83C\uDFAF NON-NEGOTIABLE: YOU ARE THE COORDINATOR
63001
63424
 
63002
- Context was compacted but the swarm is still running. You are the **COORDINATOR**.
63425
+ Context was compacted but the swarm is still running. **YOU ARE THE COORDINATOR.**
63426
+
63427
+ Your role is ORCHESTRATION, not implementation. When you catch yourself about to do work directly, STOP.
63003
63428
 
63004
63429
  ### ⛔ NEVER DO THESE (Coordinator Anti-Patterns)
63005
63430
 
@@ -63010,9 +63435,27 @@ Context was compacted but the swarm is still running. You are the **COORDINATOR*
63010
63435
  - ❌ **NEVER** implement features yourself - SPAWN A WORKER
63011
63436
  - ❌ **NEVER** "just do it myself to save time" - NO. SPAWN A WORKER.
63012
63437
  - ❌ **NEVER** reserve files with \`swarmmail_reserve\` - Workers reserve files
63438
+ - ❌ **NEVER** fetch files/docs directly - SPAWN A RESEARCHER
63013
63439
 
63014
63440
  **If you catch yourself about to edit a file, STOP. Use \`swarm_spawn_subtask\` instead.**
63015
63441
 
63442
+ ### \uD83D\uDEAB FORBIDDEN TOOLS (Coordinators MUST delegate these)
63443
+
63444
+ **NEVER use these tools directly. ALWAYS spawn a researcher worker via \`swarm_spawn_researcher\`:**
63445
+
63446
+ **Repository fetching:**
63447
+ - \`repo-crawl_file\`, \`repo-crawl_readme\`, \`repo-crawl_search\`, \`repo-crawl_structure\`, \`repo-crawl_tree\`
63448
+ - \`repo-autopsy_*\` (all repo-autopsy tools)
63449
+
63450
+ **Web/documentation fetching:**
63451
+ - \`webfetch\`, \`fetch_fetch\`
63452
+ - \`context7_resolve-library-id\`, \`context7_get-library-docs\`
63453
+
63454
+ **Knowledge base:**
63455
+ - \`pdf-brain_search\`, \`pdf-brain_read\`
63456
+
63457
+ **If you need external data:** Use \`swarm_spawn_researcher\` with a clear research task. The researcher will fetch, summarize, and return findings.
63458
+
63016
63459
  ### ✅ ALWAYS DO THESE (Coordinator Checklist)
63017
63460
 
63018
63461
  On resume, execute this checklist IN ORDER:
@@ -63062,6 +63505,87 @@ Extract from session context:
63062
63505
  - **Review work** - Use \`swarm_review\` and \`swarm_review_feedback\` for completed work
63063
63506
  - **Close the loop** - When all subtasks done, verify and close the epic
63064
63507
 
63508
+ **You are the COORDINATOR. You orchestrate. You do NOT implement. Spawn workers.**
63509
+
63510
+ ---
63511
+
63512
+ ## \uD83D\uDCCB FULL COORDINATOR WORKFLOW (Reference)
63513
+
63514
+ You are ALWAYS swarming. Here is the complete workflow for any new work:
63515
+
63516
+ ### Phase 1.5: Research Phase (FOR COMPLEX TASKS)
63517
+
63518
+ **If the task requires understanding unfamiliar technologies, spawn a researcher FIRST:**
63519
+
63520
+ \`\`\`
63521
+ swarm_spawn_researcher(
63522
+ research_id="research-<topic>",
63523
+ epic_id="<epic-id>",
63524
+ tech_stack=["<technology>"],
63525
+ project_path="<path>"
63526
+ )
63527
+ // Then spawn with Task(subagent_type="swarm/researcher", prompt="<from above>")
63528
+ \`\`\`
63529
+
63530
+ ### Phase 2: Knowledge Gathering
63531
+
63532
+ \`\`\`
63533
+ semantic-memory_find(query="<task keywords>", limit=5) # Past learnings
63534
+ cass_search(query="<task description>", limit=5) # Similar past tasks
63535
+ skills_list() # Available skills
63536
+ \`\`\`
63537
+
63538
+ ### Phase 3: Decompose
63539
+
63540
+ \`\`\`
63541
+ swarm_select_strategy(task="<task>")
63542
+ swarm_plan_prompt(task="<task>", context="<synthesized knowledge>")
63543
+ swarm_validate_decomposition(response="<CellTree JSON>")
63544
+ \`\`\`
63545
+
63546
+ ### Phase 4: Create Cells
63547
+
63548
+ \`hive_create_epic(epic_title="<task>", subtasks=[...])\`
63549
+
63550
+ ### Phase 5: DO NOT Reserve Files
63551
+
63552
+ > **⚠️ Coordinator NEVER reserves files.** Workers reserve their own files.
63553
+
63554
+ ### Phase 6: Spawn Workers
63555
+
63556
+ \`\`\`
63557
+ swarm_spawn_subtask(bead_id, epic_id, title, files, shared_context, project_path)
63558
+ Task(subagent_type="swarm/worker", prompt="<from above>")
63559
+ \`\`\`
63560
+
63561
+ ### Phase 7: MANDATORY Review Loop
63562
+
63563
+ **AFTER EVERY Task() RETURNS:**
63564
+
63565
+ 1. \`swarmmail_inbox()\` - Check for messages
63566
+ 2. \`swarm_review(project_key, epic_id, task_id, files_touched)\` - Generate review
63567
+ 3. Evaluate against epic goals
63568
+ 4. \`swarm_review_feedback(project_key, task_id, worker_id, status, issues)\`
63569
+
63570
+ **If needs_changes:**
63571
+ \`\`\`
63572
+ swarm_spawn_retry(bead_id, epic_id, original_prompt, attempt, issues, diff, files, project_path)
63573
+ // Spawn NEW worker with Task() using retry prompt
63574
+ // Max 3 attempts before marking task blocked
63575
+ \`\`\`
63576
+
63577
+ ### Phase 8: Complete
63578
+
63579
+ \`hive_sync()\` - Sync all cells to git
63580
+
63581
+ ## Strategy Reference
63582
+
63583
+ | Strategy | Best For | Keywords |
63584
+ | -------------- | ------------------------ | -------------------------------------- |
63585
+ | file-based | Refactoring, migrations | refactor, migrate, rename, update all |
63586
+ | feature-based | New features | add, implement, build, create, feature |
63587
+ | risk-based | Bug fixes, security | fix, bug, security, critical, urgent |
63588
+
63065
63589
  **You are the COORDINATOR. You orchestrate. You do NOT implement. Spawn workers.**
63066
63590
  `;
63067
63591
  var SWARM_DETECTION_FALLBACK = `## \uD83D\uDC1D Swarm Detection - Check Your Context
@@ -63736,7 +64260,7 @@ var SwarmPlugin = async (input) => {
63736
64260
  if (isInCoordinatorContext()) {
63737
64261
  const ctx = getCoordinatorContext();
63738
64262
  const violation = detectCoordinatorViolation({
63739
- sessionId: ctx.sessionId || "unknown",
64263
+ sessionId: input2.sessionID || "unknown",
63740
64264
  epicId: ctx.epicId || "unknown",
63741
64265
  toolName,
63742
64266
  toolArgs: output.args,