OpenCode Opinionated Workflow Layer
🦉

oowl

OpenCode Opinionated Workflow Layer.
Cheap models. Real process. No magic.

CI npm MIT OpenCode
Get started →

Overview

Right model for the job

Architecture on Opus. Routing on DeepSeek Flash. A README scaffold on a free model. Most workflows use one model for everything. You pay premium rates for autocomplete. oowl routes each task to the cheapest model that can actually do the work. The savings fund the runs that actually need a frontier model.

Docs before code

Architect writes design.md. Planner writes implementation.md. Both live in docs/specs/ and need your sign-off before anything gets built. Agents can't skip straight to editing files.

You approve every gate

Two hard stops in every substantial workflow: design approval and plan approval. Nothing moves to implementation without your explicit sign-off. Trivial fixes skip the ceremony entirely.

No faking done

Agents must return test output, lint results, or build logs alongside TASK_COMPLETE. File paths are declared upfront and locked. Protected spec files are tracked and must remain intact.

The backstory

Claude Code and its proprietary models are great tools. They unblock developers and accelerate real work. But they've become expensive and increasingly restricted. Token limits. Session limits. Rate limits.

I started looking for alternatives. A lot of open-weight models match proprietary ones on SWE-Bench. DeepSeek Pro at 1M tokens costs $0.10. GPT-4 costs $10. Two orders of magnitude.

Then I found OpenCode. It's model-agnostic, connects to anything. OpenCode Go gives you roughly 16,000 requests/month for $5 on request-based pricing. GitHub Copilot caps at 1,500 for more money. The math was obvious.

Once model-switching was available, the real problem came into focus: same model for everything. You're paying Opus rates to write a test scaffold or a routing comment. The solution was to route different agents to different cost tiers: cheap models for cheap work, stronger models only where the quality delta matters.

oowl is the workflow layer that makes that practical. Role-specific agents, cost-tiered profiles, a structured design → plan → review process built on top of Superpowers, with Caveman-Lite keeping inter-agent chatter token-efficient.

Quick start

Prerequisites: OpenCode installed and configured, Node.js 18+.

Install the framework

npx @jimzandueta/oowl init

The wizard asks where to install (local project or global), whether you have an OpenCode Go subscription, and which models to use for each cost tier.

Open OpenCode

opencode

Launch from your project root. OpenCode picks up opencode.jsonc and the agents in .opencode/.

Talk to dispatcher

Type your request. The dispatcher routes it: trivial fixes go straight to one implementer, substantial work flows through design → plan → implementation → review with your approvals at each gate.

The three commands

npx @jimzandueta/oowl init      # first-time setup wizard
npx @jimzandueta/oowl profile   # switch model profiles interactively
npx @jimzandueta/oowl update    # update framework files with conflict detection
💡

Install globally with npm install -g @jimzandueta/oowl to drop the npx prefix.

What you get

Feature Details
23 role-specific agents Orchestration, design, implementation, review, escalation, low-tier bounded work
23 slash commands Workflow phases, domain specialists, escalation, low-tier workers
Two approval gates Design approval and plan approval before any implementation begins
File locks Every implementation task declares exact paths; parallel tasks are collision-checked
Protected artifacts design.md, implementation.md, review.md under docs/specs/
Verification evidence No proof, no TASK_COMPLETE
Sensitive-area safeguards Auth, IAM, payments, PII, secrets, production config require explicit approval
Switchable profiles low, balanced, high, custom (from your connected models)
Superpowers methodology TDD, brainstorming, writing plans, debugging, review discipline
Caveman-Lite Terse, token-efficient agent handoffs

How it works

your request → dispatcher ├─ trivial? (under ~20 lines, under 3 files, nothing sensitive) │ └─ one implementer → done └─ substantial ├─ architect writes design.md + designer writes ui-spec.md (if UI) ├─ [ you approve the design ] ├─ planner writes implementation.md ├─ plan-reviewer validates → PLAN_APPROVED ├─ [ you approve the plan ] ├─ builder schedules implementation waves ├─ dispatcher runs assigned agents (each with file locks + verification) ├─ reviewer writes review.md └─ done

Why this instead of one big agent

One mega-agent oowl
Edits unrelated files File locks constrain every task
Design lives in chat history design.md, plan, review live in docs/specs/
"Looks done" is enough Verification evidence required
Model choice is manual Profiles assign cheap/mid/premium by role
Security review if remembered Sensitive areas trigger approval + escalation
Agent handoffs are verbose Caveman-Lite keeps runtime summaries concise

Trivial-fix fast path

A request skips the full workflow when all of these are true:

  • Under ~20 lines, under 3 files
  • No auth, IAM, payments, PII, secrets, or production config touched
  • No new dependency introduced
  • No architectural decision required

Trivial work goes directly to one implementation agent with a complete task prompt and verification requirements. You can also tell dispatcher explicitly: "this is a trivial fix, skip the design phase."

Protocol signals

Agents don't pass work through freeform chat. Every handoff is a named, structured block with required fields. That's what keeps the workflow machine-readable and stops agents from going off-script.

How signals flow

dispatcher classify request ├─ trivial → dispatcher emits TRIVIAL_FIX_DISPATCHimplementer returns TASK_COMPLETE └─ substantial ├─ architect → PHASE_COMPLETE (phase: design) │ └─ if UI → designer returns PHASE_COMPLETE (phase: ui-spec) ├─ [ user approves design ] ├─ planner → PHASE_COMPLETE ├─ plan-reviewer → PLAN_APPROVED or PLAN_REJECTED → planner revises ├─ [ user approves plan ] ├─ builder → REQUEST_CONSULT (single) or REQUEST_CONSULT_BATCH (2-3, atomic) ├─ dispatcher dispatches implementers │ ├─ success → TASK_COMPLETE │ ├─ blocked → NEEDS_USER_INPUT │ ├─ sensitive → NEEDS_USER_INPUT │ └─ low-tier + sensitive → ESCALATION_REQUEST ├─ parallel dispatch fail → PARALLEL_DISPATCH_FAILED ├─ protected artifact missing → PROTECTED_ARTIFACT_MISSING → STOP ├─ reviewer → REVIEW_COMPLETE └─ escalation agent → ESCALATION_COMPLETE

Signal reference

TRIVIAL_FIX_DISPATCH (dispatcher → implementer)

dispatcher sends this when a request qualifies for the fast path. Design, planning, and review are skipped. The implementer gets a complete task prompt and must return TASK_COMPLETE.

TRIVIAL_FIX_DISPATCH
Justification:
- under ~20 lines and under 3 files
- no sensitive area touched
- no new dependencies
- no architectural decision required
Target agent: <agent-name>
Task prompt: |
  <complete prompt>
Files: <expected files>
Verification: <required checks>
PHASE_COMPLETE (architect / designer / planner / reviewer → dispatcher)

Marks a workflow phase as complete. Used after design, UI spec, build, and review. Includes the artifact list and the next phase for dispatcher to move to.

PHASE_COMPLETE
Phase: <design | implementation-spec | build | review | ui-spec>
Summary: <summary>
Artifacts:
- <file created or modified>
Next phase: <user-design-approval | user-implementation-approval | plan-reviewer | build | review | done>
Risks: <remaining risks>
Verification:
- <checks performed or required>
PLAN_APPROVED / PLAN_REJECTED (plan-reviewer → dispatcher)

plan-reviewer returns exactly one of these. PLAN_APPROVED moves to the user approval gate. PLAN_REJECTED sends the plan back to planner with specific required changes. A new version must be approved before the workflow continues.

PLAN_APPROVED
Summary: <why this plan is executable>
Next phase: user-implementation-approval
Risks: <remaining risks>
Verification: <expected verification>
PLAN_REJECTED
Issues:
- <BLOCKER or WARNING>: <issue>
Required changes:
- <specific fix>
Return to: planner
REQUEST_CONSULT (builder → dispatcher)

Builder sends this to hand a single task to dispatcher: full prompt, file locks, and verification checks included. Dispatcher forwards it unchanged.

REQUEST_CONSULT
Target agent: <agent-name>
Task ID: <task id, if any>
File locks:
- <path>
Task prompt: |
  <complete prompt dispatcher should send directly>
Expected output: <expected result>
Verification requirements:
- <check>
Subagent summary: <current state>
REQUEST_CONSULT_BATCH (builder → dispatcher)

Builder sends this to dispatch 2–3 agents in the same assistant message. File locks must not overlap across tasks. Issuing them one at a time when a valid batch is possible is a protocol violation.

REQUEST_CONSULT_BATCH
Max parallel: 3
Wave: <wave id>
Parallel group: <group id>
Atomic dispatch required: yes
Tasks:
- Target agent: <agent-name>
  Task ID: <task id>
  File locks:
    - <path>
  Task prompt: |
    <complete prompt>
  Expected output: <expected result>
  Verification requirements:
    - <check>
  Reason parallel-safe: <reason>
Subagent summary: <why this batch is safe>
TASK_COMPLETE (implementer → builder / dispatcher)

An implementer's success signal. Must include verification evidence (test output, lint result, build output) and confirm protected artifacts under docs/specs/** are still present. No evidence, no completion.

TASK_COMPLETE
Task ID: <task id>
Files changed:
- <file>
Verification:
- <command/result>
Risks:
- <risk or none>
Notes:
- <brief note>
Protected artifacts:
- confirmed docs/specs/** unchanged and present
NEEDS_USER_INPUT (any agent → user via dispatcher)

Any agent that hits a sensitive area, an ambiguity, or a decision it can't make on its own sends this and stops. The workflow waits. Includes a safe default so the user knows what happens if they don't respond.

NEEDS_USER_INPUT
Question: <one clear question>
Why needed: <brief reason>
Default if unanswered: <safe default>
Subagent summary: <brief summary>
ESCALATION_REQUEST (low-tier agent → dispatcher)

Low-tier agents (low-engineer, low-task-worker, low-architect, low-designer) stop and send this when a task turns out to need more than their tier allows: sensitive areas, security implications, architectural decisions. They don't attempt it. Dispatcher upgrades to the right agent.

ESCALATION_REQUEST
Target agent: <high-engineer | high-architect | high-designer | security-auditor>
Why cheaper agents are insufficient: <reason>
What was attempted: <summary>
Specific output needed: <decision or deliverable>
Risk if wrong: <impact>
ESCALATION_COMPLETE (high-tier agent → dispatcher)

Sent by escalation agents (high-engineer, high-architect, high-designer, security-auditor) when escalated work is done.

ESCALATION_COMPLETE
Reason escalation was justified: <reason>
Result: <summary>
Files changed or decisions made:
- <item>
Verification:
- <command/result or recommended check>
Remaining risks:
- <risk or none>
REVIEW_COMPLETE (reviewer → dispatcher)

End of review. Lists findings by severity, any blocking issues, and gaps in verification coverage.

REVIEW_COMPLETE
Summary: <summary>
Findings:
- <finding count by severity>
Blocking issues: <yes/no>
Verification gaps:
- <gap or none>
PARALLEL_DISPATCH_FAILED (dispatcher → builder)

Dispatcher couldn't send a REQUEST_CONSULT_BATCH as one atomic message, usually context pressure. Builder responds with a fallback: run serially, split the batch, or hand the decision to the user.

PARALLEL_DISPATCH_FAILED
Reason: <why concurrent dispatch was not possible>
Fallback proposed: <serial execution | smaller batch | user decision>
PROTECTED_ARTIFACT_MISSING (any agent → dispatcher)

An agent found that a protected file under docs/specs/** is gone. Stop everything and restore it from Git before continuing.

PROTECTED_ARTIFACT_MISSING
Missing:
- <path>
Last task or batch: <summary>
Required action: restore from git or snapshot before continuing
git checkout -- docs/specs/<feature>/design.md

Agents

23 agents across 6 classes. All files live in framework/agents/ and are copied to .opencode/agents/ on install.

Orchestration

dispatcher builder

dispatcher classifies requests and routes them. Only dispatcher may invoke implementation agents via Task. builder is scheduler-only: no file edits, no shell, no Task calls.

Artifact owners

architect designer planner reviewer

Implementation

frontend-engineer frontend-polisher backend-engineer database-engineer cloud-architect test-engineer

Review

code-reviewer plan-reviewer security-reviewer security-auditor

Escalation

high-engineer high-architect high-designer

Low-tier bounded

low-engineer low-task-worker low-architect low-designer
cheap/fast tier mid/balanced tier premium/deep tier

Model profiles

Profiles assign models to agent tiers. Switch anytime with oowl profile.

Bundled profiles (OpenCode Go)

Profile Purpose
low Cost-first for long sessions and routine development
balanced Daily fullstack. Stronger models where quality pays off.
high Quality-first for higher-stakes or shorter sessions

Custom profile (no OpenCode Go needed)

oowl init and oowl profile → custom scan your connected OpenCode models and let you assign them to cheap/mid/premium tiers. If fewer than 3 models are detected, you can enter model IDs manually.

Balanced profile: current mapping

Agents Model
dispatcher, builder, low-engineer, low-task-worker opencode-go/deepseek-v4-flash
architect, planner, plan-reviewer, backend-engineer, database-engineer, code-reviewer opencode-go/minimax-m2.5
reviewer, designer, frontend-engineer, test-engineer, security-reviewer, low-architect, low-designer opencode-go/qwen3.5-plus
frontend-polisher opencode-go/kimi-k2.6
cloud-architect opencode-go/deepseek-v4-pro
security-auditor opencode-go/glm-5.1
high-engineer github-copilot/gpt-5.5
high-architect github-copilot/claude-sonnet-4.6
high-designer github-copilot/claude-opus-4.7

Model availability changes. Profiles are editable defaults, not permanent benchmarks.

Plugins

opencode.jsonc loads two plugins out of the box:

"plugin": [
  "superpowers@git+https://github.com/obra/superpowers.git",
  "caveman@git+https://github.com/juliusbrussee/caveman.git"
]
Plugin Role
obra/superpowers Skills for TDD, brainstorming, planning, systematic debugging, and review discipline. Agents load them on demand.
juliusbrussee/caveman Keeps agent handoffs short and signal-dense. Applied to routing summaries and completion signals , never to artifacts, code, or security findings.

Install

oowl init

npx @jimzandueta/oowl init

Prompts for install location, model setup, and conflict handling. Local installs write opencode.jsonc and AGENTS.md to your project root. Global installs use ~/.config/opencode/.

oowl profile

oowl profile

Updates model assignments across all 23 agent files, opencode.jsonc, and profile-models.json.

oowl update

oowl update

Updates framework files to the latest package version. Files you haven't modified update silently. Modified files show a unified diff. Keep yours or accept the update, per file.

Uninstall

# local install
rm -rf .opencode opencode.jsonc AGENTS.md .oowl.json

# global install
rm -rf ~/.config/opencode ~/.oowl.json

Customizing

Edit an agent prompt

Agent files live in .opencode/agents/ (local) or ~/.config/opencode/agents/ (global). Edit directly for immediate effect. When oowl update runs and detects the change, choose "keep mine" to preserve it.

Add an agent

  1. Add a .md file under the right class in framework/agents/
  2. Add the agent to every profile JSON under agent_order and agents
  3. Update framework/agents/README.md
  4. Add a command file in framework/commands/ if needed
  5. Run oowl profile to verify it appears in the model strategy

Change workflow rules

File Controls
framework/prompts/shared/routing.md Trivial vs substantial thresholds
framework/prompts/shared/protocols.md Core protocol blocks and agent communication
framework/prompts/shared/sensitive-data.md Sensitive-area triggers and escalation rules
framework/prompts/shared/protected-artifacts.md Artifact ownership and protection rules

Add a custom profile

cp framework/model-profiles/balanced.json framework/model-profiles/my-profile.json
# edit my-profile.json with your model assignments
bash scripts/apply-profile-models.sh framework/model-profiles/my-profile.json

Common issues

Agents aren't showing up in OpenCode

OpenCode must be launched from the directory containing opencode.jsonc. For a local install, that's your project root.

ls .opencode/agents   # should list agent subdirs
ls opencode.jsonc     # should exist

If either is missing, re-run oowl init from your project root.

oowl command not found after npm install -g

Your global npm bin directory may not be on PATH.

npm bin -g   # shows the path to add

Alternatively, keep using npx @jimzandueta/oowl, which doesn't require a global install.

Wrong model after switching profiles

Use oowl profile: it updates agent frontmatter, opencode.jsonc, and profile-models.json as one atomic operation. The legacy shell script doesn't update opencode.jsonc unless you pass the project root as a second argument.

Fewer than 3 models found during init

oowl needs at least one model per tier (cheap, mid, premium). Connect more providers in OpenCode's settings, then run oowl init again, or choose "enter manually" to type model IDs directly.

oowl update shows everything as modified

.oowl.json is missing or predates checksum tracking. Run oowl init (choose "skip existing files") to regenerate it. Future updates will detect changes correctly.

Protected artifact disappeared mid-workflow

Restore from Git:

git checkout -- docs/specs/<feature>/design.md

To protect against this, commit approved specs before implementation starts:

git add docs/specs
git commit -m "chore: preserve approved specs for <feature>"
Caveman-Lite not activating

The Caveman plugin is loaded via opencode.jsonc. Make sure OpenCode is running from the correct directory so it picks up the config. Check that the plugin URL is reachable from your network.

The workflow feels like too much ceremony

Tell dispatcher explicitly: "this is a trivial fix, skip the design phase."

Or adjust the routing thresholds directly in framework/prompts/shared/routing.md. The rules are yours to tune.

Contributing

Before opening a PR, run:

# Node tests
npm test

# CLI smoke tests
node bin/oowl.js --help
node bin/oowl.js foobar; echo "exit: $?"   # should exit 1

# Profile JSON validation
for f in framework/model-profiles/*.json framework/profile-models.json; do
  node -e "JSON.parse(require('fs').readFileSync('$f','utf8'))" && echo "OK: $f"
done

# npm pack check
npm pack --dry-run

Keep these intact:

  • dispatcher owns dispatch: only it invokes Task
  • builder schedules only: no file edits, no shell, no Task
  • Protected artifacts remain owner-controlled
  • Implementation tasks require file locks
  • Verification evidence required before TASK_COMPLETE
  • Sensitive flows require explicit approval and stricter routing
  • Caveman-Lite stays limited to runtime communication, not artifacts or code

See CONTRIBUTING.md for the full process.