No description
  • JavaScript 82.1%
  • Dockerfile 14.7%
  • Shell 3.2%
Find a file
Lennart Steffen 0b29429ed0 Update Readme
2026-09-27 20:42:26 +02:00
agents Initial Commit 2026-09-11 00:15:55 +02:00
commands Add research-search skill to search local files 2026-09-14 20:35:59 +02:00
plugins Add plugin: reasoning-effort.js 2026-09-27 20:06:51 +02:00
skills Add research-search skill to search local files 2026-09-14 20:35:59 +02:00
AGENTS.md Update Readme 2026-09-27 20:42:26 +02:00
Dockerfile Add state volume to save model selection 2026-09-27 20:42:18 +02:00
opencode.json Initial Commit 2026-09-11 00:15:55 +02:00
README.md Update Readme 2026-09-27 20:42:26 +02:00
run.sh Add state volume to save model selection 2026-09-27 20:42:18 +02:00

opencode-deepresearch

Docker image providing a pre-configured OpenCode environment for AI-based deep research workflows.

Features

  • OpenCode CLI (latest) running on Debian 13 (slim)
  • Deep research skill with a 7-step workflow: plan, scale, gather, draft, cite, review, deliver
  • PDF summary skill — download or read local PDFs, extract text, and produce faithful structured summaries with provenance
  • Research search skill — find a fact or claim in prior research outputs and report every hit verbatim with its complete citation(s) and URL(s)
  • 3 subagents: researcher, verifier, reviewer
  • Reasoning-effort plugin — injects reasoning_effort into chat completions based on the per-model variant selected in the TUI
  • Exa MCP (remote) for web search and source fetching
  • Pre-installed tooling: vim, curl, jq, ca-certificates, PDF tooling (poppler-utils: pdftotext, pdfinfo), Node.js LTS
  • Persistent config, credentials, and TUI state (last-used model) across restarts via named volumes

Prerequisites

Build

docker build -t lsteffen161/opencode-deepresearch .

Run

./run.sh

Or directly:

docker run --rm -it \
    -v opencode-deepresearch-config:/home/agent/.config/opencode-config \
    -v opencode-deepresearch-data:/home/agent/.local/share/opencode/ \
    -v opencode-deepresearch-state:/home/agent/.local/state/opencode/ \
    -v $PWD:/workspace \
    opencode-deepresearch:latest

./run.sh forwards any arguments to opencode inside the container (e.g. ./run.sh <topic>). Note: run.sh is hard-coded to the published image lsteffen161/opencode-deepresearch:latest — rebuild and retag if you change the local image name.

Your current directory is mounted at /workspace. The named volume opencode-deepresearch-config persists only the single file opencode.json (your providers, MCP settings, permissions) — in the container, ~/.config/opencode/opencode.json is a symlink into that volume. opencode-deepresearch-data persists your API keys and session state. opencode-deepresearch-state persists your TUI state, which is what makes the last-used model stick across restarts. Agents, skills, commands, and plugins are never persisted — they always come from the latest image build, so a rebuild updates them automatically.

First-Time Setup

  1. Run the container for the first time.
  2. Inside the container, launch OpenCode and connect your provider with /connect, or write your provider config directly. The image ships a minimal opencode.json (Exa MCP enabled, bash fully permitted) that you can extend:
cat > /home/agent/.config/opencode/opencode.json << 'EOF'
{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "bash": {
      "*": "ask",
      "git *": "allow",
      "grep *": "allow",
      "ls *": "allow",
      "cat *": "allow",
      "find *": "allow",
      "rg *": "allow"
    }
  },
  "mcp": {
    "exa": {
      "type": "remote",
      "url": "https://mcp.exa.ai/mcp",
      "enabled": true
    }
  },
  "provider": {
    "your-provider": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Your Provider",
      "options": {
        "baseURL": "https://your-api-endpoint/v1"
      },
      "models": {
        "your-model": {
          "name": "your-model",
          "reasoning": true,
          "tool_call": true
        }
      }
    }
  }
}
EOF
  1. Exit the container. Your changes persist in the opencode-deepresearch-config volume.
  2. On subsequent runs your config is loaded automatically.

Security note: the shipped opencode.json sets "bash": { "*": "allow" }, which grants unrestricted shell access inside the container. Tighten these permissions if you want OpenCode to ask before running commands.

Using Deep Research

Inside the container, ask OpenCode to run deep research on any topic:

/deepresearch <topic>

Or simply describe what you want researched and OpenCode will invoke the deep research workflow.

Using PDF Summaries

Inside the container, download/read and summarize one or more PDF documents (by URL or local path):

/pdf-summary https://example.com/paper.pdf
/pdf-summary path/to/report.pdf https://example.com/another.pdf

The pdf-summary skill:

  1. Acquires each PDF — downloads URLs with curl or reads local files into outputs/.pdfs/.
  2. Extracts text with pdftotext -layout and records metadata (pages, title, author) with pdfinfo (via poppler-utils).
  3. Summarizes faithfully — never fabricates content; every claim traces to extracted text.
  4. Writes outputs/<slug>-summary.md plus a <slug>.provenance.md sidecar (source, checksum, extraction status).

Scanned or image-only PDFs produce a metadata-only summary with Extraction: BLOCKED (OCR is out of scope). Dead URLs are reported as failed downloads.

Query your own prior research output for a fact, figure, or claim:

/research-search energy per training run of RISC-V vs x86

The research-search skill greps the generated research files and reports each match with its complete statement text and full source citation(s) and URL(s) — nothing is paraphrased or truncated. It only reads files that carry a resolvable source map per statement:

In scope Out of scope
outputs/*.md, papers/*.md outputs/.plans/ (uncited working notes)
outputs/.drafts/*-cited.md, *-revised.md *.provenance.md
outputs/.drafts/*-research-T<n>.md, *-research-direct.md *-draft.md, *-verification*.md, *-review.md

A standalone deliverable whose name merely contains a word like verification or review is still searched if it has its own ## Sources list and per-statement inline citations. Results are reported in chat; no files are written.

Reasoning Effort Plugin

plugins/reasoning-effort.js keeps a provider's reasoning_effort parameter in sync with the variant you picked in the TUI. It intercepts outgoing /chat/completions requests and resolves the effort in this order:

  1. the model's options.reasoningEffort in your provider config (wins always),
  2. otherwise the per-model variant map in ~/.local/state/opencode/model.json (this is what the TUI writes when you cycle variants).

Recognized variants are max, high, xhigh, medium, and low, passed through verbatim. An existing reasoning_effort in the request is preserved unless override is enabled. Note that this depends on the opencode-deepresearch-state volume — without it the variant map is empty on every start and the plugin has nothing to read.

Configure it through environment variables (pass them with docker run -e):

Variable Default Effect
REASONING_EFFORT_LOG true Write a JSONL log of each injected request
REASONING_EFFORT_OVERRIDE false Overwrite an existing reasoning_effort instead of only filling in a missing one
REASONING_EFFORT_PROVIDERS (all) Comma-separated substrings; only matching request URLs are touched
REASONING_EFFORT_LOG_FIELDS model,reasoning_effort,source,stream,tools_count,messages_count Fields recorded per log line
REASONING_EFFORT_LOG_DIR ~/.local/share/opencode/log Log directory

The log file is deleted on clean exit, so it is a debugging aid rather than a permanent audit trail.

Deep research workflow

The deepresearch skill orchestrates planning through delivery:

  1. Plan — writes a plan artifact to outputs/.plans/<slug>.md and asks for your approval before gathering evidence.
  2. Scale — chooses direct search for narrow questions, or delegates to researcher subagents for broad surveys.
  3. Gather — collects sources via search and fetch tooling.
  4. Draft — synthesizes findings into a structured draft.
  5. Cite — the verifier subagent anchors every claim to a source and verifies URLs.
  6. Review — the reviewer subagent audits the cited draft for unsupported claims and overreach.
  7. Deliver — writes the final brief plus a .provenance.md sidecar.

Artifacts

Path Purpose
outputs/.plans/<slug>.md Research plan (key questions, scale decision, task ledger, verification log)
outputs/.drafts/<slug>-draft.md Uncited synthesis
outputs/.drafts/<slug>-cited.md Draft with inline citations
outputs/.drafts/<slug>-draft.md → outputs/.drafts/<slug>-verification.md Review findings (FATAL / MAJOR / MINOR)
outputs/<slug>.md or papers/<slug>.md Final research brief
<slug>.provenance.md Provenance: sources consulted/accepted/rejected, verification status

Project Layout

├── Dockerfile              # Debian slim + OpenCode + tooling
├── run.sh                  # Docker run wrapper with volumes
├── opencode.json           # OpenCode config (Exa MCP, permissions, provider slot)
├── agents/                 # Subagents copied into the container
│   ├── researcher.md       # Evidence gathering
│   ├── verifier.md         # Citation + URL verification
│   └── reviewer.md         # Peer-review style audit
├── commands/               # Custom slash commands
│   ├── deepresearch.md     # /deepresearch entry point
│   ├── pdf-summary.md      # /pdf-summary entry point
│   └── research-search.md  # /research-search entry point
├── skills/                 # Skills copied into the container
│   ├── deepresearch/       # 7-step workflow definition
│   ├── pdf-summary/        # PDF download + summary workflow
│   └── research-search/    # Cited-statement search over prior outputs
└── plugins/                # Plugins copied into the container
    └── reasoning-effort.js # reasoning_effort injection from TUI variant state

When you rebuild the image, agents/, skills/, commands/, plugins/, and the default opencode.json are baked in. Your config edits (e.g. the provider setup above) are written through the symlink into the opencode-deepresearch-config volume and persist independently of rebuilds.

Volumes

Volume Container Path Purpose
opencode-deepresearch-config /home/agent/.config/opencode-config Persistent config: providers, MCP settings, permissions. Contains only opencode.json, which the image symlinks to ~/.config/opencode/opencode.json.
opencode-deepresearch-data /home/agent/.local/share/opencode/ Auth credentials (auth.json), database (opencode.db), and caches
opencode-deepresearch-state /home/agent/.local/state/opencode/ TUI state: model.json (recent/favorite models + per-model variant), kv.json (UI preferences), prompt-history.jsonl, frecency.jsonl
(bind mount) /workspace Research workspace and outputs

The image symlinks ~/.config/opencode/opencode.json to opencode.json inside opencode-deepresearch-config, so only that single file is persisted. A fresh volume is seeded with the image's default opencode.json on first run (Docker's copy-on-first-use). Agents, skills, and commands are baked in and always come from the image — a rebuild updates them immediately. Leftover agents//skills//commands/ directories inside an older volume are ignored (the image versions always win).

Where Your Data Lives

  • API key / logged-in provider: set via the /connect command inside the container, stored in ~/.local/share/opencode/auth.json → persisted in opencode-deepresearch-data. No more re-entering keys every start.
  • Sessions, messages & snapshot history: stored in ~/.local/share/opencode/opencode.db → also persisted in opencode-deepresearch-data.
  • Last-used model & per-model variant: stored in ~/.local/state/opencode/model.json → persisted in opencode-deepresearch-state. OpenCode resolves a new session's model as opencode.json's model setting → the recent list in model.json → the provider default. Without this volume every container start falls back to the provider default, which is why the same model appears each time. model.json also carries the per-model reasoning variant that plugins/reasoning-effort.js reads.
  • UI preferences & prompt history: ~/.local/state/opencode/kv.json and prompt-history.jsonl → also persisted in opencode-deepresearch-state.
  • Config (providers, MCP, permissions): ~/.config/opencode/opencode.json (symlink) → persisted as opencode.json in opencode-deepresearch-config. Only this single file is persisted; agents/, skills/, and commands/ always come from the rebuilt image.
  • Research outputs: written to /workspace (your mounted working directory), so everything in outputs/, papers/, and notes/ persists on your host filesystem.

License

This project is provided as-is for personal and research use.