opencode-cron-job 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.
@@ -0,0 +1,5 @@
1
+ # 周期任务
2
+
3
+ ## 喝水提醒
4
+ - cron: 0 */2 * * *
5
+ - prompt: 提醒用户喝水,保持健康
@@ -0,0 +1,19 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: lts/*
17
+ cache: npm
18
+ - run: npm ci
19
+ - run: npm run build
@@ -0,0 +1,42 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ permissions:
9
+ contents: write
10
+ packages: write
11
+
12
+ jobs:
13
+ publish-npm:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 20
20
+ registry-url: 'https://registry.npmjs.org'
21
+ - run: npm ci
22
+ - run: npm run build
23
+ - run: npm publish
24
+ env:
25
+ NODE_AUTH_TOKEN: ${{ secrets.NPM }}
26
+
27
+ release:
28
+ needs: publish-npm
29
+ runs-on: ubuntu-latest
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - name: Extract release notes from CHANGELOG.md
33
+ id: changelog
34
+ uses: release-flow/keep-a-changelog-action@v3
35
+ with:
36
+ command: query
37
+ version: ${{ github.ref_name }}
38
+ - name: Create GitHub Release
39
+ uses: softprops/action-gh-release@v2
40
+ with:
41
+ body: ${{ steps.changelog.outputs.release-notes }}
42
+ draft: false
@@ -0,0 +1,3 @@
1
+ {
2
+ "$schema": "https://opencode.ai/config.json"
3
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,50 @@
1
+ # opencode-cron-job
2
+
3
+ ## Build
4
+
5
+ ```bash
6
+ npm run build # tsc → dist/index.js
7
+ npm publish # builds + publishes to npm
8
+ ```
9
+
10
+ After build, copy to plugins dir for local testing:
11
+ ```bash
12
+ cp dist/index.js .opencode/plugins/
13
+ ```
14
+
15
+ ## Architecture
16
+
17
+ Single-file TypeScript OpenCode plugin (`src/index.ts`). Exports `CronPlugin` which is auto-discovered when installed as an npm plugin or placed in `.opencode/plugins/`.
18
+
19
+ ### Flow
20
+
21
+ 1. Plugin initializes → reads `.cron-job/tasks.md` from `directory` (project root)
22
+ 2. Parses `##` sections → extracts `- cron:` and `- prompt:` fields
23
+ 3. Schedules each valid cron expression via `node-cron`
24
+ 4. On timer fire → `client.tui.appendPrompt()` → `client.tui.submitPrompt()`
25
+ 5. Registers 4 tools: `cron_create`, `cron_list`, `cron_run`, `cron_delete`
26
+
27
+ ### Key constraints
28
+
29
+ - NOT an MCP server — runs inside OpenCode process (no HTTP API, no sidecar)
30
+ - Tools are defined with `tool()` helper from `@opencode-ai/plugin`, not raw objects
31
+ - Prompt injection uses `client.tui.*` methods (TUI mode only)
32
+ - Cron uses 5-field expressions by default, 6-field if seconds are specified (`*/30 * * * * *`)
33
+ - Jobs are in-memory and volatile — reload via `cron_reload` after editing `.cron-job/tasks.md`
34
+ - `@opencode-ai/plugin` is a peer dependency provided by OpenCode runtime
35
+ - Plugin auto-updates via npm `@latest` tag are unreliable — bump pinned version in config or clear cache manually
36
+
37
+ ### Published API
38
+
39
+ ```json
40
+ {
41
+ "plugin": ["opencode-cron-job@latest"]
42
+ }
43
+ ```
44
+
45
+ ## Gotchas
46
+
47
+ - `node-cron` v3 accepts 6-field format (seconds included)
48
+ - Plugin runs inside Bun/OpenCode runtime — use `import` syntax, not `require`
49
+ - Build artifact `.opencode/plugins/index.js` is copied from `dist/` — both gitignored
50
+ - `.opencode/package.json` has its own `node_modules/` for local plugin dev — separate from root
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-06-04
9
+
10
+ ### Added
11
+
12
+ - Initial release
13
+ - OpenCode plugin that schedules recurring prompts via `.cron-job/tasks.md`
14
+ - Tools: `cron_create`, `cron_list`, `cron_run`, `cron_delete`
15
+ - Auto-load tasks on plugin initialization
16
+ - Prompt injection via `client.tui.appendPrompt()` + `client.tui.submitPrompt()`
17
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/PUBLISH.md ADDED
@@ -0,0 +1,77 @@
1
+ # 发布指南
2
+
3
+ ## 发布前检查
4
+
5
+ ### 检查 `package.json` 版本号
6
+
7
+ ```json
8
+ {
9
+ "version": "0.1.0"
10
+ }
11
+ ```
12
+
13
+ 确保版本号符合语义化版本规范。
14
+
15
+ ### 检查 CHANGELOG.md 是否需要同步
16
+
17
+ CHANGELOG.md 是 GitHub Release Notes 的数据源:
18
+
19
+ - **每次发版前**:在 `[Unreleased]` 下方写好本次版本的变更说明
20
+ - **格式**:遵循 [Keep a Changelog](https://keepachangelog.com/) 规范,分 `Added` / `Changed` / `Fixed` / `Removed` 等分类
21
+ - **日期**:格式为 `YYYY-MM-DD`
22
+
23
+ ### 检查 README.md 是否需要同步
24
+
25
+ - **功能增减**或**行为变化**时需要同步更新中英文说明
26
+ - 纯 bug 修复不需要更新
27
+
28
+ ### 检查 AGENTS.md 是否需要同步
29
+
30
+ - **架构变更**、**工具变更**(新增/删除工具)时需要更新
31
+ - Bug 修复或内部重构不需要更新
32
+
33
+ ---
34
+
35
+ ## 发布流程
36
+
37
+ ### 1. 更新 CHANGELOG.md(必做)
38
+
39
+ 将 `[Unreleased]` 改为版本号和日期:
40
+
41
+ ```markdown
42
+ ## [0.1.0] - 2026-06-04
43
+
44
+ ### Added
45
+ - 新增功能...
46
+ ```
47
+
48
+ ### 2. 更新版本号
49
+
50
+ 编辑 `package.json` 中的 `version` 字段。
51
+
52
+ ### 3. 提交代码并推送 Tag
53
+
54
+ > ⚠️ 执行前先向用户发送消息确认(已完成以上检查和修改),获得确认后才能执行。
55
+
56
+ ```bash
57
+ git add -A
58
+ git commit -m "release: v0.1.0"
59
+ git tag v0.1.0
60
+ git push origin main --tags
61
+ ```
62
+
63
+ > push 不会自动推送 tags,必须执行 `git push origin --tags` 或 `git push origin v0.1.0`。
64
+
65
+ ### 4. 自动构建与发布
66
+
67
+ 推送 tag 后,GitHub Actions 自动触发 `.github/workflows/release.yml`:
68
+
69
+ 1. **`publish-npm`** — 安装依赖 → 构建 → 发布到 npm
70
+ 2. **`release`** — 自动创建带 Release Notes 的 GitHub Release
71
+
72
+ > **前置条件**:在 GitHub 仓库 Settings → Secrets and variables → Actions 中配置 `NPM` secret,值为 npm Automation Token。
73
+
74
+ ### 5. 检查结果
75
+
76
+ - 确认 npm 包已更新:`npm view opencode-cron-job`
77
+ - 确认 GitHub Release 已创建
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # opencode-cron-job
2
+
3
+ [![npm version](https://img.shields.io/npm/v/opencode-cron-job)](https://www.npmjs.com/package/opencode-cron-job)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+
6
+ **OpenCode plugin** — schedule recurring prompts via a simple markdown file.
7
+ **OpenCode 插件** — 通过简单的 markdown 文件定时执行 prompt。
8
+
9
+ > ⚠️ **Alpha** — This is an initial release and has not been extensively tested. Use at your own risk.
10
+ > ⚠️ **Alpha** — 此为初版,未经充分测试,请谨慎使用。
11
+
12
+ ---
13
+
14
+ ## English
15
+
16
+ ### Overview
17
+
18
+ `opencode-cron-job` is an OpenCode plugin that automatically reads cron tasks from `.cron-job/tasks.md` in your project root and injects prompts into your session on schedule. No MCP server, no HTTP API, no sidecar — runs entirely inside OpenCode.
19
+
20
+ ### How it works
21
+
22
+ 1. Create `.cron-job/tasks.md` in your project
23
+ 2. Write tasks using simple markdown format
24
+ 3. OpenCode loads the plugin automatically → reads the file → starts timers
25
+ 4. When a timer fires, the prompt is injected into your session
26
+
27
+ ### Install
28
+
29
+ ```json
30
+ {
31
+ "plugin": ["opencode-cron-job@latest"]
32
+ }
33
+ ```
34
+
35
+ OpenCode auto-installs npm plugins on startup.
36
+
37
+ ### Usage
38
+
39
+ Edit `.cron-job/tasks.md` to define your cron tasks. The plugin automatically loads them on startup.
40
+
41
+ To manage scheduled tasks, use natural language with the AI agent:
42
+
43
+ - "List all my cron jobs"
44
+ - "Run the code review task now"
45
+ - "Delete the backup task"
46
+ - "Reload the cron tasks from the file"
47
+
48
+ The agent calls the following tools internally — you don't need to use them directly:
49
+
50
+ | Tool | Description |
51
+ |------|-------------|
52
+ | `cron_create` | Create a cron job (name, schedule, prompt) |
53
+ | `cron_list` | List all scheduled tasks |
54
+ | `cron_run` | Run a task immediately |
55
+ | `cron_delete` | Delete a task |
56
+
57
+ ### Task file format
58
+
59
+ Create `.cron-job/tasks.md`:
60
+
61
+ ```markdown
62
+ # Cron Tasks
63
+
64
+ ## Daily Code Review
65
+ - cron: 0 9 * * *
66
+ - prompt: Review all changed files and generate a report
67
+
68
+ ## Hourly Backup
69
+ - cron: 0 */1 * * *
70
+ - prompt: Backup the database
71
+ ```
72
+
73
+ Each section starting with `##` is a task. Supported fields:
74
+ - `cron` — standard 5-field cron expression (supports 6-field with seconds)
75
+ - `prompt` — the prompt text to inject when the timer fires
76
+
77
+ ### Update
78
+
79
+ ```bash
80
+ # Clear plugin cache and restart OpenCode
81
+ rm -rf ~/.cache/opencode/node_modules/opencode-cron-job
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 中文
87
+
88
+ ### 概述
89
+
90
+ `opencode-cron-job` 是一个 OpenCode 插件,自动读取项目根目录下的 `.cron-job/tasks.md` 文件,按 cron 表达式定时向当前会话注入 prompt。无需 MCP 服务、无需 HTTP API、无需 sidecar 进程,完全在 OpenCode 内部运行。
91
+
92
+ ### 工作方式
93
+
94
+ 1. 在项目根目录创建 `.cron-job/tasks.md`
95
+ 2. 用简单的 markdown 格式编写任务
96
+ 3. OpenCode 启动时自动加载插件 → 读取文件 → 启动定时器
97
+ 4. 到时间自动向会话注入 prompt
98
+
99
+ ### 安装
100
+
101
+ ```json
102
+ {
103
+ "plugin": ["opencode-cron-job@latest"]
104
+ }
105
+ ```
106
+
107
+ OpenCode 启动时自动安装 npm 插件。
108
+
109
+ ### 使用方式
110
+
111
+ 编辑 `.cron-job/tasks.md` 定义定时任务,插件启动时自动加载。
112
+
113
+ 要管理任务,直接对 AI 说自然语言即可,例如:
114
+
115
+ - "列出所有周期任务"
116
+ - "立即执行一下代码检查"
117
+ - "把备份任务删掉"
118
+ - "重新加载任务文件"
119
+
120
+ Agent 内部会调用以下工具来完成,你不需要直接使用:
121
+
122
+ | 工具 | 说明 |
123
+ |------|------|
124
+ | `cron_create` | 创建周期任务(名称、cron 表达式、prompt) |
125
+ | `cron_list` | 列出所有任务 |
126
+ | `cron_run` | 立即执行一个任务 |
127
+ | `cron_delete` | 删除任务 |
128
+
129
+ ### 任务文件格式
130
+
131
+ 创建 `.cron-job/tasks.md`:
132
+
133
+ ```markdown
134
+ # 周期任务
135
+
136
+ ## 每日代码检查
137
+ - cron: 0 9 * * *
138
+ - prompt: 检查所有变更文件并生成报告
139
+
140
+ ## 每小时备份
141
+ - cron: 0 */1 * * *
142
+ - prompt: 备份数据库
143
+ ```
144
+
145
+ 每个以 `##` 开头的区块为一个任务。支持字段:
146
+ - `cron` — 标准 5 段 cron 表达式(也支持含秒的 6 段格式)
147
+ - `prompt` — 定时触发时注入的 prompt 内容
148
+
149
+ ### 更新
150
+
151
+ ```bash
152
+ # 清除插件缓存后重启 OpenCode
153
+ rm -rf ~/.cache/opencode/node_modules/opencode-cron-job
154
+ ```
155
+
156
+ ---
157
+
158
+ ## License / 许可证
159
+
160
+ MIT
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "opencode-cron-job",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin - schedule recurring prompts via markdown files",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "exports": {
8
+ ".": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "dependencies": {
15
+ "node-cron": "^3.0.3"
16
+ },
17
+ "peerDependencies": {
18
+ "@opencode-ai/plugin": "*"
19
+ },
20
+ "devDependencies": {
21
+ "@opencode-ai/plugin": "^1.15.5",
22
+ "@types/node": "^22.0.0",
23
+ "@types/node-cron": "^3.0.11",
24
+ "typescript": "^5.8.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
29
+ "license": "MIT"
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,114 @@
1
+ import { readFileSync, existsSync } from "fs"
2
+ import { join } from "path"
3
+ import cron from "node-cron"
4
+ import { tool, type Plugin } from "@opencode-ai/plugin"
5
+
6
+ interface Job {
7
+ id: string
8
+ name: string
9
+ schedule: string
10
+ prompt: string
11
+ task: cron.ScheduledTask | null
12
+ }
13
+
14
+ function parseTasks(content: string): { name: string; schedule: string; prompt: string }[] {
15
+ const tasks: { name: string; schedule: string; prompt: string }[] = []
16
+ const sections = content.split(/^## /m).filter((s) => s.trim())
17
+ for (const section of sections) {
18
+ const lines = section.split("\n")
19
+ const name = lines[0]?.trim()
20
+ if (!name) continue
21
+ let schedule = "", prompt = ""
22
+ for (const line of lines.slice(1)) {
23
+ const t = line.trim()
24
+ if (t.startsWith("- cron:")) schedule = t.slice("- cron:".length).trim()
25
+ else if (t.startsWith("- prompt:")) prompt = t.slice("- prompt:".length).trim()
26
+ }
27
+ if (schedule && prompt) tasks.push({ name, schedule, prompt })
28
+ }
29
+ return tasks
30
+ }
31
+
32
+ let jobs: Job[] = []
33
+ let nextId = 1
34
+
35
+ export const CronPlugin: Plugin = async ({ client, directory }) => {
36
+ const tasksFile = join(directory, ".cron-job", "tasks.md")
37
+
38
+ function fire(job: Job) {
39
+ client.tui.appendPrompt({ body: { text: job.prompt } })
40
+ .then(() => client.tui.submitPrompt())
41
+ .catch(() => {})
42
+ }
43
+
44
+ function schedule(j: Job) {
45
+ if (cron.validate(j.schedule)) {
46
+ j.task = cron.schedule(j.schedule, () => fire(j))
47
+ }
48
+ }
49
+
50
+ // Load from file on startup
51
+ if (existsSync(tasksFile)) {
52
+ const items = parseTasks(readFileSync(tasksFile, "utf-8"))
53
+ for (const item of items) {
54
+ const job: Job = { ...item, id: String(nextId++), task: null }
55
+ schedule(job)
56
+ jobs.push(job)
57
+ }
58
+ }
59
+
60
+ return {
61
+ tool: {
62
+ cron_create: tool({
63
+ description: "Create a new cron job. The job fires on schedule by injecting the prompt into the user's session. Jobs are ephemeral (in-memory) and lost when OpenCode restarts. To persist a job permanently, also add it to .cron-job/tasks.md so it auto-loads on startup.",
64
+ args: {
65
+ name: tool.schema.string().describe("Unique name for this job"),
66
+ schedule: tool.schema.string().describe("Cron expression. 5-field format (min hour dom mon dow), e.g. '0 9 * * *' for daily at 9am. Supports 6-field with seconds: '*/30 * * * * *' for every 30s."),
67
+ prompt: tool.schema.string().describe("The prompt text that gets injected into the session when the job fires. The AI will receive this as a user message and act on it."),
68
+ },
69
+ async execute(args) {
70
+ const job: Job = { id: String(nextId++), name: args.name, schedule: args.schedule, prompt: args.prompt, task: null }
71
+ schedule(job)
72
+ jobs.push(job)
73
+ return `Created job: ${job.name} (ID: ${job.id}, cron: ${job.schedule})`
74
+ },
75
+ }),
76
+
77
+ cron_list: tool({
78
+ description: "List all scheduled cron jobs",
79
+ args: {},
80
+ async execute() {
81
+ if (jobs.length === 0) return "No jobs scheduled."
82
+ return jobs.map((j) => `${j.id} ${j.schedule} ${j.name}`).join("\n")
83
+ },
84
+ }),
85
+
86
+ cron_run: tool({
87
+ description: "Run a job immediately (fire-and-forget). The job fires once right now regardless of its cron schedule. Useful for testing or one-off execution. The job remains scheduled and will continue firing on its normal cron schedule afterwards.",
88
+ args: {
89
+ jobId: tool.schema.string().describe("ID of the job to run. Get it from cron_list."),
90
+ },
91
+ async execute(args) {
92
+ const job = jobs.find((j) => j.id === args.jobId)
93
+ if (!job) return `Job ${args.jobId} not found.`
94
+ fire(job)
95
+ return `${job.name}: triggered`
96
+ },
97
+ }),
98
+
99
+ cron_delete: tool({
100
+ description: "Delete a cron job. Stops the timer and removes it from memory. This does not modify .cron-job/tasks.md — if the job was defined there, it will reappear after OpenCode restarts.",
101
+ args: {
102
+ jobId: tool.schema.string().describe("ID of the job to delete. Get it from cron_list."),
103
+ },
104
+ async execute(args) {
105
+ const idx = jobs.findIndex((j) => j.id === args.jobId)
106
+ if (idx === -1) return `Job ${args.jobId} not found.`
107
+ const [job] = jobs.splice(idx, 1)
108
+ job.task?.stop()
109
+ return `${job.name}: deleted`
110
+ },
111
+ }),
112
+ },
113
+ }
114
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "declaration": true,
13
+ "sourceMap": true
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }
18
+