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

opencode-agent-loop

v0.1.0

Published

Goal-oriented agent loop plugin for OpenCode — iteratively drives agents toward objectives with configurable budgets and termination conditions

Readme

opencode-agent-loop

Goal-oriented agent loop plugin for OpenCode — iteratively drives agents toward objectives with configurable budgets and termination conditions.

Quick Start

1. Register the plugin

In your opencode.json:

{
  "plugins": [
    ["file:///path/to/opencode-agent-loop/dist/index.js"]
  ]
}

2. Create a rule

Drop a JSON file named rule_<name>.json in ~/.config/opencode/agent-loop/ (e.g. rule_code-review.json):

{
  "objective": "Review the code and fix all issues",
  "doneWhen": [
    { "type": "marker", "value": "[GOAL_COMPLETE]", "where": "assistant-end" }
  ],
  "budget": { "maxTurns": 5, "maxMinutes": 10 },
  "enabled": true
}

The rule name is derived from the filename: rule_code-review.json → name code-review. Rules declare what the agent should achieve, but the loop does not start automatically. You must use the /loop slash command to activate it.

3. Use the /loop slash command

/loop start [--rule <name>] [goal] [--turns N] [--mins M] [-- <user_request>]
/loop stop
/loop status
/loop list

| Argument | Description | |----------|-------------| | --rule <name> | Reference a rule by name (derived from rule_<name>.json filename) | | goal | Overrides the rule's objective text. Used only in nudge messages, never sent directly to the LLM. | | --turns N | Override budget.maxTurns for this run | | --mins M | Override budget.maxMinutes for this run (also accepts --minutes) | | -- <user_request> | After --, everything that follows is the actual user message sent to the LLM to kick off the work |

Examples

# Start with a rule, send a task to the LLM
/loop start --rule code-review -- Review the diff and fix all issues

# Start with a custom goal overriding the rule's objective
/loop start --rule code-review "Build a test suite" -- Write tests for src/utils

# Start with turn limit override
/loop start --rule code-review --turns 10

# Start without a rule (dynamic goal) — requires goal text and/or budget
/loop start "Fix all TypeScript errors" --turns 8 --mins 15 -- Fix the build errors

# List all available rules
/loop list

# Stop the running loop
/loop stop

# Check loop status
/loop status

Usage Scenarios

Below are practical scenarios showing how to use the agent loop plugin for different tasks.

Scenario 1: Code Review with Marker Completion

Create ~/.config/opencode/agent-loop/rule_code-review.json:

{
  "objective": "Review all changed files and fix any bugs, style issues, or security vulnerabilities",
  "doneWhen": [
    { "type": "marker", "value": "[REVIEW_DONE]", "where": "assistant-end" }
  ],
  "budget": { "maxTurns": 3, "maxMinutes": 10 },
  "enabled": true
}

Then in chat:

/loop start --rule code-review "Review the latest commit" --turns 5 -- Review the diff and fix all issues

The loop nudges the agent after each idle period, and terminates when [REVIEW_DONE] appears at the end of an assistant reply, or after 5 turns / 10 minutes.

Scenario 2: Budget-Only Loop (No Marker)

For tasks where completion isn't easily detectable, rely solely on budget:

{
  "objective": "Refactor the payment module to use the new API",
  "doneWhen": [],
  "budget": { "maxTurns": 8, "maxMinutes": 30 },
  "enabled": true
}

Save as ~/.config/opencode/agent-loop/rule_refactor-module.json. Start with /loop start --rule refactor-module -- <task>. The loop runs exactly 8 iterations (or 30 minutes) then forces an exhaust nudge.

Scenario 3: Quick Ad-Hoc Task (No Rule File)

Start a loop without any rule file — the goal, budget, and task are all on the command line:

/loop start "Add unit tests for the auth middleware" --turns 6 --mins 15 -- Write comprehensive tests in src/auth/__tests__

This creates a dynamic goal with:

  • Objective: "Add unit tests for the auth middleware" (used in nudge messages)
  • Budget: 6 turns / 15 minutes
  • Default [GOAL_COMPLETE] marker (auto-added when budget is not provided)
  • The text after -- is sent directly to the LLM as the first user message

Scenario 4: Multiple Rules

Multiple rule files for different tasks:

~/.config/opencode/agent-loop/rule_write-docs.json:

{
  "objective": "Write API documentation for all public functions",
  "doneWhen": [{ "type": "marker", "value": "[DOCS_DONE]", "where": "assistant-end" }],
  "budget": { "maxTurns": 10 }
}

~/.config/opencode/agent-loop/rule_write-tests.json:

{
  "objective": "Achieve 90% test coverage for the utils package",
  "doneWhen": [{ "type": "regex", "pattern": "coverage:?\\s*9[0-9]%", "flags": "i" }],
  "budget": { "maxTurns": 15, "maxMinutes": 20 }
}

Use /loop start --rule write-docs or /loop start --rule write-tests in any agent session.

Scenario 5: Regex Completion Detection

Use a regex pattern instead of a marker:

{
  "objective": "Fix all TypeScript compilation errors",
  "doneWhen": [
    { "type": "regex", "pattern": "0\\s+errors?|all\\s+clear|compilation\\s+succeeded", "flags": "i" }
  ],
  "budget": { "maxTurns": 5 }
}

Save as ~/.config/opencode/agent-loop/rule_build-fix.json. The loop completes when any assistant reply matches the regex pattern.

Scenario 6: Time-Boxed Research Task

Use maxMinutes to enforce a strict time budget:

{
  "objective": "Research and compare 3 Rust web frameworks (Actix, Axum, Rocket)",
  "doneWhen": [
    { "type": "marker", "value": "[RESEARCH_COMPLETE]", "where": "assistant-end" }
  ],
  "budget": { "maxMinutes": 15 },
  "enabled": true
}
/loop start --rule tech-research "Research Rust web frameworks" --mins 10 -- Compare Actix, Axum, and Rocket for our next project

After 10 minutes (or 15 from the rule), the time-exhaust nudge fires and the agent must deliver its final answer.

Scenario 7: Inline Rules via opencode.json

For portable setups, embed rules directly in opencode.json:

{
  "plugins": [
    [
      "file:///path/to/opencode-agent-loop/dist/index.js",
      {
        "rules": [
          {
            "name": "daily-standup",
            "objective": "Summarize today's commits and open issues from the project board",
            "doneWhen": [{ "type": "marker", "value": "[STANDUP_DONE]", "where": "assistant-any" }],
            "budget": { "maxTurns": 2 }
          },
          {
            "name": "security-scan",
            "objective": "Scan dependencies for known vulnerabilities and suggest fixes",
            "doneWhen": [{ "type": "regex", "pattern": "vulnerabilit(y|ies).*(0|none|fixed)", "flags": "i" }],
            "budget": { "maxTurns": 3, "maxMinutes": 5 }
          }
        ],
        "enableLogging": true
      }
    ]
  ]
}

Use /loop start --rule daily-standup to activate.

Scenario 8: Silent Loop — Let the Agent Work Independently

Start a loop with no initial user message — the agent works autonomously, nudged whenever idle:

/loop start --rule refactor-module

No -- argument means no user message is sent. The loop sits idle until the agent becomes idle, then injects the nudge.

Scenario 9: Stop and Resume

Stop a running loop:

/loop stop

The loop is cancelled. When you send a new user message in the same session, the loop automatically resumes (status flips from cancelled to running), so you can continue iterating.

Scenario 10: Check Loop Status & List Rules

Monitor the current loop state:

/loop status

Example output (via toast): Status: running | Goal: "Fix all TypeScript errors" | Iteration: 3/8 | Elapsed: 5min/15min

List all available rules:

/loop list

Example output:

Available rules:
code-review
refactor-module
write-docs
write-tests

Priority (highest to lowest)

| Setting | /loop args | Rule JSON | Global default | |---------|-------------|-----------|----------------| | Rule selection | --rule <name> | filename rule_<name>.json | — | | objective (nudge text) | goal | objective | — | | maxTurns | --turns N | budget.maxTurns | — | | maxMinutes | --mins M | budget.maxMinutes | defaultMaxMinutes | | doneWhen | — | doneWhen | [GOAL_COMPLETE] marker |

If you run /loop start with no --rule and no goal, it will reject with an error. Provide a goal or use --rule to reference a rule.


Configuration

Rule Schema

{
  "objective": "Review the code changes and provide feedback",
  "doneWhen": [
    { "type": "marker", "value": "[GOAL_COMPLETE]", "where": "assistant-end" }
  ],
  "budget": {
    "maxTurns": 5,
    "maxMinutes": 10
  },
  "idleDelayMs": 3000,
  "cooldownMs": 10000,
  "enabled": true
}

Rule Options

| Field | Type | Description | |-------|------|-------------| | objective | string | Goal text used in nudge messages (required) | | doneWhen | DoneCondition[] | Termination conditions (default: [GOAL_COMPLETE] marker) | | budget.maxTurns | number | Max nudge iterations before forced completion | | budget.maxMinutes | number | Max time before forced completion | | idleDelayMs | number | Delay before nudging an idle agent (default: read from config.json, fallback 3000) | | cooldownMs | number | Minimum interval between nudges (default: read from config.json, fallback 10000) | | enabled | boolean | Whether the rule is available (default: true). When enabled, the loop must still be activated with /loop start. |

The rule name is derived from the filename: ~/.config/opencode/agent-loop/rule_my-task.json → name my-task.

Done Condition Types

Marker — checks for a specific text in assistant output:

{ "type": "marker", "value": "[GOAL_COMPLETE]", "where": "assistant-end" }
  • where: "assistant-end" — matches only at the end of assistant text
  • where: "assistant-any" — matches anywhere in assistant text

Regex — matches against assistant text:

{ "type": "regex", "pattern": "review\\s+complete", "flags": "i" }

Global Config

Create ~/.config/opencode/agent-loop/config.json for global defaults:

{
  "idleDelayMs": 5000,
  "cooldownMs": 15000,
  "enableLogging": true,
  "defaultMaxMinutes": 30
}

These values are loaded from config.json and serve as fallbacks when not specified in the rule or /loop arguments.

Inline Rules

Pass rules directly in opencode.json:

{
  "plugins": [
    [
      "file:///path/to/opencode-agent-loop/dist/index.js",
      {
        "rules": [
          {
            "name": "daily-standup",
            "objective": "Summarize today's commits and open issues",
            "budget": { "maxTurns": 3 }
          }
        ],
        "enableLogging": true
      }
    ]
  ]
}

Inline rules require an explicit name field (not derived from filename).

Development

npm install
npm run dev       # watch mode
npm run build     # production build
npm run test      # run tests
npm run lint      # lint
npm run typecheck # type check

License

MIT