Background goals that loop.
opencode-loopd is a Codex-inspired goal engine for OpenCode.
Create a goal with /goal, watch an autonomous child session loop in the background,
and monitor everything from a modal TUI dashboard — without blocking the parent chat.
Install in 30s
Add to both configs, restart OpenCode:
// ~/.config/opencode/opencode.jsonc
{ "plugin": ["@bojackduy/opencode-loopd"] }
// ~/.config/opencode/tui.json
{ "plugin": ["@bojackduy/opencode-loopd"] }
Or one-line installer (adds /goal + skill):
npx -y @bojackduy/opencode-loopd@latest
Dashboard: /loop or <leader>d (Ctrl+X → d) • Shift+B bug report
Modal dashboard (/loop / <leader>d): vivid per-status coloring, running spinner, NORMAL vs INSERT — keys trapped, no chat leak.
⚡ Engine-driven
Idle + lease + 30s polling re-prompts the child with accumulated steering (progress + transcript tail + inbox). Not a parent re-prompt machine.
👁️ Parent ↔ child
Owner tools inspect/steer; inbox injects into next turn. Wake-up on complete/block without polling.
🛡️ Safe
Per-goal artifact isolation .opencode/loopd/goals/<id>/, maxTurns force-finish → semantic summary → parent.
Architecture — how the loop runs
┌─────────────────────────┐
│ Main session (parent) │
│ owner tools │
│ inspect / steer / pause│
└────────────┬────────────┘
│ inbox → child
│ wake-up ← engine
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌─────────┐
│ Goal A │ │ Goal B │ │ Goal C │
│ active │ │ blocked │ │ paused │
└────┬────┘ └────┬─────┘ └────┬────┘
│ │ │
└─────────────────────┼───────────────────────┘
│
┌─────────────┴─────────────┐
▼ ▼
Loopd engine ──────────► Child worker (subagent)
(plugin server) steering get_goal / report / complete
│ lease / retry / polling │ block / question
│ force-finish / notify ▼
│ .opencode/loopd/goals/<id>/
│ progress.md / artifacts
└─────────────────────────────────┘
Each goal is a GoalID — the loop identity. The dashboard is a modal dialog that pushes loopd.dashboard onto the keymap so the chat prompt never sees your keys. Artifacts are namespaced per-goal; if your objective says save to ./reports/, that wins.
Quick start — background goals in OpenCode
/goal fetch the latest AI news and save 10 items to ai-news.md
# agent clarifies → loopd_create_goal → child loops every ~30s
/loop # or <leader>d (Ctrl+X → d)
j/k move o open child :send hello :force done ? help q close
Shift+B → prefilled GitHub bug report (like lazyjira / lazyconfluence)
Prefer the TUI? Ctrl+L also works if your terminal passes it; <leader>d is the reliable leader binding.
Why loopd vs. plain loop?
Plain /loop injects prompts into the main session — it pollutes the transcript. Codex goals keep history per-thread and steer via internal context. Loopd does the same for OpenCode: one child session per goal, continuation steering with progress history + transcript tail, and an explicit artifact namespace. The parent stays clean.
opencode-loopd
Child session per goal, no transcript pollution, artifact isolation, parent wake-up injection.
Plain /loop
Single session, prompt injected into main chat, manual polling to see progress.
Codex / Claude Code
Inspiration: Codex goals keep per-thread history and steer via internal context — same model for OpenCode.
FAQ — SEO for OpenCode loop & background agents
How to run background agents in OpenCode?
Install @bojackduy/opencode-loopd as a plugin (both opencode.jsonc and tui.json), restart, then /goal in any session. The engine spawns a child worker that loops autonomously; you monitor with /loop.
Is this like Claude Code's loop or Codex goals?
Yes — same mental model: engine-driven idle → continuation steering with accumulated context → child calls complete_goal/block_goal → parent wake-up. Like Codex, but native to OpenCode's session model.
Where do loop outputs go?
Per-goal: .opencode/loopd/goals/<id>/ (e.g. progress.md). If your objective names ./reports/, that wins.
How to report a bug?
In the dashboard press Shift+B or :bug — opens github.com/bojackduy/opencode-loopd/issues/new prefilled with environment (version, OS, goal context).