The plugin wrapper at ~/.config/opencode/plugin/swarm.ts must have ZERO imports from opencode-swarm-plugin.
╔═══════════════════════════════════════════════════════════════════════════╗
║ ║
║ ❌ NEVER DO THIS IN THE PLUGIN WRAPPER: ║
║ ║
║ import { anything } from "opencode-swarm-plugin"; // BREAKS OPENCODE ║
║ import { stuff } from "swarm-mail"; // BREAKS OPENCODE ║
║ ║
║ ✅ ONLY THESE IMPORTS ARE SAFE: ║
║ ║
║ import { ... } from "@opencode-ai/plugin"; // Provided by OpenCode ║
║ import { ... } from "@opencode-ai/sdk"; // Provided by OpenCode ║
║ import { ... } from "node:*"; // Node.js builtins ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════╝
Why? The npm package has transitive dependencies (evalite, etc.) that aren't available in OpenCode's plugin context. Importing causes: Cannot find module 'evalite/runner' → trace trap → OpenCode crash.
Pattern: Plugin wrapper is DUMB, CLI is SMART.
- Wrapper: Thin shell, no logic, just bridges to
swarmCLI viaspawn() - CLI: All the smarts, all the deps, runs in its own Node.js context
If you need logic in the wrapper: INLINE IT. Copy the code directly into the template. See the // Swarm Signature Detection (INLINED) section for an example of ~250 lines of inlined logic.
Template location: packages/opencode-swarm-plugin/examples/plugin-wrapper-template.ts
NEVER use bd CLI commands in code. The bd CLI is deprecated and should not be called via Bun.$ or any shell execution.
Instead, use the HiveAdapter from swarm-mail package:
import { createHiveAdapter } from "swarm-mail";
const adapter = await createHiveAdapter({ projectPath: "/path/to/project" });
// Query cells
const cells = await adapter.queryCells({ status: "open" });
// Create cell
const cell = await adapter.createCell({ title: "Task", type: "task" });
// Update cell
await adapter.updateCell(cellId, { description: "Updated" });
// Close cell
await adapter.closeCell(cellId, "Done");Why? The bd CLI requires a separate installation and isn't available in all environments. The HiveAdapter provides the same functionality programmatically with proper TypeScript types.
All swarm data lives in ONE database: ~/.config/swarm-tools/swarm.db
╔═══════════════════════════════════════════════════════════════════════════╗
║ ║
║ ✅ GLOBAL DATABASE (the only one): ║
║ ~/.config/swarm-tools/swarm.db ║
║ ║
║ ❌ LOCAL DATABASES (banned, auto-migrated): ║
║ .opencode/swarm.db ║
║ .hive/swarm-mail.db ║
║ packages/*/.opencode/swarm.db ║
║ ║
╚═══════════════════════════════════════════════════════════════════════════╝
- No stray data - All events, beads, messages in one place
- Cross-project visibility -
swarm statsshows everything - Simpler debugging - One database to inspect
- No migration headaches - Data doesn't get lost in project-local DBs
The getOrCreateAdapter() function in swarm-mail has a runtime guard that throws if code attempts to create a non-global database:
// This will THROW:
const adapter = await createLibSQLAdapter({ url: "file:./local.db" });
// This is CORRECT (uses global path automatically):
const swarmMail = await getSwarmMailLibSQL();When swarm setup runs, it:
- Detects stray databases in
.opencode/,.hive/,packages/*/ - Migrates unique data to global database (INSERT OR IGNORE)
- Renames strays to
*.db.migratedto prevent re-migration
Tests can use in-memory databases:
// ✅ CORRECT for tests
const swarmMail = await createInMemorySwarmMailLibSQL("test-123");
// ❌ WRONG - don't create file-based test DBs
const adapter = await createLibSQLAdapter({ url: "file:./test.db" });See .hive/analysis/stray-database-audit.md for the full audit of database paths in the codebase.
All code changes MUST follow Test-Driven Development:
- Red - Write a failing test first
- Green - Write minimal code to make it pass
- Refactor - Clean up while tests stay green
No exceptions. If you're touching code, you're touching tests first.
- New feature? Write the test that describes the behavior.
- Bug fix? Write the test that reproduces the bug.
- Refactor? Ensure existing tests cover the behavior before changing.
Run tests continuously: bun turbo test --filter=<package>
Slow tests don't get run. Fast tests catch bugs early.
| Tier | Suffix | Speed | Dependencies | When to Run |
|---|---|---|---|---|
| Unit | .test.ts |
<100ms | None | Every save |
| Integration | .integration.test.ts |
<5s | libSQL, filesystem | Pre-commit |
| E2E | .e2e.test.ts |
<30s | External services | CI only |
- Prefer in-memory databases - Use
createInMemorySwarmMail()over file-based libSQL - Share instances when possible - Use
beforeAll/afterAllfor expensive setup, notbeforeEach/afterEach - Don't skip tests - If a test needs external services, mock them or make them optional
- Clean up after yourself - But don't recreate the world for each test
// GOOD: Shared instance for related tests
describe("feature X", () => {
let swarmMail: SwarmMailAdapter;
beforeAll(async () => {
swarmMail = await createInMemorySwarmMail("test");
});
afterAll(async () => {
await swarmMail.close();
});
test("does thing A", async () => { /* uses swarmMail */ });
test("does thing B", async () => { /* uses swarmMail */ });
});
// BAD: New instance per test (slow, wasteful)
beforeEach(async () => {
swarmMail = await createInMemorySwarmMail("test");
});Note: We use libSQL (SQLite-compatible) for all database operations. PGLite is only used for migration from legacy databases.
- Creating new database instances per test
test.skip()without a tracking issue- Tests that pass by accident (no assertions)
- Tests that only run in CI
See TEST-STATUS.md for full testing documentation.
opencode-swarm-plugin/
├── package.json # Workspace root (NO dependencies here)
├── turbo.json # Pipeline configuration
├── bun.lock # Single lockfile for all packages
├── packages/
│ ├── swarm-mail/ # Event sourcing primitives
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── src/
│ └── opencode-swarm-plugin/ # Main plugin
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
The root package.json is workspace-only. Per bun docs, it should NOT contain dependencies or devDependencies:
{
"name": "opencode-swarm-monorepo",
"private": true,
"packageManager": "bun@1.3.4",
"workspaces": ["packages/*"]
}Why? Each package is self-contained. Root deps cause hoisting confusion and version conflicts.
Turborepo requires packageManager in root package.json:
{
"packageManager": "bun@1.3.4"
}Without this, turbo fails with: Could not resolve workspaces. Missing packageManager field
Reference sibling packages with workspace:*:
{
"dependencies": {
"swarm-mail": "workspace:*"
}
}After adding, run bun install from root to link.
# Install all workspace dependencies
bun install
# Build all packages (respects dependency order)
bun turbo build
# Build specific package
bun turbo build --filter=swarm-mail
# Test all packages
bun turbo test
# Typecheck all packages
bun turbo typecheck
# Run command in specific package
bun --filter=opencode-swarm-plugin test
# Add dependency to specific package
cd packages/swarm-mail && bun add zod{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["^build"]
},
"typecheck": {
"dependsOn": ["^build"]
}
}
}Key points:
^buildmeans "build dependencies first" (topological order)outputsenables caching - turbo skips if inputs unchanged- Tasks without
dependsOnrun in parallel
Each package needs its own scripts in package.json:
{
"scripts": {
"build": "bun build ./src/index.ts --outdir ./dist --target node && tsc",
"test": "bun test src/",
"typecheck": "tsc --noEmit"
}
}# 1. Create directory
mkdir -p packages/new-package/src
# 2. Create package.json
cat > packages/new-package/package.json << 'EOF'
{
"name": "new-package",
"version": "0.1.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "bun build ./src/index.ts --outdir ./dist --target node && tsc",
"test": "bun test src/",
"typecheck": "tsc --noEmit"
}
}
EOF
# 3. Create tsconfig.json
cat > packages/new-package/tsconfig.json << 'EOF'
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"strict": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
EOF
# 4. Link workspaces
bun install
# 5. Verify
bun turbo build --filter=new-packageRun bun install from root to link workspaces.
# Clear turbo cache
rm -rf .turbo/cache
# Or force rebuild
bun turbo build --forceEnsure dependsOn: ["^build"] in turbo.json so types are generated before dependent packages typecheck.
PGLite may fail to initialize in parallel test runs. Tests fall back to in-memory mode automatically - this is expected behavior, not an error.
Note: PGLite is deprecated. New code should use libSQL via createInMemorySwarmMail() or getSwarmMailLibSQL().
Known issue: COUNT(*) returns 0 on tables with vector columns, but data IS there.
-- WRONG: Returns 0 even with 9000+ rows
SELECT COUNT(*) FROM memories; -- Returns: 0
-- CORRECT: Use COUNT(column_name) instead
SELECT COUNT(id) FROM memories; -- Returns: 9021Why? The libSQL vector extension (F32_BLOB) interferes with COUNT(*) aggregation. This is a known quirk, not a bug in our code.
Affected tables: memories (has embedding F32_BLOB(1024) column)
Workaround: Always use COUNT(id) or COUNT(column_name) instead of COUNT(*) when querying tables with vector columns.
We use bee/hive metaphors consistently across the project. This isn't just branding - it's a mental model for multi-agent coordination.
| Concept | Name | Metaphor |
|---|---|---|
| Work items (issues/tasks) | Hive | Honeycomb cells where work lives |
| Individual work item | Cell | Single unit of work in the hive |
| Agent coordination | Swarm | Bees working together |
| Inter-agent messaging | Swarm Mail | Bees communicating via dance/pheromones |
| Parallel workers | Workers | Worker bees |
| Task orchestrator | Coordinator | Queen directing the swarm |
| File locks | Reservations | Bees claiming cells |
| Checkpoints | Nectar | Progress stored for later |
Naming rules:
- New features should fit the hive/swarm metaphor when possible
- Avoid generic names (tasks, issues, tickets) - use the domain language
- CLI commands:
swarm,hive(notbeads,tasks) - Tool prefixes:
hive_*,swarm_*,swarmmail_*
Why bees?
- Swarms are decentralized but coordinated
- Worker bees are autonomous but follow protocols
- The hive is the shared state (event log)
- Waggle dance = message passing
- Honey = accumulated value from work
Event sourcing primitives for multi-agent coordination:
EventStore- append-only event log with libSQLProjections- materialized views (agents, messages, reservations)- Effect-TS durable primitives (mailbox, cursor, lock, deferred)
DatabaseAdapterinterface for dependency injection- Hive - git-synced work item tracking (formerly "beads")
Database: Uses libSQL (SQLite-compatible) as the primary database. PGLite support exists only for migrating legacy databases.
OpenCode plugin providing:
- Hive integration (work item tracking, epics, dependencies)
- Swarm coordination (task decomposition, parallel agents)
- Swarm Mail (inter-agent messaging)
- Learning system (pattern maturity, anti-pattern detection)
- Skills system (knowledge injection)
Several features are inspired by Chainlink by @dollspace-gay:
| Feature | Chainlink Inspiration |
|---|---|
| Session Handoff | Chainlink's session management with handoff notes for context preservation |
| Stub Detection | post-edit-check.py patterns for detecting TODO, FIXME, pass, unimplemented!() |
| Tree View | tree command with ASCII box-drawing and status indicators |
| Adversarial Review | VDD methodology - hostile fresh-context reviewer (Sarcasmotron) |
The adversarial reviewer pattern comes from VDD:
- Fresh context per review - prevents "relationship drift" (becoming lenient)
- HALLUCINATING verdict - when adversary invents issues, code is zero-slop
- Hostile tone - zero tolerance for slop, no participation trophies
Hivemind's unified session search is inspired by CASS by @Dicklesworthstone:
- Semantic search across AI coding agent histories
- Multi-agent indexing (Claude, Cursor, Codex, etc.)
- Session + memory unification
Skills live in .opencode/skills/ and provide reusable knowledge for agents.
Context-efficient PR comment handling. Evaluate → Decide → Act. Fix important issues, resolve the rest silently.
Location: .opencode/skills/pr-triage/
Philosophy: Replies are SECONDARY to addressing concerns. Don't reply to every comment - that's noise.
| Comment Type | Action | Reply? |
|---|---|---|
| Security/correctness bug | FIX → reply with commit | ✅ Yes |
| Valid improvement, in scope | FIX → reply with commit | ✅ Yes |
| Valid but out of scope | Create cell → resolve | ❌ No |
| Style/formatting nit | Resolve silently | ❌ No |
| Metadata file (.jsonl, etc) | Resolve silently | ❌ No |
| Already fixed | Reply with commit → resolve | ✅ Yes |
SOP:
# 1. Get unreplied comments (start here)
bun run .opencode/skills/pr-triage/scripts/pr-comments.ts unreplied owner/repo 42
# 2. Evaluate: fetch body for important files only
bun run .opencode/skills/pr-triage/scripts/pr-comments.ts expand owner/repo 123456
# 3. Decide & Act:
# - Important issue? FIX IT in code, then:
bun run .opencode/skills/pr-triage/scripts/pr-comments.ts reply owner/repo 42 123456 "✅ Fixed in abc123"
# - Not important? Resolve silently:
bun run .opencode/skills/pr-triage/scripts/pr-comments.ts resolve owner/repo 42 123456Skip these (resolve silently):
.hive/issues.jsonl,.hive/memories.jsonl- auto-generated- Changeset formatting suggestions
- Import ordering, style nits
- Suggestions you disagree with
Fix these (reply + resolve):
- Security vulnerabilities
- Correctness bugs
- Missing error handling
- Type safety issues
SDK: scripts/pr-comments.ts - Zod-validated, pagination-aware
References: references/gh-api-patterns.md for raw jq/GraphQL patterns
This repo uses Changesets for versioning and bun publish for npm publishing.
Changesets doesn't support Bun workspaces out of the box - it doesn't resolve workspace:* references. We use Ian Macalinao's approach:
{
"scripts": {
"ci:version": "changeset version && bun update",
"ci:publish": "for dir in packages/*; do (cd \"$dir\" && bun publish --access public || true); done && changeset tag"
}
}Why bun update after changeset version?
changeset versionbumps package.json versionsbun updatesyncs the lockfile soworkspace:*resolves to the new versions- Without this,
bun publishwould publish with unresolvedworkspace:*references
Why iterate and bun publish each package?
bun publishresolvesworkspace:*during pack (unlikechangeset publish)|| truecontinues if a package is already publishedchangeset tagcreates git tags after all packages are published
We use the standard changesets/action@v1 with BOTH version and publish scripts. Don't fight the action - it handles the state machine internally:
- name: Create and publish versions
uses: changesets/action@v1
with:
version: bun run ci:version
commit: "chore: update versions"
title: "chore: update versions"
publish: bun run ci:publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}The action automatically determines:
- Changesets exist → runs
versionscript, creates PR - No changesets, PR just merged → runs
publishscript - Nothing to do → exits cleanly
Normal flow:
- Make changes to packages
- Create a changeset file:
cat > .changeset/your-change-name.md << 'EOF' --- "package-name": patch --- Description of the change EOF
- Commit the changeset file with your changes
- Push to main → action creates "chore: update versions" PR
- Merge that PR → action runs
ci:publish→ packages on npm
CRITICAL: Don't create changesets for ignored packages. If you create a changeset that only affects @swarmtools/web (which is in .changeset/config.json ignore list), the action will try to create a version PR with no actual changes, causing a "No commits between main and changeset-release/main" error.
Edge cases handled:
- Version PR merged but publish failed → next push retries publish
- No changes at all → clean exit
Pack changesets with lore. Changesets are not just version bumps - they're the story of the release. They get read by humans deciding whether to upgrade.
Good changeset:
---
"swarm-mail": minor
---
## 🐝 Cell IDs Now Wear Their Project Colors
Cell IDs finally know where they came from. Instead of anonymous `bd-xxx` prefixes,
new cells proudly display their project name: `swarm-mail-lf2p4u-abc123`.
**What changed:**
- `generateBeadId()` reads `package.json` name field
- Slugifies project name (lowercase, dashes for special chars)
- Falls back to `cell-` prefix if no package.json
**Why it matters:**
- Cells identifiable at a glance in multi-project workspaces
- Easier filtering/searching across projects
- Removes legacy "bead" terminology from user-facing IDs
**Backward compatible:** Existing `bd-*` IDs still work fine.Bad changeset:
---
"swarm-mail": patch
---
Updated ID generationRules:
- Use emoji sparingly but effectively (🐝 for hive/swarm features)
- Explain WHAT changed, WHY it matters, and any MIGRATION notes
- Include code examples if API changed
- Mention backward compatibility explicitly
- Make it scannable (headers, bullets, bold for key points)
MANDATORY: Pull a quote from pdf-brain. This is NOT optional:
pdf-brain_search(query="<thematic keyword from your change>", limit=5)Add the quote as an epigraph. Makes changelogs memorable and connects our work to the broader craft. Examples:
- Adding observability? Search "observability monitoring visibility"
- Refactoring? Search "refactoring Fowler small steps"
- Event sourcing? Search "event sourcing CQRS"
- Testing? Search "Beck TDD red green"
ASCII Art Guidelines:
- ASCII art is NOT always box-drawing block diagrams
- Be creative: animals, objects, scenes, characters with speech bubbles
- Match the metaphor: bees for swarm, telescopes for observability, locks for auth
- Hand-drawn feel > corporate flowchart
- Surprise and delight > functional documentation
# GOOD: Creative, thematic, memorable
.-.
(o o) "Should I ADD or UPDATE?"
| O |
'-'
# BAD: Generic box diagram (every time)
┌─────────────────────────┐
│ SOME FEATURE │
└─────────────────────────┘
The following packages are excluded from changesets (won't be published):
@swarmtools/web- docs site, not an npm package
# Create a new changeset (interactive)
bunx changeset
# Preview what versions would be bumped
bunx changeset status
# Manually bump versions (CI does this automatically)
bun run ci:version
# Manually publish (CI does this automatically)
bun run ci:publishCLI bin scripts need their imports in dependencies, not devDependencies. If bin/swarm.ts imports @clack/prompts, it must be in dependencies or users get "Cannot find module" errors.
| Package | npm |
|---|---|
opencode-swarm-plugin |
npm |
swarm-mail |
npm |
- Add
publishConfigto package.json:{ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" } } - First publish happens automatically when changeset PR is merged
Problem: bun pm pack resolves workspace:* from the lockfile, not package.json. If lockfile is stale, you get old versions.
Solution: ci:version runs bun update after changeset version to sync the lockfile.
Tracking:
- Bun native npm token support: oven-sh/bun#15601
- When resolved, can switch to
bun publishdirectly
| Key | Purpose | Used By |
|---|---|---|
AI_GATEWAY_API_KEY |
Vercel AI Gateway authentication | Evals, LLM calls |
The .env file lives at monorepo root (/.env). For packages that need it:
# Copy to package that needs env vars
cp .env packages/opencode-swarm-plugin/.envWhy copy instead of reference? bunx and some tools don't traverse up to find .env files. Each package that needs env vars should have its own copy.
gitignore: All .env files are gitignored. Don't commit secrets.
For scripts that need env vars (like evals), use bun --env-file:
{
"scripts": {
"eval:run": "bun --env-file=.env run bunx evalite run evals/"
}
}This loads .env before spawning the subprocess.
The plugin includes an evaluation system using Evalite to score coordinator behavior, decomposition quality, and compaction.
cd packages/opencode-swarm-plugin
# Run all evals
bun run eval:run
# Run specific eval suites
bun run eval:decomposition # Task decomposition quality
bun run eval:coordinator # Coordinator protocol adherence| File | What It Tests | Data Source |
|---|---|---|
coordinator-session.eval.ts |
Real coordinator protocol adherence | ~/.config/swarm-tools/sessions/*.jsonl |
coordinator-behavior.eval.ts |
LLM coordinator mindset | Synthetic prompts → LLM |
swarm-decomposition.eval.ts |
Task decomposition quality | Fixtures + LLM |
compaction-resumption.eval.ts |
Context compaction correctness | Fixtures |
example.eval.ts |
Sanity check | Static |
Real sessions are captured during swarm runs to ~/.config/swarm-tools/sessions/. These are actual coordinator decisions (worker spawns, reviews, etc.) that get scored.
How session capture works:
- Automatic: No manual instrumentation - tool calls are inspected in real-time
- Violation detection: Pattern matching detects edit/write/test/reserve tool calls by coordinators
- JSONL format: One event per line, append-only, streamable
- Event types: DECISION, VIOLATION, OUTCOME, COMPACTION
See evals/README.md - Coordinator Session Capture (Deep Dive) for full details on:
- Capture flow diagram
- Violation detection patterns
- Event schema
- Viewing sessions with
jq - Integration points in code
Synthetic fixtures in evals/fixtures/ provide known-good and known-bad examples for baseline validation.
Scorers live in evals/scorers/ and measure specific aspects:
- violationCount - Protocol violations (editing files directly, skipping reviews)
- spawnEfficiency - Did coordinator spawn workers vs do work itself?
- reviewThoroughness - Did coordinator review worker output?
- timeToFirstSpawn - How fast did coordinator delegate?
- overallDiscipline - Weighted composite of above
- Create
evals/your-eval.eval.ts - Use
evalite()from evalite package - Define
data,task, andscorers - Scorers use
createScorer()- returns async function, NOT object with.scorer
import { evalite } from "evalite";
import { createScorer } from "evalite";
const myScorer = createScorer({
name: "My Scorer",
description: "What it measures",
scorer: async ({ output, expected, input }) => {
// Return 0-1 score
return { score: 0.8, message: "Details" };
},
});
evalite("My Eval", {
data: async () => [{ input: "...", expected: "..." }],
task: async (input) => "output",
scorers: [myScorer],
});When combining multiple scorers, call them directly with await:
// CORRECT - scorers are async functions
const result = await childScorer({ output, expected, input });
const score = result.score ?? 0;
// WRONG - .scorer property doesn't exist
const result = childScorer.scorer({ output, expected }); // ❌"GatewayAuthenticationError" - Missing AI_GATEWAY_API_KEY. Copy .env to package folder.
"no such table: eval_records" - Run any swarm-mail operation to trigger schema creation. Tables are created lazily with CREATE TABLE IF NOT EXISTS.
The swarm CLI provides observability, analytics, and debugging tools for multi-agent coordination.
swarm query - SQL analytics with presets for common patterns
# Execute custom SQL query
swarm query --sql "SELECT * FROM events WHERE type='worker_spawned' LIMIT 10"
# Use preset query
swarm query --preset failed_decompositions
swarm query --preset duration_by_strategy
swarm query --preset file_conflicts
swarm query --preset worker_success_rate
swarm query --preset review_rejections
swarm query --preset blocked_tasks
swarm query --preset agent_activity
swarm query --preset event_frequency
swarm query --preset error_patterns
swarm query --preset compaction_stats
swarm query --preset decision_quality
swarm query --preset strategy_success_rates
swarm query --preset decisions_by_pattern
# Output formats
swarm query --preset failed_decompositions --format table # Default
swarm query --preset duration_by_strategy --format csv
swarm query --preset file_conflicts --format jsonAvailable Presets:
| Preset | What It Shows |
|---|---|
failed_decompositions |
Epics that failed with error details |
duration_by_strategy |
Avg duration grouped by decomposition strategy |
file_conflicts |
File reservation conflicts between workers |
worker_success_rate |
Success rate per worker agent |
review_rejections |
Tasks rejected during coordinator review |
blocked_tasks |
Tasks currently blocked with reasons |
agent_activity |
Agent activity timeline |
event_frequency |
Event type distribution |
error_patterns |
Common error patterns |
compaction_stats |
Context compaction metrics |
decision_quality |
Recent decisions with quality scores and rationale |
strategy_success_rates |
Success rates by decomposition strategy |
decisions_by_pattern |
Which semantic memory patterns are cited most often |
swarm dashboard - Live terminal UI with worker status
# Launch dashboard (auto-refresh every 1s)
swarm dashboard
# Focus on specific epic
swarm dashboard --epic mjmas3zxlmg
# Custom refresh rate (milliseconds)
swarm dashboard --refresh 2000Dashboard shows:
- Active workers and their current tasks
- Progress bars for in-progress work
- File reservations (who owns what)
- Recent messages between agents
- Error alerts
swarm replay - Replay epic events with timing control
# Replay epic at normal speed
swarm replay mjmas3zxlmg
# Fast playback
swarm replay mjmas3zxlmg --speed 2x
swarm replay mjmas3zxlmg --speed instant
# Filter by event type
swarm replay mjmas3zxlmg --type worker_spawned,task_completed
# Filter by agent
swarm replay mjmas3zxlmg --agent DarkHawk
# Time range filters
swarm replay mjmas3zxlmg --since "2025-12-25T10:00:00"
swarm replay mjmas3zxlmg --until "2025-12-25T12:00:00"
# Combine filters
swarm replay mjmas3zxlmg --speed 2x --type worker_spawned --agent BlueLakeUse cases:
- Debug coordination failures by replaying the sequence
- Understand timing of worker spawns vs completions
- Identify where bottlenecks occurred
- Review coordinator decision points
swarm export - Export events for external analysis
# Export all events as JSON (stdout)
swarm export
# Export specific epic
swarm export --epic mjmas3zxlmg
# Export formats
swarm export --format json --output events.json
swarm export --format csv --output events.csv
swarm export --format otlp --output events.otlp # OpenTelemetry Protocol
# Pipe to jq for filtering
swarm export --format json | jq '.[] | select(.type=="worker_spawned")'swarm stats - Health metrics powered by swarm-insights
# Last 7 days (default)
swarm stats
# Custom time period
swarm stats --since 24h
swarm stats --since 30m
# JSON output for scripting
swarm stats --jsonswarm history - Recent swarm activity timeline
# Last 10 swarms (default)
swarm history
# More results
swarm history --limit 20
# Filter by status
swarm history --status success
swarm history --status failed
swarm history --status in_progress
# Filter by strategy
swarm history --strategy file-based
swarm history --strategy feature-based
# Verbose mode (show subtasks)
swarm history --verboseswarm log sessions - View captured coordinator sessions
# List all sessions
swarm log sessions
# View specific session
swarm log sessions <session_id>
# Most recent session
swarm log sessions --latest
# Filter by event type
swarm log sessions --type DECISION
swarm log sessions --type VIOLATION
swarm log sessions --type OUTCOME
swarm log sessions --type COMPACTION
# JSON output for jq
swarm log sessions --jsonUse DEBUG env var to enable swarm debug logs. Logs use box-drawing characters for readability.
Patterns:
# All swarm logs
DEBUG=swarm:* swarm dashboard
# Coordinator only
DEBUG=swarm:coordinator swarm replay <epic-id>
# Workers only
DEBUG=swarm:worker swarm export
# Swarm mail only
DEBUG=swarm:mail swarm query --preset agent_activity
# Multiple namespaces (comma-separated)
DEBUG=swarm:coordinator,swarm:worker swarm dashboardOutput format:
┌─ swarm:coordinator ─────────────────────
│ Spawning worker for task: mjmas40ys7g
│ {"epic_id":"mjmas3zxlmg","strategy":"file-based"}
└──────────────────────────────────────────
Namespaces:
| Namespace | What It Logs |
|---|---|
swarm:* |
All swarm activity |
swarm:coordinator |
Coordinator decisions (spawn, review, approve/reject) |
swarm:worker |
Worker progress, reservations, completions |
swarm:mail |
Inter-agent messages, inbox/outbox activity |
Use cases:
- Debugging coordination failures:
DEBUG=swarm:coordinatorto see decision flow - Worker issues:
DEBUG=swarm:workerto see what workers are doing - Message passing problems:
DEBUG=swarm:mailto trace communication - Everything:
DEBUG=swarm:*when you need full visibility
swarm log - Tail and filter swarm logs
# Recent logs (last 50 lines)
swarm log
# Filter by module
swarm log compaction
# Filter by level
swarm log --level error
swarm log --level warn
# Time filters
swarm log --since 30s
swarm log --since 5m
swarm log --since 2h
# JSON output
swarm log --json
# Limit output
swarm log --limit 100
# Watch mode (live tail)
swarm log --watch
swarm log --watch --interval 500 # Poll every 500msSwarmError provides structured context for debugging multi-agent failures.
import { SwarmError, enrichError } from "opencode-swarm-plugin";
// Throw with context
throw new SwarmError("File reservation failed", {
file: "src/auth.ts",
line: 42,
agent: "DarkHawk",
epic_id: "mjmas3zxlmg",
bead_id: "mjmas40ys7g",
recent_events: [
{ type: "worker_spawned", timestamp: "2025-12-25T10:00:00Z", message: "Worker started" },
{ type: "reservation_attempted", timestamp: "2025-12-25T10:01:00Z", message: "Tried to reserve src/auth.ts" }
]
});
// Enrich existing error
try {
await doWork();
} catch (error) {
throw enrichError(error, {
agent: "BlueLake",
epic_id: "mjmas3zxlmg",
bead_id: "mjmas40ys7g"
});
}| Field | Purpose | Example |
|---|---|---|
file |
File where error occurred | "src/auth.ts" |
line |
Line number | 42 |
agent |
Agent that encountered error | "DarkHawk" |
epic_id |
Epic being worked on | "mjmas3zxlmg" |
bead_id |
Specific task/cell | "mjmas40ys7g" |
recent_events |
Last N events before error | [{type, timestamp, message}] |
The suggestFix() function pattern-matches common errors and provides actionable fixes:
import { suggestFix } from "opencode-swarm-plugin";
try {
await swarmmail_reserve(["src/auth.ts"]);
} catch (error) {
const suggestion = suggestFix(error);
if (suggestion) {
console.log(suggestion);
}
throw error;
}Patterns detected:
| Error Pattern | Suggested Fix |
|---|---|
| "agent not registered" | Call swarmmail_init() before any swarm operations |
| "already reserved" | File is reserved by another agent. Wait for release or coordinate. |
| "uncommitted changes" | Run hive_sync() or commit changes before proceeding |
| "manual close" detected | Use swarm_complete() instead of hive_close() in workers |
| "context exhausted" | Use /checkpoint or spawn subagent |
| "libsql not initialized" | Ensure swarmmail_init() is called |
Example output:
┌─ Fix Suggestion ─────────────────────────
│ Problem: Agent not initialized
│ Solution: Call swarmmail_init() before any swarm operations
│ Context: agent=DarkHawk epic_id=mjmas3zxlmg
└──────────────────────────────────────────
Combine SwarmError context with DEBUG logging:
# See full error context in logs
DEBUG=swarm:* swarm replay <epic-id>Errors logged with SwarmError.toJSON() include:
- Error name and message
- Stack trace
- Full context object (file, line, agent, epic, events)
This creates an audit trail from error → context → recent events → root cause.
The hive remembers everything. Learnings, sessions, patterns—all searchable.
Unified storage: Manual learnings and AI agent session histories stored in the same database, searchable together. Powered by libSQL vectors + Ollama embeddings.
Inspired by CASS (coding_agent_session_search) by Dicklesworthstone - sessions + semantic memory unified under one API.
Indexed agents: Claude Code, Codex, Cursor, Gemini, Aider, ChatGPT, Cline, OpenCode, Amp, Pi-Agent
- BEFORE implementing - check if you or any agent solved it before
- After solving hard problems - store learnings for future sessions
- Debugging - search past sessions for similar errors
- Architecture decisions - record reasoning, alternatives, tradeoffs
- Project-specific patterns - capture domain rules and gotchas
| Tool | Purpose |
|---|---|
hivemind_store |
Store a memory (learnings, decisions, patterns) |
hivemind_find |
Search all memories (learnings + sessions, semantic + FTS fallback) |
hivemind_get |
Get specific memory by ID |
hivemind_remove |
Delete outdated/incorrect memory |
hivemind_validate |
Confirm memory still accurate (resets 90-day decay timer) |
hivemind_stats |
Memory statistics and health check |
hivemind_index |
Index AI session directories |
hivemind_sync |
Sync to .hive/memories.jsonl (git-backed, team-shared) |
Store a learning (include WHY, not just WHAT):
hivemind_store({
information: "OAuth refresh tokens need 5min buffer before expiry to avoid race conditions. Without buffer, token refresh can fail mid-request if expiry happens between check and use.",
tags: "auth,oauth,tokens,race-conditions"
})Search all memories (learnings + sessions):
// Search everything
hivemind_find({ query: "token refresh", limit: 5 })
// Search only learnings (manual entries)
hivemind_find({ query: "authentication", collection: "default" })
// Search only Claude sessions
hivemind_find({ query: "Next.js caching", collection: "claude" })
// Search only Cursor sessions
hivemind_find({ query: "API design", collection: "cursor" })Get specific memory:
hivemind_get({ id: "mem_xyz123" })Delete outdated memory:
hivemind_remove({ id: "mem_old456" })Validate memory is still accurate (resets decay):
// Confirmed this memory is still relevant
hivemind_validate({ id: "mem_xyz123" })Index new sessions:
// Automatically indexes ~/.config/opencode/sessions, ~/.cursor-tutor, etc.
hivemind_index()Sync to git:
// Writes learnings to .hive/memories.jsonl for git sync
hivemind_sync()Check stats:
hivemind_stats()# 1. Before starting work - query for relevant learnings
hivemind_find({ query: "<task keywords>", limit: 5 })
# 2. Do the work...
# 3. After solving hard problem - store learning
hivemind_store({
information: "<what you learned, WHY it matters>",
tags: "<relevant,tags>"
})
# 4. Validate memories when you confirm they're still accurate
hivemind_validate({ id: "<memory-id>" })At task start (query BEFORE implementing):
# Check if you or any agent solved similar problems
hivemind_find({ query: "OAuth token refresh buffer", limit: 5 })During debugging (search past sessions):
# Find similar errors from past sessions
hivemind_find({ query: "cannot read property of undefined", collection: "claude" })After solving problems (store learnings):
# Store root cause + solution, not just "fixed it"
hivemind_store({
information: "Next.js searchParams causes dynamic rendering. Workaround: destructure in parent, pass as props to cached child.",
tags: "nextjs,cache-components,dynamic-rendering,searchparams"
})Learning from other agents:
# See how Cursor handled similar feature
hivemind_find({ query: "implement authentication", collection: "cursor" })Pro tip: Query Hivemind at the START of complex tasks. Past solutions (yours or other agents') save time and prevent reinventing wheels.
Custom commands available via /command:
| Command | Purpose |
|---|---|
/swarm <task> |
Decompose task into cells, spawn parallel agents with shared context |
/parallel "t1" "t2" |
Run explicit task list in parallel |
/fix-all |
Survey PRs + cells, dispatch agents to fix issues |
/review-my-shit |
Pre-PR self-review: lint, types, common mistakes |
/handoff |
End session: sync hive, generate continuation prompt |
/sweep |
Codebase cleanup: type errors, lint, dead code |
/focus <cell-id> |
Start focused session on specific cell |
/context-dump |
Dump state for model switch or context recovery |
/checkpoint |
Compress context: summarize session, preserve decisions |
/retro <cell-id> |
Post-mortem: extract learnings, update knowledge files |
/worktree-task <id> |
Create git worktree for isolated cell work |
/commit |
Smart commit with conventional format + cell refs |
/pr-create |
Create PR with cell linking + smart summary |
/debug <error> |
Investigate error, check known patterns first |
/debug-plus |
Enhanced debug with swarm integration and prevention pipeline |
/iterate <task> |
Evaluator-optimizer loop: generate, critique, improve until good |
/triage <request> |
Intelligent routing: classify and dispatch to right handler |
/repo-dive <repo> |
Deep analysis of GitHub repo with autopsy tools |
Specialized subagents (invoke with @agent-name or auto-dispatched):
| Agent | Model | Purpose |
|---|---|---|
swarm-planner |
claude-sonnet-4-5 | Strategic task decomposition for swarm coordination |
swarm-worker |
claude-sonnet-4-5 | PRIMARY for /swarm - parallel task implementation |
hive |
claude-haiku | Work item tracker operations (locked down) |
archaeologist |
claude-sonnet-4-5 | Read-only codebase exploration, architecture mapping |
explore |
claude-haiku-4-5 | Fast codebase search, pattern discovery (read-only) |
refactorer |
default | Pattern migration across codebase |
reviewer |
default | Read-only code review, security/perf audits |