JSPM

  • Created
  • Published
  • Downloads 4161
  • Score
    100M100P100Q103753F
  • License MIT

Strict Goal Mode agents, commands, and guard plugin for OpenCode.

Package Exports

  • opencode-goal-mode
  • opencode-goal-mode/package.json
  • opencode-goal-mode/tui

Readme

OpenCode Goal Mode

The OpenCode agent that can't fake "done" — and can't wreck your repo doing it.

Give it a goal. It writes a contract, does the work, reviews itself with a fleet of specialist subagents, and physically cannot tell you it's finished until those reviews actually pass. Reach for rm -rf mid-run and it stops the command cold.

npm version npm downloads CI Release license

npm install -g opencode-goal-mode && opencode-goal-mode --global

Every coding agent will happily announce "✅ Done!" over a half-finished feature and a red test suite. Goal Mode ends that. It moves the discipline out of the prompt — where a confident model just talks past it — and into the harness, where it's enforced in code. The agent's "Goal Completed" is intercepted and rewritten to "Goal Not Completed" unless every required review gate has a fresh pass. Dangerous shell commands are blocked before they ever execute.

It's the difference between asking an agent to be careful and making it.

Watch it refuse to lie

The agent tries to declare victory early. The guard catches it and hands back the truth:

- Goal Completed
+ Goal Not Completed
+
+ Goal Guard blocked completion: required review gates are missing or stale
+ (goal-security-reviewer, goal-final-auditor). State: active=true; dirty=true;
+ reviewCycles=1; missingGates=goal-security-reviewer goal-final-auditor

The agent reaches for something irreversible. The guard kills it before it runs:

$ rm -rf build
✕ Goal Guard blocked a destructive or high-risk bash command
  (rm with recursive force deletion). Use a safer, reversible command
  or ask the user to confirm.

OpenCode Goal Mode sidebar preview

↑ While a goal runs, Goal Mode takes over the TUI sidebar with a live, evidence-aware todo list: the goal title, gate progress, and a row per acceptance criterion and missing reviewer — each ticking off as the work gets verified.

Why you'll want it

🔒 "Done" actually means done. Completion is gated on real review verdicts, not vibes. The model can't answer Goal Completed until every required reviewer returns Verdict: PASS after the last edit — and the claimed Review cycles: N has to match the counter the guard kept.

🤖 The reviews run themselves. When the agent stops with work outstanding, the guard launches the reviewer subagents itself — security, diff, verification, and more — reads their verdicts, and loops fix → review until they pass. You never rely on the model to remember to check its own work.

♻️ One edit reopens the gates. Approvals are stamped with a monotonic sequence, so any change after a review instantly goes stale and forces the relevant reviews to re-run. There's no sneaking a "fix" in after the green light.

🧠 It knows which experts to call. Touch auth and the security reviewer becomes mandatory. Touch a migration and the data reviewer joins. API, performance, tests, UX, ops, docs, quality — the right specialist gates are required automatically from your goal and your diff.

🛡️ Your repo survives. A real shell tokenizer — not a brittle regex — blocks the destructive stuff even when it's disguised: $(rm -rf …), bash -c "…", /bin/rm, busybox rm -rf, git reset --hard, curl | sh. Harmless look-alikes like git checkout -b sail right through.

🚀 It doesn't quit on you. An idle-but-unfinished goal gets automatically pushed forward — told exactly what's left — until it's genuinely complete, with hard caps and a no-progress breaker so it can never spin.

The numbers

Tested on 704 real-world commands from tldr-pages (common/linux/osx) — commands written by hundreds of contributors who've never seen this guard:

On 704 commands it has never seen Regex guard Goal Mode
Dangerous commands caught 53.8% 93.3%
Safe commands wrongly blocked 0.2% 0.2%

Guard accuracy on real third-party commands

And it's effectively free: ~1µs per command, hundreds of thousands of classifications a second. Run it yourself with npm run bench.

Per-command analysis latency

How it stacks up

Goal Mode Claude Code Codex
Blocks a premature "done" out of the box Yes Only via a custom hook Review is advisory
Edits auto-invalidate stale approvals Yes
Specialist reviews auto-required from the task Yes
Destructive commands blocked by a real shell parser Yes Regex ("fragile") Sandbox

Mechanically-enforced goal discipline vs. Claude Code and Codex

Full side-by-side with citations: research/goal-mode-comparison.md.

Install

One command. Needs Node 20.11+ and OpenCode. macOS and Linux:

npm install -g opencode-goal-mode && opencode-goal-mode --global

Then restart OpenCode. That's it. The installer drops the Goal agent, its reviewer subagents, slash commands, and the guard plugin into ~/.config/opencode, and registers the live sidebar. In the agent picker you'll see just goal — the reviewers are subagents it drives for you. It's idempotent (re-run to upgrade), never touches files you've edited, and --uninstall removes exactly what it added. Goal Mode uses whatever model and provider OpenCode is already set up with.

Other ways to install
# Preview first — no writes on --dry-run
opencode-goal-mode --global --dry-run

# One-off with npx (no global package needed)
npx opencode-goal-mode --global

# Into a single project (writes ./.opencode)
npx opencode-goal-mode

# Remove everything it installed
opencode-goal-mode --global --uninstall

# From source
git clone https://github.com/devinoldenburg/opencode-goal-mode
cd opencode-goal-mode && npm ci && npm run install:global

--global writes to ~/.config/opencode; no flag writes to ./.opencode; --target writes to the directory you pass. On upgrade it replaces files it owns but refuses to clobber files you've modified unless --force is passed.

Quick start

# After installing + restarting OpenCode, confirm it loaded — you'll see ONLY
# `goal (primary)`, with every specialist as a (subagent):
opencode agent list | grep goal

Then, in OpenCode, just give it a goal:

/goal add rate limiting to the login endpoint and prove it works

It writes a contract, delegates research to subagents, implements, and verifies — then stops and lets the guard run the reviews. It won't say Goal Completed until they pass. Want to feel the seatbelt? Ask it to rm -rf build mid-session and watch the guard slap it down.

See ARCHITECTURE.md for the full design and research/ for the platform reference, comparison, and threat model.

Configure it (or don't)

Goal Mode works great with zero configuration. When you want to tune it, set options in opencode.json or GOAL_GUARD_* environment variables:

{
  "plugin": [
    ["./plugins/goal-guard.js", { "blockDestructive": true, "contextualGates": true }]
  ]
}
Option / env Default Effect
blockDestructive / GOAL_GUARD_BLOCK_DESTRUCTIVE true Block destructive bash before execution.
blockNetworkExec / GOAL_GUARD_BLOCK_NETWORK_EXEC true Block curl | sh-style remote execution.
enforceCompletion / GOAL_GUARD_ENFORCE_COMPLETION true Rewrite premature Goal Completed.
autoContinue / GOAL_GUARD_AUTO_CONTINUE true Auto-continue an idle goal that isn't complete yet.
maxAutoContinue / GOAL_GUARD_MAX_AUTO_CONTINUE 50 Hard cap on automatic continuations per goal session.
programmaticReview / GOAL_GUARD_PROGRAMMATIC_REVIEW true Have the guard launch the required reviewers itself on idle.
reviewTimeoutMs / GOAL_GUARD_REVIEW_TIMEOUT_MS 360000 Per-reviewer wall-clock cap (ms) for a programmatic review.
reviewPollMs / GOAL_GUARD_REVIEW_POLL_MS 2500 Poll cadence (ms) while waiting for a reviewer's verdict.
maxReviewCycles / GOAL_GUARD_MAX_REVIEW_CYCLES 12 Hard cap on review cycles per goal; on reaching it the guard pauses for you.
abortGraceMs / GOAL_GUARD_ABORT_GRACE_MS 1200 Grace (ms) before an idle goal auto-continues, so a user cancel is always honored.
injectSystemState / GOAL_GUARD_INJECT_SYSTEM_STATE true Inject live guard state into the prompt.
persist / GOAL_GUARD_PERSIST true Persist state under the XDG state dir.
contextualGates / GOAL_GUARD_CONTEXTUAL_GATES true Require specialist gates by goal keywords.
restrictSubagents / GOAL_GUARD_RESTRICT_SUBAGENTS true Lock the goal-* subagents to the Goal agent.
maxSessions / GOAL_GUARD_MAX_SESSIONS 200 Session cache size.
sessionTtlMs / GOAL_GUARD_SESSION_TTL_MS 86400000 Idle session TTL.
toastOnBlock / GOAL_GUARD_TOAST_ON_BLOCK true Toast when something is blocked.
toastOnReview / GOAL_GUARD_TOAST_ON_REVIEW true Toast on each review verdict and when completion unlocks.
sidebarBanner / GOAL_GUARD_SIDEBAR_BANNER true Show the live Goal todo section in the TUI sidebar.
sidebarColor / GOAL_GUARD_SIDEBAR_COLOR #FFD700 Colour of the GOAL label for a running goal.
sidebarDoneColor / GOAL_GUARD_SIDEBAR_DONE_COLOR #FF5555 Colour of a done goal in the sidebar.
sidebarMutedColor / GOAL_GUARD_SIDEBAR_MUTED_COLOR #808080 Reserved muted colour for no-goal projections.
completionMarker / GOAL_GUARD_COMPLETION_MARKER Goal Completed Phrase that, at the start of a message, claims completion.
blockedMarker / GOAL_GUARD_BLOCKED_MARKER Goal Not Completed Replacement written when a completion claim is blocked.

Slash commands: /goal, /goal-contract, /goal-review, /goal-evidence-map, /goal-status, /goal-repair, /goal-final.

Tools the model can call: goal_contract, goal_evidence, goal_evidence_map, goal_reviewer_memory, goal_status, goal_reset.

Troubleshooting

  • opencode agent list doesn't show goal? The agents didn't land where OpenCode reads them — re-run opencode-goal-mode --global and restart OpenCode.
  • No sidebar todo section? TUI plugins load from tui.json, not plugins/. Confirm ~/.config/opencode/tui.json lists opencode-goal-mode, then fully restart OpenCode. The sidebar is experimental and only shows inside a Goal session with a goal set; enforcement works regardless of the sidebar.
  • Reviews didn't kick off on their own? The auto-review fires on session idle. A few free models stall mid-turn without ever going idle, so on those it may not run live — /goal-review and /goal-final run a cycle on demand, and the completion guard still blocks an unearned Goal Completed either way.
  • A safe command got blocked? Run node benchmarks/external.mjs --json to see how the analyzer reads it, set blockDestructive: false for that project, and please open an issue.

Good to know

  • Requirements: Node 20.11+, OpenCode configured to load local agents/commands/ plugins (tested against @opencode-ai/plugin 1.17.6, compatible with the 1.15+ hook surface), and a working provider/model. Agents inherit your OpenCode default model.
  • Safety: The installer copies only agents/*.md, commands/*.md, and plugins/ — never auth files, tokens, or provider config. The guard is a guardrail, not a sandbox, and fails open on input it can't parse; see SECURITY.md for the threat model and a private reporting channel.

Contributing

PRs welcome — CONTRIBUTING.md has the dev loop and release process, and CHANGELOG.md has the full history. Releases are automated and version-synced: one pushed vX.Y.Z tag runs the CI gate, publishes to npm, and creates the matching GitHub Release.

License

MIT · built for OpenCode.