opencode-scheduled-tasks 0.1.0

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/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # opencode-scheduled-tasks
2
+
3
+ Scheduled task runner plugin for [OpenCode](https://opencode.ai). Define recurring tasks as markdown files with cron schedules, or let agents schedule one-off tasks via tool calls. A background daemon executes tasks on schedule via `opencode run`.
4
+
5
+ ## Installation
6
+
7
+ ### 1. Add the plugin to your OpenCode config
8
+
9
+ ```json
10
+ {
11
+ "plugin": ["opencode-scheduled-tasks"]
12
+ }
13
+ ```
14
+
15
+ ### 2. Install the scheduler daemon
16
+
17
+ The daemon runs every 60 seconds and executes any tasks that are due. It auto-detects your platform (macOS launchd or Linux systemd).
18
+
19
+ ```bash
20
+ npx opencode-scheduler --install
21
+ ```
22
+
23
+ ### 3. Install the agent skill (optional)
24
+
25
+ This gives the agent context on how to use the scheduling tools, especially around permissions.
26
+
27
+ ```bash
28
+ npx opencode-scheduler --install-skill
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ Create a task file at `~/.config/opencode/tasks/daily-standup.md`:
34
+
35
+ ```yaml
36
+ ---
37
+ schedule: "0 9 * * 1-5"
38
+ cwd: ~/projects/my-app
39
+ ---
40
+
41
+ Summarize all git commits from yesterday. Include the files changed and a brief
42
+ description of each change. Format as a bulleted list.
43
+ ```
44
+
45
+ That's it. The scheduler will run this task every weekday at 9 AM.
46
+
47
+ ## Recurring tasks
48
+
49
+ Recurring tasks are markdown files in `~/.config/opencode/tasks/`. The filename (without `.md`) is used as the task name.
50
+
51
+ The file has YAML frontmatter followed by the prompt that gets sent to the agent.
52
+
53
+ ### Frontmatter reference
54
+
55
+ | Field | Type | Required | Default | Description |
56
+ |-------|------|----------|---------|-------------|
57
+ | `description` | string | no | — | Human-readable description. |
58
+ | `schedule` | string | yes | — | 5-field cron expression. Uses system local timezone. |
59
+ | `cwd` | string | yes | — | Working directory. Supports `~` expansion. |
60
+ | `session_name` | string | no | — | If set, reuses the same session across runs. If omitted, creates a fresh session each run. |
61
+ | `model` | string | no | user default | Model in `provider/model` format (e.g., `anthropic/claude-sonnet-4-6`). |
62
+ | `agent` | string | no | user default | Agent to use. |
63
+ | `permission` | object | no | opencode defaults | Permission config. Same schema as the `permission` key in `opencode.json`. See [Permissions](#permissions). |
64
+ | `enabled` | boolean | no | `true` | Set to `false` to temporarily disable without deleting. |
65
+
66
+ ### Cron expression reference
67
+
68
+ ```
69
+ ┌───────── minute (0-59)
70
+ │ ┌───────── hour (0-23)
71
+ │ │ ┌───────── day of month (1-31)
72
+ │ │ │ ┌───────── month (1-12)
73
+ │ │ │ │ ┌───────── day of week (0-7, 0 and 7 are Sunday)
74
+ │ │ │ │ │
75
+ * * * * *
76
+ ```
77
+
78
+ | Expression | Meaning |
79
+ |------------|---------|
80
+ | `0 9 * * *` | Every day at 9:00 AM |
81
+ | `0 9 * * 1-5` | Every weekday at 9:00 AM |
82
+ | `*/30 * * * *` | Every 30 minutes |
83
+ | `0 0 * * 0` | Every Sunday at midnight |
84
+ | `0 9 1 * *` | First day of every month at 9:00 AM |
85
+
86
+ ### Examples
87
+
88
+ **Daily branch cleanup** (reuses the same session):
89
+
90
+ ```yaml
91
+ ---
92
+ description: Clean up merged git branches
93
+ schedule: "0 9 * * *"
94
+ cwd: ~/projects/my-app
95
+ session_name: daily-cleanup
96
+ permission:
97
+ bash:
98
+ "*": "allow"
99
+ "git push *": "deny"
100
+ edit: "deny"
101
+ ---
102
+
103
+ Check for local branches that have been merged into main and delete them.
104
+ List any branches that look stale but haven't been merged yet.
105
+ ```
106
+
107
+ **Weekly project report** (fresh session each time):
108
+
109
+ ```yaml
110
+ ---
111
+ description: Generate a weekly summary of project activity
112
+ schedule: "0 8 * * 1"
113
+ cwd: ~/projects/my-app
114
+ model: anthropic/claude-sonnet-4-6
115
+ permission:
116
+ bash:
117
+ "*": "allow"
118
+ edit: "deny"
119
+ ---
120
+
121
+ Generate a weekly summary of project activity for the past 7 days.
122
+ Include commits, files changed, open PRs, and a brief velocity analysis.
123
+ ```
124
+
125
+ ## One-off tasks
126
+
127
+ Agents can schedule one-off tasks using the `schedule_task` tool. These are stored in a SQLite database and executed once at the scheduled time.
128
+
129
+ The agent has access to these tools:
130
+
131
+ | Tool | Description |
132
+ |------|-------------|
133
+ | `schedule_task` | Schedule a one-off task for a specific time |
134
+ | `list_tasks` | List all recurring and one-off tasks |
135
+ | `cancel_task` | Cancel a pending one-off task or disable a recurring task |
136
+ | `task_history` | View execution history for a task |
137
+ | `get_task_instructions` | Get the full frontmatter format for recurring tasks |
138
+
139
+ Example agent interaction:
140
+
141
+ > "Schedule a task to run the test suite tomorrow at 8 AM"
142
+
143
+ The agent will call `schedule_task` with the appropriate prompt, time, working directory, and permissions.
144
+
145
+ ## Permissions
146
+
147
+ Scheduled tasks run in the background with no user present. Any permission set to `"ask"` will effectively be **denied** since there's nobody to approve the prompt.
148
+
149
+ Most permissions (`bash`, `edit`, `read`) default to `"allow"` and work fine without explicit configuration.
150
+
151
+ ### `external_directory` — the common gotcha
152
+
153
+ The `external_directory` permission defaults to `"ask"`, which means **any file access outside the task's `cwd` will silently fail** in background execution.
154
+
155
+ If your task reads or writes files outside its working directory, you must explicitly allow those paths:
156
+
157
+ ```yaml
158
+ permission:
159
+ external_directory:
160
+ "/tmp/*": "allow"
161
+ "~/other-project/*": "allow"
162
+ ```
163
+
164
+ ### Rule of thumb
165
+
166
+ Ask: "Will this task touch any files outside its `cwd`?" If yes, add `external_directory` rules.
167
+
168
+ ## CLI reference
169
+
170
+ The `opencode-scheduler` CLI manages the scheduler daemon and provides task visibility. All commands are available via `npx`.
171
+
172
+ ```
173
+ npx opencode-scheduler --install Install the system scheduler (launchd/systemd)
174
+ npx opencode-scheduler --uninstall Remove the system scheduler
175
+ npx opencode-scheduler --install-skill Install the scheduled-tasks agent skill
176
+ npx opencode-scheduler --status Show scheduler and task status
177
+ npx opencode-scheduler --list List all tasks with next run times
178
+ npx opencode-scheduler --help Show help
179
+ ```
180
+
181
+ The following commands are used internally by the scheduler daemon and generally don't need to be run manually:
182
+
183
+ ```
184
+ opencode-scheduler --run-once Run one scheduler tick
185
+ opencode-scheduler --exec-task <id> Execute a specific task (used by worker processes)
186
+ ```
187
+
188
+ ### Example output
189
+
190
+ ```
191
+ $ npx opencode-scheduler --status
192
+
193
+ Scheduler: installed (macos-launchd)
194
+ Plist: ~/Library/LaunchAgents/ai.opencode.scheduled-tasks.plist
195
+
196
+ Recurring tasks: 2 (1 enabled, 1 disabled)
197
+ daily-cleanup next: 2026-03-31T13:00:00.000Z last: completed 2026-03-30T13:00:12.000Z
198
+ weekly-report disabled
199
+
200
+ One-off tasks: 1 pending
201
+ abc123def4... "Run migration check" scheduled: 2026-03-30T19:00:00.000Z
202
+ ```
203
+
204
+ ## Session behavior
205
+
206
+ By default, each task run creates a fresh OpenCode session. This is good for independent, stateless tasks.
207
+
208
+ If you set `session_name`, the task reuses the same session across runs. The agent can see previous messages and build on prior context. This is useful for tasks like:
209
+
210
+ - A daily standup that references yesterday's summary
211
+ - An ongoing code review that accumulates findings
212
+ - A monitoring task that tracks changes over time
213
+
214
+ ```yaml
215
+ session_name: daily-standup
216
+ ```
217
+
218
+ The session is created on the first run and reused on subsequent runs. Session ID mappings are stored in the SQLite database.
219
+
220
+ ## Architecture
221
+
222
+ The plugin has three components:
223
+
224
+ 1. **Plugin** (`dist/plugin.js`) — Loaded by OpenCode's Bun-based plugin runtime. Exposes tools to the agent and reads/writes the SQLite database. Uses `bun:sqlite`.
225
+
226
+ 2. **CLI** (`dist/cli.js`, bin: `opencode-scheduler`) — Standalone Node.js script. Manages the scheduler daemon, runs scheduler ticks, and executes task workers. Uses `better-sqlite3`.
227
+
228
+ 3. **Task files** (`~/.config/opencode/tasks/*.md`) — User-editable recurring task definitions with YAML frontmatter.
229
+
230
+ Both the plugin and CLI read/write the same SQLite database at `~/.config/opencode/.tasks.db`.
231
+
232
+ ### How tasks execute
233
+
234
+ ```
235
+ launchd/systemd (every 60s)
236
+ └─ opencode-scheduler --run-once # scheduler tick
237
+ ├─ checks which tasks are due
238
+ ├─ spawns worker for each due task # returns immediately
239
+ │ └─ opencode-scheduler --exec-task <id>
240
+ │ └─ opencode run ... # full LLM session
241
+ │ └─ updates DB on completion
242
+ └─ reaps any crashed workers
243
+ ```
244
+
245
+ The scheduler tick is non-blocking — it spawns detached worker processes and exits immediately. Each worker runs `opencode run` synchronously, captures the session ID from the JSON output, and updates the database when done.
246
+
247
+ Concurrency is managed via PID tracking. If a task is already running (its worker PID is still alive), the scheduler skips it.
248
+
249
+ ## Development
250
+
251
+ ```bash
252
+ # Install dependencies
253
+ npm install
254
+
255
+ # Build
256
+ npm run build
257
+
258
+ # Run tests
259
+ npm test
260
+
261
+ # Watch mode
262
+ npm run dev
263
+ npm run test:watch
264
+ ```
265
+
266
+ ### Project structure
267
+
268
+ ```
269
+ src/
270
+ cli.ts # CLI entry point (opencode-scheduler)
271
+ plugin.ts # OpenCode plugin entry point
272
+ lib/
273
+ types.ts # Shared TypeScript types
274
+ db.ts # SQLite database (schema, migrations, CRUD)
275
+ sqlite.ts # Runtime-agnostic SQLite abstraction (bun:sqlite / better-sqlite3)
276
+ tasks.ts # Task file parser (frontmatter validation)
277
+ cron.ts # Cron evaluation (isDue, nextRunTime)
278
+ runner.ts # Task execution (spawn workers, run opencode)
279
+ installer.ts # Platform detection + launchd/systemd installation
280
+ __tests__/ # Unit tests
281
+ examples/ # Example task files
282
+ skill/ # Agent skill (SKILL.md)
283
+ ```
284
+
285
+ ## License
286
+
287
+ MIT