npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode2-goals

v1.0.4

Published

OpenCode v2 plugin: a durable, session-scoped goal loop with evidence-gated completion, budget caps, and an unattended permission sandbox

Readme

opencode2-goals

CI license

An OpenCode v2 plugin that implements a Codex-style goal loop: durable, session-scoped goal state; a /goal command surface; the objective injected into every model call; event-driven continuation on turn boundaries; an evidence-gated completion tool; stall suppression; budget caps safe by default; an unattended permission sandbox; and a live TUI progress widget in the sidebar.

The goal loop in the OpenCode TUI: /goal commands drive a durable goal whose live status, budget counters, and task breakdown render in the sidebar

What it does

Commands — for you

| Command | Effect | | --- | --- | | /goal set <objective> [--turns N] [--tokens N] [--unbounded] | Start the loop. No flags = 10-turn / 100,000-token cap; --unbounded (also --no-cap, --unlimited) opts out of numeric limits. | | /goal view | Full state: status, budget usage, tasks, evidence, outcome. | | /goal pause / /goal resume | Halt or re-arm continuation without losing state. | | /goal complete <evidence> / /goal block <reason> | Terminal outcomes, same gates as the tools below. | | /goal clear | Remove the goal (archived first — see Durability). | | /goal task add <title> / /goal task <n> todo\|doing\|done | Drive the task breakdown from the keyboard (either order: task 1 done or task done 1). | | /goal history | Every terminal or superseded goal this session produced (log is an alias). |

Verbs have aliases: status (view), done (complete), blocked (block), tasks (task list). --cap N is an alias for --turns N, and a bare /goal <objective> means /goal set <objective>.

Tools — for the model

| Tool | Gate | | --- | --- | | goal_set(objective, turns?, tokens?, unbounded?) | Refuses while a non-terminal goal exists — the model can't clobber yours, and cap defaults match /goal set. | | goal_complete(evidence) | Evidence ≥24 chars with a checkable anchor (path, number, test result), grounded in transcript tokens when history is available. Weak claims are rejected; the goal stays active. | | goal_block(reason) | Requires a specific reason; the sanctioned "I can't proceed" exit. | | goal_clear(request) | Must quote the user's own clearing ask, grounded against non-assistant transcript text — assistant prose can't launder it, and no transcript fails closed. | | goal_add_task(title) / goal_update_task(ref, status) | None (active goal required); the widget stays in sync. | | goal_history() | Read-only. |

The loop

  • The objective is re-injected into every agent-loop model call via session.hook("context"), keeping focus across long runs.
  • Turn boundaries come from terminal session.execution.* events — session.idle is not delivered to plugins in this build. Exactly one continuation is injected per boundary, deduped by event id and scoped to the session's owning project.
  • A continuation that makes no tool call counts as a stall; reaching a cap produces the distinct budget_limited outcome — neither completion nor blocked.

Durability

  • Goal state is stored per session (goal/<sessionID>) and survives reloads and restarts.
  • Terminal goals — and any active goal superseded by a new set — are archived under goal/<sessionID>/archived/<goalID>, so outcomes, budgets consumed, and evidence survive across goal replacements.

Safety

  • Caps by default; --unbounded is always an explicit opt-in.
  • Permission sandbox: while a goal is active, in-scope path requests are auto-allowed and out-of-scope ones auto-denied with a message steering the model to goal_block. Configured deny rules are never widened.
  • Evidence gating is a heuristic, not semantic verification — see src/evidence.ts and Safe configuration.

Status outcomes

active · paused · completed · blocked · budget_limited · stalled · cleared

Requirements

  • OpenCode v2 (@opencode/plugin 2.x). Built and live-verified on 2.0.15/2.0.16 (npm install, directory install, and the TUI widget); weekly CI (Host compatibility) re-checks the type surface against the latest 2.x and boots a real isolated host to verify the plugin still loads. Run it any time with gh workflow run compat.yml.
  • Bun, for tests, typecheck, and building the widget entry.

Install

From npm

// ~/.config/opencode/opencode.jsonc
{
  "plugins": [
    { "package": "opencode2-goals", "options": {} }
  ]
}

Shorthand works too ("plugins": ["opencode2-goals"]), but prefer pinning: "[email protected]". The host's @latest resolution caches aggressively and may keep serving an older version for a while after a publish, so a pin also gives you a knowingly-upgradeable install. (npm view opencode2-goals version shows the current release. And note the 2: the legacy npm name opencode-goals belongs to an unrelated package.)

From source (local directory)

git clone https://github.com/wukrit/opencode2-goals.git ~/Projects/opencode2-goals
{ "plugins": [{ "package": "/path/to/opencode2-goals", "options": {} }] }

With options (all optional):

{
  "package": "opencode2-goals",
  "options": {
    "stallLimit": 1,
    "defaultCapTurns": 10,
    "defaultCapTokens": 100000,
    "continuationText": "Continue the goal. End the turn with a tool call."
  }
}

continuationText replaces the default continuation prompt (the message injected at each turn boundary); the other options set the stall tolerance and the default caps that /goal set applies when no flags are given.

Then reload locations:

opencode api post /api/location/reload

A local (directory) install cannot resolve @opencode/plugin, so the runtime entry imports only local modules and declares the context shape structurally.

Usage

# in a session:
/goal set Land the release --turns 8
/goal view
/goal task add Write migration
/goal task 1 doing
/goal task done 1
/goal complete tests: 42/42 pass at commit abc123, output in build/log.txt
/goal block Waiting on credentials for the staging cluster
/goal pause
/goal resume
/goal clear
/goal history

The model can maintain the same breakdown with goal_add_task(title) and goal_update_task(ref, status) — the widget stays in sync.

Unattended goals: rely on the default cap, keep work inside the session working directory, and let the permission sandbox deny the rest. If a goal genuinely needs no numeric limit, pass --unbounded explicitly.

Live progress widget (sidebar)

The server exposes goals.get({ sessionID }) and emits goals.updated on every write (see src/rpc.ts). The TUI entry (source src/tui.tsx, shipped as the pre-compiled ./tui export) renders a live sidebar block in three sections — objective; a counter grid (Status:, Turns:, Tokens:, Tasks: sharing one label column) with a task-progress bar; and the task list — updating via the RPC event, no polling. Colored accents collapse to base text on light themes so the widget stays legible in both modes.

With a from-source (directory) install, no extra configuration is needed — the TUI discovers the ./tui entry from the same location and renders the widget automatically alongside the server plugin.

// opencode.jsonc — everything works from one directory entry
{ "plugins": [{ "package": "/path/to/opencode2-goals" }] }

The npm install renders the widget too: since v1.0.2 the ./tui entry ships pre-compiled (dist/tui.js), because the host's Solid/JSX transform skips .tsx under node_modules — see issue #3 for the full root-cause writeup. No cli.json entry is required for either install shape; adding one on top of a working location just duplicates the widget.

Safe configuration

  • Keep each loaded location on a distinct project (the default). The plugin scopes continuation and permission decisions to the session's owning project; two locations sharing one project id would both act, and ctx.storage has no compare-and-set to arbitrate them.
  • Run a single server process against the plugin DB. Two servers sharing one opencode.db race on last-write-wins storage.
  • Treat evidence as a heuristic gate, not proof. A second-model or shell-check evaluator is out of scope; see src/evidence.ts.

Development

bun install
bun run typecheck
bun test
bun run build:tui   # compiles the widget to dist/tui.js (also runs via prepack on publish)

If you change src/tui.tsx, run bun run build:tui before testing the npm install shape — exports["./tui"] serves dist/tui.js (see issue #3 for why); directory installs compile the source directly.

Releasing

Maintainers: bump package.json, date the [Unreleased] section in CHANGELOG.md, then push a tag — .github/workflows/release.yml verifies the tag, runs typecheck + tests, publishes to npm with OIDC provenance, and creates the GitHub release with notes from the changelog:

git tag v1.0.4 && git push origin v1.0.4

Full flow and the trusted-publishing caveat are in CONTRIBUTING.md.

First CI publish: npm does not auto-register trusted publishers. Enable it once on npmjs.com → package → Access tab → Trusted publishing → add owner wukrit, repo opencode2-goals, workflow release.yml. Also note actions/setup-node defaults NPM_AUTH_TOKEN to the GITHUB_TOKEN, which breaks the OIDC flow (ENEEDAUTH) — the workflow clears it.

Layout

index.ts            # loader entry: { id, setup } + goals RPC
tui.tsx             # top-level TUI shim (re-exports src/tui.tsx for directory-install discovery)
src/
  controller.ts     # the goal loop (commands, tools, hooks, events, permission sandbox, goal archive)
  state.ts          # durable goal record + tasks + pure transitions + archive keys
  command.ts        # /goal parsing (caps, --unbounded, tasks, history) + status formatting
  rpc.ts            # goals.get / goals.updated for the widget (import-free)
  tui.tsx           # sidebar progress widget source (pre-compiled to dist/tui.js, the ./tui export)
  evidence.ts       # completion-evidence gate + user-request gate for goal_clear
  permission.ts     # path sandbox (decidePermission, fail-closed containment)
  options.ts        # plugin options (stallLimit, continuationText, defaultCapTurns/Tokens)
  types.ts          # structural slice of the plugin context
dist/
  tui.js            # built widget (gitignored; produced by scripts/build-tui.ts)
scripts/
  build-tui.ts      # compiles src/tui.tsx with the host's own @opentui/solid bun-plugin
  live-smoke.sh     # boots an isolated real host and asserts /goal is registered (no model call)
  demo-env.sh       # throwaway XDG dirs + this checkout, for the demo recording
  demo.tape         # VHS script that generates docs/demo.gif
  logcheck.py       # timestamp-accurate plugin-load verification against the opencode log
test/
  harness.ts        # mocked context + deterministic event bus (+ permission hook)
  goal-loop.test.ts # integration tests through the real setup()
  tasks.test.ts     # tasks + widget-bridge integration tests

Changelog

Release history lives in CHANGELOG.md — GitHub releases reuse its sections as notes.

License

Released into the public domain under The Unlicense. No attribution required, no warranty of any kind.