Skip to content
    ↑↓ select↵ openesc close
    ByBrawe

    Loop

    @bybrawe/opencode-loop·v0.6.6·Other

    Idle-safe /loop scheduling and background automation for OpenCode, with optional legacy Loop Goal compatibility and a separately installable OpenCode Goal companion.

    GitHub stars

    190

    +19 in 30 days

    Monthly installs

    4,716

    2k in 7 days

    Composite score

    68.0

    Multi-signal model

    Last commit

    17 hours ago

    2026-10-04

    Install and configure

    opencode.json

    Writes to this project's opencode.json — applies to this repository only.

    opencode.json

    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["@bybrawe/opencode-loop@0.6.6"]
    }

    OpenCode loads npm dependencies through its embedded runtime on startup and caches them locally — no manual global install needed.

    Native OpenCode 2 (0.6.6)

    The npm and V2 entrypoints use the official @opencode/plugin Plugin.define() contract. The installer now registers the exact @bybrawe/opencode-loop package in OpenCode's plural plugins configuration instead of copying a loose global plugin directory. OpenCode therefore resolves @opencode/plugin from Loop's own production dependency graph; explicit --legacy-v1 remains a separate compatibility path.

    The installer defaults to the native V2 package plugin. It needs no loose local V2 copy, V1 SDK, or legacy command files. Valid legacy plugin registrations are now persisted into the single native plugins list using OpenCode 2.0.21's own normalization semantics, so native installs no longer leave parallel plugin and plugins blocks. Object options and entry order are preserved, malformed legacy values are left untouched, and unrelated provider/model settings are not rewritten. Existing 0.6.1/0.6.2 loose plugins/opencode-loop installs are still removed on update. Use --legacy-v1 explicitly for the compatibility installation.

    V2 prompt admission and durable inbox/execution/compaction events own scheduling. Core-generated user-role messages never authorize a session abort. An unfinished dedicated Goal reserves its session, including paused and handed-off states; Loop does not wake or replace that Goal. Soft iteration timeouts do not abort a native model/tool/compaction turn.

    Scheduled shell commands use Loop-managed bounded local child processes; the OpenCode 2 ctx.shell surface is a hook domain, not a command-execution API. Automatic compaction stays host-owned. The public OpenCode 2 SessionDomain has no manual compaction action, so /loop-compact, --compact, and --compact-every are rejected before job creation instead of probing undocumented session.compact fields. Use OpenCode's built-in manual/automatic compaction on V2.

    Prompt/command timers, watch and stop conditions, verification, preflight/postrun, notifications, checkpoints and diagnostic commands use the V2 runtime. Uncertain restart admissions pause for review rather than replaying potentially admitted work. The dedicated @bybrawe/opencode-goal plugin owns /goal; legacy Loop Goal records are preserved and are not silently reinterpreted as new Goal contracts.

    Idle-safe auto-continue, scheduled OpenCode work, and background loops.

    OpenCode Loop adds /loop, scheduled prompt/command/shell jobs, compact scheduling, verification/checkpoints, and the opencode-loopd background daemon.

    Current stable release: 0.6.6.

    Install or update

    Recommended OpenCode 2 setup

    Install both packages separately:

    npx -y @bybrawe/opencode-loop@latest
    npx -y @bybrawe/opencode-goal@latest
    

    This is the recommended setup. The two installers stay independent:

    • the Loop command installs or updates OpenCode Loop only;
    • the Goal command installs or updates the dedicated OpenCode Goal plugin;
    • Loop does not install, detect, refresh, migrate, or otherwise modify @bybrawe/opencode-goal during a normal install/update;
    • Loop omits/removes its older experimental /loop-goal* command files by default.

    OpenCode Goal owns /goal, durable objective state, evidence, verification, restart recovery, and semantic completion. See OpenCode Goal.

    A combined --with-goals path remains only as a compatibility/convenience option; the recommended setup is the two separate commands above:

    npx -y @bybrawe/opencode-loop@latest --with-goals
    

    If you explicitly need Loop's legacy experimental /loop-goal* commands for compatibility, opt in with:

    npx -y @bybrawe/opencode-loop@latest --with-loop-goals
    

    Those legacy commands are not recommended for new setups; prefer the dedicated Goal plugin instead.

    --loop-only remains as an explicit alias for the new default Loop-only behavior, and --without-loop-goals remains as a compatibility alias for the new default clean command surface.

    Run the relevant install command again to update that package. Then fully restart OpenCode and verify:

    /loop-help
    /loop-doctor
    

    If you installed Goal too:

    /goal status
    

    Global npm alternative:

    npm install -g @bybrawe/opencode-loop@latest
    opencode-loop
    

    To uninstall:

    npx -y @bybrawe/opencode-loop@latest --uninstall
    

    or, for a global install:

    opencode-loop --uninstall
    npm uninstall -g @bybrawe/opencode-loop
    

    Loop uninstall never removes the dedicated Goal plugin. Project Loop state under .opencode/opencode-loop/ is intentionally preserved by uninstall.

    The mental model

    Loop separates two ideas:

    1. When does work become due? Idle, a timer, a watch trigger, or /loop-now.
    2. When is it safe to dispatch? Only when the session is actually idle and no active tool/child work would overlap it.

    A timer expiring does not intentionally inject a second model turn on top of an active one. Due work waits for idle.

    Quick start

    Keep saying “continue” whenever the assistant stops

    /loop continue
    

    Turkish shorthand works naturally too:

    /loop devam et
    

    Explicit equivalent:

    /loop idle continue
    

    This is an unlimited idle loop by default. When the assistant finishes and the session becomes safely idle, Loop sends the prompt again. When that turn finishes, it does the same again, until you pause/stop it or a configured limit is reached.

    For very short continuation prompts such as continue, keep going, or devam et, Loop adds project-continuation guidance: treat the turn as continuation of the current repository/conversation, inspect relevant files/TODO/progress/git state as needed, find the next unfinished step, avoid redoing completed work, and verify meaningful changes when practical.

    For project work, a strong default is:

    /loop --safe --ask-never --progress-file progress.md devam et
    

    Create a starter progress file first if needed:

    /loop-init
    

    Repeat every 5 minutes

    /loop every 5m continue the project
    

    This waits five minutes before the first run, then becomes due every five minutes. If the timer expires while OpenCode is busy, it waits for the next safe idle boundary rather than stacking prompts.

    Do it once after 5 minutes

    /loop after 5m continue once
    

    Alias:

    /loop in 5m continue once
    

    This is a one-shot delayed job. Five minutes passing makes it due; it still waits for idle before dispatching.

    Legacy compact syntax remains supported

    /loop 5m continue the project
    

    For backward compatibility, this form starts on the next safe idle boundary and then follows a five-minute interval.

    Delay its first run explicitly:

    /loop 5m --no-now continue the project
    

    Legacy idle form is still valid:

    /loop 0s continue
    

    For the exact schedule truth table and edge cases, see docs/SCHEDULING.md.

    Schedule summary

    Command Meaning First dispatch Repeats
    /loop continue auto-continue on idle next safe idle every idle
    /loop idle continue explicit idle loop next safe idle every idle
    /loop every 5m continue recurring timer after 5m, then idle yes
    /loop after 5m continue delayed one-shot after 5m, then idle no
    /loop in 5m continue delayed one-shot alias after 5m, then idle no
    /loop 5m continue legacy recurring form next safe idle yes
    /loop 5m --no-now continue legacy delayed-first recurring after 5m, then idle yes
    /loop 0s continue legacy idle form next safe idle every idle

    Understand an unfamiliar project and keep going

    A useful first loop for an unfamiliar repository is:

    /loop-init
    /loop --safe --ask-never --progress-file progress.md Understand the existing project architecture and current state first. Inspect relevant source, tests, docs, TODOs, git status, and recent work. Record useful state in progress.md, choose the next unfinished safe improvement, implement it, verify it, update progress.md, and continue from there on later idle turns.
    

    Once the project state is established, this is enough:

    /loop --safe --ask-never --progress-file progress.md devam et
    

    The short continuation form is deliberately interpreted as “resume this project”, not “invent a new task from scratch”.

    Other job types

    Schedule an OpenCode slash command:

    /loop-command 200m /compact
    

    Run a real shell command:

    /loop-shell 10m npm test
    

    Ask a recurring quality-control prompt:

    /loop-ask 1h did you run tests, typecheck, and build? If not, run them and fix errors.
    

    Watch a path:

    /loop --watch progress.md inspect the updated progress and continue
    

    Prompt, command, shell, compact, and watch jobs use the same idle-safe scheduling path but different execution transports.

    Core commands

    Command Purpose
    /loop <prompt> Add/update an unlimited idle prompt loop
    /loop idle <prompt> Explicit idle-loop form
    /loop every <duration> <prompt> Recurring timer, first run delayed
    /loop after <duration> <prompt> One-shot delayed prompt
    /loop in <duration> <prompt> Alias for after
    /loop <duration> <prompt> Backward-compatible compact interval form
    /loop-command <interval> <slash-command> Schedule an OpenCode command
    /loop-cmd <interval> <slash-command> Alias for /loop-command
    /loop-ask <interval> <question> Schedule a recurring check/question
    /loop-shell <interval> <command> Schedule a shell command
    /loop-status Show jobs plus schedule/state
    /loop-now [id/name/all] Mark selected jobs due now; still waits for idle
    /loop-pause [id/name/all] Pause jobs
    /loop-resume [id/name/all] Resume jobs
    /loop-remove [id/name/all] Remove jobs
    /loop-clear Remove all jobs for the current session
    /loop-logs Show recent scheduler/runtime events
    /loop-doctor Diagnose session/state/scheduling problems
    /loop-init Create a starter progress.md
    /loop-export Export current-session Loop state

    Useful flags

    Lifecycle and limits:

    --name <name>
    --max-runs <n>
    --max-runtime <duration>
    --max-failures <n>
    --timeout <duration>
    --no-now
    --now
    

    Safety and verification:

    --safe
    --ask-never
    --no-overlap
    --verify "npm test"
    --preflight "npm install"
    --postrun "git status --short"
    --pause-on-verify-fail
    

    Project context:

    --progress-file progress.md
    --prompt-file loop-prompt.md
    --include-file ARCHITECTURE.md
    --batch 5
    --compact-every 20
    --watch progress.md
    

    Checkpoints:

    --checkpoint-only
    --git-checkpoint
    

    --git-checkpoint may stage/commit work; use it only when intentional.

    Status and diagnostics

    /loop-status reports both the schedule definition and the current scheduler state. Examples:

    schedule=every idle | state=waiting for idle
    schedule=every 5m, first after 5m | state=due in 3m
    schedule=once after 5m | state=due; waiting for idle
    

    This is intentional: “due” is a clock fact, while “waiting for idle” is an admission/safety fact.

    If a job says enabled but never runs

    The problematic shape is typically:

    enabled=true
    paused=false
    runCount=0
    lastRunAt=0
    

    Some OpenCode TUI versions can leave session.status at busy/retry after a plugin command acknowledgement. Loop cross-checks that stale state against the chronological message tail before the first Loop run too. It recovers only when the latest assistant message is actually completed and no active tool/child work is known. A genuinely unfinished assistant turn, latest user turn, active tool, busy child, or unknown completion remains busy.

    Recovery is logged as:

    status-message-idle-recovery
    

    Busy retries are also written as throttled deferred events so loop.log does not misleadingly contain only the original add line.

    Use:

    /loop-status
    /loop-logs
    /loop-doctor
    

    Session-bound state

    Normal plugin Loop jobs are session-bound and stored under:

    .opencode/opencode-loop/<session-id>.json
    

    A new OpenCode session does not silently inherit another session's jobs. /loop-doctor reports other persisted session files that still contain enabled jobs, including jobs that have never run, so an old loop is visible instead of appearing lost.

    For work that must continue after the TUI/session closes, use opencode-loopd.

    Loop and dedicated OpenCode Goals

    They can be installed together. Recommended split:

    • use /goal for durable outcome-driven work, evidence, verification, restart recovery, and semantic completion;
    • use /loop, /loop-command, /loop-shell, and opencode-loopd for scheduling/repetition/background infrastructure.

    A prompt-producing Loop and an active dedicated /goal should not both own autonomous continuation of the same session. The runtime therefore blocks a new prompt Loop when it detects an active dedicated Goal for that session.

    Recommended choices are to finish/pause the Goal or use a separate session. Advanced users can deliberately override the guard with:

    /loop --allow-goal-overlap continue
    

    That escape hatch can create competing autonomous turns; use it only when that is intentional.

    Loop's older experimental /loop-goal* commands are omitted by default. They remain available only as an explicit compatibility opt-in with --with-loop-goals. For new strongly verified Goal work, use the separate OpenCode Goal plugin:

    npx -y @bybrawe/opencode-goal@latest
    

    Recommended development loop

    /loop --name dev --ask-never --safe --no-overlap --batch 5 --compact-every 200m --checkpoint-only --progress-file progress.md Treat progress.md as the project state. Continue with the next unfinished TODO, implement it, mark completed items, add useful follow-up TODOs, run tests/lint/build when available, and keep going while work remains.
    

    Test/fix loop:

    /loop --name testfix --ask-never --safe --verify "npm test" --max-failures 3 Continue from progress.md. If tests fail, analyze the failure, fix it, and run the tests again.
    

    Background daemon

    The normal /loop plugin is session-bound. opencode-loopd pins one exact OpenCode session for daemon iterations and can keep scheduling outside the normal interactive Loop timer lifecycle.

    opencode-loopd --project . --every 5m --prompt-file loop-prompt.md
    

    Immediate daemon cadence:

    opencode-loopd --project . --every 0s --prompt "continue from progress.md and implement the next unfinished TODO"
    

    Pin a session and limit runs:

    opencode-loopd --project . --session ses_xxx --every 5m --max-runs 20 --timeout 30m --prompt-file loop-prompt.md
    

    Dedicated Goal pulse-check

    Use the daemon as a read-only stall detector for dedicated /goal state:

    # one-shot (good for systemd timers / cron-style runners)
    opencode-loopd pulse-check --project . --stale-after 30m
    
    # repeating opt-in watcher
    opencode-loopd pulse-check --project . --stale-after 30m --every 1m
    
    # watch one Goal by Goal ID or session ID
    opencode-loopd pulse-check --project . --goal <goal-or-session-id> --stale-after 30m --every 1m
    

    The pulse-check reads .opencode/goals/*.json and alerts only while the dedicated Goal is active. paused, waiting_user, blocked, budget/usage-limited, and completed Goals do not alert. A stall episode is based on durable Goal activity (updatedAt, progress/revision changes); OpenCode session updated time is included only as corroborating telemetry because it is not reliable enough to be the stall authority on every host version.

    Episode state is stored separately in .opencode/opencode-loop/pulse-check.json. The checker never writes Goal state, never dispatches a prompt, never runs /goal resume, and therefore does not participate in the prompt-producing Goal-overlap guard. It emits at most one alert per stall episode and re-arms after observed Goal activity or a status change. The alert explicitly names the human recovery action: run /goal resume in the Goal's session.

    Windows Task Scheduler:

    opencode-loopd install-task --project "C:\path\to\project" --every 10m --prompt-file loop-prompt.md --name OpenCodeLoop
    opencode-loopd uninstall-task --name OpenCodeLoop
    

    State and checkpoints

    Runtime state and logs:

    .opencode/opencode-loop/
    .opencode/opencode-loop/loop.log
    

    Patch checkpoints:

    .opencode/opencode-loop/checkpoints/
    

    Add .opencode/opencode-loop/ to .gitignore if runtime state should not be committed.

    Permissions

    For unattended work, avoid permanently allowing every shell command. Grant routine read/edit/test operations while keeping destructive commands on ask/deny. --safe reduces risk but does not replace OpenCode permission configuration.

    Package and compatibility

    @bybrawe/opencode-loop
    

    Stable package peer range:

    @opencode-ai/plugin >=1.4.0 <2
    

    The repository contains experimental OpenCode 2 work, but the stable package does not claim full OpenCode 2 parity.

    See docs/SCHEDULING.md for the scheduling contract and CHANGELOG.md for release history.

    License

    MIT

    Similar plugins