opencode-jobs 0.1.2 → 1.0.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 CHANGED
@@ -36,7 +36,7 @@ and reports the manual change required.
36
36
 
37
37
  Job files are committed agent prompts that run on your machine on a
38
38
  schedule — only install and enable projects whose
39
- `.opencode/scheduler/jobs/` you trust, the same care you would take before
39
+ `.opencode/jobs/` you trust, the same care you would take before
40
40
  running an unfamiliar repository's build.
41
41
 
42
42
  Alternatively, add the package manually to the project root `opencode.json`
@@ -67,8 +67,8 @@ the uninstaller cannot rewrite safely is left untouched with a non-zero
67
67
  exit. Job definitions, run history, session state, and logs are kept.
68
68
 
69
69
  Add `--purge` to also delete the project's job definitions
70
- (`.opencode/scheduler/`) and its scheduler data (run scripts, run history,
71
- session state, and logs):
70
+ (`.opencode/jobs/`) and its job data (run scripts, run history,
71
+ session state, run locks, worktrees, and logs):
72
72
 
73
73
  ```sh
74
74
  opencode-jobs uninstall --purge
@@ -87,21 +87,21 @@ can also be used from setup scripts and CI.
87
87
 
88
88
  ## Tools
89
89
 
90
- | Tool | What it does |
91
- | ----------------- | ---------------------------------------------------------------------------------------------- |
92
- | `schedule_job` | Create or update a job (cron schedule, prompt or custom command, session mode, guard, timeout) |
93
- | `list_jobs` | List job definitions in the project with next/last run status |
94
- | `get_job` | Show one job: definition, timer state, last runs, recent log tail |
95
- | `run_job` | Fire a job now, through the exact script the timer would run |
96
- | `job_logs` | Tail a job's log (scheduled and manual runs both append) |
97
- | `delete_job` | Delete a job, its units, run script, and session state |
98
- | `enable_project` | Install systemd user units for all jobs in the project and register it |
99
- | `disable_project` | Stop and remove the project's units (jobs and history are kept) |
100
- | `list_projects` | List all projects that have enabled jobs |
90
+ | Tool | What it does |
91
+ | ----------------- | -------------------------------------------------------------------------------------------------------- |
92
+ | `schedule_job` | Create or update a job (cron schedule, prompt or custom command, session mode, guard, worktree, timeout) |
93
+ | `list_jobs` | List job definitions in the project with next/last run status |
94
+ | `get_job` | Show one job: definition, timer state, last runs, recent log tail |
95
+ | `run_job` | Fire a job now, through the exact script the timer would run |
96
+ | `job_logs` | Tail a job's log (scheduled and manual runs both append) |
97
+ | `delete_job` | Delete a job, its units, run script, and session state |
98
+ | `enable_project` | Install systemd user units for all jobs in the project and register it |
99
+ | `disable_project` | Stop and remove the project's units (jobs and history are kept) |
100
+ | `list_projects` | List all projects that have enabled jobs |
101
101
 
102
102
  ## Job definitions
103
103
 
104
- Jobs live in your repo at `.opencode/scheduler/jobs/<slug>.json` — review
104
+ Jobs live in your repo at `.opencode/jobs/<slug>.json` — review
105
105
  them in PRs like any other code:
106
106
 
107
107
  ```json
@@ -114,6 +114,7 @@ them in PRs like any other code:
114
114
  },
115
115
  "session": "compact+last",
116
116
  "guard": "! git diff --quiet",
117
+ "worktree": true,
117
118
  "timeoutSeconds": 1800
118
119
  }
119
120
  ```
@@ -127,6 +128,8 @@ them in PRs like any other code:
127
128
  - `guard` — shell command run first; a non-zero exit skips the run (recorded
128
129
  as `skipped`). Example: `"! git diff --quiet"` runs only when the repo has
129
130
  changes.
131
+ - `worktree` — run in a fresh git worktree instead of the project checkout
132
+ (below).
130
133
  - `timeoutSeconds` — hard limit; systemd stops the run with SIGTERM.
131
134
 
132
135
  ### Session modes
@@ -141,6 +144,48 @@ them in PRs like any other code:
141
144
  Tracked modes (`persist`/`compact`/`compact+last`) self-heal a deleted or
142
145
  stale session by retrying once with a fresh session.
143
146
 
147
+ ### Worktree jobs
148
+
149
+ Set `"worktree": true` (or an object) to run a job in a fresh git worktree
150
+ instead of the project checkout, so scheduled runs never race your editor or
151
+ leave the main tree dirty:
152
+
153
+ ```json
154
+ "worktree": {
155
+ "base": "/srv/worktrees",
156
+ "ref": "origin/main",
157
+ "commitMessage": "nightly sweep: automated fixes"
158
+ }
159
+ ```
160
+
161
+ Each run:
162
+
163
+ 1. takes a per-job lock (so an overlapping timer and `run_job` invocation
164
+ cannot fight over the worktree — the later run is recorded as
165
+ `skipped`), then creates a worktree at `<base>/<slug>` on a new branch
166
+ `opencode-jobs/<slug>/<timestamp>-<pid>`, starting from `ref`
167
+ (default: `HEAD` of the project checkout). A leftover worktree from a
168
+ crashed or timed-out run is committed first (recovery commit on its own
169
+ branch) and then removed — unless the recovery commit fails (for
170
+ example a `pre-commit` hook rejects the changes), in which case the
171
+ stale worktree is kept and the run fails instead of discarding work;
172
+ 2. runs the job with the worktree as the working directory — if the
173
+ project is a subdirectory of a larger repository, the job runs in the
174
+ matching subdirectory of the worktree;
175
+ 3. commits everything (`git add -A`) as `opencode-jobs` with
176
+ `--no-gpg-sign`, then removes the worktree — the branch and its commits
177
+ stay in the repository, and the run record stores `worktreeBranch` and
178
+ `worktreeCommit`.
179
+
180
+ If the safety commit fails, the worktree is kept on disk rather than
181
+ discarded. The default base is
182
+ `~/.local/state/opencode/jobs/worktrees/<scopeId>/<slug>` (respecting
183
+ `XDG_STATE_HOME`); override with `base` (relative paths resolve against the
184
+ project directory, and the base should be dedicated to job worktrees).
185
+ Worktree jobs require `git` and a git repository — a missing repo fails the
186
+ run with a clear record. Branches accumulate per run by design; merge or
187
+ delete them when you no longer need the work.
188
+
144
189
  ## How it works
145
190
 
146
191
  - `enable_project` generates, for each job: a frozen POSIX `run-<slug>.sh`
@@ -149,6 +194,9 @@ stale session by retrying once with a fresh session.
149
194
  - Run scripts work standalone: they append a JSONL record (status, exit
150
195
  code, duration, session id) per run, capture `--format json` output for
151
196
  tracked session modes, and never leave temp files behind.
197
+ - Worktree jobs create a fresh worktree per run, commit all changes to a
198
+ per-run branch, and remove the worktree afterwards — see
199
+ [Worktree jobs](#worktree-jobs).
152
200
  - A global registry tracks enabled projects so `list_projects` and
153
201
  `disable_project` work from any session.
154
202
  - Schedules are cron expressions compiled to systemd `OnCalendar` and
@@ -156,32 +204,45 @@ stale session by retrying once with a fresh session.
156
204
 
157
205
  ## Storage
158
206
 
159
- | What | Where |
160
- | ------------------- | ---------------------------------------------------------------------- |
161
- | Job definitions | `<project>/.opencode/scheduler/jobs/<slug>.json` (git-committed) |
162
- | Run scripts | `~/.config/opencode/scheduler/scopes/<scopeId>/run-<slug>.sh` |
163
- | Run history (JSONL) | `~/.config/opencode/scheduler/runs/<scopeId>/<slug>.jsonl` |
164
- | Session state | `~/.config/opencode/scheduler/sessions/<scopeId>/<slug>.txt` |
165
- | Job logs | `~/.config/opencode/logs/scheduler/<scopeId>/<slug>.log` |
166
- | Project registry | `~/.config/opencode/scheduler/registry.json` |
167
- | systemd units | `~/.config/systemd/user/opencode-sched-<scope>-<slug>.{service,timer}` |
207
+ | What | Where |
208
+ | ------------------- | ----------------------------------------------------------------------------- |
209
+ | Job definitions | `<project>/.opencode/jobs/<slug>.json` (git-committed) |
210
+ | Run scripts | `~/.config/opencode/jobs/scopes/<scopeId>/run-<slug>.sh` |
211
+ | Run history (JSONL) | `~/.config/opencode/jobs/runs/<scopeId>/<slug>.jsonl` |
212
+ | Session state | `~/.config/opencode/jobs/sessions/<scopeId>/<slug>.txt` |
213
+ | Run locks | `~/.config/opencode/jobs/locks/<scopeId>/<slug>.lock` (worktree jobs) |
214
+ | Job worktrees | `~/.local/state/opencode/jobs/worktrees/<scopeId>/<slug>` (removed after run) |
215
+ | Job logs | `~/.config/opencode/logs/jobs/<scopeId>/<slug>.log` |
216
+ | Project registry | `~/.config/opencode/jobs/registry.json` |
217
+ | systemd units | `~/.config/systemd/user/opencode-sched-<scope>-<slug>.{service,timer}` |
168
218
 
169
219
  `scopeId` is a stable hash of the project path, so multiple projects can
170
220
  define jobs without colliding. Because it is path-derived, moving or renaming
171
221
  a project directory orphans its units, history, and registry entry — run
172
222
  `opencode-jobs uninstall --purge` from the old path before moving, or clean
173
- up `~/.config/opencode/scheduler/` (and the unit files) manually afterwards.
223
+ up `~/.config/opencode/jobs/` (and the unit files) manually afterwards.
224
+
225
+ ### Upgrading storage
226
+
227
+ When the plugin or CLI first runs after an upgrade, it moves legacy 0.1.x
228
+ definitions and state to the paths above, preserving the registry, run history,
229
+ session state, logs, locks, and worktrees. Enabled projects are re-synced so
230
+ their existing systemd units use the new paths. Migration is idempotent and
231
+ refuses to overwrite a new path when both old and new data exist; reconcile or
232
+ back up one side and retry.
174
233
 
175
234
  ## Configuration
176
235
 
177
- - `OPENCODE_SCHEDULER_OPENCODE_PATH` — absolute path to the `opencode`
236
+ - `OPENCODE_JOBS_OPENCODE_PATH` — absolute path to the `opencode`
178
237
  binary the run scripts invoke (default: resolved from `PATH`, then
179
238
  `~/.opencode/bin/opencode`, then common install locations).
239
+ `OPENCODE_SCHEDULER_OPENCODE_PATH` remains accepted for 0.1.x compatibility.
180
240
 
181
241
  ## Requirements
182
242
 
183
243
  - Linux with a systemd user session (jobs run via `systemctl --user`)
184
244
  - `curl` (used by `compact`/`compact+last` modes only)
245
+ - `git` and `flock` (from util-linux; used by `worktree` jobs only)
185
246
  - POSIX `sh` (generated scripts are `sh`/`dash`-verified)
186
247
  - The `opencode` CLI available to the timer environment
187
248