@bybrawe/opencode-loop 0.4.4 → 0.5.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0
4
+
5
+ - Added `opencode-loopd` background daemon for long-running loops outside the OpenCode TUI.
6
+ - Added `opencode-loopd install-task` and `opencode-loopd uninstall-task` helpers for Windows Task Scheduler.
7
+ - Updated README to clearly explain the difference between session-bound TUI loops and background daemon loops.
8
+ - Added npm bin entry for `opencode-loopd`.
9
+ - Updated package check script to validate the daemon script.
10
+
3
11
  ## 0.4.4
4
12
 
5
13
  - Made the npm install path the primary README installation method now that the package is published as `@bybrawe/opencode-loop`.
package/README.md CHANGED
@@ -2,14 +2,18 @@
2
2
 
3
3
  **Claude Code style auto-continue for OpenCode.**
4
4
 
5
- OpenCode Loop adds a practical `/loop` command to OpenCode so an agent can keep working after each idle turn instead of waiting for you to type “continue” again. It is useful for long coding sessions, `progress.md` workflows, TODO automation, test-fix loops, periodic `/compact`, checkpoints, and safe autonomous development.
5
+ OpenCode Loop adds a practical `/loop` command and `opencode-loopd` daemon to OpenCode so an agent can keep working after each idle turn instead of waiting for you to type “continue” again.
6
+
7
+ It is useful for long coding sessions, `progress.md` workflows, TODO automation, test-fix loops, periodic `/compact`, checkpoints, safe autonomous development, and background OpenCode continuation jobs.
6
8
 
7
9
  Repository: **ByBrawe/opencode-loop**
8
10
  NPM package name: **@bybrawe/opencode-loop**
9
11
 
10
12
  ## Why this exists
11
13
 
12
- Claude Code users often rely on a loop-like workflow where the agent finishes one step, then immediately continues with the next step. OpenCode is powerful, but long-running autonomous workflows usually need extra control around:
14
+ Claude Code users often rely on a loop-like workflow where the agent finishes one step, then immediately continues with the next step.
15
+
16
+ OpenCode is powerful, but long-running autonomous workflows usually need extra control around:
13
17
 
14
18
  - auto-continue after idle
15
19
  - compact / summarize scheduling
@@ -18,6 +22,8 @@ Claude Code users often rely on a loop-like workflow where the agent finishes on
18
22
  - patch checkpoints
19
23
  - maximum runtime limits
20
24
  - failure limits
25
+ - background daemon execution
26
+ - Windows Task Scheduler support
21
27
  - safety prompts and destructive command guards
22
28
 
23
29
  OpenCode Loop is designed for developers searching for:
@@ -32,15 +38,22 @@ OpenCode Loop is designed for developers searching for:
32
38
  - Claude Code style loop for OpenCode
33
39
  - autonomous coding loop for OpenCode
34
40
  - progress.md TODO automation for OpenCode
41
+ - OpenCode background daemon
42
+ - OpenCode loop daemon
43
+ - OpenCode Windows Task Scheduler loop
35
44
 
36
45
  ## Features
37
46
 
38
- > Note: OpenCode Loop is session-bound. It runs while OpenCode is open and the session receives idle events. If the terminal closes, the machine sleeps, the process is killed, or the provider connection is lost for a long time, the loop cannot continue in the background. Use an external process manager or scheduled `opencode run` script for true daemon behavior.
39
-
47
+ > The TUI `/loop` command is session-bound. It runs while OpenCode is open and the session receives idle/status events. If the terminal closes, the machine sleeps, the process is killed, or the provider connection is lost for a long time, the TUI loop cannot continue in the background. For long-running loops, use `opencode-loopd` daemon mode.
40
48
 
41
49
  - **Claude Code style auto-continue** with `/loop 0s ...`.
50
+ - **TUI loop mode** with `/loop`, active while OpenCode is open.
51
+ - **Background daemon mode** with `opencode-loopd` for long-running loops outside the OpenCode TUI.
52
+ - **Windows Task Scheduler helper** with `opencode-loopd install-task`.
53
+ - **One-shot scheduled runs** for periodic continuation jobs.
42
54
  - **Interval loops** for prompts, slash commands, and shell commands.
43
55
  - **Prompt-file support** with `--prompt-file loop-prompt.md` for long reusable instructions.
56
+ - **Prompt-file daemon support** with `opencode-loopd --prompt-file loop-prompt.md`.
44
57
  - **Include extra context files** with `--include-file`.
45
58
  - **progress.md workflow** with `--progress-file progress.md`.
46
59
  - **Large TODO support** with `--batch`.
@@ -50,6 +63,8 @@ OpenCode Loop is designed for developers searching for:
50
63
  - **Post-run commands** with `--postrun`.
51
64
  - **Failure control** with `--max-failures` and `--pause-on-verify-fail`.
52
65
  - **Runtime control** with `--max-runtime 6h`.
66
+ - **Run limit support** with `--max-runs`.
67
+ - **Sleep-before-first-run support** with `--sleep-first`.
53
68
  - **Checkpoints** with `--checkpoint-only` or `--git-checkpoint`.
54
69
  - **Safe mode** with `--safe` and prompt-level destructive command warnings.
55
70
  - **Branch setup** with `--branch ai-loop`.
@@ -59,38 +74,6 @@ OpenCode Loop is designed for developers searching for:
59
74
  - **Starter progress file** with `/loop-init`.
60
75
  - **State export** with `/loop-export`.
61
76
 
62
- ## Quick start
63
-
64
- ```text
65
- /loop 0s continue from progress.md and implement the next unfinished TODO
66
- ```
67
-
68
- This is the closest behavior to a Claude Code CLI style loop: every time OpenCode becomes idle, the loop can send the next continuation prompt.
69
-
70
- A safer development loop:
71
-
72
- ```text
73
- /loop 0s --name dev --ask-never --safe --no-overlap --batch 5 --compact-every 200m --checkpoint-only --progress-file progress.md Treat progress.md as the main project state file. Continue with the next unfinished TODO, implement it, mark completed items with [x], add useful follow-up TODOs when you discover them, run tests/lint/build when available, and keep going while work remains.
74
- ```
75
-
76
- Periodic compact:
77
-
78
- ```text
79
- /loop 200m --name compact --no-now /compact
80
- ```
81
-
82
- Test-fix loop:
83
-
84
- ```text
85
- /loop 0s --name testfix --ask-never --safe --verify "npm test" Continue from progress.md. If tests fail, analyze the failure, fix it, and run the tests again.
86
- ```
87
-
88
- Shell command loop:
89
-
90
- ```text
91
- /loop 10m --name tests --safe !npm test
92
- ```
93
-
94
77
  ## Installation
95
78
 
96
79
  ### Recommended: install from npm
@@ -136,9 +119,20 @@ Then fully restart OpenCode and run:
136
119
  /loop-doctor
137
120
  ```
138
121
 
122
+ The npm package also installs the `opencode-loopd` CLI for background loops:
123
+
124
+ ```bash
125
+ opencode-loopd --help
126
+ ```
127
+
139
128
  ### Why `npx` is the recommended npm install
140
129
 
141
- OpenCode can load npm plugins from the `plugin` array in `opencode.json`, but OpenCode slash commands are discovered from command definitions such as markdown files in a `commands/` directory or command entries in config. The `npx` installer installs both parts: the plugin file and the `/loop-*` command files.
130
+ OpenCode can load npm plugins from the `plugin` array in `opencode.json`, but OpenCode slash commands are discovered from command definitions such as markdown files in a `commands/` directory or command entries in config.
131
+
132
+ The `npx` installer installs both parts:
133
+
134
+ - the OpenCode plugin file
135
+ - the `/loop-*` command markdown files
142
136
 
143
137
  Use the OpenCode config-only method only if you already installed the command files separately or you are only testing plugin loading.
144
138
 
@@ -153,7 +147,9 @@ If you want OpenCode to load the npm plugin package directly, add the scoped pac
153
147
  }
154
148
  ```
155
149
 
156
- Use the scoped package name exactly as shown. `opencode-loop` without `@bybrawe/` is a different npm package name.
150
+ Use the scoped package name exactly as shown.
151
+
152
+ `opencode-loop` without `@bybrawe/` is a different npm package name.
157
153
 
158
154
  If `/loop` does not appear after using only the config method, run the installer once:
159
155
 
@@ -227,52 +223,132 @@ copy .\src\index.js .opencode\plugins\opencode-loop.js
227
223
  copy .\commands\*.md .opencode\commands\
228
224
  ```
229
225
 
230
- <<<<<<< HEAD
231
- =======
232
- ### npm install - after publishing `@bybrawe/opencode-loop`
226
+ ### Verify installation
227
+
228
+ After restarting OpenCode, run:
229
+
230
+ ```text
231
+ /loop-help
232
+ /loop-doctor
233
+ ```
234
+
235
+ If the commands do not appear:
236
+
237
+ 1. Make sure OpenCode was fully restarted.
238
+ 2. Check that `opencode-loop.js` exists in the OpenCode plugin directory.
239
+ 3. Check that `loop.md`, `loop-help.md`, and the other command files exist in the OpenCode commands directory.
240
+ 4. Run `npx -y @bybrawe/opencode-loop` again to reinstall the command files.
241
+
242
+ ## Quick start
243
+
244
+ ```text
245
+ /loop 0s continue from progress.md and implement the next unfinished TODO
246
+ ```
247
+
248
+ This is the closest behavior to a Claude Code CLI style loop: every time OpenCode becomes idle, the loop can send the next continuation prompt.
249
+
250
+ A safer development loop:
251
+
252
+ ```text
253
+ /loop 0s --name dev --ask-never --safe --no-overlap --batch 5 --compact-every 200m --checkpoint-only --progress-file progress.md Treat progress.md as the main project state file. Continue with the next unfinished TODO, implement it, mark completed items with [x], add useful follow-up TODOs when you discover them, run tests/lint/build when available, and keep going while work remains.
254
+ ```
255
+
256
+ Periodic compact:
233
257
 
234
- Do **not** use this section for a plain GitHub clone. This is only for the npm package.
258
+ ```text
259
+ /loop 200m --name compact --no-now /compact
260
+ ```
261
+
262
+ Test-fix loop:
263
+
264
+ ```text
265
+ /loop 0s --name testfix --ask-never --safe --verify "npm test" Continue from progress.md. If tests fail, analyze the failure, fix it, and run the tests again.
266
+ ```
235
267
 
236
- The npm package name is scoped:
268
+ Shell command loop:
237
269
 
238
270
  ```text
239
- @bybrawe/opencode-loop
271
+ /loop 10m --name tests --safe !npm test
240
272
  ```
241
273
 
242
- Recommended npm install command:
274
+ ## Background daemon
275
+
276
+ The `/loop` command is session-bound. It works while OpenCode is open and the current session emits idle/status events.
277
+
278
+ If you close OpenCode, restart the terminal, lose connection, or your PC sleeps, the TUI loop will not keep running.
279
+
280
+ For long-running loops, use the daemon:
243
281
 
244
282
  ```bash
245
- npx -y @bybrawe/opencode-loop
283
+ opencode-loopd --project . --every 5m --prompt-file loop-prompt.md
246
284
  ```
247
285
 
248
- This copies the plugin file and all `/loop-*` markdown command files into your OpenCode config directory, then you restart OpenCode. This is the most reliable install path because OpenCode slash commands are discovered from command files.
286
+ Run immediately after each OpenCode turn:
287
+
288
+ ```bash
289
+ opencode-loopd --project . --every 0s --prompt "continue from progress.md and implement the next unfinished TODO"
290
+ ```
249
291
 
250
- If you only want OpenCode to load the npm plugin package directly, add the scoped package name to your OpenCode config:
292
+ Limit runs:
251
293
 
252
- ```json
253
- {
254
- "$schema": "https://opencode.ai/config.json",
255
- "plugin": ["@bybrawe/opencode-loop"]
256
- }
294
+ ```bash
295
+ opencode-loopd --project . --every 5m --max-runs 20 --prompt-file loop-prompt.md
257
296
  ```
258
297
 
298
+ Wait before the first run:
259
299
 
260
- >>>>>>> 600c1cf8146d317efd682e20298f53c0cf2bf213
261
- ### Verify installation
300
+ ```bash
301
+ opencode-loopd --project . --every 10m --sleep-first --prompt-file loop-prompt.md
302
+ ```
262
303
 
263
- After restarting OpenCode, run:
304
+ Use an inline prompt:
264
305
 
265
- ```text
266
- /loop-help
267
- /loop-doctor
306
+ ```bash
307
+ opencode-loopd --project . --every 0s --prompt "continue from progress.md and implement the next unfinished TODO"
268
308
  ```
269
309
 
270
- If the commands do not appear:
310
+ Example `loop-prompt.md`:
271
311
 
272
- 1. Make sure OpenCode was fully restarted.
273
- 2. Check that `opencode-loop.js` exists in the OpenCode plugin directory.
274
- 3. Check that `loop.md`, `loop-help.md`, and the other command files exist in the OpenCode commands directory.
275
- 4. Run `npx -y @bybrawe/opencode-loop` again to reinstall the command files.
312
+ ```md
313
+ Continue from progress.md and implement the next unfinished TODO.
314
+
315
+ Rules:
316
+ - Do not ask questions.
317
+ - Make reasonable assumptions.
318
+ - Mark completed TODO items with [x].
319
+ - Add useful follow-up TODOs when needed.
320
+ - Run tests/lint/build when available.
321
+ - Do not run destructive commands such as git reset, git clean, rm -rf, force push, deploy, or production migrations.
322
+ - Keep going while work remains.
323
+ ```
324
+
325
+ ## Windows Task Scheduler
326
+
327
+ You can create a Windows scheduled task that runs a one-shot daemon job every N minutes.
328
+
329
+ Install a scheduled task:
330
+
331
+ ```powershell
332
+ opencode-loopd install-task --project "C:\path\to\project" --every 10m --prompt-file loop-prompt.md --name OpenCodeLoop
333
+ ```
334
+
335
+ Remove it:
336
+
337
+ ```powershell
338
+ opencode-loopd uninstall-task --name OpenCodeLoop
339
+ ```
340
+
341
+ Check existing tasks:
342
+
343
+ ```powershell
344
+ Get-ScheduledTask | Where-Object { $_.TaskName -like "*OpenCode*" }
345
+ ```
346
+
347
+ For active development, a visible terminal running daemon mode is usually easier to monitor:
348
+
349
+ ```powershell
350
+ opencode-loopd --project "C:\path\to\project" --every 0s --prompt-file loop-prompt.md
351
+ ```
276
352
 
277
353
  ## Multiple loops and duplicate protection
278
354
 
@@ -614,6 +690,12 @@ By default a new loop is due immediately. Use `--no-now` to wait for the first i
614
690
  /loop 0s --name dev --ask-never --safe --no-overlap --compact-every 20 --timeout 45m --max-runtime 6h --max-failures 3 --stop-file STOP_LOOP --checkpoint-only Continue from progress.md. Do not ask questions. Make reasonable assumptions. Complete TODOs in order, mark finished items with [x], add useful new ideas to progress.md, and keep going while work remains.
615
691
  ```
616
692
 
693
+ ### Daemon-based long-running development loop
694
+
695
+ ```bash
696
+ opencode-loopd --project . --every 5m --prompt-file loop-prompt.md
697
+ ```
698
+
617
699
  ### Test-fix loop
618
700
 
619
701
  ```text
@@ -727,23 +809,34 @@ Improve the application in small safe steps.
727
809
  - None.
728
810
  ```
729
811
 
730
- ## Bug fixes and hardening in v0.4.0
812
+ ## Changelog highlights
813
+
814
+ ### v0.5.1
815
+
816
+ - Cleaned up README installation docs.
817
+ - Removed duplicate npm install section.
818
+ - Removed unresolved merge conflict markers.
819
+ - Added clearer daemon mode documentation.
820
+ - Added Windows Task Scheduler examples.
821
+ - Added daemon usage examples for `--every`, `--max-runs`, `--sleep-first`, and `--prompt-file`.
822
+
823
+ ### v0.5.0
824
+
825
+ - Added `opencode-loopd` background daemon.
826
+ - Added Windows Task Scheduler helper.
827
+ - Documented TUI loop vs daemon loop.
828
+ - Added background long-running loop examples.
829
+
830
+ ### v0.4.3
731
831
 
732
- - Renamed the public package/repo metadata to `opencode-loop` for `ByBrawe/opencode-loop`.
733
- - Replaced project-specific examples with public, English OpenCode examples.
734
- - Added `--prompt-file` for long prompts instead of huge command lines.
735
- - Added `--include-file` for extra context files.
736
- - Added `--max-runtime` so loops can stop after a total runtime.
737
- - Added `--max-failures` and `--pause-on-verify-fail` for failing verification loops.
738
- - Added `--postrun` and `--notify` hooks.
739
- - Added `/loop-doctor`, `/loop-init`, and `/loop-export`.
740
- - Fixed the max-runs/checkpoint ordering issue so the last run can still finalize and checkpoint.
741
- - Improved no-overlap behavior and state cleanup for non-assistant actions such as `/compact`.
742
- - Kept all README examples in English for public GitHub discovery.
832
+ - Added duplicate loop protection.
833
+ - Quieted command markdown files.
743
834
 
744
835
  ## Notes and limits
745
836
 
746
- - The plugin is idle-driven. It does not run a background daemon while OpenCode is busy.
837
+ - The TUI plugin is idle-driven. It does not run a background daemon while OpenCode is busy.
838
+ - The TUI `/loop` command stops when OpenCode closes, the terminal closes, the machine sleeps, or the session stops emitting idle events.
839
+ - For long-running work outside the TUI, use `opencode-loopd`.
747
840
  - `--timeout` is best-effort and relies on OpenCode's abort API.
748
841
  - `--verify`, `--preflight`, `--postrun`, and `--notify` run shell commands, so configure OpenCode permissions carefully.
749
842
  - `--until` scans common state files and a limited number of markdown/text/json/yaml files to avoid walking huge projects.
@@ -752,4 +845,4 @@ Improve the application in small safe steps.
752
845
 
753
846
  ## License
754
847
 
755
- MIT
848
+ MIT
@@ -0,0 +1,10 @@
1
+ Continue from progress.md and implement the next unfinished TODO.
2
+
3
+ Rules:
4
+ - Do not ask questions.
5
+ - Make reasonable assumptions.
6
+ - Mark completed TODO items with [x].
7
+ - Add useful follow-up TODOs when needed.
8
+ - Run tests/lint/build when available.
9
+ - Do not run destructive commands such as git reset, git clean, rm -rf, force push, deploy, or production migrations.
10
+ - Keep going while work remains.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bybrawe/opencode-loop",
3
- "version": "0.4.4",
4
- "description": "Claude Code style auto-continue /loop command for OpenCode: autonomous loops, compact scheduling, progress.md TODO automation, verification, checkpoints, and safe long-running coding sessions.",
3
+ "version": "0.5.1",
4
+ "description": "Claude Code style /loop command and background daemon for OpenCode: auto-continue, progress.md TODO automation, compact scheduling, verification, checkpoints, and safe autonomous coding loops.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
7
7
  "exports": {
@@ -17,7 +17,7 @@
17
17
  "CHANGELOG.md"
18
18
  ],
19
19
  "scripts": {
20
- "check": "node --check src/index.js",
20
+ "check": "node --check src/index.js && node --check scripts/install-node.mjs && node --check scripts/loopd.mjs",
21
21
  "install:global": "node scripts/install-node.mjs",
22
22
  "pack:zip": "node scripts/make-zip.mjs"
23
23
  },
@@ -36,7 +36,10 @@
36
36
  "scheduler",
37
37
  "progress-md",
38
38
  "todo-automation",
39
- "ralph-loop"
39
+ "ralph-loop",
40
+ "daemon",
41
+ "background-runner",
42
+ "windows-task-scheduler"
40
43
  ],
41
44
  "author": "ByBrawe",
42
45
  "license": "MIT",
@@ -52,6 +55,7 @@
52
55
  "@opencode-ai/plugin": ">=1.4.0"
53
56
  },
54
57
  "bin": {
55
- "opencode-loop": "./scripts/install-node.mjs"
58
+ "opencode-loop": "./scripts/install-node.mjs",
59
+ "opencode-loopd": "./scripts/loopd.mjs"
56
60
  }
57
61
  }
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs"
3
+ import path from "node:path"
4
+ import { spawn, spawnSync } from "node:child_process"
5
+
6
+ const args = process.argv.slice(2)
7
+
8
+ function arg(name, fallback = null) {
9
+ const i = args.indexOf(name)
10
+ return i >= 0 ? args[i + 1] ?? fallback : fallback
11
+ }
12
+
13
+ function has(name) {
14
+ return args.includes(name)
15
+ }
16
+
17
+ function parseMs(value) {
18
+ const v = String(value || "0s").trim().toLowerCase()
19
+ if (v === "0" || v === "0s" || v === "now") return 0
20
+
21
+ const m = v.match(/^(\d+(?:\.\d+)?)\s*(ms|s|sec|secs|m|min|mins|h|hr|hrs|d|day|days)$/)
22
+ if (!m) throw new Error(`Invalid duration: ${value}`)
23
+
24
+ const n = Number(m[1])
25
+ const unit = m[2]
26
+
27
+ if (unit === "ms") return n
28
+ if (unit.startsWith("s")) return n * 1000
29
+ if (unit.startsWith("m")) return n * 60_000
30
+ if (unit.startsWith("h")) return n * 3_600_000
31
+ if (unit.startsWith("d")) return n * 86_400_000
32
+
33
+ return n
34
+ }
35
+
36
+ function sleep(ms) {
37
+ return new Promise((resolve) => setTimeout(resolve, ms))
38
+ }
39
+
40
+ function run(command, cwd) {
41
+ return new Promise((resolve) => {
42
+ const child = spawn(command, {
43
+ cwd,
44
+ shell: true,
45
+ stdio: "inherit",
46
+ env: process.env,
47
+ })
48
+
49
+ child.on("exit", (code) => resolve(code ?? 0))
50
+ })
51
+ }
52
+
53
+ function readPrompt(project) {
54
+ const promptFile = arg("--prompt-file")
55
+ const promptArg = arg("--prompt")
56
+
57
+ if (promptFile) {
58
+ return fs.readFileSync(path.resolve(project, promptFile), "utf8")
59
+ }
60
+
61
+ if (promptArg) return promptArg
62
+
63
+ return [
64
+ "Continue from progress.md and implement the next unfinished TODO.",
65
+ "Do not ask questions.",
66
+ "Make reasonable assumptions.",
67
+ "Mark completed TODO items with [x].",
68
+ "Add useful follow-up TODOs when needed.",
69
+ "Run tests/lint/build when available.",
70
+ "Do not run destructive commands such as git reset, git clean, rm -rf, force push, deploy, or production migrations.",
71
+ "Keep going while work remains.",
72
+ ].join(" ")
73
+ }
74
+
75
+ async function daemon() {
76
+ const project = path.resolve(arg("--project", process.cwd()))
77
+ const every = arg("--every", "0s")
78
+ const delay = parseMs(every)
79
+ const maxRuns = Number(arg("--max-runs", "0")) || 0
80
+ const sleepFirst = has("--sleep-first")
81
+ const prompt = readPrompt(project)
82
+
83
+ console.log("OpenCode Loop daemon")
84
+ console.log(`project: ${project}`)
85
+ console.log(`every: ${every}`)
86
+ console.log(`maxRuns: ${maxRuns || "unlimited"}`)
87
+
88
+ let count = 0
89
+
90
+ if (sleepFirst && delay > 0) {
91
+ await sleep(delay)
92
+ }
93
+
94
+ while (true) {
95
+ count += 1
96
+
97
+ console.log("")
98
+ console.log(`[opencode-loopd] run #${count} ${new Date().toISOString()}`)
99
+
100
+ const command = `opencode run --continue --prompt ${JSON.stringify(prompt)}`
101
+ const code = await run(command, project)
102
+
103
+ if (code !== 0) {
104
+ console.log(`[opencode-loopd] opencode exited with code ${code}`)
105
+ }
106
+
107
+ if (maxRuns > 0 && count >= maxRuns) {
108
+ console.log("[opencode-loopd] max runs reached")
109
+ break
110
+ }
111
+
112
+ if (delay > 0) {
113
+ await sleep(delay)
114
+ }
115
+ }
116
+ }
117
+
118
+ function installTask() {
119
+ if (process.platform !== "win32") {
120
+ throw new Error("install-task is currently implemented for Windows Task Scheduler only. Use daemon mode on macOS/Linux.")
121
+ }
122
+
123
+ const project = path.resolve(arg("--project", process.cwd()))
124
+ const every = arg("--every", "10m")
125
+ const minutes = Math.max(1, Math.round(parseMs(every) / 60_000))
126
+ const name = arg("--name", "OpenCodeLoop")
127
+ const promptFile = arg("--prompt-file")
128
+ const promptArg = arg("--prompt")
129
+ const node = process.execPath
130
+ const script = path.resolve(new URL(import.meta.url).pathname)
131
+
132
+ const commandParts = [
133
+ `"${node}"`,
134
+ `"${script}"`,
135
+ "daemon",
136
+ "--project",
137
+ `"${project}"`,
138
+ "--every",
139
+ "0s",
140
+ "--max-runs",
141
+ "1",
142
+ ]
143
+
144
+ if (promptFile) commandParts.push("--prompt-file", `"${promptFile}"`)
145
+ if (promptArg) commandParts.push("--prompt", `"${promptArg.replaceAll('"', '\\"')}"`)
146
+
147
+ const taskCommand = commandParts.join(" ")
148
+ const schtasks = `schtasks /Create /F /SC MINUTE /MO ${minutes} /TN "${name}" /TR ${JSON.stringify(taskCommand)}`
149
+
150
+ console.log(schtasks)
151
+ const result = spawnSync(schtasks, { shell: true, stdio: "inherit" })
152
+ process.exit(result.status ?? 0)
153
+ }
154
+
155
+ function uninstallTask() {
156
+ if (process.platform !== "win32") {
157
+ throw new Error("uninstall-task is currently implemented for Windows Task Scheduler only.")
158
+ }
159
+
160
+ const name = arg("--name", "OpenCodeLoop")
161
+ const result = spawnSync(`schtasks /Delete /F /TN "${name}"`, { shell: true, stdio: "inherit" })
162
+ process.exit(result.status ?? 0)
163
+ }
164
+
165
+ function help() {
166
+ console.log(`
167
+ OpenCode Loop daemon
168
+
169
+ Usage:
170
+ opencode-loopd --project . --every 5m --prompt-file loop-prompt.md
171
+ opencode-loopd --project . --every 0s --prompt "continue from progress.md"
172
+ opencode-loopd install-task --project . --every 10m --prompt-file loop-prompt.md --name OpenCodeLoop
173
+ opencode-loopd uninstall-task --name OpenCodeLoop
174
+
175
+ Options:
176
+ --project <path> Project directory
177
+ --every <duration> 0s, 5m, 1h, etc.
178
+ --prompt <text> Prompt text
179
+ --prompt-file <file> Read prompt from file relative to the project
180
+ --max-runs <n> Stop after n runs
181
+ --sleep-first Wait before first run
182
+ `)
183
+ }
184
+
185
+ const command = args[0]
186
+
187
+ try {
188
+ if (command === "daemon" || command === "loopd") {
189
+ args.shift()
190
+ await daemon()
191
+ } else if (command === "install-task") {
192
+ args.shift()
193
+ installTask()
194
+ } else if (command === "uninstall-task") {
195
+ args.shift()
196
+ uninstallTask()
197
+ } else if (has("--help") || has("-h") || command === "help") {
198
+ help()
199
+ } else {
200
+ await daemon()
201
+ }
202
+ } catch (error) {
203
+ console.error(error?.message || error)
204
+ process.exit(1)
205
+ }