@bojackduy/opencode-loopd 1.10.4 → 1.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/goal.md +2 -2
- package/dist/server.js +205 -38
- package/dist/tui.js +1007 -811
- package/package.json +1 -1
- package/skills/loopd/SKILL.md +36 -10
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@bojackduy/opencode-loopd",
|
|
4
|
-
"version": "1.10.
|
|
4
|
+
"version": "1.10.5",
|
|
5
5
|
"description": "Codex-inspired background goal engine for OpenCode — autonomous subagents, engine-driven loop, child worker sessions and modal TUI dashboard. Like Claude Code loop for OpenCode.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "AGPL-3.0-or-later",
|
package/skills/loopd/SKILL.md
CHANGED
|
@@ -60,8 +60,8 @@ Every goal has an **immutable contract** at creation — the source of truth for
|
|
|
60
60
|
|
|
61
61
|
* **Objective** — semantic requirements (free text, self-contained). The worker derives concrete requirements from it.
|
|
62
62
|
* **Checks** — deterministic shell commands that **must pass** for `complete_goal` to be accepted. For `workspaceWrite:true` goals they are **mandatory** (explicit `checks` or plugin `defaultChecks`), and they run from `checkCwd` (writers default to project root; artifact-only jobs run from their `artifactDir`).
|
|
63
|
-
* **Agent** — any OpenCode agent name: built-in (`build`, `plan`, `explore`, `general`), file `~/.config/opencode/agents/*.md`, or `opencode.jsonc` `agent.*`. Discover with `opencode agent list`. Prefer `subagent`-mode agents for workers; `primary`-mode agents work but may expect user interaction. Resolution: explicit arg → **calling session's live agent** (read at creation) → `defaultAgent` → OpenCode global default. So a parent in Plan spawns a Plan worker unless told otherwise.
|
|
64
|
-
* **Model** — any model as `"providerID/modelID"` (e.g. `openai/gpt-5.6-sol`, `ollama/qwen3.8:27b`, `openrouter/google/gemma-4-31b-it:free`). Discover with `opencode models [provider]`. Sent on every worker prompt. Resolution: explicit arg → **calling session's live model** → `defaultModel` → agent config → session history → global default. Caveat: OpenCode exposes no live-model field for history-derived models, so those fall through to agent config.
|
|
63
|
+
* **Agent** — any OpenCode agent name: built-in (`build`, `plan`, `explore`, `general`), file `~/.config/opencode/agents/*.md`, or `opencode.jsonc` `agent.*`. Discover with `opencode agent list`. Prefer `subagent`-mode agents for workers; `primary`-mode agents work but may expect user interaction. Resolution: explicit arg → **calling session's live agent** (read at creation) → `defaultAgent` → OpenCode global default. So a parent in Plan spawns a Plan worker unless told otherwise. **Omit by default — inherit.** Do NOT pass `agent` unless the caller explicitly requests a different identity or the task needs it; explicit values freeze identity and break session upgrades.
|
|
64
|
+
* **Model** — any model as `"providerID/modelID"` (e.g. `openai/gpt-5.6-sol`, `ollama/qwen3.8:27b`, `openrouter/google/gemma-4-31b-it:free`). Discover with `opencode models [provider]`. Sent on every worker prompt. Resolution: explicit arg → **calling session's live model** → `defaultModel` → agent config → session history → global default. Caveat: OpenCode exposes no live-model field for history-derived models, so those fall through to agent config. **Omit by default — inherit.** Do NOT pass `model` unless explicitly requested.
|
|
65
65
|
* **WorkspaceWrite** — `true` (default) = may touch the shared repo; **only one active writer at a time** is allowed (enforced on `start`/`resume`/`retry` with rollback). Set `false` explicitly for artifact-only/read-only work to allow concurrency.
|
|
66
66
|
* **Budgets** — `tokenBudget` (tokens) and `costBudget` (dollars, e.g. `0.5`). When either is exceeded the engine aborts the worker mid-turn and sets `budget_limited`. The worker is stopped immediately, not just at the next turn boundary.
|
|
67
67
|
* **Limits** — `maxTurns` (default 50), `maxNoProgress`, `maxFailures`, `maxEvaluatorRejections` (default 3), `timeoutMs`, `compactEvery`, `progressFile`.
|
|
@@ -87,8 +87,9 @@ Use `loopd_create_goal` **after** clarifying the objective with the user (what /
|
|
|
87
87
|
loopd_create_goal({
|
|
88
88
|
name: "short-name",
|
|
89
89
|
objective: "Detailed description of what the goal should accomplish.",
|
|
90
|
-
agent:
|
|
91
|
-
|
|
90
|
+
// agent/model: omit by default to inherit calling session — only pass when explicitly requested
|
|
91
|
+
// agent: "researcher",
|
|
92
|
+
// model: "openai/gpt-5.6-sol",
|
|
92
93
|
costBudget: 0.5, // optional dollars; aborts worker when exceeded
|
|
93
94
|
checks: ["npm test"], // mandatory if workspaceWrite:true (or configure defaultChecks)
|
|
94
95
|
checkCwd: "/project/root", // optional; writers default to project root, readers to artifactDir
|
|
@@ -204,10 +205,24 @@ Owner-side equivalents of TUI `:force` / `:block`. Bypass checks — use only wh
|
|
|
204
205
|
|
|
205
206
|
## Command Sessions (Standalone Interactive Processes)
|
|
206
207
|
|
|
207
|
-
A **command session** is a raw OS process (spawned via `bun-pty`, falling back to a plain pipe) — no agent, no turns, no checks. It is completely independent from goals: starting/stopping one never pauses, blocks, or completes any goal, and vice versa. Use it whenever the request is "run X and let me watch/type into it" rather than "figure out/build X".
|
|
208
|
+
A **command session** is a raw OS process (spawned via `bun-pty`, falling back to a plain pipe) — no agent, no turns, no checks. It is completely independent from goals: starting/stopping one never pauses, blocks, or completes any goal, and vice versa. Use it whenever the request is "run X and let me watch/type into it" rather than "figure out/build X". By default, natural exits (including code 0), timeouts, and missing processes notify the owner; manual termination stays silent. Notification tails are sanitized for chat, while the retained terminal log remains raw. A finished command can still be opened to read its saved output; Ctrl+C/input cannot affect it.
|
|
208
209
|
|
|
209
210
|
Typical uses: dev servers (`npm run dev`), watch mode (`npm test -- --watch`), REPLs (`python3`, `node`), log tails (`tail -f`), one-off scripts, interactive shells.
|
|
210
211
|
|
|
212
|
+
### Prefer commands over the built-in shell
|
|
213
|
+
|
|
214
|
+
**Rule: if it might take a while or never return, start it as a command — never run it in the built-in shell/bash tool.** The built-in shell blocks your entire turn until the process exits: a 5-minute `npm install` freezes you mid-turn (and can hit tool timeouts, losing the output). `loopd_command_start` returns immediately with a `command_id` and pushes you on exit or pattern match, so you stay responsive.
|
|
215
|
+
|
|
216
|
+
Use a command (not the built-in shell) for: installs (`npm/pip/brew/cargo install`, downloads), builds, full test suites, migrations, dev servers, watchers, log tails, REPLs, anything interactive, or anything whose duration you can't guarantee is seconds. The built-in shell is only for quick one-shots (`ls`, `git status`, a single fast test).
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
User: install the dependencies and set up the project.
|
|
220
|
+
# WRONG: built-in shell `npm install` — blocks your turn for minutes.
|
|
221
|
+
# RIGHT:
|
|
222
|
+
Agent: loopd_command_start({ title: "npm install", command: "npm", args: ["install"], notify_on_exit: true })
|
|
223
|
+
# → returns immediately; you keep working, and get pushed when it finishes.
|
|
224
|
+
```
|
|
225
|
+
|
|
211
226
|
### Agent Tools
|
|
212
227
|
|
|
213
228
|
| Tool | Purpose |
|
|
@@ -248,7 +263,7 @@ On the Commands tab, `o` **never** opens a chat session or a dialog — it close
|
|
|
248
263
|
- Paste forwards raw bytes immediately.
|
|
249
264
|
- The viewport is measured and resizes both the emulator and the real PTY.
|
|
250
265
|
|
|
251
|
-
|
|
266
|
+
Single-key helpers on the Commands tab: `X` force-kill (SIGKILL now, no grace — for hung processes), `R` restart with the same spec (new ID, old record kept; env values are never persisted so restarted env commands lose their env), `x` remove finished (running commands get a hint, never a kill). Colon commands (before opening fullscreen): `:new <command> [args...]` (quoted args preserved, e.g. `:new bash -c "echo hi"`), `:interrupt`, `:terminate`, `:kill`, `:restart`, `:remove`, `:await <goalID>`, and bare text + Enter writes stdin to the selected command. Goal-only keys (`p`/`r`/`A`/`N`) never fire on the Commands tab; `R`/`x` are tab-scoped (retry/clear on Goals, restart/remove-done on Commands).
|
|
252
267
|
|
|
253
268
|
### Example: Watching a Dev Server
|
|
254
269
|
|
|
@@ -330,7 +345,17 @@ Open the shared dashboard with `/loop` (or `<leader>o`) — focuses the **Goals*
|
|
|
330
345
|
| `h` | Select Goals directionally |
|
|
331
346
|
| `l` | Select Commands directionally |
|
|
332
347
|
|
|
333
|
-
`j`/`k`/`g`/`G` select within whichever tab is active. Goal controls (`p`/`r`/`
|
|
348
|
+
`j`/`k`/`g`/`G` select within whichever tab is active. Goal controls (`p`/`r`/`A`/`N`) only fire on the Goals tab and are refused with a hint on Commands; `R`/`x` are tab-scoped (retry/clear on Goals, restart/remove-done on Commands).
|
|
349
|
+
|
|
350
|
+
### Commands Tab — Keyboard Shortcuts
|
|
351
|
+
|
|
352
|
+
| Key | Action |
|
|
353
|
+
|-----|--------|
|
|
354
|
+
| `X` | Force-kill selected command (SIGKILL now, no grace) |
|
|
355
|
+
| `R` | Restart selected command with the same spec (new ID) |
|
|
356
|
+
| `x` | Remove selected command — finished only (running gets a hint) |
|
|
357
|
+
| `o` | Open fullscreen terminal page |
|
|
358
|
+
| `Ctrl+C` | Interrupt selected command (SIGINT delivery, never a kill) |
|
|
334
359
|
|
|
335
360
|
### Goals Tab — Keyboard Shortcuts (Normal Mode)
|
|
336
361
|
|
|
@@ -373,11 +398,13 @@ Open the shared dashboard with `/loop` (or `<leader>o`) — focuses the **Goals*
|
|
|
373
398
|
| `:new <command> [args...]` | Start a new command (quoted args preserved) | `loopd_command_start` |
|
|
374
399
|
| `:interrupt` | SIGINT the selected command | `loopd_command_interrupt` |
|
|
375
400
|
| `:terminate` | SIGTERM→SIGKILL the selected command | `loopd_command_terminate` |
|
|
401
|
+
| `:kill` | SIGKILL now, no grace (same as `X`) | — (no agent equivalent; agents use `loopd_command_terminate`) |
|
|
402
|
+
| `:restart` | Stop + respawn same spec, new ID (same as `R`) | — (no agent equivalent) |
|
|
376
403
|
| `:remove` | Delete a finished command's record + log | `loopd_command_remove` |
|
|
377
404
|
| bare text + Enter | Write that line as stdin to the selected command | `loopd_command_write` |
|
|
378
405
|
| `q` | Detach (view closes; command keeps running) | — |
|
|
379
406
|
|
|
380
|
-
Inside the **fullscreen terminal page** (reached via `o`): every keystroke forwards immediately as raw input (no line-submit box), `Ctrl+C` is interrupt input to the process, `Ctrl+]` detaches back to where you came from without stopping the process.
|
|
407
|
+
Inside the **fullscreen terminal page** (reached via `o`): every keystroke forwards immediately as raw input (no line-submit box), `Ctrl+C` is interrupt input to the process, `Ctrl+]` detaches back to where you came from without stopping the process. Press `?` on the Commands tab to see force-kill, restart, and removal shortcuts before opening the terminal.
|
|
381
408
|
|
|
382
409
|
**Scrolling:** both lists are a `<scrollbox>` capped at 10 rows — short lists sit compact with no gap, long lists scroll and follow `j`/`k`/`g`/`G` via `scrollChildIntoView`. Detail + input stay pinned below in both cases. Empty list shows the fallback tip.
|
|
383
410
|
|
|
@@ -411,8 +438,7 @@ Agent: Got it — creating with a contract.
|
|
|
411
438
|
loopd_create_goal({
|
|
412
439
|
name: "docs-write",
|
|
413
440
|
objective: "Write a README.md covering: what it is, install, usage, architecture, and examples. Then create CHANGELOG.md with a v1.0.0 entry.",
|
|
414
|
-
agent
|
|
415
|
-
model: "openai/gpt-5.6-sol",
|
|
441
|
+
// omit agent/model to inherit calling session — only pass when explicitly requested
|
|
416
442
|
costBudget: 0.5,
|
|
417
443
|
checks: ["test -f README.md", "test -f CHANGELOG.md"],
|
|
418
444
|
checkCwd: "/Users/you/project", // explicit for writers; defaults to project root
|