opencode-routines 0.1.6 → 0.1.8

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
@@ -34,16 +34,31 @@ Add the server plugin to your OpenCode config (`~/.config/opencode/opencode.json
34
34
 
35
35
  OpenCode installs the package from npm on next start. Use `@latest` if you want new versions on restart, or pin a version such as `"opencode-routines@0.1.1"`.
36
36
 
37
- Optional TUI slash commands are published as a companion package:
37
+ That single entry is the whole packaged experience: on load, the server plugin also installs slash commands (`/loop`, `/loops`, `/stop-loop`, `/schedule-standalone-session`) as managed OpenCode custom command files under `~/.config/opencode/commands/`. These work in **both** the terminal TUI and OpenCode Desktop. The install is idempotent and marker-guarded: only files containing the `managed-by: opencode-routines` marker are ever created or updated, so a user-edited `loop.md` (marker removed) is never touched. Restart OpenCode once after the first install for the commands to appear. Opt out with plugin options:
38
38
 
39
39
  ```jsonc
40
40
  {
41
- "$schema": "https://opencode.ai/config.json",
42
- "plugin": ["opencode-routines@latest", "opencode-routines-tui@latest"]
41
+ "plugin": [["opencode-routines@latest", { "commands": false }]]
42
+ }
43
+ ```
44
+
45
+ ### Optional native TUI dialogs
46
+
47
+ `opencode-routines-tui` is an optional companion package that replaces the prompt-based commands with native TUI dialogs (interactive loop list, prompt dialogs). It is a **TUI plugin**, which OpenCode loads from `tui.json` — NOT from `opencode.json`'s `plugin` array (that array is for server plugins only; the server loader will reject it with `must default export an object with server()`).
48
+
49
+ Add it to `~/.config/opencode/tui.json` (or a project-level `.opencode/tui.json`, or a custom path via the `OPENCODE_TUI_CONFIG` env var):
50
+
51
+ ```jsonc
52
+ // ~/.config/opencode/tui.json
53
+ {
54
+ "$schema": "https://opencode.ai/tui.json",
55
+ "plugin": ["opencode-routines-tui@latest"]
43
56
  }
44
57
  ```
45
58
 
46
- `opencode-routines-tui` is separate so OpenCode can explicitly install and load the TUI plugin entrypoint. The root package still ships a `./tui` export for advanced/manual loaders, but managed installs should use the companion package.
59
+ `opencode-routines-tui` is separate so the terminal TUI can install and load the TUI plugin entrypoint (`exports["./tui"]`). The root package still ships a `./tui` export for advanced/manual loaders, but installs should use the companion package via `tui.json`.
60
+
61
+ The TUI plugin only works in the **terminal TUI** (`opencode` in a terminal); OpenCode Desktop builds its slash list from a different command registry and does not load `tui.json` TUI plugins. Note that installing the TUI plugin alongside the managed command files shows both variants of each slash command in the terminal TUI — if that bothers you, disable the managed commands with `{ "commands": false }` (above) and rely on the TUI plugin there.
47
62
 
48
63
  ## What it provides
49
64
 
@@ -131,7 +146,7 @@ Legacy compatibility tools from `opencode-scheduler` are still present: `schedul
131
146
 
132
147
  ## TUI slash commands
133
148
 
134
- Available when `opencode-routines-tui` is installed:
149
+ Available in the terminal TUI when `opencode-routines-tui` is installed via `tui.json` (see Install above):
135
150
 
136
151
  | Command | Meaning |
137
152
  |---|---|
@@ -165,7 +180,7 @@ Standalone schedule backends:
165
180
 
166
181
  - Requires OpenCode `1.17.3` or newer.
167
182
  - OpenCode loads config once at startup. Restart OpenCode after changing plugin configuration.
168
- - `opencode-routines-tui` requires OpenCode's TUI plugin runtime. If your OpenCode build does not support TUI plugins, install only `opencode-routines`.
183
+ - `opencode-routines-tui` requires OpenCode's terminal TUI plugin runtime and is loaded via `tui.json`, not `opencode.json`. OpenCode Desktop does not load TUI plugins yet — there, install only `opencode-routines`.
169
184
  - `CronCreate({ durable: true })` is accepted for Claude Code compatibility but currently behaves as session-only.
170
185
 
171
186
  ## Debugging
package/dist/index.js CHANGED
@@ -12335,7 +12335,7 @@ function tool(input) {
12335
12335
  }
12336
12336
  tool.schema = exports_external;
12337
12337
  // src/index.ts
12338
- import { createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync, unlinkSync } from "fs";
12338
+ import { createWriteStream, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, unlinkSync } from "fs";
12339
12339
  import { basename, dirname, join, resolve as resolvePath } from "path";
12340
12340
  import { homedir, platform } from "os";
12341
12341
  import { execFileSync, execSync, spawn } from "child_process";
@@ -14497,8 +14497,91 @@ function getJobLogs(job, options) {
14497
14497
  return null;
14498
14498
  }
14499
14499
  }
14500
- var SchedulerPlugin = async (input) => {
14500
+ var MANAGED_COMMAND_MARKER = "managed-by: opencode-routines";
14501
+ var MANAGED_COMMANDS = {
14502
+ "loop.md": `---
14503
+ description: Start a same-session loop (opencode-routines)
14504
+ ---
14505
+ <!-- ${MANAGED_COMMAND_MARKER} -->
14506
+
14507
+ Use the LoopCreate tool from opencode-routines to start a same-session loop.
14508
+
14509
+ Arguments: $ARGUMENTS
14510
+
14511
+ Rules:
14512
+ - If the first argument looks like an interval (30s, 5m, 1h), pass it as \`interval\` and the rest as \`prompt\`.
14513
+ - Otherwise start a dynamic loop with the full arguments as \`prompt\`.
14514
+ - If no arguments were provided, ask what prompt to loop.
14515
+ - Confirm the created loop id and mode in one short sentence.
14516
+ `,
14517
+ "loops.md": `---
14518
+ description: List active same-session loops (opencode-routines)
14519
+ ---
14520
+ <!-- ${MANAGED_COMMAND_MARKER} -->
14521
+
14522
+ Call the LoopList tool from opencode-routines and show the active loops as a short table (id, mode/interval, fires, prompt). If there are none, say so in one sentence.
14523
+ `,
14524
+ "stop-loop.md": `---
14525
+ description: Stop a same-session loop (opencode-routines)
14526
+ ---
14527
+ <!-- ${MANAGED_COMMAND_MARKER} -->
14528
+
14529
+ Stop a same-session loop using opencode-routines tools.
14530
+
14531
+ Arguments: $ARGUMENTS
14532
+
14533
+ Rules:
14534
+ - If an argument matches a loop id, call LoopDelete with it.
14535
+ - Otherwise call LoopList first; if exactly one loop is active, stop it; if several, list them and ask which to stop.
14536
+ `,
14537
+ "schedule-standalone-session.md": `---
14538
+ description: Create a durable standalone scheduled opencode run (opencode-routines)
14539
+ ---
14540
+ <!-- ${MANAGED_COMMAND_MARKER} -->
14541
+
14542
+ Create a durable OS-backed standalone schedule using the ScheduleCreate tool from opencode-routines.
14543
+
14544
+ Arguments: $ARGUMENTS
14545
+
14546
+ Rules:
14547
+ - Derive a short job name and a 5-field cron expression from the arguments; ask if the schedule is ambiguous.
14548
+ - Use the remaining text as the prompt for the standalone run.
14549
+ - After creating, report the job name, cron, and next steps (ScheduleList / ScheduleLogs) in two sentences max.
14550
+ `
14551
+ };
14552
+ function managedCommandsDir() {
14553
+ const xdg = process.env.XDG_CONFIG_HOME?.trim();
14554
+ const base = xdg ? xdg : join(homedir(), ".config");
14555
+ return join(base, "opencode", "commands");
14556
+ }
14557
+ function syncManagedCommands() {
14558
+ const dir = managedCommandsDir();
14559
+ ensureDir(dir);
14560
+ for (const [filename, content] of Object.entries(MANAGED_COMMANDS)) {
14561
+ const dest = join(dir, filename);
14562
+ const desired = `${content.trimEnd()}
14563
+ `;
14564
+ try {
14565
+ if (existsSync(dest)) {
14566
+ const current = readFileSync(dest, "utf-8");
14567
+ if (!current.includes(MANAGED_COMMAND_MARKER))
14568
+ continue;
14569
+ if (current === desired)
14570
+ continue;
14571
+ }
14572
+ const staged = `${dest}.opencode-routines.tmp`;
14573
+ writeFileSync(staged, desired);
14574
+ renameSync(staged, dest);
14575
+ } catch (error45) {
14576
+ console.error(`[opencode-routines] failed to install command ${filename}:`, error45);
14577
+ }
14578
+ }
14579
+ }
14580
+ var SchedulerPlugin = async (input, options) => {
14501
14581
  const client = input?.client;
14582
+ if (options?.commands !== false) {
14583
+ syncManagedCommands();
14584
+ }
14502
14585
  async function executeTool(name, args, context) {
14503
14586
  const plugin = await SchedulerPlugin(input);
14504
14587
  const definition = plugin.tool?.[name];
package/dist/tui.js CHANGED
@@ -155,48 +155,11 @@ function showStandaloneSchedulesHelp(api) {
155
155
  message: "Use the ScheduleCreate tool (or natural language like 'create a standalone scheduled run...') to create durable OS-backed standalone sessions. The ambiguous /schedule command is intentionally not registered."
156
156
  }));
157
157
  }
158
- function legacyCommands(api) {
159
- return [
160
- {
161
- title: "Start same-session loop",
162
- value: "routines.loop",
163
- description: "Start a same-session loop. Fixed: 5m <prompt>; dynamic: <prompt>.",
164
- category: "Scheduler",
165
- slash: { name: "loop" },
166
- onSelect: () => openLoopPrompt(api)
167
- },
168
- {
169
- title: "List active loops",
170
- value: "routines.loops",
171
- description: "List and stop active same-session loops.",
172
- category: "Scheduler",
173
- slash: { name: "loops" },
174
- onSelect: () => showLoops(api)
175
- },
176
- {
177
- title: "Stop a same-session loop",
178
- value: "routines.stop-loop",
179
- description: "List active loops and select one to stop.",
180
- category: "Scheduler",
181
- slash: { name: "stop-loop" },
182
- onSelect: () => showLoops(api)
183
- },
184
- {
185
- title: "Create standalone scheduled session",
186
- value: "routines.schedule-standalone-session",
187
- description: "Show help for durable OS-backed standalone schedules.",
188
- category: "Scheduler",
189
- slash: { name: "schedule-standalone-session" },
190
- onSelect: () => showStandaloneSchedulesHelp(api)
191
- }
192
- ];
193
- }
194
158
  var tui = async (api) => {
195
159
  api.lifecycle.onDispose(() => {
196
160
  for (const id of [...loops.keys()])
197
161
  stopLoop(id);
198
162
  });
199
- api.command?.register(() => legacyCommands(api));
200
163
  api.keymap.registerLayer({
201
164
  commands: [
202
165
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-routines",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "OpenCode routines: same-session loops, cron prompts, and host-backed standalone scheduled agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",