cyberine
UsageRun recipes

Research mode — cyberine research (plan -> search -> verify -> synthesize)

For questions that need current web evidence — comparisons, market scans, "what is the state of X in 2026?", trade-off write-ups — use cyberine research. The orchestrator decomposes the question into angle sub-questions, fans them out to parallel loop workers backed by the Perplexity sonar-pro native web-search tool, adversarially verifies every claim, then synthesizes the survivors into a single cited markdown Report on disk. No code is written, no working tree is touched: research is read-only and writes only the Report file.

When to use research vs compete / swarm. Pick by intended output, not task size:

competeswarmresearch
Intended outputone winning code diffmerged code diff across many chunkscited markdown Report (no diff)
Best forsingle design choice with multiple implementations to comparemass multi-file scope decomposed into disjoint chunksopen-ended "what / how / why" questions that need fresh web evidence
TopologyN writers race, DiffJudge picks ONEplan -> N disjoint-chunk writers -> SwarmMergeJudge merges allplan -> N angle workers (web_search + fetch_url) -> per-claim skeptics -> reducer synthesizer
Working treecompetes in isolated worktrees, winner applied via cyberine applymerged diff applied via cyberine applynever touched — Report written to disk
Commandcyberine run "..."cyberine run --mode swarm "..."cyberine research "..."

The flow (plan -> search -> verify -> synthesize). The workflow is src/engine/researchWorkflow.ts:

  1. plan — fans out to the same thinking fleet implement-task's design step uses (DESIGN_FLEET: the strongest ENABLED workers, widened one strength band at a time until at least two compete, so a retired or disabled id never leaves the step with too few candidates) and rewrites the raw question into one sharp, self-contained, answerable formulation (core question + 2-3 sentences of scope). A standard text-judge panel picks the best rewrite as the plan template every downstream angle worker inherits — so each worker reads the refined question, not the raw input. The run records which workers were picked and which subscription, if any, that bills.
  2. search — N parallel loop workers (N from the --depth preset: quick = 3, deep = 5, max = 7) each receive ONE angle sub-question from the multi-modal sweep (by-entity, by-time, by-source-type, by-perspective, by-counter, by-domain-specific, by-edge-case — first N taken in that order). Each worker uses web_search (Perplexity sonar-pro) to find authoritative sources, then fetch_url (SSRF-safe HTML-to-text) to read the most relevant page for a concrete quote, and returns a structured Finding (claim + cited sources + confidence). The step's winnerOutput is { findings: [...] }.
  3. verify — every Finding runs through --depth-tied independent skeptics (quick = 2, deep = 3, max = 5) via in-process ClaimVerifier. Default-refuted-if-unsupported: a claim survives unless a strict majority of its skeptics vote to refute. Survivors land in { verified: [...], dropped: [...], totalSkepticCalls } for the next step.
  4. synthesize — SynthesizerReducer makes ONE LLM call combining every verified claim into a single cited markdown Report (sections + inline [N] citation markers + numbered Sources list). The reducer has a no-LLM fallback (one section listing every verified claim verbatim) so a missing key never silently drops the research. winnerOutput is the { report, markdown, verifiedCount, droppedCount, degradationNote? } envelope; markdown is written to --out as the final Report.

The command. --depth is the effort knob; --out overrides the default report path:

# Default (deep): 5 angles, 3 skeptics per claim
cyberine research "What are the trade-offs between SQLite and DuckDB for embedded analytics?"

# Quick: 3 angles, 2 skeptics — fast, narrow coverage
cyberine research --depth quick "How does WebGPU adoption compare across browsers in 2026?"

# Max: 7 angles, 5 skeptics — pays the verify cost for ambiguous / open-ended questions
cyberine research --depth max "Compare LiteLLM vs Portkey for production LLM routing"

# Explicit output path
cyberine research --depth deep --out ./reports/duckdb-vs-sqlite.md \
  "Trade-offs between SQLite and DuckDB for embedded analytics?"

Default --out is <cwd>/.local/research/<slug>.md (slug = kebab-case of the question, capped at 60 chars). The Report is the rendered markdown only — HTML rendering is left to a downstream viewer. The run also lands in .local/runs/<runId>/ the same way every other dispatch does, so cyberine agents shows it from any directory and cyberine digest <runId> can re-inspect the synthesized envelope. The CLI prints runId, state, depth preset, verified / dropped claim counts, and both the report path and the run dir on completion.

Search backend (kx1bv): a Claude seat by default, Perplexity selectable. web_search returns one shape whatever runs it — {query, summary, sources[{url, title}]}, the URLs the verify step needs — and picks its backend on every call:

BackendWhat runsSpends
seatclaude -p on a Claude subscription seat with only the WebSearch tool allowed and a --json-schema for the summary + sources; token passed by file descriptor like every seat-pinned workerone claude -p turn of that seat's quota per search
perplexityPerplexity's OpenAI-compatible chat completions with a sonar-* model (search_results / citations become the sources)one Perplexity request per search

auto (the default) takes seat when one is usable and falls back to perplexity when no seat is and PERPLEXITY_API_KEY is set. A usable seat is, in order: the seat CYBERINE_WEB_SEARCH_SEAT names; otherwise every enabled claude seat in fleet.json whose tokenFile exists, rotated per call (seat-rotation.json, profile web-search); otherwise the logged-in claude CLI itself. cyberine doctor prints the choice under Web search::

cyberine doctor | grep -A1 '^Web search:'
CYBERINE_WEB_SEARCH_BACKEND=perplexity cyberine research "<q>"
CYBERINE_WEB_SEARCH_SEAT=<seat-name> cyberine research "<q>"
Envconfig.json keyDefaultUsed by
CYBERINE_WEB_SEARCH_BACKENDresearch.searchBackendautoauto, seat or perplexity; any other value makes web_search return a "not configured" note naming the valid ones
CYBERINE_WEB_SEARCH_SEATresearch.searchSeat (global config only)(empty)pins the seat backend to one fleet.json seat; not settable from a project .cyberine/config.json, because it names which account pays
CYBERINE_WEB_SEARCH_MODELresearch.searchModelhaikuthe --model of the seat backend's claude -p turn
CYBERINE_WEB_SEARCH_TIMEOUT_MSresearch.searchTimeoutMs180000per-search timeout of the seat backend
PERPLEXITY_API_KEYperplexity.apiKey(empty)the perplexity backend; also enables the loop-perplexity-build worker
PERPLEXITY_MODELperplexity.modelsonar-promodel id sent to the Perplexity endpoint. sonar-pro has native web search; override only to A/B another sonar variant
PERPLEXITY_BASE_URLperplexity.baseUrlhttps://api.perplexity.aiendpoint base. Rarely overridden

fetch_url needs no key (SSRF-safe stripped-to-text fetch, 50k char cap, 15s timeout, 5 redirect hops max — private / loopback / link-local / cloud-metadata addresses are refused). The planner + verifier + synthesizer reuse the standard DEEPSEEK_API_KEY / OPENROUTER_API_KEY the rest of the fleet uses.

Depth presets. The CLI flag maps to two env vars the workflow reads at module load:

--depthCYBERINE_DEPTHCYBERINE_SKEPTICSAnglesNotes
quick123 (by-entity, by-time, by-source-type)fast; pick when the question is narrow
deep (default)235 (+ by-perspective, by-counter)recommended for most questions
max357 (+ by-domain-specific, by-edge-case)thorough; pays the verify cost

CYBERINE_DEPTH = N requires at least N loop adapters available in the fleet — smaller fleets must lower it (the engine throws a clear count-mismatch error, same contract as sampleSearchFanOutWorkflow.ts). CYBERINE_SKEPTICS is the per-claim verifier count; override between presets if you want non-standard verification cost.

Run the workflow directly (no cyberine research parent) for dogfooding:

CYBERINE_TASK="What are the trade-offs between SQLite and DuckDB for embedded analytics?" \
CYBERINE_DEPTH=2 CYBERINE_SKEPTICS=3 \
  npm run wf -- src/engine/researchWorkflow.ts

Failure modes. When web_search has no usable backend (no seat, no logged-in claude, no PERPLEXITY_API_KEY), the search step still runs — web_search returns the "not configured" note, the verifier drops the unverifiable findings, and the synthesizer either emits an empty-but-honest Report or surfaces a degradationNote on the envelope. When the synthesizer LLM is unreachable, the reducer's no-LLM fallback lists every verified claim verbatim. When no markdown was produced at all, the CLI exits non-zero and points at cyberine digest <runId> for inspection.

Also reachable from other surfaces (kz5sr, khh1q). cyberine research is the foreground CLI, but the same workflow is wired into two more entry points, both non-blocking (return a runId immediately, poll the outcome): the research MCP tool (see "Non-blocking research" under "Expose Cyberine as an MCP server" above) and cyberine chat's /xmode research + /run (see /xmode under the chat command reference above). Both spawn a fresh detached child process per dispatch rather than running in-process, because researchWorkflow.ts reads its question/depth from env vars at module load time and both callers are long-lived processes — see src/engine/researchAsync.ts's doc comment for the full reasoning.

From inside Claude Code — /cyberine:research. The bundled plugin skill (plugin/skills/research/) is the front door for the operator's daily "spawn some agents and find out X" habit: it preflights the web-search backend with cyberine doctor (reads the web search backend: line under Web search:), refuses before spending when that line is [WARN], otherwise picks a --depth from the question's shape, states the spend including the search backend, runs the command above, then reads the report back and returns the verified findings with their citations plus the verified/dropped counts. It is disable-model-invocation: true (spends quota, so plain language does not reach it directly — only an explicit /cyberine:research "<question>") and deliberately web-only: a question about this repo's own code stays with Claude Code's own agents, never this skill.

/cyberine:research "What are the trade-offs between SQLite and DuckDB for embedded analytics?"
/cyberine:research --depth max "Compare LiteLLM vs Portkey for production LLM routing"

A loop worker is required, and research says so up front. Every research step dispatches through a raw-API loop worker, and CLI / seat workers are build-fleet-only: a machine whose fleet.json enables only seats and CLIs builds ZERO adapters for cyberine research, however good its credentials are. Since k1ggk the run refuses with the reason and the fix rather than check your credentials — it names the loop worker(s) the fleet does resolve and the exact pin:

CYBERINE_BUILD_FLEET="loop-commandcode-free-build" cyberine research --depth quick "<question>"
# or permanently: cyberine fleet list, then cyberine fleet enable <provider>

Known Limitations and Deferred Work. The seat backend was built and checked offline against a stub claude (flags, token by descriptor, source filtering); no full research run has produced a verified claim yet, and kf15g covers running one end to end. The backend is resolved per web_search call, not per run, so a seat that runs out of quota mid-run shows up as failed searches rather than a preflight refusal; the seat backend is not a fleet worker, so its turns do not appear in cyberine agents or the spend ledger. web_search is the same tool in cyberine chat and every loop worker, so the default seat backend spends seat quota there too. Anthropic API and OpenRouter search backends are not built; the selector is shaped for them. The skill's depth heuristic (narrow fact -> quick, ordinary question -> deep, open-ended/market-scan -> max) is a starting point an explicit --depth in the request always overrides, not a classifier. It never promotes a finding onto the task board — that is /cyberine:tasks.