Skip to content

Deterministic /loop parsing bypassed in opencode run — documented guards (missing prompt, cron rejection, unknown flags) never fire #18

Description

@jkrandom-sudo

Summary

When /loop is invoked via opencode run (both headless and -i interactive mode), the plugin's deterministic command parser (handleUserCommand, wired to the command.execute.before hook) never runs. The raw $ARGUMENTS go straight to the model, which improvises via the loop_schedule tool. As a result, every documented input guard in the README is silently bypassed in this mode.

Environment

  • opencode 1.18.3 (also reproduces the wiring described for opencode run instances in the README)
  • opencode-plugin-loop 0.7.2, installed globally per README (plugin: ["opencode-plugin-loop"] in ~/.config/opencode/opencode.json, command defined with template: "$ARGUMENTS")
  • macOS (darwin), Node v24
  • Model: deepseek-v4-flash via OpenAI-compatible provider (behavior is model-dependent by construction — that's the point of this issue)

Minimal reproduction

mkdir /tmp/loop-test && cd /tmp/loop-test

# Case 1: bare interval — README says this must return
# "❌ Missing prompt after interval \"5m\". Usage: /loop <interval> <prompt> — see /loop help."
opencode run "/loop 5m"

# Case 2: cron syntax — README says this must return
# "❌ Cron expressions are not supported. ..."
opencode run "/loop */5 * * * * check something"

Expected (per README, "Upgrading to 0.4.0" section)

scheduling-like input that used to silently create an Adaptive task (cron syntax, bare intervals like /loop 5m, unknown flags) now returns an explicit error pointing at /loop help.

This is implemented in src/scheduler.ts (handleUserCommand, e.g. the looksLikeCron rejection at line ~300), reachable only through the command.execute.before hook in src/index.ts.

Actual

Case 1 — the model creates a real fixed task with a self-invented placeholder prompt:

⚙ loop_schedule {"action":"create","intervalMs":300000,"mode":"fixed",
  "prompt":"The user requested a loop every 5 minutes. Ask them what they'd like to do."}
Created `/loop` task `0jg2mox8` — fires every 5 minutes.

Case 2 — the cron expression is silently converted to a fixed interval instead of rejected:

⚙ loop_schedule {"action":"create","prompt":"check something","intervalMs":300000,"mode":"fixed"}
Created loop task `1rvh3oge` — runs every 5 minutes (fixed mode) ...

Task persisted in .opencode/cache/loop/tasks.json (mode fixed, intervalMs 300000). Reproduced identically with opencode run -i under a pseudo-TTY, so this is not specific to headless mode.

Additionally, /loop help in opencode run prints model-generated help (derived from the tool schema) rather than the canonical LOOP_HELP text from src/scheduler.ts.

Impact

In opencode run, all deterministic behavior documented in the README — missing-prompt errors, cron rejection, unknown-flag rejection, exact help text — is replaced by model improvisation. Junk tasks with meta-prompts ("Ask them what they'd like to do") actually get scheduled and would fire every 5 minutes in a long-lived process.

Suspected cause

command.execute.before is not emitted by opencode when a command is executed through opencode run (with or without -i), so src/index.ts's interception of input.command === "loop" never happens and the command template ($ARGUMENTS) is sent to the model unguarded. I could not verify the full TUI path headlessly; if the hook fires correctly there, the plugin may need a run-mode fallback (e.g. input validation inside loop_schedule itself, mirroring handleUserCommand's guards).

Notes

  • Existing test suite (164 tests) passes; ephemeral task cleanup, instance lock takeover, and per-session scoping all verified working in real opencode run usage.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    fix-meTrigger mini-swe-agent auto-fix

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions