Skip to content

Repository files navigation

opencode-workflows

An OpenCode plugin for running resumable, multi-agent workflows with durable run state.

@mcrescenzo/opencode-workflows adds a workflow_* tool family to OpenCode that lets you (or an agent) fan work out across multiple agent lanes, preview and approve an exact plan by default, inspect persisted status, resume after an interruption, and apply edits to your repo only through a reviewed, hash-gated boundary.

It is the engine and harness, with one flagship workflow included; you bring the rest.

Why use it

OpenCode chats are great for one-off tasks, but some jobs are bigger or riskier than a single unstructured prompt: a whole-repo review, a multi-step research fan-out, a staged edit plan, a long-running background sweep. This plugin gives those jobs a structure:

  • Plan, then approve by default. The default/manual path previews authority, models, budgets, lanes, and a source hash before a matching hashed approval. A plugin owner may instead configure options.autoApprove for eligible first-call launches within a fixed authority ceiling.
  • Fan out safely. Work runs in isolated lanes (including git worktrees for edits), inside a deterministic QuickJS sandbox, with deny-by-default permissions per lane.
  • Recover after interruptions. Run state and completed lane results persist; pause, cancel, reconcile, and resume re-run only what's needed. Live execution still stops with the owning OpenCode process.
  • Extend a stopped wave deliberately. maxAgents is cumulative approved logical-call credit, not an attempt counter. An agent-capacity stop persists a single-use request that an approval-bound grantAgents resume can fund additively while completed lanes replay.
  • Apply, don't assume. Normal edit workflows reach your real tree only through workflow_apply, after source/base/diff/domain hashes and a clean git base are checked. The explicit exception is a trusted autonomous-local drain: its launch approval can bind verified in-run apply and domain finalization.

What you get

  • A workflow_* tool family: workflow_run, workflow_status, workflow_events, workflow_apply, workflow_save, workflow_list, workflow_templates, plus lifecycle tools (workflow_cancel, workflow_pause, workflow_kill, workflow_reconcile, workflow_cleanup, workflow_salvage) and references (workflow_roles, workflow_models, workflow_template_save).
  • Workflow primitives in the sandbox body: agent, parallel, pipeline, nested workflow, phases, budgets, structured-output schemas, and background runs with completion notifications.
  • Authority profiles that scope what a run can do — from read-only review up to hash-gated primary-tree apply and (via a trusted extension) autonomous local drains.
  • Starter templates (first-run-slice, scoped-parallel, edit-review) to copy and adapt.
  • One bundled flagship workflow — deep-research — plus its /deep-research command: deep multi-source web research with adversarial fact-checking. It doubles as the living gold-standard example of every convention below.
  • Three bundled skills: opencode-workflow-authoring, workflow-model-tiering, and workflow-plan-review.
  • A trusted-extension seam so operators can contribute their own workflows, commands, skills, tools, and drain adapters.

What it is not

  • It is an engine, not a pack of automations. It ships exactly one bundled workflow (deep-research, with its /deep-research command) as the flagship exemplar; everything else you write yourself (or install via a trusted extension) and run with workflow_run.
  • It is not a daemon. Background runs live inside the OpenCode process and stop if that process exits; use workflow_reconcile to recover stale runs.
  • It does not silently write to your working tree. Normal edit runs stop at an approval boundary; you apply explicitly.
  • Trusted extensions are not sandboxed — they run as normal Node code in your process. Only install extensions you'd trust as local code.

Install

Requires OpenCode, Node ≥ 20.11, and git on your PATH.

bun add @mcrescenzo/opencode-workflows
# or
npm install @mcrescenzo/opencode-workflows

Register it in opencode.json (the singular plugin array key):

{
  "plugin": ["@mcrescenzo/opencode-workflows"]
}

Restart OpenCode after changing plugin config.

Bundled workflow: deep-research

Deep multi-source research with adversarial fact-checking: Scope → parallel web search per angle → URL-dedup + source budget → indexed, target-bound evidence extraction through WebSearch → per-claim adversarial vote panels (3 votes at thorough; verifier infrastructure errors are reported as unverified, never as refutations) → cited synthesis.

workflow_run({ name: "deep-research", args: "is fish oil effective for ADHD?", background: true })

Or with options: args: { question, depth: "quick" | "normal" | "thorough", maxSources, seedUrls }. Seed inputs must be bounded, already-public and indexed HTTP(S) URLs; local files, non-public address literals, and credential-bearing URLs are rejected and surfaced in the result. Do not submit confidential research questions or confidential, unlisted, capability-bearing, or signed URLs. The research question, generated queries, and seed URL targets are sent through OpenCode WebSearch to its configured external search provider (Exa or Parallel on the supported runtime). Depth thorough (default) is full 3-vote verification.

The run asks for network authority at its one-time approval, but every child lane explicitly denies direct WebFetch. Search, extraction, and verification use remote WebSearch only; scope and synthesize lanes deny network access. Consequently, seed URLs prioritize public indexed targets but are not an offline or direct-fetch fallback when the external search provider is unavailable. No shell, no MCP, no edits. The /deep-research command wraps the full flow: clarify → model tiers → approval → report persisted to .deep-research/runs/.

Quick start

The default/manual workflow path is two phases: preview, then approve. Configured options.autoApprove is the explicit exception described below.

  1. Save or write a workflow, then preview it (runs nothing, returns an approvalHash):
    workflow_run({ name: "my-workflow", args: {...}, background: true })
    
  2. Review the preview (authority, models, budgets, lanes, hashes), then run it:
    workflow_run({ name: "my-workflow", args: {...}, background: true, approve: true, approvalHash: "<hash>" })
    
  3. The background call returns a run id. Yield instead of polling; the best-effort completion prompt normally resumes the invoking session. A successful promptAsync submission proves acceptance only, not observation or acknowledgement. Then read the terminal result exactly once:
    workflow_status({ runId, detail: "result" })
    

If launch warns that completion prompts are unavailable, polling is the fallback. Use background: false only when you intentionally want the approval call to block and return the result inline.

detail: "result" preserves the legacy status.result.output path and also returns resultEnvelope version 1. The envelope states whether the persisted shape is full, partial, projected, or absent, with explicit terminal, resumable, partial, output (or null), and budgetRequest fields. Follow the returned nextActions; do not infer output for an absent/projected result.

Approving an inline-source preview doesn't require re-transmitting the source: omit source, preserve the same envelope inputs (including args and background: true), and send approve: true + approvalHash; the previewed bytes are reused from a bounded in-memory store (approve-by-reference). A mismatched approve returns changedFields naming which envelope fields re-keyed — null when the supplied hash no longer matches a recorded preview.

New to workflows? Save a copy of the smallest safe shape and run it read-only:

workflow_template_save({ template: "first-run-slice" })
workflow_run({ name: "first-run-slice", background: true })

The bundled workflow-plan-review skill owns the full launch → approval → background → completion-notification → result-readback contract; opencode-workflow-authoring covers source shape, fan-out, and edit/apply boundaries; workflow-model-tiering covers fast/deep tier mapping.

Configuration (optional)

  • OPENCODE_WORKFLOWS_DIR — where global run state, roles, and templates live. Defaults to $XDG_CONFIG_HOME/opencode/workflows.
  • OPENCODE_WORKFLOWS_DEBUG_CAPTURE=1 — write per-lane debug artifacts (prompt/schema/transcript) under each private run directory. Off by default.
  • OPENCODE_WORKFLOWS_TUI_ASCII=1 — replace the native monitor's Unicode status glyphs with distinct one-cell ASCII markers. TERM=dumb and explicit ASCII locales select the same fallback automatically.
  • Plugin autoApprove option — set to "readOnly", "worktree", or "all" to let eligible workflow_run calls launch on the first call when within that tier. A per-call autoApprove arg can narrow, never widen, the ceiling. Normal edit workflows still keep workflow_apply as a separate hash gate; a trusted autonomous-local drain may instead carry approval-bound in-run apply.
  • OPENCODE_WORKFLOWS_HARD_CONCURRENCY_LIMIT — raises/lowers the per-run concurrency ceiling (default 64; effective default remains 4).

Safety & privacy

  • The default/manual launch path needs a one-time hashed approval before it starts. Configured options.autoApprove may authorize an eligible first-call launch within its fixed ceiling. Normal edit workflows retain workflow_apply as a separate reviewed hash gate; a trusted autonomous-local drain can bind verified primary-tree apply and domain finalization into its launch approval. Elevated authority additionally requires a compatible OpenCode server (≥ 1.17.13).
  • Lane permissions are deny-by-default and re-checked against the session at launch; edit-capable lanes run in isolated git worktrees.
  • Raw run artifacts under .opencode/workflows/runs/ can contain sensitive local evidence. Prefer workflow_status({ detail: "result" }) and workflow_events (both redacted) over reading raw files.
  • Model-facing status, completion prompts, and newly emitted exact run references do not contain absolute run/result paths. Exact duplicate-run references bind to a one-way run-root fingerprint; copied legacy references remain readable for compatibility.
  • Model-facing artifact receipts expose availability, bounded file names/logical references, counts, and one-way hashes; canonical artifact directories and file paths remain private controller state.

The deep contracts — full trust model, source-of-truth hierarchy, salvage/crash recovery, apply internals — live in the docs linked below, not here.

For agents

Agents install and use this plugin exactly like users: add @mcrescenzo/opencode-workflows to the plugin array in opencode.json and restart OpenCode. Then drive it through tools:

  • Author a workflow body (export const meta = {...} + top-level statements ending in return; no imports) and save it with workflow_save, or run it inline. The body runs in a QuickJS sandbox with injected globals: agent, parallel, pipeline, workflow, phase, log, budget, args, persistArtifacts, inventoryFiles, drain.
  • Launch with workflow_run (preview → approve: true + approvalHash; inline-source approves may omit source — approve-by-reference). Pass background: true by default and use profile: "read-only-review" until a task truly needs more.
  • Yield after launch instead of polling. The completion prompt normally resumes the invoking session; then read workflow_status({ detail: "result" }) exactly once. Poll only on the explicit no-notification fallback, for user-requested progress/control, or for recovery. Foreground results are already inline.
  • For edit runs, review the diff plan and apply with workflow_apply plus the required hashes.
  • See the bundled workflow-plan-review, opencode-workflow-authoring, and workflow-model-tiering skills and the tool reference below for the full contract (launch/readback, sandbox limits, fan-out arity, schemas, model tiers, edit/apply boundaries).

Documentation Map

Use workflow_list({ format: "json" }) as the machine-canonical discovery surface for saved and bundled workflow names, args schemas, examples, authority profile, model-tier hints, and safe readback steps. The docs below are operator guidance, technical contracts, or historical context.

Packaged vs GitHub-only. The tarball includes the runtime plugin, its bundled workflow and command, all bundled skills, root package/community documents, and docs/workflow-plugin.md. Within the docs/ tree, docs/workflow-plugin.md is the only packaged file: it is the canonical workflow_* tool reference (docs/workflow-plugin.md#workflow-tool-reference) that every extension, skill, or agent invoking workflow_run/workflow_apply/workflow_status depends on — independent of any bundled command beyond the one flagship workflow and command pair (deep-research). Every other doc below lives in the GitHub repository only: read it from a source checkout, or follow the GitHub links in the table.

Category Documents Packaged?
Packaged reference surfaces README.md, skills/*/SKILL.md, docs/workflow-plugin.md Yes
Active operator references (GitHub only) docs/workflow-recipes.md, docs/plugin-system-tests.md, docs/run-audit-playbook.md, docs/goal-supervision-autonomous-drains.md No
Active technical contracts (GitHub only) docs/workflow-extensions.md No
Roadmap / planning (GitHub only) docs/claude-parity-roadmap.md No
Historical snapshots / audits / completed plans (GitHub only) docs/release-gate-validation-2026-06-16.md, docs/dogfood-rollout-2026-06-16.md, docs/workflow-autonomous-harness-design.md, docs/workflow-autonomous-harness-plan.md, docs/review-2026-06-19-bug-robustness-remediation-plan.md, docs/general-purpose-harness-extraction-plan.md, docs/superpowers/specs/2026-06-23-session-aware-model-tiering-design.md, docs/superpowers/plans/2026-06-23-session-aware-model-tiering-plan.md, docs/superpowers/plans/2026-06-23-port-repo-bughunt-to-opencode.md, docs/superpowers/specs/2026-07-07-toast-status-cards-design.md, docs/superpowers/plans/2026-07-07-design-c-gate-simplification.md, docs/superpowers/plans/2026-07-08-agent-surface-docs-accuracy.md, docs/superpowers/specs/2026-07-08-deep-research-bundled-workflow-design.md, docs/superpowers/plans/2026-07-08-deep-research-bundled-workflow.md, docs/superpowers/plans/2026-07-08-inline-approval-rekey-hardening.md, docs/superpowers/specs/2026-07-08-pure-architecture-extraction-design.md, docs/superpowers/plans/2026-07-08-pure-architecture-extraction.md, docs/superpowers/plans/2026-07-09-deep-research-hardening.md No

Drains and extensions. The kernel ships no domain drain workflow or domain extension — drain workflows (harness: "drain") are contributed by configured trusted extensions and invoked by name (see docs/workflow-extensions.md and the "Workflow Boundaries" section of AGENTS.md). docs/goal-supervision-autonomous-drains.md describes the /goal oversight boundary for such extension-supplied drains; it does not itself ship or assume a bundled drain workflow.

Canonical safety references — apply authority and primary-tree writes, the raw-artifact source-of-truth hierarchy, lifecycle recovery and cleanup, and the deterministic launch-time trust checks — live in docs/workflow-plugin.md; the complete workflow_* tool table is in docs/workflow-plugin.md#workflow-tool-reference.

Native workflow monitor

The package ships a native OpenCode workflow monitor as a dedicated TUI plugin, alongside the server plugin, from the same npm package. Open it in OpenCode with the /workflows command or <leader>w.

Run artifacts stay in the existing project/worktree roots, but the monitor is scoped to the exact OpenCode session currently being viewed. Its minimal aggregate sidebar and full-host-content workspace include only runs that session successfully launched or resumed. /workflows, <leader>w, and clicking the sidebar all navigate to the same namespaced public TUI route; Escape backs out drill levels and then restores the exact prior route. With no exact host session, the sidebar renders nothing and the route shows no workflow data.

  • Adaptive layout by available host width: three panes (Runs / Phases and lanes / Detail) on wide terminals, two on medium, single-pane drill-down on narrow.
  • Detail tabs: Details, Activity, and Output, with contextual recovery banners and pane maximize. Keyboard and mouse.
  • Shared runs and unread results: resuming a run adds that session to its durable invoker set without removing earlier invokers, so every session that launched or resumed it can see it. Completed results stay unread until their Output loads or they are acknowledged. Acknowledgement belongs to the root-qualified run/output identity, so it is shared across those sessions and remains durable across reopen, restart, and another process viewing the same session.
  • Controls: Pause/Resume (one reversible slot), resumable Stop, permanent Cancel (distinct from Remove), exact single-run Remove, bulk Cleanup (which protects unread and non-terminal runs), selected-run Save, and exact selected-lane Stop/Restart.
  • Attention: when the terminal is blurred, the monitor raises a bounded, name-free host notification for results/attention transitions. This is distinct from the unchanged durable completion-notification prompt.

The monitor reads the kernel's bounded, secret-masked observer projections and durable request/receipt files — no raw transcripts and no new run-artifact reader. Approval stays an OpenCode-only interaction; the monitor has no approval action and never resumes, applies, reconciles, or starts an owner. Live execution still dies with its owner: after owner death the monitor shows stale state; recover with workflow_reconcile (optionally workflow_salvage), then an approved workflow_run({ resumeRunId }).

Session scoping is a display predicate, not an authorization boundary or a new storage root. Session IDs are persisted only as domain-separated digests and are never rendered. Existing runs without valid invocation attribution are hidden from the monitor by design; workflow_status remains unscoped and project/worktree-wide, so use it to inspect or recover legacy runs. There is no project-history toggle, and a forked session does not inherit monitor visibility unless it independently launches or resumes the run. Approval hashes, authority, durable completion-notification targeting, and run ownership are unchanged.

Register the TUI target. The monitor is a separate registration from the server plugin — OpenCode discovers TUI plugins only through a tui.json file. Keep the server plugin in opencode.json (above) and add a .opencode/tui.json:

{
  "$schema": "https://opencode.ai/tui.json",
  "plugin": ["@mcrescenzo/opencode-workflows"]
}

Restart OpenCode after changing either file. The support floor is OpenCode >= 1.17.13; the native monitor's interactive live-test target is exactly 1.18.9. Installing the package does not modify your PATH and does not by itself register either target.

Removed legacy monitoring surfaces

The standalone terminal companion, its diagnostic command-line interface, and ambient lifecycle progress notifications have been removed. Use the native monitor above for live observation and control.

Monitor detail is a bounded, secret-masked display boundary, not semantic filtering. Raw prompts, reasoning, tool input/output/error, transcripts, checkpoints, journals, and full result files are excluded. A successful lane may expose one allowlisted result preview only after secret and absolute-path masking, bounded to 600 Unicode code points; names, hostnames, and business data can still remain visible within that preview. Treat raw run artifacts as local-sensitive.

Linux is the verified release platform. Windows and macOS observation have not been runtime-verified for this release. The workflow_save/workflow_template_save write paths, descriptor-pinned edit/integration lane control, and the attempt journal are unsupported on those platforms and fail closed when Linux /proc/self/fd, O_NOFOLLOW, or O_DIRECTORY guarantees are unavailable. The destination registry filesystem must also support same-directory hard links and directory fsync; unsupported operations fail explicitly rather than falling back to a weaker transaction.

Ordinary workflow journals, events, and ledgers remain available on other platforms through the prior private-file implementation. On verified Linux hosts, those JSONL files additionally use descriptor-pinned target checks, cross-process append locks, fail-closed interior validation, and a version-matched validation sidecar so appends do not rescan the full history. Exact run deletion similarly uses validated-inode quarantine rename before recursive removal on Linux. It fails closed when descriptor-backed quarantine is unavailable; there is no pathname-only recursive-deletion fallback.

Source Checkout Verification

The npm package ships the server plugin, the native TUI monitor target (@mcrescenzo/opencode-workflows/tui), all bundled skills, exactly one workflow and command (deep-research), root package/community documents, and docs/workflow-plugin.md (see files in package.json for the exact list). The other active operator and technical docs are GitHub-only. It does not ship the "Historical snapshots / audits" or "Roadmap / planning" docs; completed plans stay with those historical sources. It also does not ship this repository's tests/, scripts/, or reference extension source. The npm run ... verification commands below are for a source checkout or contributor clone, not for an installed package tarball.

Run the nested repo workflow regression wrapper from this directory:

npm run test:workflows

This wrapper covers the core workflow_run / workflow_apply paths. Kernel drain, extension-seam, and durable state coverage live in the focused scripts below and in the catch-all npm test matrix.

Run focused kernel and extension coverage from this directory:

npm run test:workflow-kernel
npm run test:workflow-adapters
npm run test:extension-seam

Run the full plugin test matrix (all workflow, adapter, runtime, durable-state, and extension integration tests) from this directory:

npm test

npm test recursively discovers every tests/**/*.test.mjs file, orders the paths deterministically, and passes the complete list to Node's built-in test runner. The broader npm run release:no-token gate also type-checks and builds the native TUI, packs and exercises the installed artifact with Node and Bun, and validates the final dry-run package manifest. Its installed-artifact step requires a tar executable on PATH.

The public CI workflow in .github/workflows/ci.yml runs that gate on ubuntu-latest with Node 20.11.0, 22, and 24, plus the repository-pinned Bun 1.3.8 and Node-20.11-compatible npm 10.9.4. It installs dependencies from bun.lock. The automated release runs that same npm pin in an unprivileged gate job, then uses the exact pinned npm 11.18.0 packer to create one release tarball and runs the installed-artifact Node/Bun import and TUI-build smoke against those exact validated and hashed bytes. A checkout-free repository-write job then reserves the exact version tag. A separate source-checkout-free minimal job uses npm 11.18.0 on Node 22.23.1 with OIDC permission, downloads that tarball by immutable artifact ID, revalidates its SHA-256 and package contents, and publishes those exact bytes with lifecycle scripts disabled. A final checkout-free repository-write job creates or repairs the GitHub Release. Checkout credentials are not persisted, every third-party action is pinned to a full commit SHA, and GitHub's native max-queue concurrency serializes the whole four-job flow without consuming a hosted runner while waiting. The platform retains up to 100 pending release runs; additional runs are canceled and must be rerun after capacity is available. Each release push must introduce exactly one strictly increasing, npm-compatible stable X.Y.Z transition. Prerelease and build-metadata versions are intentionally rejected until they have an explicit channel policy; npm normalizes build metadata and a prerelease must not silently become latest. Public CI applies an effective-merge at-most-one, monotonic transition guard to pull requests and the exact first-parent guard to pushes; configure that check as required before merging. Version-bump PRs must use squash or merge-commit integration, or contain only one version-changing commit when rebased. Because queue wait order can differ from dispatch order, the OIDC job refreshes the current main manifest immediately before publishing. A run whose package version is no longer current publishes under a commit-qualified npm tag instead of moving the default latest tag backward. Reserving the exact version tag after the no-token gate makes both failed/ambiguous publication and post-publication finalization crashes recoverable by rerunning the same workflow. Already-published versions require an existing remote version tag that points exactly at the workflow commit (annotated tags are checked at their peeled commit); the release refuses to invent an unproven tag. Tag provenance is checked before publishing and again before finalization, and only an explicit registry E404 is treated as an unpublished version. The vX.Y.Z tag records gated immutable release intent; the published GitHub Release records completion. A reserved tag can therefore remain without an npm version or GitHub Release after a persistent publish failure. Repair the registry/service issue and rerun that same current-definition workflow. Do not rerun pre-migration historical release runs, because a rerun uses the workflow definition from its original commit.

Configure npm trusted publishing for this exact workflow and treat it as the package's sole publisher. In npm package settings, the trusted publisher must name this repository and release.yml, and Allowed actions must include npm publish. Set Publishing access to Require two-factor authentication and disallow tokens, and remove other out-of-band publication paths; otherwise another publisher can race the registry check, reservation, and OIDC publish boundaries. Registry reads and publication are executed from a clean runner directory with both the default and @mcrescenzo registries pinned to https://registry.npmjs.org, so a repository .npmrc cannot redirect them.

The repository must also enforce a v* tag ruleset that permits the release workflow to create a missing tag but blocks tag updates and deletions, with bypass access minimized. The workflow rechecks the reserved direct/peeled ref immediately before npm publication and twice before GitHub Release creation; the ruleset supplies the immutability that a read-then-publish workflow cannot create by itself. The full matrix remains Linux-only because Linux is the verified release platform; it intentionally does not run publishing, token-using live probes, the private parent integration check, or the required live child system smoke.

For system-level plugin startup checks, use docs/plugin-system-tests.md (GitHub only, not packaged). Those checks start disposable child opencode servers and verify startup health, registries, and cleanup without restarting the parent TUI.

Contributing

See CONTRIBUTING.md for contributor prerequisites, lockfile policy, and release-readiness notes. Verification matrix: npm test; workflow_run/workflow_apply wrapper: npm run test:workflows.

Roadmap

The current roadmap is docs/claude-parity-roadmap.md — proposed parity work where every item is marked proposed or [shipped]. The earlier docs/workflow-autonomous-harness-plan.md is a historical moonshot plan retained for context (its beads domain and live-gate subsystem were implemented and later removed; see CHANGELOG 0.2.0). Current autonomous-drain behavior comes from configured trusted extensions (docs/workflow-extensions.md), not a bundled domain workflow.

About

No description, website, or topics provided.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages