cron-claude 1.0.13 → 2.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 +103 -214
- package/dist/cli.js +82 -10
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +31 -16
- package/dist/config.js +43 -2
- package/dist/config.js.map +1 -1
- package/dist/mcp-server.js +93 -11
- package/dist/mcp-server.js.map +1 -1
- package/package.json +5 -10
- package/install.sh +0 -59
- package/postinstall.js +0 -13
package/README.md
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
# cron-claude
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://www.npmjs.com/package/cron-claude)
|
|
5
|
-
[](https://opensource.org/licenses/MIT)
|
|
3
|
+
> Schedule tasks for Claude. Claude runs them — with full tool access.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
## The problem with daemon-based cron
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
The old approach spawned `claude --print` on a schedule. `--print` disables MCP tools. No Slack, no bash, no files — just text in, text out. You had to use `--dangerously-skip-permissions` just to get anything done.
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
## How v2 works
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
**Claude is the daemon.** You paste one line into a Claude Code session and it loops forever:
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
1. Every 60s: call `get_due_jobs` → atomically claims due jobs (disk state = source of truth)
|
|
14
|
+
2. For each due job: spawn a **subagent** with the job's prompt
|
|
15
|
+
3. Subagent does the work with full tool access (Slack, bash, files, any MCP)
|
|
16
|
+
4. Subagent calls `mark_job_run` → releases the lock, writes a log
|
|
17
|
+
5. Subagents run in parallel — no blocking
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
No background daemon. No LaunchAgent. No permission dialogs. Survives Claude session restarts (disk state persists).
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
@@ -24,144 +26,125 @@ Use `cron-claude` when you want scheduled Claude automation without the full Ope
|
|
|
24
26
|
npm install -g cron-claude
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
On macOS, the LaunchAgent is automatically installed so the daemon starts on login. On other platforms, start the daemon manually with `cron-claude-daemon` or use your system's process manager.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
29
|
## Quick Start
|
|
32
30
|
|
|
31
|
+
**1. Install the MCP server into Claude Code:**
|
|
33
32
|
```bash
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
cron-claude mcp install
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**2. Add a job:**
|
|
37
|
+
```bash
|
|
38
|
+
cron-claude add \
|
|
39
|
+
--id morning-standup \
|
|
40
|
+
--schedule "0 9 * * 1-5" \
|
|
41
|
+
--prompt "Post a morning standup summary to the #standup Slack channel. Include what's in my calendar today."
|
|
42
|
+
```
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
cron-claude list
|
|
44
|
+
**3. Start the loop in Claude Code:**
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
Paste this into any Claude Code session (or use the `cron-loop` prompt from the MCP prompt picker):
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Call start_loop to begin.
|
|
42
50
|
```
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
That's it. Claude will read its operating instructions from `start_loop` and begin polling every 60 seconds.
|
|
45
53
|
|
|
46
54
|
---
|
|
47
55
|
|
|
48
|
-
## CLI
|
|
56
|
+
## CLI Commands
|
|
57
|
+
|
|
58
|
+
| Command | Description |
|
|
59
|
+
|---------|-------------|
|
|
60
|
+
| `cron-claude list` | List all jobs with status, last run, schedule |
|
|
61
|
+
| `cron-claude add --id <id> --schedule <cron> --prompt <text>` | Add a new job |
|
|
62
|
+
| `cron-claude remove <id>` | Remove a job |
|
|
63
|
+
| `cron-claude enable <id>` | Enable a job |
|
|
64
|
+
| `cron-claude disable <id>` | Disable a job |
|
|
65
|
+
| `cron-claude status` | Summary of job counts |
|
|
66
|
+
| `cron-claude agent-loop` | Print setup instructions |
|
|
67
|
+
| `cron-claude mcp install` | Install MCP into Claude Code |
|
|
68
|
+
| `cron-claude stop` | Stop and remove the legacy v1 LaunchAgent daemon |
|
|
69
|
+
|
|
70
|
+
### `add` options
|
|
71
|
+
|
|
72
|
+
| Flag | Default | Description |
|
|
73
|
+
|------|---------|-------------|
|
|
74
|
+
| `--id` | required | Unique job identifier |
|
|
75
|
+
| `--schedule` | required | Cron expression (e.g. `"0 9 * * 1-5"`) |
|
|
76
|
+
| `--prompt` | required | What to tell Claude to do |
|
|
77
|
+
| `--model` | `sonnet` | Claude model for the subagent |
|
|
78
|
+
| `--disabled` | — | Create the job in disabled state |
|
|
49
79
|
|
|
50
|
-
|
|
51
|
-
List all scheduled jobs.
|
|
80
|
+
---
|
|
52
81
|
|
|
53
|
-
|
|
54
|
-
ID Schedule Model Enabled Prompt
|
|
55
|
-
--------------------------------------------------------------------------------
|
|
56
|
-
daily-summary 0 14 * * * sonnet true Summarize my day...
|
|
57
|
-
weekly-review 0 9 * * 1 opus true Weekly project review
|
|
58
|
-
```
|
|
82
|
+
## MCP Tools
|
|
59
83
|
|
|
60
|
-
|
|
61
|
-
Add a new scheduled job.
|
|
84
|
+
The `cron-claude-mcp` server exposes these tools to Claude:
|
|
62
85
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
| Tool | Description |
|
|
87
|
+
|------|-------------|
|
|
88
|
+
| `start_loop` | Call once to get operating instructions (entry point) |
|
|
89
|
+
| `get_due_jobs` | Returns due jobs, atomically marks them `running` |
|
|
90
|
+
| `mark_job_run` | Clears running state, writes log. Call after each job. |
|
|
91
|
+
| `add_cron` | Add a new job |
|
|
92
|
+
| `list_crons` | List all jobs |
|
|
93
|
+
| `remove_cron` | Remove a job by ID |
|
|
94
|
+
| `enable_cron` | Enable a job |
|
|
95
|
+
| `disable_cron` | Disable a job |
|
|
96
|
+
| `get_status` | Get job counts (total, enabled, disabled, running) |
|
|
71
97
|
|
|
72
|
-
|
|
73
|
-
| Flag | Required | Default | Description |
|
|
74
|
-
|------|----------|---------|-------------|
|
|
75
|
-
| `--id` | ✅ | — | Unique job identifier |
|
|
76
|
-
| `--schedule` | ✅ | — | Cron expression |
|
|
77
|
-
| `--prompt` | ✅ | — | Prompt to send Claude |
|
|
78
|
-
| `--model` | ❌ | `sonnet` | Claude model (`sonnet`, `opus`, `haiku`) |
|
|
79
|
-
| `--disabled` | ❌ | — | Create job in disabled state |
|
|
98
|
+
### MCP Prompts
|
|
80
99
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
cron-claude add --id daily-summary --schedule "0 14 * * *" --prompt "Summarize my day"
|
|
85
|
-
|
|
86
|
-
# Weekly Monday 9 AM with opus
|
|
87
|
-
cron-claude add --id weekly-review --schedule "0 9 * * 1" --prompt "Weekly project review" --model opus
|
|
100
|
+
| Prompt | Description |
|
|
101
|
+
|--------|-------------|
|
|
102
|
+
| `cron-loop` | Tells Claude to call `start_loop` — use from the prompt picker |
|
|
88
103
|
|
|
89
|
-
|
|
90
|
-
cron-claude add --id heartbeat --schedule "*/30 * * * *" --prompt "Check system health" --disabled
|
|
91
|
-
```
|
|
104
|
+
---
|
|
92
105
|
|
|
93
|
-
|
|
94
|
-
Remove a job permanently.
|
|
106
|
+
## Atomic Job Locking
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
cron-claude remove daily-summary
|
|
98
|
-
```
|
|
108
|
+
`get_due_jobs` atomically claims jobs before returning them:
|
|
99
109
|
|
|
100
|
-
|
|
101
|
-
|
|
110
|
+
1. Finds jobs where: `enabled=true`, `status=idle`, `lastRunAt` was >55s ago, cron schedule fired in last 60s
|
|
111
|
+
2. Sets `status=running`, `startedAt=now` on disk **before** returning the list
|
|
112
|
+
3. A second call to `get_due_jobs` sees `status=running` and skips those jobs
|
|
102
113
|
|
|
103
|
-
|
|
104
|
-
cron-claude disable weekly-review
|
|
105
|
-
cron-claude enable weekly-review
|
|
106
|
-
```
|
|
114
|
+
This prevents duplicate execution if two loop iterations somehow overlap.
|
|
107
115
|
|
|
108
|
-
|
|
109
|
-
Show a summary of all jobs.
|
|
116
|
+
`mark_job_run` resets `status=idle`, records `lastRunAt`, and writes a log to `~/.cron-claude/logs/`.
|
|
110
117
|
|
|
118
|
+
If a job is stuck `running` for >10 minutes (subagent crashed), call:
|
|
111
119
|
```
|
|
112
|
-
|
|
113
|
-
Total jobs: 3
|
|
114
|
-
Enabled: 2
|
|
115
|
-
Disabled: 1
|
|
120
|
+
mark_job_run with id="<job-id>", error="timeout"
|
|
116
121
|
```
|
|
117
122
|
|
|
118
|
-
### `cron-claude --version`
|
|
119
|
-
Show the installed version.
|
|
120
|
-
|
|
121
123
|
---
|
|
122
124
|
|
|
123
|
-
##
|
|
124
|
-
|
|
125
|
-
`cron-claude` ships with an MCP server (`cron-claude-mcp`) that lets you manage your cron jobs from directly inside Claude Code or any MCP-compatible client.
|
|
125
|
+
## Logs
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
Logs are written to `~/.cron-claude/logs/` as `<job-id>_<timestamp>.log`:
|
|
128
128
|
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"mcpServers": {
|
|
132
|
-
"cron-claude": {
|
|
133
|
-
"command": "cron-claude-mcp"
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
129
|
```
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
| Tool | Description | Parameters |
|
|
142
|
-
|------|-------------|------------|
|
|
143
|
-
| `add_cron` | Add a new scheduled job | `id`, `schedule`, `prompt`, `model?`, `disabled?` |
|
|
144
|
-
| `list_crons` | List all jobs | — |
|
|
145
|
-
| `remove_cron` | Remove a job | `id` |
|
|
146
|
-
| `enable_cron` | Enable a job | `id` |
|
|
147
|
-
| `disable_cron` | Disable a job | `id` |
|
|
148
|
-
| `get_status` | Get job counts (total/enabled/disabled) | — |
|
|
149
|
-
|
|
130
|
+
Job: morning-standup
|
|
131
|
+
Time: 2025-01-15T09:00:03.241Z
|
|
132
|
+
Status: success
|
|
150
133
|
---
|
|
134
|
+
Posted standup to #standup. Calendar shows: 10am design review, 2pm 1:1 with Alex.
|
|
135
|
+
```
|
|
151
136
|
|
|
152
|
-
|
|
137
|
+
---
|
|
153
138
|
|
|
154
|
-
|
|
139
|
+
## Why Not `--print` Mode?
|
|
155
140
|
|
|
156
|
-
|
|
157
|
-
claude plugin marketplace add github:tygiacalone/cron-claude
|
|
158
|
-
```
|
|
141
|
+
`claude --print` disables all MCP tools. You can't use Slack MCP, bash, file access, or any external integrations. The only way around it was `--dangerously-skip-permissions`, which bypasses all permission checks.
|
|
159
142
|
|
|
160
|
-
|
|
143
|
+
In agent loop mode, subagents run inside your existing Claude session with all your approved MCP servers already connected. No flags needed.
|
|
161
144
|
|
|
162
145
|
---
|
|
163
146
|
|
|
164
|
-
##
|
|
147
|
+
## Configuration
|
|
165
148
|
|
|
166
149
|
Jobs are stored in `~/.cron-claude/jobs.json`:
|
|
167
150
|
|
|
@@ -169,127 +152,33 @@ Jobs are stored in `~/.cron-claude/jobs.json`:
|
|
|
169
152
|
{
|
|
170
153
|
"jobs": [
|
|
171
154
|
{
|
|
172
|
-
"id": "
|
|
173
|
-
"schedule": "0
|
|
174
|
-
"prompt": "
|
|
155
|
+
"id": "morning-standup",
|
|
156
|
+
"schedule": "0 9 * * 1-5",
|
|
157
|
+
"prompt": "Post a morning standup to #standup",
|
|
175
158
|
"model": "sonnet",
|
|
176
|
-
"enabled": true
|
|
159
|
+
"enabled": true,
|
|
160
|
+
"status": "idle",
|
|
161
|
+
"lastRunAt": 1736931603241,
|
|
162
|
+
"startedAt": null
|
|
177
163
|
}
|
|
178
164
|
]
|
|
179
165
|
}
|
|
180
166
|
```
|
|
181
167
|
|
|
182
|
-
You can edit this file directly — the daemon watches for changes and reloads automatically.
|
|
183
|
-
|
|
184
|
-
### Cron Syntax
|
|
185
|
-
|
|
186
|
-
Standard 5-field cron: `minute hour day-of-month month day-of-week`
|
|
187
|
-
|
|
188
|
-
| Expression | Meaning |
|
|
189
|
-
|------------|---------|
|
|
190
|
-
| `0 14 * * *` | Every day at 2:00 PM |
|
|
191
|
-
| `*/30 * * * *` | Every 30 minutes |
|
|
192
|
-
| `0 9 * * 1` | Every Monday at 9:00 AM |
|
|
193
|
-
| `0 0 1 * *` | First of each month at midnight |
|
|
194
|
-
| `0 8-18 * * 1-5` | Every hour, 8 AM–6 PM, weekdays |
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Logs
|
|
199
|
-
|
|
200
|
-
Job output is saved to `~/.cron-claude/logs/` with timestamped filenames:
|
|
201
|
-
|
|
202
|
-
```
|
|
203
|
-
~/.cron-claude/logs/
|
|
204
|
-
daily-summary_2026-02-19T14-00-00-000Z.log
|
|
205
|
-
weekly-review_2026-02-17T09-00-00-000Z.log
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Each log includes the job ID, timestamp, schedule, model, status, and Claude's output.
|
|
209
|
-
|
|
210
168
|
---
|
|
211
169
|
|
|
212
|
-
##
|
|
213
|
-
|
|
214
|
-
```
|
|
215
|
-
┌─────────────────────────────────────────────────┐
|
|
216
|
-
│ cron-claude daemon │
|
|
217
|
-
│ │
|
|
218
|
-
│ ┌─────────────┐ ┌──────────────────────┐ │
|
|
219
|
-
│ │ Scheduler │───▶│ claude --print ... │ │
|
|
220
|
-
│ │ (node-cron) │ │ (Claude CLI exec) │ │
|
|
221
|
-
│ └─────────────┘ └──────────────────────┘ │
|
|
222
|
-
│ ▲ │ │
|
|
223
|
-
│ │ ▼ │
|
|
224
|
-
│ ┌─────────────┐ ┌──────────────────────┐ │
|
|
225
|
-
│ │ jobs.json │ │ ~/.cron-claude/logs/ │ │
|
|
226
|
-
│ │ (config) │ │ (output logs) │ │
|
|
227
|
-
│ └─────────────┘ └──────────────────────┘ │
|
|
228
|
-
└─────────────────────────────────────────────────┘
|
|
229
|
-
▲
|
|
230
|
-
│ reads/writes
|
|
231
|
-
┌─────────────┐ ┌─────────────┐
|
|
232
|
-
│ cron-claude │ │ cron-claude │
|
|
233
|
-
│ (CLI) │ │ -mcp (MCP) │
|
|
234
|
-
└─────────────┘ └─────────────┘
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Components:**
|
|
238
|
-
- **Daemon** (`src/daemon.ts`): Loads jobs from config, schedules them via `node-cron`, watches for file changes
|
|
239
|
-
- **Scheduler** (`src/scheduler.ts`): Wraps `node-cron`, executes jobs via `execFile(claude, ...)`, logs output
|
|
240
|
-
- **Config** (`src/config.ts`): Reads/writes `~/.cron-claude/jobs.json`, validates with Zod
|
|
241
|
-
- **CLI** (`src/cli.ts`): Commander-based CLI for managing jobs
|
|
242
|
-
- **MCP Server** (`src/mcp-server.ts`): Exposes all job management as MCP tools
|
|
243
|
-
|
|
244
|
-
---
|
|
170
|
+
## Upgrading from v1
|
|
245
171
|
|
|
246
|
-
|
|
172
|
+
If you ran v1 (daemon mode), stop the LaunchAgent:
|
|
247
173
|
|
|
248
174
|
```bash
|
|
249
|
-
|
|
250
|
-
cd cron-claude
|
|
251
|
-
npm install
|
|
252
|
-
|
|
253
|
-
# Build
|
|
254
|
-
npm run build
|
|
255
|
-
|
|
256
|
-
# Run tests with coverage
|
|
257
|
-
npm test
|
|
258
|
-
|
|
259
|
-
# Run CLI in dev mode (tsx, no build needed)
|
|
260
|
-
npm run cli -- list
|
|
261
|
-
npm run cli -- add --id test --schedule "* * * * *" --prompt "hello"
|
|
262
|
-
|
|
263
|
-
# Run daemon in dev mode
|
|
264
|
-
npm run dev
|
|
175
|
+
cron-claude stop
|
|
265
176
|
```
|
|
266
177
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
```
|
|
270
|
-
src/
|
|
271
|
-
cli.ts # Commander CLI
|
|
272
|
-
config.ts # Config read/write with Zod validation
|
|
273
|
-
daemon.ts # Main daemon (loads config, watches for changes)
|
|
274
|
-
scheduler.ts # node-cron wrapper + Claude CLI executor
|
|
275
|
-
mcp-server.ts # MCP server (StdioServerTransport)
|
|
276
|
-
plugin/ # Claude Code plugin descriptor
|
|
277
|
-
tests/
|
|
278
|
-
cli.test.ts
|
|
279
|
-
config.test.ts
|
|
280
|
-
daemon.test.ts
|
|
281
|
-
scheduler.test.ts
|
|
282
|
-
mcp-server.test.ts
|
|
283
|
-
.github/workflows/ci.yml
|
|
284
|
-
.claude-plugin/
|
|
285
|
-
marketplace.json
|
|
286
|
-
plugin.json
|
|
287
|
-
install.sh # macOS LaunchAgent installer
|
|
288
|
-
postinstall.js # npm postinstall hook (auto-runs install.sh on macOS global install)
|
|
289
|
-
```
|
|
178
|
+
Your jobs in `~/.cron-claude/jobs.json` are migrated automatically — v2 just ignores the old `headless`, `dangerouslySkipPermissions`, and `executor` fields (they'll be stripped on next write).
|
|
290
179
|
|
|
291
180
|
---
|
|
292
181
|
|
|
293
182
|
## License
|
|
294
183
|
|
|
295
|
-
MIT
|
|
184
|
+
MIT
|
package/dist/cli.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'fs';
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
import { homedir } from 'os';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
7
8
|
import { addJob, removeJob, listJobs, toggleJob, JobSchema } from './config.js';
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
9
10
|
const { version } = require('../package.json');
|
|
10
11
|
const program = new Command();
|
|
11
12
|
program
|
|
12
13
|
.name('cron-claude')
|
|
13
|
-
.description('Manage scheduled Claude
|
|
14
|
+
.description('Manage scheduled Claude jobs (agent loop mode)')
|
|
14
15
|
.version(version);
|
|
15
16
|
program
|
|
16
17
|
.command('list')
|
|
@@ -21,10 +22,18 @@ program
|
|
|
21
22
|
console.log('No jobs configured.');
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
|
-
console.log(`\n${'ID'.padEnd(20)} ${'Schedule'.padEnd(15)} ${'Model'.padEnd(10)} ${'Enabled'.padEnd(8)} ${'
|
|
25
|
-
console.log('-'.repeat(
|
|
25
|
+
console.log(`\n${'ID'.padEnd(20)} ${'Schedule'.padEnd(15)} ${'Model'.padEnd(10)} ${'Enabled'.padEnd(8)} ${'Status'.padEnd(18)} ${'Last Run'.padEnd(22)} Prompt`);
|
|
26
|
+
console.log('-'.repeat(115));
|
|
26
27
|
for (const job of jobs) {
|
|
27
|
-
|
|
28
|
+
const lastRun = job.lastRunAt
|
|
29
|
+
? new Date(job.lastRunAt).toLocaleString()
|
|
30
|
+
: 'never';
|
|
31
|
+
let statusStr = job.status ?? 'idle';
|
|
32
|
+
if (job.status === 'running' && job.startedAt) {
|
|
33
|
+
const mins = Math.floor((Date.now() - job.startedAt) / 60000);
|
|
34
|
+
statusStr = `running (${mins}min ago)`;
|
|
35
|
+
}
|
|
36
|
+
console.log(`${job.id.padEnd(20)} ${job.schedule.padEnd(15)} ${job.model.padEnd(10)} ${String(job.enabled).padEnd(8)} ${statusStr.padEnd(18)} ${lastRun.padEnd(22)} ${job.prompt.slice(0, 40)}`);
|
|
28
37
|
}
|
|
29
38
|
console.log('');
|
|
30
39
|
});
|
|
@@ -36,8 +45,6 @@ program
|
|
|
36
45
|
.requiredOption('--prompt <prompt>', 'Prompt to send to Claude')
|
|
37
46
|
.option('--model <model>', 'Claude model to use', 'sonnet')
|
|
38
47
|
.option('--disabled', 'Create job in disabled state')
|
|
39
|
-
.option('--no-headless', 'Run as full agent (tool use, bash, files) instead of --print mode')
|
|
40
|
-
.option('--dangerously-skip-permissions', 'Pass --dangerously-skip-permissions to claude (skips all permission prompts)')
|
|
41
48
|
.action((opts) => {
|
|
42
49
|
try {
|
|
43
50
|
const job = JobSchema.parse({
|
|
@@ -46,8 +53,6 @@ program
|
|
|
46
53
|
prompt: opts.prompt,
|
|
47
54
|
model: opts.model,
|
|
48
55
|
enabled: !opts.disabled,
|
|
49
|
-
headless: opts.headless,
|
|
50
|
-
dangerouslySkipPermissions: opts.dangerouslySkipPermissions ?? false,
|
|
51
56
|
});
|
|
52
57
|
addJob(job);
|
|
53
58
|
console.log(`Job "${job.id}" added successfully.`);
|
|
@@ -98,7 +103,7 @@ program
|
|
|
98
103
|
});
|
|
99
104
|
program
|
|
100
105
|
.command('status')
|
|
101
|
-
.description('Show
|
|
106
|
+
.description('Show job status summary')
|
|
102
107
|
.action(() => {
|
|
103
108
|
const jobs = listJobs();
|
|
104
109
|
const enabled = jobs.filter((j) => j.enabled).length;
|
|
@@ -108,6 +113,73 @@ program
|
|
|
108
113
|
console.log(` Enabled: ${enabled}`);
|
|
109
114
|
console.log(` Disabled: ${disabled}\n`);
|
|
110
115
|
});
|
|
116
|
+
program
|
|
117
|
+
.command('stop')
|
|
118
|
+
.description('Stop and remove the legacy cron-claude LaunchAgent daemon (if running)')
|
|
119
|
+
.action(() => {
|
|
120
|
+
const plistPath = join(homedir(), 'Library', 'LaunchAgents', 'com.cron-claude.daemon.plist');
|
|
121
|
+
try {
|
|
122
|
+
execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`, { stdio: 'ignore' });
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// ignore launchctl errors
|
|
126
|
+
}
|
|
127
|
+
if (existsSync(plistPath)) {
|
|
128
|
+
rmSync(plistPath);
|
|
129
|
+
}
|
|
130
|
+
console.log('Daemon stopped and removed.');
|
|
131
|
+
});
|
|
132
|
+
program
|
|
133
|
+
.command('agent-loop')
|
|
134
|
+
.description('Show instructions for running cron-claude in Claude agent loop mode')
|
|
135
|
+
.action(() => {
|
|
136
|
+
const jobs = listJobs();
|
|
137
|
+
console.log(`
|
|
138
|
+
Agent Loop Mode
|
|
139
|
+
===============
|
|
140
|
+
cron-claude v2 uses Claude itself as the cron executor — no background daemon needed.
|
|
141
|
+
|
|
142
|
+
QUICKSTART
|
|
143
|
+
----------
|
|
144
|
+
1. Install the MCP (once):
|
|
145
|
+
cron-claude mcp install
|
|
146
|
+
|
|
147
|
+
2. Paste this into a Claude Code session:
|
|
148
|
+
|
|
149
|
+
"Call start_loop to begin."
|
|
150
|
+
|
|
151
|
+
(Or use the built-in 'cron-loop' MCP prompt from the prompt picker)
|
|
152
|
+
|
|
153
|
+
3. Claude will call start_loop, receive operating instructions, then loop forever:
|
|
154
|
+
• Every 60s: call get_due_jobs (atomically claims due jobs)
|
|
155
|
+
• For each due job: spawn a SUBAGENT with the job prompt
|
|
156
|
+
• Subagent calls mark_job_run when done (releases the lock + writes log)
|
|
157
|
+
• Subagents run IN PARALLEL — no blocking
|
|
158
|
+
|
|
159
|
+
WHY SUBAGENTS?
|
|
160
|
+
--------------
|
|
161
|
+
Subagents get full tool access: Slack MCP, bash, files, APIs — everything.
|
|
162
|
+
No --print flag, no --dangerously-skip-permissions, no permission dialogs.
|
|
163
|
+
The disk is the source of truth. Survives Claude session restarts.
|
|
164
|
+
|
|
165
|
+
LOGS
|
|
166
|
+
----
|
|
167
|
+
~/.cron-claude/logs/
|
|
168
|
+
|
|
169
|
+
YOUR JOBS`);
|
|
170
|
+
if (jobs.length === 0) {
|
|
171
|
+
console.log(' (none — use "cron-claude add" to add one)');
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
for (const job of jobs) {
|
|
175
|
+
const lastRun = job.lastRunAt ? new Date(job.lastRunAt).toLocaleString() : 'never';
|
|
176
|
+
const statusStr = job.status === 'running' ? ' [RUNNING]' : '';
|
|
177
|
+
console.log(` [${job.enabled ? 'ON ' : 'OFF'}] ${job.id}${statusStr} | ${job.schedule} | last: ${lastRun}`);
|
|
178
|
+
console.log(` ${job.prompt.slice(0, 80)}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
console.log('');
|
|
182
|
+
});
|
|
111
183
|
const mcpCmd = program
|
|
112
184
|
.command('mcp')
|
|
113
185
|
.description('MCP server management');
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -5,24 +5,27 @@ export declare const JobSchema: z.ZodObject<{
|
|
|
5
5
|
prompt: z.ZodString;
|
|
6
6
|
model: z.ZodDefault<z.ZodString>;
|
|
7
7
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
lastRunAt: z.ZodDefault<z.ZodOptional<z.ZodNullable<z.ZodNumber>>>;
|
|
9
|
+
status: z.ZodDefault<z.ZodEnum<["idle", "running"]>>;
|
|
10
|
+
startedAt: z.ZodDefault<z.ZodOptional<z.ZodNullable<z.ZodNumber>>>;
|
|
10
11
|
}, "strip", z.ZodTypeAny, {
|
|
11
12
|
id: string;
|
|
12
13
|
schedule: string;
|
|
13
14
|
prompt: string;
|
|
14
15
|
model: string;
|
|
15
16
|
enabled: boolean;
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
lastRunAt: number | null;
|
|
18
|
+
status: "idle" | "running";
|
|
19
|
+
startedAt: number | null;
|
|
18
20
|
}, {
|
|
19
21
|
id: string;
|
|
20
22
|
schedule: string;
|
|
21
23
|
prompt: string;
|
|
22
24
|
model?: string | undefined;
|
|
23
25
|
enabled?: boolean | undefined;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
lastRunAt?: number | null | undefined;
|
|
27
|
+
status?: "idle" | "running" | undefined;
|
|
28
|
+
startedAt?: number | null | undefined;
|
|
26
29
|
}>;
|
|
27
30
|
export declare const ConfigSchema: z.ZodObject<{
|
|
28
31
|
jobs: z.ZodArray<z.ZodObject<{
|
|
@@ -31,24 +34,27 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
31
34
|
prompt: z.ZodString;
|
|
32
35
|
model: z.ZodDefault<z.ZodString>;
|
|
33
36
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
lastRunAt: z.ZodDefault<z.ZodOptional<z.ZodNullable<z.ZodNumber>>>;
|
|
38
|
+
status: z.ZodDefault<z.ZodEnum<["idle", "running"]>>;
|
|
39
|
+
startedAt: z.ZodDefault<z.ZodOptional<z.ZodNullable<z.ZodNumber>>>;
|
|
36
40
|
}, "strip", z.ZodTypeAny, {
|
|
37
41
|
id: string;
|
|
38
42
|
schedule: string;
|
|
39
43
|
prompt: string;
|
|
40
44
|
model: string;
|
|
41
45
|
enabled: boolean;
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
lastRunAt: number | null;
|
|
47
|
+
status: "idle" | "running";
|
|
48
|
+
startedAt: number | null;
|
|
44
49
|
}, {
|
|
45
50
|
id: string;
|
|
46
51
|
schedule: string;
|
|
47
52
|
prompt: string;
|
|
48
53
|
model?: string | undefined;
|
|
49
54
|
enabled?: boolean | undefined;
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
lastRunAt?: number | null | undefined;
|
|
56
|
+
status?: "idle" | "running" | undefined;
|
|
57
|
+
startedAt?: number | null | undefined;
|
|
52
58
|
}>, "many">;
|
|
53
59
|
}, "strip", z.ZodTypeAny, {
|
|
54
60
|
jobs: {
|
|
@@ -57,8 +63,9 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
57
63
|
prompt: string;
|
|
58
64
|
model: string;
|
|
59
65
|
enabled: boolean;
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
lastRunAt: number | null;
|
|
67
|
+
status: "idle" | "running";
|
|
68
|
+
startedAt: number | null;
|
|
62
69
|
}[];
|
|
63
70
|
}, {
|
|
64
71
|
jobs: {
|
|
@@ -67,8 +74,9 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
67
74
|
prompt: string;
|
|
68
75
|
model?: string | undefined;
|
|
69
76
|
enabled?: boolean | undefined;
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
lastRunAt?: number | null | undefined;
|
|
78
|
+
status?: "idle" | "running" | undefined;
|
|
79
|
+
startedAt?: number | null | undefined;
|
|
72
80
|
}[];
|
|
73
81
|
}>;
|
|
74
82
|
export type Job = z.infer<typeof JobSchema>;
|
|
@@ -83,3 +91,10 @@ export declare function addJob(job: Job, configPath?: string): Config;
|
|
|
83
91
|
export declare function removeJob(jobId: string, configPath?: string): Config;
|
|
84
92
|
export declare function listJobs(configPath?: string): Job[];
|
|
85
93
|
export declare function toggleJob(jobId: string, enabled: boolean, configPath?: string): Config;
|
|
94
|
+
export declare function updateJob(id: string, updates: Partial<Job>, configPath?: string): void;
|
|
95
|
+
/**
|
|
96
|
+
* Returns true if the job's cron schedule fired within the last 60 seconds.
|
|
97
|
+
* Does NOT check status or lastRunAt — callers handle those guards.
|
|
98
|
+
*/
|
|
99
|
+
export declare function isJobDue(job: Job, now?: number): boolean;
|
|
100
|
+
export declare function logExecution(jobId: string, output: string, error?: string): void;
|
package/dist/config.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { CronExpressionParser } from 'cron-parser';
|
|
4
5
|
export const JobSchema = z.object({
|
|
5
6
|
id: z.string().min(1),
|
|
6
7
|
schedule: z.string().min(1),
|
|
7
8
|
prompt: z.string().min(1),
|
|
8
9
|
model: z.string().default('sonnet'),
|
|
9
10
|
enabled: z.boolean().default(true),
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
lastRunAt: z.number().nullable().optional().default(null),
|
|
12
|
+
status: z.enum(['idle', 'running']).default('idle'),
|
|
13
|
+
startedAt: z.number().nullable().optional().default(null),
|
|
12
14
|
});
|
|
13
15
|
export const ConfigSchema = z.object({
|
|
14
16
|
jobs: z.array(JobSchema),
|
|
@@ -84,4 +86,43 @@ export function toggleJob(jobId, enabled, configPath) {
|
|
|
84
86
|
saveConfig(config, configPath);
|
|
85
87
|
return config;
|
|
86
88
|
}
|
|
89
|
+
export function updateJob(id, updates, configPath) {
|
|
90
|
+
const config = loadConfig(configPath);
|
|
91
|
+
const idx = config.jobs.findIndex((j) => j.id === id);
|
|
92
|
+
if (idx === -1)
|
|
93
|
+
throw new Error(`Job "${id}" not found`);
|
|
94
|
+
config.jobs[idx] = { ...config.jobs[idx], ...updates };
|
|
95
|
+
saveConfig(config, configPath);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Returns true if the job's cron schedule fired within the last 60 seconds.
|
|
99
|
+
* Does NOT check status or lastRunAt — callers handle those guards.
|
|
100
|
+
*/
|
|
101
|
+
export function isJobDue(job, now = Date.now()) {
|
|
102
|
+
if (!job.enabled)
|
|
103
|
+
return false;
|
|
104
|
+
try {
|
|
105
|
+
const interval = CronExpressionParser.parse(job.schedule);
|
|
106
|
+
const prev = interval.prev().toDate();
|
|
107
|
+
return now - prev.getTime() < 60000;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export function logExecution(jobId, output, error) {
|
|
114
|
+
const logsDir = getLogsDir();
|
|
115
|
+
if (!fs.existsSync(logsDir))
|
|
116
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
117
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
118
|
+
const logFile = path.join(logsDir, `${jobId}_${timestamp}.log`);
|
|
119
|
+
const lines = [
|
|
120
|
+
`Job: ${jobId}`,
|
|
121
|
+
`Time: ${new Date().toISOString()}`,
|
|
122
|
+
error ? `Error: ${error}` : 'Status: success',
|
|
123
|
+
'---',
|
|
124
|
+
output,
|
|
125
|
+
];
|
|
126
|
+
fs.writeFileSync(logFile, lines.join('\n'), 'utf-8');
|
|
127
|
+
}
|
|
87
128
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp-server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { createRequire } from 'module';
|
|
6
|
-
import { addJob, removeJob, listJobs, toggleJob, JobSchema } from './config.js';
|
|
6
|
+
import { addJob, removeJob, listJobs, toggleJob, updateJob, isJobDue, logExecution, JobSchema, } from './config.js';
|
|
7
7
|
const require = createRequire(import.meta.url);
|
|
8
8
|
const { version } = require('../package.json');
|
|
9
9
|
export const server = new McpServer({
|
|
@@ -11,14 +11,12 @@ export const server = new McpServer({
|
|
|
11
11
|
version,
|
|
12
12
|
});
|
|
13
13
|
// add_cron
|
|
14
|
-
server.tool('add_cron', 'Add a new scheduled Claude
|
|
14
|
+
server.tool('add_cron', 'Add a new scheduled Claude job (agent loop mode)', {
|
|
15
15
|
id: z.string().min(1).describe('Unique job identifier'),
|
|
16
16
|
schedule: z.string().min(1).describe('Cron schedule expression (e.g. "0 14 * * *")'),
|
|
17
17
|
prompt: z.string().min(1).describe('Prompt to send to Claude'),
|
|
18
18
|
model: z.string().optional().describe('Claude model to use (default: sonnet)'),
|
|
19
19
|
disabled: z.boolean().optional().describe('Create job in disabled state'),
|
|
20
|
-
headless: z.boolean().optional().describe('true (default) = --print mode (text only). false = full agent mode with tool use, bash, file access'),
|
|
21
|
-
dangerouslySkipPermissions: z.boolean().optional().describe('Pass --dangerously-skip-permissions to claude, skipping all permission prompts. Use with caution.'),
|
|
22
20
|
}, (args) => {
|
|
23
21
|
try {
|
|
24
22
|
const job = JobSchema.parse({
|
|
@@ -27,8 +25,6 @@ server.tool('add_cron', 'Add a new scheduled Claude CLI job', {
|
|
|
27
25
|
prompt: args.prompt,
|
|
28
26
|
model: args.model ?? 'sonnet',
|
|
29
27
|
enabled: !args.disabled,
|
|
30
|
-
headless: args.headless ?? true,
|
|
31
|
-
dangerouslySkipPermissions: args.dangerouslySkipPermissions ?? false,
|
|
32
28
|
});
|
|
33
29
|
addJob(job);
|
|
34
30
|
return {
|
|
@@ -43,7 +39,7 @@ server.tool('add_cron', 'Add a new scheduled Claude CLI job', {
|
|
|
43
39
|
}
|
|
44
40
|
});
|
|
45
41
|
// list_crons
|
|
46
|
-
server.tool('list_crons', 'List all scheduled Claude
|
|
42
|
+
server.tool('list_crons', 'List all scheduled Claude jobs', {}, (_args) => {
|
|
47
43
|
const jobs = listJobs();
|
|
48
44
|
return {
|
|
49
45
|
content: [
|
|
@@ -55,7 +51,7 @@ server.tool('list_crons', 'List all scheduled Claude CLI jobs', {}, (_args) => {
|
|
|
55
51
|
};
|
|
56
52
|
});
|
|
57
53
|
// remove_cron
|
|
58
|
-
server.tool('remove_cron', 'Remove a scheduled Claude
|
|
54
|
+
server.tool('remove_cron', 'Remove a scheduled Claude job by ID', {
|
|
59
55
|
id: z.string().min(1).describe('Job ID to remove'),
|
|
60
56
|
}, (args) => {
|
|
61
57
|
try {
|
|
@@ -72,7 +68,7 @@ server.tool('remove_cron', 'Remove a scheduled Claude CLI job by ID', {
|
|
|
72
68
|
}
|
|
73
69
|
});
|
|
74
70
|
// enable_cron
|
|
75
|
-
server.tool('enable_cron', 'Enable a scheduled Claude
|
|
71
|
+
server.tool('enable_cron', 'Enable a scheduled Claude job by ID', {
|
|
76
72
|
id: z.string().min(1).describe('Job ID to enable'),
|
|
77
73
|
}, (args) => {
|
|
78
74
|
try {
|
|
@@ -89,7 +85,7 @@ server.tool('enable_cron', 'Enable a scheduled Claude CLI job by ID', {
|
|
|
89
85
|
}
|
|
90
86
|
});
|
|
91
87
|
// disable_cron
|
|
92
|
-
server.tool('disable_cron', 'Disable a scheduled Claude
|
|
88
|
+
server.tool('disable_cron', 'Disable a scheduled Claude job by ID', {
|
|
93
89
|
id: z.string().min(1).describe('Job ID to disable'),
|
|
94
90
|
}, (args) => {
|
|
95
91
|
try {
|
|
@@ -110,11 +106,97 @@ server.tool('get_status', 'Get status of all cron-claude jobs (counts)', {}, (_a
|
|
|
110
106
|
const jobs = listJobs();
|
|
111
107
|
const enabled = jobs.filter((j) => j.enabled).length;
|
|
112
108
|
const disabled = jobs.filter((j) => !j.enabled).length;
|
|
113
|
-
const
|
|
109
|
+
const running = jobs.filter((j) => j.status === 'running').length;
|
|
110
|
+
const status = { total: jobs.length, enabled, disabled, running };
|
|
114
111
|
return {
|
|
115
112
|
content: [{ type: 'text', text: JSON.stringify(status, null, 2) }],
|
|
116
113
|
};
|
|
117
114
|
});
|
|
115
|
+
// get_due_jobs — atomically claims due jobs to prevent duplicate execution
|
|
116
|
+
server.tool('get_due_jobs', 'Returns jobs that are due to run right now. Atomically marks them as "running" before returning so concurrent calls cannot pick up the same job. Call this every 60 seconds in agent loop mode.', {}, (_args) => {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
// Find jobs that are due and not already claimed
|
|
119
|
+
const dueJobs = listJobs().filter((job) => {
|
|
120
|
+
if (!job.enabled)
|
|
121
|
+
return false;
|
|
122
|
+
if (job.status === 'running')
|
|
123
|
+
return false; // already claimed
|
|
124
|
+
if (job.lastRunAt != null && now - job.lastRunAt < 55000)
|
|
125
|
+
return false;
|
|
126
|
+
return isJobDue(job, now);
|
|
127
|
+
});
|
|
128
|
+
// Atomically mark all as 'running' BEFORE returning
|
|
129
|
+
for (const job of dueJobs) {
|
|
130
|
+
updateJob(job.id, { status: 'running', startedAt: now });
|
|
131
|
+
}
|
|
132
|
+
// Return the pre-claim snapshots (with status still showing idle for context)
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: 'text', text: JSON.stringify(dueJobs, null, 2) }],
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
// mark_job_run — releases the running lock and writes a log
|
|
138
|
+
server.tool('mark_job_run', 'Records that a job was executed. Clears the running status and writes a log. Call after completing each job returned by get_due_jobs.', {
|
|
139
|
+
id: z.string().min(1).describe('Job ID that was run'),
|
|
140
|
+
output: z.string().optional().describe('Output or summary of what was done'),
|
|
141
|
+
error: z.string().optional().describe('Error message if the job failed'),
|
|
142
|
+
}, (args) => {
|
|
143
|
+
try {
|
|
144
|
+
updateJob(args.id, {
|
|
145
|
+
status: 'idle',
|
|
146
|
+
lastRunAt: Date.now(),
|
|
147
|
+
startedAt: null,
|
|
148
|
+
});
|
|
149
|
+
logExecution(args.id, args.output ?? '', args.error);
|
|
150
|
+
return {
|
|
151
|
+
content: [{ type: 'text', text: `Job "${args.id}" recorded.` }],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
return {
|
|
156
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
157
|
+
isError: true,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
// start_loop — returns operating instructions for the agent
|
|
162
|
+
server.tool('start_loop', 'Start the agent loop. Call once to get operating instructions for running cron-claude as an autonomous daemon.', {}, (_args) => {
|
|
163
|
+
const jobs = listJobs().filter((j) => j.enabled);
|
|
164
|
+
const jobList = jobs.length === 0
|
|
165
|
+
? '(no enabled jobs — add some with cron-claude add or the add_cron tool)'
|
|
166
|
+
: jobs.map((j) => ` • ${j.id} [${j.schedule}]: ${j.prompt.slice(0, 80)}`).join('\n');
|
|
167
|
+
const instructions = `You are the cron-claude agent daemon. Follow these instructions continuously:
|
|
168
|
+
|
|
169
|
+
Every 60 seconds:
|
|
170
|
+
1. Call get_due_jobs — returns due jobs AND atomically marks them "running" (prevents duplicates)
|
|
171
|
+
2. For each job returned, spawn a SUBAGENT with this prompt:
|
|
172
|
+
"Execute this scheduled task: {job.prompt}
|
|
173
|
+
When done, call mark_job_run with id="{job.id}", output=<what you did>, error=<if failed>"
|
|
174
|
+
3. Run subagents IN PARALLEL — don't wait for one before starting the next
|
|
175
|
+
4. Wait 60 seconds, go back to step 1
|
|
176
|
+
|
|
177
|
+
Rules:
|
|
178
|
+
- Always delegate to subagents, never execute jobs yourself in this loop
|
|
179
|
+
- Subagents MUST call mark_job_run when done (releases the disk lock)
|
|
180
|
+
- If a job is stuck "running" >10min, call mark_job_run with error="timeout" to reset it
|
|
181
|
+
- Loop indefinitely until explicitly told to stop
|
|
182
|
+
|
|
183
|
+
${jobs.length} enabled job(s) configured:
|
|
184
|
+
${jobList}
|
|
185
|
+
|
|
186
|
+
Starting now.`;
|
|
187
|
+
return {
|
|
188
|
+
content: [{ type: 'text', text: instructions }],
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
// cron-loop prompt — quick entry point
|
|
192
|
+
server.prompt('cron-loop', 'Start cron-claude in agent loop mode', () => ({
|
|
193
|
+
messages: [
|
|
194
|
+
{
|
|
195
|
+
role: 'user',
|
|
196
|
+
content: { type: 'text', text: 'Call start_loop to begin.' },
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
}));
|
|
118
200
|
// Start the server if run directly (not when imported as a module in tests)
|
|
119
201
|
if (process.env.VITEST !== 'true') {
|
|
120
202
|
const transport = new StdioServerTransport();
|
package/dist/mcp-server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,
|
|
1
|
+
{"version":3,"file":"mcp-server.js","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EACL,MAAM,EACN,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,SAAS,GACV,MAAM,aAAa,CAAC;AAErB,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAEtE,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAClC,IAAI,EAAE,aAAa;IACnB,OAAO;CACR,CAAC,CAAC;AAEH,WAAW;AACX,MAAM,CAAC,IAAI,CACT,UAAU,EACV,kDAAkD,EAClD;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IACvD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,8CAA8C,CAAC;IACpF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,0BAA0B,CAAC;IAC9D,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,uCAAuC,CAAC;IAC9E,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;CAC1E,EACD,CAAC,IAAI,EAAE,EAAE;IACP,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC;YAC1B,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,QAAQ;YAC7B,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,CAAC;QACZ,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,GAAG,CAAC,EAAE,uBAAuB,EAAE,CAAC;SAClF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAW,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,aAAa;AACb,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,gCAAgC,EAChC,EAAE,EACF,CAAC,KAAK,EAAE,EAAE;IACR,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;IACxB,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;aACpC;SACF;KACF,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,cAAc;AACd,MAAM,CAAC,IAAI,CACT,aAAa,EACb,qCAAqC,EACrC;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;CACnD,EACD,CAAC,IAAI,EAAE,EAAE;IACP,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC;SACxE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAW,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,cAAc;AACd,MAAM,CAAC,IAAI,CACT,aAAa,EACb,qCAAqC,EACrC;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC;CACnD,EACD,CAAC,IAAI,EAAE,EAAE;IACP,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACzB,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC;SACxE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAW,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,eAAe;AACf,MAAM,CAAC,IAAI,CACT,cAAc,EACd,sCAAsC,EACtC;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;CACpD,EACD,CAAC,IAAI,EAAE,EAAE;IACP,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC1B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,IAAI,CAAC,EAAE,aAAa,EAAE,CAAC;SACzE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAW,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,aAAa;AACb,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,6CAA6C,EAC7C,EAAE,EACF,CAAC,KAAK,EAAE,EAAE;IACR,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;IACxB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACvD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAClE,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAClE,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAC5E,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,2EAA2E;AAC3E,MAAM,CAAC,IAAI,CACT,cAAc,EACd,iMAAiM,EACjM,EAAE,EACF,CAAC,KAAK,EAAE,EAAE;IACR,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,iDAAiD;IACjD,MAAM,OAAO,GAAG,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QACxC,IAAI,CAAC,GAAG,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC/B,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,CAAC,kBAAkB;QAC9D,IAAI,GAAG,CAAC,SAAS,IAAI,IAAI,IAAI,GAAG,GAAG,GAAG,CAAC,SAAS,GAAG,KAAK;YAAE,OAAO,KAAK,CAAC;QACvE,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,oDAAoD;IACpD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,8EAA8E;IAC9E,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAC7E,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,4DAA4D;AAC5D,MAAM,CAAC,IAAI,CACT,cAAc,EACd,uIAAuI,EACvI;IACE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IACrD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IAC5E,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;CACzE,EACD,CAAC,IAAI,EAAE,EAAE;IACP,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE;YACjB,MAAM,EAAE,MAAM;YACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,IAAI,CAAC,EAAE,aAAa,EAAE,CAAC;SACzE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,UAAW,GAAa,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CACF,CAAC;AAEF,4DAA4D;AAC5D,MAAM,CAAC,IAAI,CACT,YAAY,EACZ,gHAAgH,EAChH,EAAE,EACF,CAAC,KAAK,EAAE,EAAE;IACR,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,KAAK,CAAC;QAC/B,CAAC,CAAC,wEAAwE;QAC1E,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAExF,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;EAgBvB,IAAI,CAAC,MAAM;EACX,OAAO;;cAEK,CAAC;IAEX,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;KACzD,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,uCAAuC;AACvC,MAAM,CAAC,MAAM,CACX,WAAW,EACX,sCAAsC,EACtC,GAAG,EAAE,CAAC,CAAC;IACL,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,MAAe;YACrB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,2BAA2B,EAAE;SACtE;KACF;CACF,CAAC,CACH,CAAC;AAEF,4EAA4E;AAC5E,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAC5B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cron-claude",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Schedule Claude jobs in agent loop mode — no daemon, full tool access",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "dist/
|
|
6
|
+
"main": "dist/cli.js",
|
|
7
7
|
"author": "tygi",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": {
|
|
@@ -29,10 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"dist/",
|
|
32
|
-
"install.sh",
|
|
33
32
|
"README.md",
|
|
34
|
-
"LICENSE"
|
|
35
|
-
"postinstall.js"
|
|
33
|
+
"LICENSE"
|
|
36
34
|
],
|
|
37
35
|
"bin": {
|
|
38
36
|
"cron-claude": "dist/cli.js",
|
|
@@ -40,18 +38,15 @@
|
|
|
40
38
|
},
|
|
41
39
|
"scripts": {
|
|
42
40
|
"build": "tsc",
|
|
43
|
-
"dev": "tsx src/daemon.ts",
|
|
44
|
-
"start": "node dist/daemon.js",
|
|
45
41
|
"cli": "tsx src/cli.ts",
|
|
46
42
|
"test": "vitest run --coverage",
|
|
47
43
|
"test:watch": "vitest",
|
|
48
|
-
"install-service": "bash install.sh",
|
|
49
|
-
"postinstall": "node postinstall.js",
|
|
50
44
|
"prepublishOnly": "npm run build"
|
|
51
45
|
},
|
|
52
46
|
"dependencies": {
|
|
53
47
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
54
48
|
"commander": "^12.1.0",
|
|
49
|
+
"cron-parser": "^5.5.0",
|
|
55
50
|
"node-cron": "^3.0.3",
|
|
56
51
|
"zod": "^3.23.8"
|
|
57
52
|
},
|
package/install.sh
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
PLIST_NAME="com.cron-claude.daemon"
|
|
5
|
-
PLIST_PATH="$HOME/Library/LaunchAgents/${PLIST_NAME}.plist"
|
|
6
|
-
INSTALL_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
-
LOG_DIR="$HOME/.cron-claude/logs"
|
|
8
|
-
|
|
9
|
-
mkdir -p "$HOME/Library/LaunchAgents"
|
|
10
|
-
mkdir -p "$LOG_DIR"
|
|
11
|
-
|
|
12
|
-
cat > "$PLIST_PATH" << EOF
|
|
13
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
14
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
15
|
-
<plist version="1.0">
|
|
16
|
-
<dict>
|
|
17
|
-
<key>Label</key>
|
|
18
|
-
<string>${PLIST_NAME}</string>
|
|
19
|
-
<key>ProgramArguments</key>
|
|
20
|
-
<array>
|
|
21
|
-
<string>$(which node)</string>
|
|
22
|
-
<string>${INSTALL_DIR}/dist/daemon.js</string>
|
|
23
|
-
</array>
|
|
24
|
-
<key>RunAtLoad</key>
|
|
25
|
-
<true/>
|
|
26
|
-
<key>KeepAlive</key>
|
|
27
|
-
<true/>
|
|
28
|
-
<key>StandardOutPath</key>
|
|
29
|
-
<string>${LOG_DIR}/daemon-stdout.log</string>
|
|
30
|
-
<key>StandardErrorPath</key>
|
|
31
|
-
<string>${LOG_DIR}/daemon-stderr.log</string>
|
|
32
|
-
<key>WorkingDirectory</key>
|
|
33
|
-
<string>${INSTALL_DIR}</string>
|
|
34
|
-
<key>EnvironmentVariables</key>
|
|
35
|
-
<dict>
|
|
36
|
-
<key>PATH</key>
|
|
37
|
-
<string>/usr/local/bin:/usr/bin:/bin:$HOME/.local/bin</string>
|
|
38
|
-
<key>HOME</key>
|
|
39
|
-
<string>${HOME}</string>
|
|
40
|
-
<key>USER</key>
|
|
41
|
-
<string>${USER}</string>
|
|
42
|
-
<key>LOGNAME</key>
|
|
43
|
-
<string>${USER}</string>
|
|
44
|
-
</dict>
|
|
45
|
-
</dict>
|
|
46
|
-
</plist>
|
|
47
|
-
EOF
|
|
48
|
-
|
|
49
|
-
echo "LaunchAgent plist written to: $PLIST_PATH"
|
|
50
|
-
|
|
51
|
-
# Unload if already loaded
|
|
52
|
-
launchctl unload "$PLIST_PATH" 2>/dev/null || true
|
|
53
|
-
|
|
54
|
-
# Load the agent
|
|
55
|
-
launchctl load "$PLIST_PATH"
|
|
56
|
-
|
|
57
|
-
echo "cron-claude daemon installed and started."
|
|
58
|
-
echo "To stop: launchctl unload $PLIST_PATH"
|
|
59
|
-
echo "To restart: launchctl unload $PLIST_PATH && launchctl load $PLIST_PATH"
|
package/postinstall.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { execSync } from 'child_process';
|
|
3
|
-
import { join, dirname } from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
if (process.platform !== 'darwin') process.exit(0);
|
|
7
|
-
if (!process.env.npm_config_global) process.exit(0);
|
|
8
|
-
try {
|
|
9
|
-
console.log('cron-claude: installing macOS LaunchAgent...');
|
|
10
|
-
execSync(`bash "${join(__dirname, 'install.sh')}"`, { stdio: 'inherit' });
|
|
11
|
-
} catch (err) {
|
|
12
|
-
console.warn('cron-claude: LaunchAgent setup failed (run `cron-claude install-service` manually)');
|
|
13
|
-
}
|